MouseExplorer.ts 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  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 based on mouse events.
  19. *
  20. * @author v.sorge@mathjax.org (Volker Sorge)
  21. */
  22. import {A11yDocument, DummyRegion, Region} from './Region.js';
  23. import {Explorer, AbstractExplorer} from './Explorer.js';
  24. import '../sre.js';
  25. /**
  26. * Interface for mouse explorers. Adds the necessary mouse events.
  27. * @interface
  28. * @extends {Explorer}
  29. */
  30. export interface MouseExplorer extends Explorer {
  31. /**
  32. * Function to be executed on mouse over.
  33. * @param {MouseEvent} event The mouse event.
  34. */
  35. MouseOver(event: MouseEvent): void;
  36. /**
  37. * Function to be executed on mouse out.
  38. * @param {MouseEvent} event The mouse event.
  39. */
  40. MouseOut(event: MouseEvent): void;
  41. }
  42. /**
  43. * @constructor
  44. * @extends {AbstractExplorer}
  45. *
  46. * @template T The type that is consumed by the Region of this explorer.
  47. */
  48. export abstract class AbstractMouseExplorer<T> extends AbstractExplorer<T> implements MouseExplorer {
  49. /**
  50. * @override
  51. */
  52. protected events: [string, (x: Event) => void][] =
  53. super.Events().concat([
  54. ['mouseover', this.MouseOver.bind(this)],
  55. ['mouseout', this.MouseOut.bind(this)]
  56. ]);
  57. /**
  58. * @override
  59. */
  60. public MouseOver(_event: MouseEvent) {
  61. this.Start();
  62. }
  63. /**
  64. * @override
  65. */
  66. public MouseOut(_event: MouseEvent) {
  67. this.Stop();
  68. }
  69. }
  70. /**
  71. * Exploration via hovering.
  72. * @constructor
  73. * @extends {AbstractMouseExplorer}
  74. */
  75. export abstract class Hoverer<T> extends AbstractMouseExplorer<T> {
  76. /**
  77. * Remember the last position to avoid flickering.
  78. * @type {[number, number]}
  79. */
  80. protected coord: [number, number];
  81. /**
  82. * @constructor
  83. * @extends {AbstractMouseExplorer<T>}
  84. *
  85. * @param {A11yDocument} document The current document.
  86. * @param {Region<T>} region A region to display results.
  87. * @param {HTMLElement} node The node on which the explorer works.
  88. * @param {(node: HTMLElement) => boolean} nodeQuery Predicate on nodes that
  89. * will fire the hoverer.
  90. * @param {(node: HTMLElement) => T} nodeAccess Accessor to extract node value
  91. * that is passed to the region.
  92. *
  93. * @template T
  94. */
  95. protected constructor(public document: A11yDocument,
  96. protected region: Region<T>,
  97. protected node: HTMLElement,
  98. protected nodeQuery: (node: HTMLElement) => boolean,
  99. protected nodeAccess: (node: HTMLElement) => T) {
  100. super(document, region, node);
  101. }
  102. /**
  103. * @override
  104. */
  105. public MouseOut(event: MouseEvent) {
  106. if (event.clientX === this.coord[0] &&
  107. event.clientY === this.coord[1]) {
  108. return;
  109. }
  110. this.highlighter.unhighlight();
  111. this.region.Hide();
  112. super.MouseOut(event);
  113. }
  114. /**
  115. * @override
  116. */
  117. public MouseOver(event: MouseEvent) {
  118. super.MouseOver(event);
  119. let target = event.target as HTMLElement;
  120. this.coord = [event.clientX, event.clientY];
  121. let [node, kind] = this.getNode(target);
  122. if (!node) {
  123. return;
  124. }
  125. this.highlighter.unhighlight();
  126. this.highlighter.highlight([node]);
  127. this.region.Update(kind);
  128. this.region.Show(node, this.highlighter);
  129. }
  130. /**
  131. * Retrieves the closest node on which the node query fires. Thereby closest
  132. * is defined as:
  133. * 1. The node or its ancestor on which the query is true.
  134. * 2. In case 1 does not exist the left-most child on which query is true.
  135. * 3. Otherwise fails.
  136. *
  137. * @param {HTMLElement} node The node on which the mouse event fired.
  138. * @return {[HTMLElement, T]} Node and output pair if successful.
  139. */
  140. public getNode(node: HTMLElement): [HTMLElement, T] {
  141. let original = node;
  142. while (node && node !== this.node) {
  143. if (this.nodeQuery(node)) {
  144. return [node, this.nodeAccess(node)];
  145. }
  146. node = node.parentNode as HTMLElement;
  147. }
  148. node = original;
  149. while (node) {
  150. if (this.nodeQuery(node)) {
  151. return [node, this.nodeAccess(node)];
  152. }
  153. let child = node.childNodes[0] as HTMLElement;
  154. node = (child && child.tagName === 'defs') ? // This is for SVG.
  155. node.childNodes[1] as HTMLElement : child;
  156. }
  157. return [null, null];
  158. }
  159. }
  160. /**
  161. * Hoverer that displays information on nodes (e.g., as tooltips).
  162. * @constructor
  163. * @extends {Hoverer}
  164. */
  165. export class ValueHoverer extends Hoverer<string> { }
  166. /**
  167. * Hoverer that displays node content (e.g., for magnification).
  168. * @constructor
  169. * @extends {Hoverer}
  170. */
  171. export class ContentHoverer extends Hoverer<HTMLElement> { }
  172. /**
  173. * Highlights maction nodes on hovering.
  174. * @constructor
  175. * @extends {Hoverer}
  176. */
  177. export class FlameHoverer extends Hoverer<void> {
  178. /**
  179. * @override
  180. */
  181. protected constructor(
  182. public document: A11yDocument,
  183. _ignore: any,
  184. protected node: HTMLElement) {
  185. super(document, new DummyRegion(document), node,
  186. x => this.highlighter.isMactionNode(x),
  187. () => {});
  188. }
  189. }