123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470 |
- import { AuditoryDescription } from '../audio/auditory_description.js';
- import * as AuralRendering from '../audio/aural_rendering.js';
- import * as DomUtil from '../common/dom_util.js';
- import { setup as EngineSetup } from '../common/engine_setup.js';
- import { KeyCode } from '../common/event_util.js';
- import { Attribute } from '../enrich_mathml/enrich_attr.js';
- import { LOCALE } from '../l10n/locale.js';
- import { Grammar } from '../rule_engine/grammar.js';
- import { SemanticSkeleton } from '../semantic_tree/semantic_skeleton.js';
- import * as SpeechGeneratorFactory from '../speech_generator/speech_generator_factory.js';
- import * as SpeechGeneratorUtil from '../speech_generator/speech_generator_util.js';
- import { Focus } from './focus.js';
- import { RebuildStree } from './rebuild_stree.js';
- import { WalkerMoves, WalkerState } from './walker.js';
- import * as WalkerUtil from './walker_util.js';
- import * as XpathUtil from '../common/xpath_util.js';
- export class AbstractWalker {
- constructor(node, generator, highlighter, xml) {
- this.node = node;
- this.generator = generator;
- this.highlighter = highlighter;
- this.modifier = false;
- this.keyMapping = new Map([
- [KeyCode.UP, this.up.bind(this)],
- [KeyCode.DOWN, this.down.bind(this)],
- [KeyCode.RIGHT, this.right.bind(this)],
- [KeyCode.LEFT, this.left.bind(this)],
- [KeyCode.TAB, this.repeat.bind(this)],
- [KeyCode.DASH, this.expand.bind(this)],
- [KeyCode.SPACE, this.depth.bind(this)],
- [KeyCode.HOME, this.home.bind(this)],
- [KeyCode.X, this.summary.bind(this)],
- [KeyCode.Z, this.detail.bind(this)],
- [KeyCode.V, this.virtualize.bind(this)],
- [KeyCode.P, this.previous.bind(this)],
- [KeyCode.U, this.undo.bind(this)],
- [KeyCode.LESS, this.previousRules.bind(this)],
- [KeyCode.GREATER, this.nextRules.bind(this)]
- ]);
- this.cursors = [];
- this.xml_ = null;
- this.rebuilt_ = null;
- this.focus_ = null;
- this.active_ = false;
- if (this.node.id) {
- this.id = this.node.id;
- }
- else if (this.node.hasAttribute(AbstractWalker.SRE_ID_ATTR)) {
- this.id = this.node.getAttribute(AbstractWalker.SRE_ID_ATTR);
- }
- else {
- this.node.setAttribute(AbstractWalker.SRE_ID_ATTR, AbstractWalker.ID_COUNTER.toString());
- this.id = AbstractWalker.ID_COUNTER++;
- }
- this.rootNode = WalkerUtil.getSemanticRoot(node);
- this.rootId = this.rootNode.getAttribute(Attribute.ID);
- this.xmlString_ = xml;
- this.moved = WalkerMoves.ENTER;
- }
- getXml() {
- if (!this.xml_) {
- this.xml_ = DomUtil.parseInput(this.xmlString_);
- }
- return this.xml_;
- }
- getRebuilt() {
- if (!this.rebuilt_) {
- this.rebuildStree();
- }
- return this.rebuilt_;
- }
- isActive() {
- return this.active_;
- }
- activate() {
- if (this.isActive()) {
- return;
- }
- this.toggleActive_();
- }
- deactivate() {
- if (!this.isActive()) {
- return;
- }
- WalkerState.setState(this.id, this.primaryId());
- this.toggleActive_();
- }
- getFocus(update = false) {
- if (this.rootId === null) {
- this.getRebuilt();
- }
- if (!this.focus_) {
- this.focus_ = this.singletonFocus(this.rootId);
- }
- if (update) {
- this.updateFocus();
- }
- return this.focus_;
- }
- setFocus(focus) {
- this.focus_ = focus;
- }
- getDepth() {
- return this.levels.depth() - 1;
- }
- isSpeech() {
- return this.generator.modality === Attribute.SPEECH;
- }
- focusDomNodes() {
- return this.getFocus().getDomNodes();
- }
- focusSemanticNodes() {
- return this.getFocus().getSemanticNodes();
- }
- speech() {
- const nodes = this.focusDomNodes();
- if (!nodes.length) {
- return '';
- }
- const special = this.specialMove();
- if (special !== null) {
- return special;
- }
- switch (this.moved) {
- case WalkerMoves.DEPTH:
- return this.depth_();
- case WalkerMoves.SUMMARY:
- return this.summary_();
- case WalkerMoves.DETAIL:
- return this.detail_();
- default: {
- const speech = [];
- const snodes = this.focusSemanticNodes();
- for (let i = 0, l = nodes.length; i < l; i++) {
- const node = nodes[i];
- const snode = snodes[i];
- speech.push(node
- ? this.generator.getSpeech(node, this.getXml(), this.node)
- : SpeechGeneratorUtil.recomputeMarkup(snode));
- }
- return this.mergePrefix_(speech);
- }
- }
- }
- move(key) {
- const direction = this.keyMapping.get(key);
- if (!direction) {
- return null;
- }
- const focus = direction();
- if (!focus || focus === this.getFocus()) {
- return false;
- }
- this.setFocus(focus);
- if (this.moved === WalkerMoves.HOME) {
- this.levels = this.initLevels();
- }
- return true;
- }
- up() {
- this.moved = WalkerMoves.UP;
- return this.getFocus();
- }
- down() {
- this.moved = WalkerMoves.DOWN;
- return this.getFocus();
- }
- left() {
- this.moved = WalkerMoves.LEFT;
- return this.getFocus();
- }
- right() {
- this.moved = WalkerMoves.RIGHT;
- return this.getFocus();
- }
- repeat() {
- this.moved = WalkerMoves.REPEAT;
- return this.getFocus().clone();
- }
- depth() {
- this.moved = this.isSpeech() ? WalkerMoves.DEPTH : WalkerMoves.REPEAT;
- return this.getFocus().clone();
- }
- home() {
- this.moved = WalkerMoves.HOME;
- const focus = this.singletonFocus(this.rootId);
- return focus;
- }
- getBySemanticId(id) {
- return WalkerUtil.getBySemanticId(this.node, id);
- }
- primaryId() {
- return this.getFocus().getSemanticPrimary().id.toString();
- }
- expand() {
- const primary = this.getFocus().getDomPrimary();
- const expandable = this.actionable_(primary);
- if (!expandable) {
- return this.getFocus();
- }
- this.moved = WalkerMoves.EXPAND;
- expandable.dispatchEvent(new Event('click'));
- return this.getFocus().clone();
- }
- expandable(node) {
- const parent = !!this.actionable_(node);
- return parent && node.childNodes.length === 0;
- }
- collapsible(node) {
- const parent = !!this.actionable_(node);
- return parent && node.childNodes.length > 0;
- }
- restoreState() {
- if (!this.highlighter) {
- return;
- }
- const state = WalkerState.getState(this.id);
- if (!state) {
- return;
- }
- let node = this.getRebuilt().nodeDict[state];
- const path = [];
- while (node) {
- path.push(node.id);
- node = node.parent;
- }
- path.pop();
- while (path.length > 0) {
- this.down();
- const id = path.pop();
- const focus = this.findFocusOnLevel(id);
- if (!focus) {
- break;
- }
- this.setFocus(focus);
- }
- this.moved = WalkerMoves.ENTER;
- }
- updateFocus() {
- this.setFocus(Focus.factory(this.getFocus().getSemanticPrimary().id.toString(), this.getFocus()
- .getSemanticNodes()
- .map((x) => x.id.toString()), this.getRebuilt(), this.node));
- }
- rebuildStree() {
- this.rebuilt_ = new RebuildStree(this.getXml());
- this.rootId = this.rebuilt_.stree.root.id.toString();
- this.generator.setRebuilt(this.rebuilt_);
- this.skeleton = SemanticSkeleton.fromTree(this.rebuilt_.stree);
- this.skeleton.populate();
- this.focus_ = this.singletonFocus(this.rootId);
- this.levels = this.initLevels();
- SpeechGeneratorUtil.connectMactions(this.node, this.getXml(), this.rebuilt_.xml);
- }
- previousLevel() {
- const dnode = this.getFocus().getDomPrimary();
- return dnode
- ? WalkerUtil.getAttribute(dnode, Attribute.PARENT)
- : this.getFocus().getSemanticPrimary().parent.id.toString();
- }
- nextLevel() {
- const dnode = this.getFocus().getDomPrimary();
- let children;
- let content;
- if (dnode) {
- children = WalkerUtil.splitAttribute(WalkerUtil.getAttribute(dnode, Attribute.CHILDREN));
- content = WalkerUtil.splitAttribute(WalkerUtil.getAttribute(dnode, Attribute.CONTENT));
- const type = WalkerUtil.getAttribute(dnode, Attribute.TYPE);
- const role = WalkerUtil.getAttribute(dnode, Attribute.ROLE);
- return this.combineContentChildren(type, role, content, children);
- }
- const toIds = (x) => x.id.toString();
- const snode = this.getRebuilt().nodeDict[this.primaryId()];
- children = snode.childNodes.map(toIds);
- content = snode.contentNodes.map(toIds);
- if (children.length === 0) {
- return [];
- }
- return this.combineContentChildren(snode.type, snode.role, content, children);
- }
- singletonFocus(id) {
- this.getRebuilt();
- const ids = this.retrieveVisuals(id);
- return this.focusFromId(id, ids);
- }
- retrieveVisuals(id) {
- if (!this.skeleton) {
- return [id];
- }
- const num = parseInt(id, 10);
- const semStree = this.skeleton.subtreeNodes(num);
- if (!semStree.length) {
- return [id];
- }
- semStree.unshift(num);
- const mmlStree = {};
- const result = [];
- XpathUtil.updateEvaluator(this.getXml());
- for (const child of semStree) {
- if (mmlStree[child]) {
- continue;
- }
- result.push(child.toString());
- mmlStree[child] = true;
- this.subtreeIds(child, mmlStree);
- }
- return result;
- }
- subtreeIds(id, nodes) {
- const xmlRoot = XpathUtil.evalXPath(`//*[@data-semantic-id="${id}"]`, this.getXml());
- const xpath = XpathUtil.evalXPath('*//@data-semantic-id', xmlRoot[0]);
- xpath.forEach((x) => (nodes[parseInt(x.textContent, 10)] = true));
- }
- focusFromId(id, ids) {
- return Focus.factory(id, ids, this.getRebuilt(), this.node);
- }
- summary() {
- this.moved = this.isSpeech() ? WalkerMoves.SUMMARY : WalkerMoves.REPEAT;
- return this.getFocus().clone();
- }
- detail() {
- this.moved = this.isSpeech() ? WalkerMoves.DETAIL : WalkerMoves.REPEAT;
- return this.getFocus().clone();
- }
- specialMove() {
- return null;
- }
- virtualize(opt_undo) {
- this.cursors.push({
- focus: this.getFocus(),
- levels: this.levels,
- undo: opt_undo || !this.cursors.length
- });
- this.levels = this.levels.clone();
- return this.getFocus().clone();
- }
- previous() {
- const previous = this.cursors.pop();
- if (!previous) {
- return this.getFocus();
- }
- this.levels = previous.levels;
- return previous.focus;
- }
- undo() {
- let previous;
- do {
- previous = this.cursors.pop();
- } while (previous && !previous.undo);
- if (!previous) {
- return this.getFocus();
- }
- this.levels = previous.levels;
- return previous.focus;
- }
- update(options) {
- EngineSetup(options).then(() => SpeechGeneratorFactory.generator('Tree').getSpeech(this.node, this.getXml()));
- }
- nextRules() {
- this.generator.nextRules();
- const options = this.generator.getOptions();
- if (options.modality !== 'speech') {
- return this.getFocus();
- }
- this.update(options);
- this.moved = WalkerMoves.REPEAT;
- return this.getFocus().clone();
- }
- previousRules() {
- var _a;
- this.generator.nextStyle((_a = this.getFocus().getSemanticPrimary()) === null || _a === void 0 ? void 0 : _a.id.toString());
- const options = this.generator.getOptions();
- if (options.modality !== 'speech') {
- return this.getFocus();
- }
- this.update(options);
- this.moved = WalkerMoves.REPEAT;
- return this.getFocus().clone();
- }
- refocus() {
- let focus = this.getFocus();
- let last;
- while (!focus.getNodes().length) {
- last = this.levels.peek();
- const up = this.up();
- if (!up) {
- break;
- }
- this.setFocus(up);
- focus = this.getFocus(true);
- }
- this.levels.push(last);
- this.setFocus(focus);
- }
- toggleActive_() {
- this.active_ = !this.active_;
- }
- mergePrefix_(speech, pre = []) {
- const prefix = this.isSpeech() ? this.prefix_() : '';
- if (prefix) {
- speech.unshift(prefix);
- }
- const postfix = this.isSpeech() ? this.postfix_() : '';
- if (postfix) {
- speech.push(postfix);
- }
- return AuralRendering.finalize(AuralRendering.merge(pre.concat(speech)));
- }
- prefix_() {
- const nodes = this.getFocus().getDomNodes();
- const snodes = this.getFocus().getSemanticNodes();
- return nodes[0]
- ? WalkerUtil.getAttribute(nodes[0], Attribute.PREFIX)
- : SpeechGeneratorUtil.retrievePrefix(snodes[0]);
- }
- postfix_() {
- const nodes = this.getFocus().getDomNodes();
- return nodes[0]
- ? WalkerUtil.getAttribute(nodes[0], Attribute.POSTFIX)
- : '';
- }
- depth_() {
- const oldDepth = Grammar.getInstance().getParameter('depth');
- Grammar.getInstance().setParameter('depth', true);
- const primary = this.getFocus().getDomPrimary();
- const expand = this.expandable(primary)
- ? LOCALE.MESSAGES.navigate.EXPANDABLE
- : this.collapsible(primary)
- ? LOCALE.MESSAGES.navigate.COLLAPSIBLE
- : '';
- const level = LOCALE.MESSAGES.navigate.LEVEL + ' ' + this.getDepth();
- const snodes = this.getFocus().getSemanticNodes();
- const prefix = SpeechGeneratorUtil.retrievePrefix(snodes[0]);
- const audio = [
- new AuditoryDescription({ text: level, personality: {} }),
- new AuditoryDescription({ text: prefix, personality: {} }),
- new AuditoryDescription({ text: expand, personality: {} })
- ];
- Grammar.getInstance().setParameter('depth', oldDepth);
- return AuralRendering.finalize(AuralRendering.markup(audio));
- }
- actionable_(node) {
- const parent = node === null || node === void 0 ? void 0 : node.parentNode;
- return parent && this.highlighter.isMactionNode(parent) ? parent : null;
- }
- summary_() {
- const sprimary = this.getFocus().getSemanticPrimary();
- const sid = sprimary.id.toString();
- const snode = this.getRebuilt().xml.getAttribute('id') === sid
- ? this.getRebuilt().xml
- : DomUtil.querySelectorAllByAttrValue(this.getRebuilt().xml, 'id', sid)[0];
- const summary = SpeechGeneratorUtil.retrieveSummary(snode);
- const speech = this.mergePrefix_([summary]);
- return speech;
- }
- detail_() {
- const sprimary = this.getFocus().getSemanticPrimary();
- const sid = sprimary.id.toString();
- const snode = this.getRebuilt().xml.getAttribute('id') === sid
- ? this.getRebuilt().xml
- : DomUtil.querySelectorAllByAttrValue(this.getRebuilt().xml, 'id', sid)[0];
- const oldAlt = snode.getAttribute('alternative');
- snode.removeAttribute('alternative');
- const detail = SpeechGeneratorUtil.computeMarkup(snode);
- const speech = this.mergePrefix_([detail]);
- snode.setAttribute('alternative', oldAlt);
- return speech;
- }
- }
- AbstractWalker.ID_COUNTER = 0;
- AbstractWalker.SRE_ID_ATTR = 'sre-explorer-id';
|