abstract_walker.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. exports.AbstractWalker = void 0;
  4. const auditory_description_js_1 = require("../audio/auditory_description.js");
  5. const AuralRendering = require("../audio/aural_rendering.js");
  6. const DomUtil = require("../common/dom_util.js");
  7. const engine_setup_js_1 = require("../common/engine_setup.js");
  8. const event_util_js_1 = require("../common/event_util.js");
  9. const enrich_attr_js_1 = require("../enrich_mathml/enrich_attr.js");
  10. const locale_js_1 = require("../l10n/locale.js");
  11. const grammar_js_1 = require("../rule_engine/grammar.js");
  12. const semantic_skeleton_js_1 = require("../semantic_tree/semantic_skeleton.js");
  13. const SpeechGeneratorFactory = require("../speech_generator/speech_generator_factory.js");
  14. const SpeechGeneratorUtil = require("../speech_generator/speech_generator_util.js");
  15. const focus_js_1 = require("./focus.js");
  16. const rebuild_stree_js_1 = require("./rebuild_stree.js");
  17. const walker_js_1 = require("./walker.js");
  18. const WalkerUtil = require("./walker_util.js");
  19. const XpathUtil = require("../common/xpath_util.js");
  20. class AbstractWalker {
  21. constructor(node, generator, highlighter, xml) {
  22. this.node = node;
  23. this.generator = generator;
  24. this.highlighter = highlighter;
  25. this.modifier = false;
  26. this.keyMapping = new Map([
  27. [event_util_js_1.KeyCode.UP, this.up.bind(this)],
  28. [event_util_js_1.KeyCode.DOWN, this.down.bind(this)],
  29. [event_util_js_1.KeyCode.RIGHT, this.right.bind(this)],
  30. [event_util_js_1.KeyCode.LEFT, this.left.bind(this)],
  31. [event_util_js_1.KeyCode.TAB, this.repeat.bind(this)],
  32. [event_util_js_1.KeyCode.DASH, this.expand.bind(this)],
  33. [event_util_js_1.KeyCode.SPACE, this.depth.bind(this)],
  34. [event_util_js_1.KeyCode.HOME, this.home.bind(this)],
  35. [event_util_js_1.KeyCode.X, this.summary.bind(this)],
  36. [event_util_js_1.KeyCode.Z, this.detail.bind(this)],
  37. [event_util_js_1.KeyCode.V, this.virtualize.bind(this)],
  38. [event_util_js_1.KeyCode.P, this.previous.bind(this)],
  39. [event_util_js_1.KeyCode.U, this.undo.bind(this)],
  40. [event_util_js_1.KeyCode.LESS, this.previousRules.bind(this)],
  41. [event_util_js_1.KeyCode.GREATER, this.nextRules.bind(this)]
  42. ]);
  43. this.cursors = [];
  44. this.xml_ = null;
  45. this.rebuilt_ = null;
  46. this.focus_ = null;
  47. this.active_ = false;
  48. if (this.node.id) {
  49. this.id = this.node.id;
  50. }
  51. else if (this.node.hasAttribute(AbstractWalker.SRE_ID_ATTR)) {
  52. this.id = this.node.getAttribute(AbstractWalker.SRE_ID_ATTR);
  53. }
  54. else {
  55. this.node.setAttribute(AbstractWalker.SRE_ID_ATTR, AbstractWalker.ID_COUNTER.toString());
  56. this.id = AbstractWalker.ID_COUNTER++;
  57. }
  58. this.rootNode = WalkerUtil.getSemanticRoot(node);
  59. this.rootId = this.rootNode.getAttribute(enrich_attr_js_1.Attribute.ID);
  60. this.xmlString_ = xml;
  61. this.moved = walker_js_1.WalkerMoves.ENTER;
  62. }
  63. getXml() {
  64. if (!this.xml_) {
  65. this.xml_ = DomUtil.parseInput(this.xmlString_);
  66. }
  67. return this.xml_;
  68. }
  69. getRebuilt() {
  70. if (!this.rebuilt_) {
  71. this.rebuildStree();
  72. }
  73. return this.rebuilt_;
  74. }
  75. isActive() {
  76. return this.active_;
  77. }
  78. activate() {
  79. if (this.isActive()) {
  80. return;
  81. }
  82. this.toggleActive_();
  83. }
  84. deactivate() {
  85. if (!this.isActive()) {
  86. return;
  87. }
  88. walker_js_1.WalkerState.setState(this.id, this.primaryId());
  89. this.toggleActive_();
  90. }
  91. getFocus(update = false) {
  92. if (this.rootId === null) {
  93. this.getRebuilt();
  94. }
  95. if (!this.focus_) {
  96. this.focus_ = this.singletonFocus(this.rootId);
  97. }
  98. if (update) {
  99. this.updateFocus();
  100. }
  101. return this.focus_;
  102. }
  103. setFocus(focus) {
  104. this.focus_ = focus;
  105. }
  106. getDepth() {
  107. return this.levels.depth() - 1;
  108. }
  109. isSpeech() {
  110. return this.generator.modality === enrich_attr_js_1.Attribute.SPEECH;
  111. }
  112. focusDomNodes() {
  113. return this.getFocus().getDomNodes();
  114. }
  115. focusSemanticNodes() {
  116. return this.getFocus().getSemanticNodes();
  117. }
  118. speech() {
  119. const nodes = this.focusDomNodes();
  120. if (!nodes.length) {
  121. return '';
  122. }
  123. const special = this.specialMove();
  124. if (special !== null) {
  125. return special;
  126. }
  127. switch (this.moved) {
  128. case walker_js_1.WalkerMoves.DEPTH:
  129. return this.depth_();
  130. case walker_js_1.WalkerMoves.SUMMARY:
  131. return this.summary_();
  132. case walker_js_1.WalkerMoves.DETAIL:
  133. return this.detail_();
  134. default: {
  135. const speech = [];
  136. const snodes = this.focusSemanticNodes();
  137. for (let i = 0, l = nodes.length; i < l; i++) {
  138. const node = nodes[i];
  139. const snode = snodes[i];
  140. speech.push(node
  141. ? this.generator.getSpeech(node, this.getXml(), this.node)
  142. : SpeechGeneratorUtil.recomputeMarkup(snode));
  143. }
  144. return this.mergePrefix_(speech);
  145. }
  146. }
  147. }
  148. move(key) {
  149. const direction = this.keyMapping.get(key);
  150. if (!direction) {
  151. return null;
  152. }
  153. const focus = direction();
  154. if (!focus || focus === this.getFocus()) {
  155. return false;
  156. }
  157. this.setFocus(focus);
  158. if (this.moved === walker_js_1.WalkerMoves.HOME) {
  159. this.levels = this.initLevels();
  160. }
  161. return true;
  162. }
  163. up() {
  164. this.moved = walker_js_1.WalkerMoves.UP;
  165. return this.getFocus();
  166. }
  167. down() {
  168. this.moved = walker_js_1.WalkerMoves.DOWN;
  169. return this.getFocus();
  170. }
  171. left() {
  172. this.moved = walker_js_1.WalkerMoves.LEFT;
  173. return this.getFocus();
  174. }
  175. right() {
  176. this.moved = walker_js_1.WalkerMoves.RIGHT;
  177. return this.getFocus();
  178. }
  179. repeat() {
  180. this.moved = walker_js_1.WalkerMoves.REPEAT;
  181. return this.getFocus().clone();
  182. }
  183. depth() {
  184. this.moved = this.isSpeech() ? walker_js_1.WalkerMoves.DEPTH : walker_js_1.WalkerMoves.REPEAT;
  185. return this.getFocus().clone();
  186. }
  187. home() {
  188. this.moved = walker_js_1.WalkerMoves.HOME;
  189. const focus = this.singletonFocus(this.rootId);
  190. return focus;
  191. }
  192. getBySemanticId(id) {
  193. return WalkerUtil.getBySemanticId(this.node, id);
  194. }
  195. primaryId() {
  196. return this.getFocus().getSemanticPrimary().id.toString();
  197. }
  198. expand() {
  199. const primary = this.getFocus().getDomPrimary();
  200. const expandable = this.actionable_(primary);
  201. if (!expandable) {
  202. return this.getFocus();
  203. }
  204. this.moved = walker_js_1.WalkerMoves.EXPAND;
  205. expandable.dispatchEvent(new Event('click'));
  206. return this.getFocus().clone();
  207. }
  208. expandable(node) {
  209. const parent = !!this.actionable_(node);
  210. return parent && node.childNodes.length === 0;
  211. }
  212. collapsible(node) {
  213. const parent = !!this.actionable_(node);
  214. return parent && node.childNodes.length > 0;
  215. }
  216. restoreState() {
  217. if (!this.highlighter) {
  218. return;
  219. }
  220. const state = walker_js_1.WalkerState.getState(this.id);
  221. if (!state) {
  222. return;
  223. }
  224. let node = this.getRebuilt().nodeDict[state];
  225. const path = [];
  226. while (node) {
  227. path.push(node.id);
  228. node = node.parent;
  229. }
  230. path.pop();
  231. while (path.length > 0) {
  232. this.down();
  233. const id = path.pop();
  234. const focus = this.findFocusOnLevel(id);
  235. if (!focus) {
  236. break;
  237. }
  238. this.setFocus(focus);
  239. }
  240. this.moved = walker_js_1.WalkerMoves.ENTER;
  241. }
  242. updateFocus() {
  243. this.setFocus(focus_js_1.Focus.factory(this.getFocus().getSemanticPrimary().id.toString(), this.getFocus()
  244. .getSemanticNodes()
  245. .map((x) => x.id.toString()), this.getRebuilt(), this.node));
  246. }
  247. rebuildStree() {
  248. this.rebuilt_ = new rebuild_stree_js_1.RebuildStree(this.getXml());
  249. this.rootId = this.rebuilt_.stree.root.id.toString();
  250. this.generator.setRebuilt(this.rebuilt_);
  251. this.skeleton = semantic_skeleton_js_1.SemanticSkeleton.fromTree(this.rebuilt_.stree);
  252. this.skeleton.populate();
  253. this.focus_ = this.singletonFocus(this.rootId);
  254. this.levels = this.initLevels();
  255. SpeechGeneratorUtil.connectMactions(this.node, this.getXml(), this.rebuilt_.xml);
  256. }
  257. previousLevel() {
  258. const dnode = this.getFocus().getDomPrimary();
  259. return dnode
  260. ? WalkerUtil.getAttribute(dnode, enrich_attr_js_1.Attribute.PARENT)
  261. : this.getFocus().getSemanticPrimary().parent.id.toString();
  262. }
  263. nextLevel() {
  264. const dnode = this.getFocus().getDomPrimary();
  265. let children;
  266. let content;
  267. if (dnode) {
  268. children = WalkerUtil.splitAttribute(WalkerUtil.getAttribute(dnode, enrich_attr_js_1.Attribute.CHILDREN));
  269. content = WalkerUtil.splitAttribute(WalkerUtil.getAttribute(dnode, enrich_attr_js_1.Attribute.CONTENT));
  270. const type = WalkerUtil.getAttribute(dnode, enrich_attr_js_1.Attribute.TYPE);
  271. const role = WalkerUtil.getAttribute(dnode, enrich_attr_js_1.Attribute.ROLE);
  272. return this.combineContentChildren(type, role, content, children);
  273. }
  274. const toIds = (x) => x.id.toString();
  275. const snode = this.getRebuilt().nodeDict[this.primaryId()];
  276. children = snode.childNodes.map(toIds);
  277. content = snode.contentNodes.map(toIds);
  278. if (children.length === 0) {
  279. return [];
  280. }
  281. return this.combineContentChildren(snode.type, snode.role, content, children);
  282. }
  283. singletonFocus(id) {
  284. this.getRebuilt();
  285. const ids = this.retrieveVisuals(id);
  286. return this.focusFromId(id, ids);
  287. }
  288. retrieveVisuals(id) {
  289. if (!this.skeleton) {
  290. return [id];
  291. }
  292. const num = parseInt(id, 10);
  293. const semStree = this.skeleton.subtreeNodes(num);
  294. if (!semStree.length) {
  295. return [id];
  296. }
  297. semStree.unshift(num);
  298. const mmlStree = {};
  299. const result = [];
  300. XpathUtil.updateEvaluator(this.getXml());
  301. for (const child of semStree) {
  302. if (mmlStree[child]) {
  303. continue;
  304. }
  305. result.push(child.toString());
  306. mmlStree[child] = true;
  307. this.subtreeIds(child, mmlStree);
  308. }
  309. return result;
  310. }
  311. subtreeIds(id, nodes) {
  312. const xmlRoot = XpathUtil.evalXPath(`//*[@data-semantic-id="${id}"]`, this.getXml());
  313. const xpath = XpathUtil.evalXPath('*//@data-semantic-id', xmlRoot[0]);
  314. xpath.forEach((x) => (nodes[parseInt(x.textContent, 10)] = true));
  315. }
  316. focusFromId(id, ids) {
  317. return focus_js_1.Focus.factory(id, ids, this.getRebuilt(), this.node);
  318. }
  319. summary() {
  320. this.moved = this.isSpeech() ? walker_js_1.WalkerMoves.SUMMARY : walker_js_1.WalkerMoves.REPEAT;
  321. return this.getFocus().clone();
  322. }
  323. detail() {
  324. this.moved = this.isSpeech() ? walker_js_1.WalkerMoves.DETAIL : walker_js_1.WalkerMoves.REPEAT;
  325. return this.getFocus().clone();
  326. }
  327. specialMove() {
  328. return null;
  329. }
  330. virtualize(opt_undo) {
  331. this.cursors.push({
  332. focus: this.getFocus(),
  333. levels: this.levels,
  334. undo: opt_undo || !this.cursors.length
  335. });
  336. this.levels = this.levels.clone();
  337. return this.getFocus().clone();
  338. }
  339. previous() {
  340. const previous = this.cursors.pop();
  341. if (!previous) {
  342. return this.getFocus();
  343. }
  344. this.levels = previous.levels;
  345. return previous.focus;
  346. }
  347. undo() {
  348. let previous;
  349. do {
  350. previous = this.cursors.pop();
  351. } while (previous && !previous.undo);
  352. if (!previous) {
  353. return this.getFocus();
  354. }
  355. this.levels = previous.levels;
  356. return previous.focus;
  357. }
  358. update(options) {
  359. (0, engine_setup_js_1.setup)(options).then(() => SpeechGeneratorFactory.generator('Tree').getSpeech(this.node, this.getXml()));
  360. }
  361. nextRules() {
  362. this.generator.nextRules();
  363. const options = this.generator.getOptions();
  364. if (options.modality !== 'speech') {
  365. return this.getFocus();
  366. }
  367. this.update(options);
  368. this.moved = walker_js_1.WalkerMoves.REPEAT;
  369. return this.getFocus().clone();
  370. }
  371. previousRules() {
  372. var _a;
  373. this.generator.nextStyle((_a = this.getFocus().getSemanticPrimary()) === null || _a === void 0 ? void 0 : _a.id.toString());
  374. const options = this.generator.getOptions();
  375. if (options.modality !== 'speech') {
  376. return this.getFocus();
  377. }
  378. this.update(options);
  379. this.moved = walker_js_1.WalkerMoves.REPEAT;
  380. return this.getFocus().clone();
  381. }
  382. refocus() {
  383. let focus = this.getFocus();
  384. let last;
  385. while (!focus.getNodes().length) {
  386. last = this.levels.peek();
  387. const up = this.up();
  388. if (!up) {
  389. break;
  390. }
  391. this.setFocus(up);
  392. focus = this.getFocus(true);
  393. }
  394. this.levels.push(last);
  395. this.setFocus(focus);
  396. }
  397. toggleActive_() {
  398. this.active_ = !this.active_;
  399. }
  400. mergePrefix_(speech, pre = []) {
  401. const prefix = this.isSpeech() ? this.prefix_() : '';
  402. if (prefix) {
  403. speech.unshift(prefix);
  404. }
  405. const postfix = this.isSpeech() ? this.postfix_() : '';
  406. if (postfix) {
  407. speech.push(postfix);
  408. }
  409. return AuralRendering.finalize(AuralRendering.merge(pre.concat(speech)));
  410. }
  411. prefix_() {
  412. const nodes = this.getFocus().getDomNodes();
  413. const snodes = this.getFocus().getSemanticNodes();
  414. return nodes[0]
  415. ? WalkerUtil.getAttribute(nodes[0], enrich_attr_js_1.Attribute.PREFIX)
  416. : SpeechGeneratorUtil.retrievePrefix(snodes[0]);
  417. }
  418. postfix_() {
  419. const nodes = this.getFocus().getDomNodes();
  420. return nodes[0]
  421. ? WalkerUtil.getAttribute(nodes[0], enrich_attr_js_1.Attribute.POSTFIX)
  422. : '';
  423. }
  424. depth_() {
  425. const oldDepth = grammar_js_1.Grammar.getInstance().getParameter('depth');
  426. grammar_js_1.Grammar.getInstance().setParameter('depth', true);
  427. const primary = this.getFocus().getDomPrimary();
  428. const expand = this.expandable(primary)
  429. ? locale_js_1.LOCALE.MESSAGES.navigate.EXPANDABLE
  430. : this.collapsible(primary)
  431. ? locale_js_1.LOCALE.MESSAGES.navigate.COLLAPSIBLE
  432. : '';
  433. const level = locale_js_1.LOCALE.MESSAGES.navigate.LEVEL + ' ' + this.getDepth();
  434. const snodes = this.getFocus().getSemanticNodes();
  435. const prefix = SpeechGeneratorUtil.retrievePrefix(snodes[0]);
  436. const audio = [
  437. new auditory_description_js_1.AuditoryDescription({ text: level, personality: {} }),
  438. new auditory_description_js_1.AuditoryDescription({ text: prefix, personality: {} }),
  439. new auditory_description_js_1.AuditoryDescription({ text: expand, personality: {} })
  440. ];
  441. grammar_js_1.Grammar.getInstance().setParameter('depth', oldDepth);
  442. return AuralRendering.finalize(AuralRendering.markup(audio));
  443. }
  444. actionable_(node) {
  445. const parent = node === null || node === void 0 ? void 0 : node.parentNode;
  446. return parent && this.highlighter.isMactionNode(parent) ? parent : null;
  447. }
  448. summary_() {
  449. const sprimary = this.getFocus().getSemanticPrimary();
  450. const sid = sprimary.id.toString();
  451. const snode = this.getRebuilt().xml.getAttribute('id') === sid
  452. ? this.getRebuilt().xml
  453. : DomUtil.querySelectorAllByAttrValue(this.getRebuilt().xml, 'id', sid)[0];
  454. const summary = SpeechGeneratorUtil.retrieveSummary(snode);
  455. const speech = this.mergePrefix_([summary]);
  456. return speech;
  457. }
  458. detail_() {
  459. const sprimary = this.getFocus().getSemanticPrimary();
  460. const sid = sprimary.id.toString();
  461. const snode = this.getRebuilt().xml.getAttribute('id') === sid
  462. ? this.getRebuilt().xml
  463. : DomUtil.querySelectorAllByAttrValue(this.getRebuilt().xml, 'id', sid)[0];
  464. const oldAlt = snode.getAttribute('alternative');
  465. snode.removeAttribute('alternative');
  466. const detail = SpeechGeneratorUtil.computeMarkup(snode);
  467. const speech = this.mergePrefix_([detail]);
  468. snode.setAttribute('alternative', oldAlt);
  469. return speech;
  470. }
  471. }
  472. exports.AbstractWalker = AbstractWalker;
  473. AbstractWalker.ID_COUNTER = 0;
  474. AbstractWalker.SRE_ID_ATTR = 'sre-explorer-id';