Explorer.ts 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282
  1. /*************************************************************
  2. *
  3. * Copyright (c) 2009-2022 The MathJax Consortium
  4. *
  5. * Licensed under the Apache License, Version 2.0 (the "License");
  6. * you may not use this file except in compliance with the License.
  7. * You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. */
  17. /**
  18. * @fileoverview Explorers for A11Y purposes.
  19. *
  20. * @author v.sorge@mathjax.org (Volker Sorge)
  21. */
  22. import {A11yDocument, Region} from './Region.js';
  23. import Sre from '../sre.js';
  24. /**
  25. * A11y explorers.
  26. * @interface
  27. */
  28. export interface Explorer {
  29. /**
  30. * Flag indicating if the explorer is active.
  31. * @type {boolean}
  32. */
  33. active: boolean;
  34. /**
  35. * Flag indicating if event bubbling is stopped.
  36. * @type {boolean}
  37. */
  38. stoppable: boolean;
  39. /**
  40. * Attaches navigator and its event handlers to a node.
  41. */
  42. Attach(): void;
  43. /**
  44. * Detaches navigator and its event handlers to a node.
  45. */
  46. Detach(): void;
  47. /**
  48. * Starts the explorer.
  49. */
  50. Start(): void;
  51. /**
  52. * Stops the explorer.
  53. */
  54. Stop(): void;
  55. /**
  56. * Adds the events of the explorer to the node's event listener.
  57. */
  58. AddEvents(): void;
  59. /**
  60. * Removes the events of the explorer from the node's event listener.
  61. */
  62. RemoveEvents(): void;
  63. /**
  64. * Update the explorer after state changes.
  65. * @param {boolean=} force Forces the update in any case. (E.g., even if
  66. * explorer is inactive.)
  67. */
  68. Update(force?: boolean): void;
  69. }
  70. /**
  71. * Abstract class implementing the very basic explorer functionality.
  72. *
  73. * Explorers use creator pattern to ensure they automatically attach themselves
  74. * to their node. This class provides the create method and is consequently not
  75. * declared abstract.
  76. *
  77. * @constructor
  78. * @implements {Explorer}
  79. *
  80. * @template T The type that is consumed by the Region of this explorer.
  81. */
  82. export class AbstractExplorer<T> implements Explorer {
  83. /**
  84. * @override
  85. */
  86. public stoppable: boolean = true;
  87. /**
  88. * Named events and their functions.
  89. * @type {[string, function(x: Event)][]}
  90. */
  91. protected events: [string, (x: Event) => void][] = [];
  92. /**
  93. * The Sre highlighter associated with the walker.
  94. * @type {Sre.highlighter}
  95. */
  96. protected highlighter: Sre.highlighter = this.getHighlighter();
  97. /**
  98. * Flag if explorer is active.
  99. * @type {boolean}
  100. */
  101. private _active: boolean = false;
  102. /**
  103. * Stops event bubbling.
  104. * @param {Event} event The event that is stopped.
  105. */
  106. protected static stopEvent(event: Event) {
  107. if (event.preventDefault) {
  108. event.preventDefault();
  109. } else {
  110. event.returnValue = false;
  111. }
  112. if (event.stopImmediatePropagation) {
  113. event.stopImmediatePropagation();
  114. } else if (event.stopPropagation) {
  115. event.stopPropagation();
  116. }
  117. event.cancelBubble = true;
  118. }
  119. /**
  120. * Creator pattern for explorers.
  121. * @param {A11yDocument} document The current document.
  122. * @param {Region<T>} region A region to display results.
  123. * @param {HTMLElement} node The node on which the explorer works.
  124. * @param {any[]} ...rest Remaining information.
  125. * @return {Explorer} An object of the particular explorer class.
  126. *
  127. * @template T
  128. */
  129. public static create<T>(
  130. document: A11yDocument,
  131. region: Region<T>,
  132. node: HTMLElement, ...rest: any[]
  133. ): Explorer {
  134. let explorer = new this(document, region, node, ...rest);
  135. return explorer;
  136. }
  137. /**
  138. * @constructor
  139. * @param {A11yDocument} document The current document.
  140. * @param {Region<T>} region A region to display results.
  141. * @param {HTMLElement} node The node on which the explorer works.
  142. * @param {any[]} ...rest Remaining information.
  143. */
  144. protected constructor(
  145. public document: A11yDocument,
  146. protected region: Region<T>,
  147. protected node: HTMLElement, ..._rest: any[]
  148. ) {
  149. }
  150. /**
  151. * @return {[string, (x: Event) => void][]} The events associated with this
  152. * explorer.
  153. */
  154. protected Events(): [string, (x: Event) => void][] {
  155. return this.events;
  156. }
  157. /**
  158. * @override
  159. */
  160. public get active(): boolean {
  161. return this._active;
  162. }
  163. /**
  164. * @override
  165. */
  166. public set active(flag: boolean) {
  167. this._active = flag;
  168. }
  169. /**
  170. * @override
  171. */
  172. public Attach() {
  173. this.AddEvents();
  174. }
  175. /**
  176. * @override
  177. */
  178. public Detach() {
  179. this.RemoveEvents();
  180. }
  181. /**
  182. * @override
  183. */
  184. public Start() {
  185. this.highlighter = this.getHighlighter();
  186. this.active = true;
  187. }
  188. /**
  189. * @override
  190. */
  191. public Stop() {
  192. if (this.active) {
  193. this.region.Clear();
  194. this.region.Hide();
  195. this.active = false;
  196. }
  197. }
  198. /**
  199. * @override
  200. */
  201. public AddEvents() {
  202. for (let [eventkind, eventfunc] of this.events) {
  203. this.node.addEventListener(eventkind, eventfunc);
  204. }
  205. }
  206. /**
  207. * @override
  208. */
  209. public RemoveEvents() {
  210. for (let [eventkind, eventfunc] of this.events) {
  211. this.node.removeEventListener(eventkind, eventfunc);
  212. }
  213. }
  214. /**
  215. * @override
  216. */
  217. // @ts-ignore: unused variable
  218. public Update(force: boolean = false): void {}
  219. /**
  220. * @return {Sre.Highlighter} A highlighter for the explorer.
  221. */
  222. protected getHighlighter(): Sre.highlighter {
  223. let opts = this.document.options.a11y;
  224. let foreground = {color: opts.foregroundColor.toLowerCase(),
  225. alpha: opts.foregroundOpacity / 100};
  226. let background = {color: opts.backgroundColor.toLowerCase(),
  227. alpha: opts.backgroundOpacity / 100};
  228. return Sre.getHighlighter(
  229. background, foreground,
  230. {renderer: this.document.outputJax.name, browser: 'v3'});
  231. }
  232. /**
  233. * Stops the events of this explorer from bubbling.
  234. * @param {Event} event The event to stop.
  235. */
  236. protected stopEvent(event: Event) {
  237. if (this.stoppable) {
  238. AbstractExplorer.stopEvent(event);
  239. }
  240. }
  241. }