dom.rs 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692
  1. //! Implementation of a renderer for Dioxus on the web.
  2. //!
  3. //! Oustanding todos:
  4. //! - Removing event listeners (delegation)
  5. //! - Passive event listeners
  6. //! - no-op event listener patch for safari
  7. //! - tests to ensure dyn_into works for various event types.
  8. //! - Partial delegation?>
  9. use dioxus_core::{
  10. events::{SyntheticEvent, UserEvent},
  11. mutations::NodeRefMutation,
  12. scheduler::SchedulerMsg,
  13. DomEdit, ElementId, ScopeId,
  14. };
  15. use fxhash::FxHashMap;
  16. use std::{fmt::Debug, rc::Rc, sync::Arc};
  17. use wasm_bindgen::{closure::Closure, JsCast};
  18. use web_sys::{
  19. Attr, CssStyleDeclaration, Document, Element, Event, HtmlElement, HtmlInputElement,
  20. HtmlOptionElement, HtmlTextAreaElement, Node, NodeList,
  21. };
  22. use crate::{nodeslab::NodeSlab, WebConfig};
  23. pub struct WebsysDom {
  24. stack: Stack,
  25. /// A map from ElementID (index) to Node
  26. nodes: NodeSlab,
  27. document: Document,
  28. root: Element,
  29. sender_callback: Rc<dyn Fn(SchedulerMsg)>,
  30. // map of listener types to number of those listeners
  31. // This is roughly a delegater
  32. // TODO: check how infero delegates its events - some are more performant
  33. listeners: FxHashMap<&'static str, ListenerEntry>,
  34. // We need to make sure to add comments between text nodes
  35. // We ensure that the text siblings are patched by preventing the browser from merging
  36. // neighboring text nodes. Originally inspired by some of React's work from 2016.
  37. // -> https://reactjs.org/blog/2016/04/07/react-v15.html#major-changes
  38. // -> https://github.com/facebook/react/pull/5753
  39. last_node_was_text: bool,
  40. }
  41. type ListenerEntry = (usize, Closure<dyn FnMut(&Event)>);
  42. impl WebsysDom {
  43. pub fn new(root: Element, cfg: WebConfig, sender_callback: Rc<dyn Fn(SchedulerMsg)>) -> Self {
  44. let document = load_document();
  45. let mut nodes = NodeSlab::new(2000);
  46. let mut listeners = FxHashMap::default();
  47. // re-hydrate the page - only supports one virtualdom per page
  48. if cfg.hydrate {
  49. // Load all the elements into the arena
  50. let node_list: NodeList = document.query_selector_all("dio_el").unwrap();
  51. let len = node_list.length() as usize;
  52. for x in 0..len {
  53. let node: Node = node_list.get(x as u32).unwrap();
  54. let el: &Element = node.dyn_ref::<Element>().unwrap();
  55. let id: String = el.get_attribute("dio_el").unwrap();
  56. let id = id.parse::<usize>().unwrap();
  57. nodes[id] = Some(node);
  58. }
  59. // Load all the event listeners into our listener register
  60. // TODO
  61. }
  62. let mut stack = Stack::with_capacity(10);
  63. let root_node = root.clone().dyn_into::<Node>().unwrap();
  64. stack.push(root_node);
  65. Self {
  66. stack,
  67. nodes,
  68. listeners,
  69. document,
  70. sender_callback,
  71. root,
  72. last_node_was_text: false,
  73. }
  74. }
  75. pub fn apply_refs(&mut self, refs: &[NodeRefMutation]) {
  76. for item in refs {
  77. if let Some(bla) = &item.element {
  78. let node = self.nodes[item.element_id.as_u64() as usize]
  79. .as_ref()
  80. .unwrap()
  81. .clone();
  82. bla.set(Box::new(node)).unwrap();
  83. }
  84. }
  85. }
  86. pub fn process_edits(&mut self, edits: &mut Vec<DomEdit>) {
  87. for edit in edits.drain(..) {
  88. match edit {
  89. DomEdit::PushRoot { id: root } => self.push(root),
  90. DomEdit::PopRoot => self.pop(),
  91. DomEdit::AppendChildren { many } => self.append_children(many),
  92. DomEdit::ReplaceWith { m, root } => self.replace_with(m, root),
  93. DomEdit::Remove { root } => self.remove(root),
  94. DomEdit::RemoveAllChildren => self.remove_all_children(),
  95. DomEdit::CreateTextNode { text, id } => self.create_text_node(text, id),
  96. DomEdit::CreateElement { tag, id } => self.create_element(tag, None, id),
  97. DomEdit::CreateElementNs { tag, id, ns } => self.create_element(tag, Some(ns), id),
  98. DomEdit::CreatePlaceholder { id } => self.create_placeholder(id),
  99. DomEdit::NewEventListener {
  100. event_name,
  101. scope,
  102. mounted_node_id,
  103. } => self.new_event_listener(event_name, scope, mounted_node_id),
  104. DomEdit::RemoveEventListener { event } => todo!(),
  105. DomEdit::SetText { text } => self.set_text(text),
  106. DomEdit::SetAttribute { field, value, ns } => self.set_attribute(field, value, ns),
  107. DomEdit::RemoveAttribute { name } => self.remove_attribute(name),
  108. DomEdit::InsertAfter { n, root } => self.insert_after(n, root),
  109. DomEdit::InsertBefore { n, root } => self.insert_before(n, root),
  110. }
  111. }
  112. }
  113. fn push(&mut self, root: u64) {
  114. let key = root as usize;
  115. let domnode = &self.nodes[key];
  116. let real_node: Node = match domnode {
  117. Some(n) => n.clone(),
  118. None => todo!(),
  119. };
  120. self.stack.push(real_node);
  121. }
  122. // drop the node off the stack
  123. fn pop(&mut self) {
  124. self.stack.pop();
  125. }
  126. fn append_children(&mut self, many: u32) {
  127. let root: Node = self
  128. .stack
  129. .list
  130. .get(self.stack.list.len() - (1 + many as usize))
  131. .unwrap()
  132. .clone();
  133. for child in self
  134. .stack
  135. .list
  136. .drain((self.stack.list.len() - many as usize)..)
  137. {
  138. if child.dyn_ref::<web_sys::Text>().is_some() {
  139. if self.last_node_was_text {
  140. let comment_node = self
  141. .document
  142. .create_comment("dioxus")
  143. .dyn_into::<Node>()
  144. .unwrap();
  145. root.append_child(&comment_node).unwrap();
  146. }
  147. self.last_node_was_text = true;
  148. } else {
  149. self.last_node_was_text = false;
  150. }
  151. root.append_child(&child).unwrap();
  152. }
  153. }
  154. fn replace_with(&mut self, m: u32, root: u64) {
  155. let old = self.nodes[root as usize].as_ref().unwrap();
  156. let arr: js_sys::Array = self
  157. .stack
  158. .list
  159. .drain((self.stack.list.len() - m as usize)..)
  160. .collect();
  161. if let Some(el) = old.dyn_ref::<Element>() {
  162. el.replace_with_with_node(&arr).unwrap();
  163. } else if let Some(el) = old.dyn_ref::<web_sys::CharacterData>() {
  164. el.replace_with_with_node(&arr).unwrap();
  165. } else if let Some(el) = old.dyn_ref::<web_sys::DocumentType>() {
  166. el.replace_with_with_node(&arr).unwrap();
  167. }
  168. }
  169. fn remove(&mut self, root: u64) {
  170. let node = self.nodes[root as usize].as_ref().unwrap();
  171. if let Some(element) = node.dyn_ref::<Element>() {
  172. element.remove();
  173. } else {
  174. if let Some(parent) = node.parent_node() {
  175. parent.remove_child(&node).unwrap();
  176. }
  177. }
  178. }
  179. fn remove_all_children(&mut self) {
  180. todo!()
  181. }
  182. fn create_placeholder(&mut self, id: u64) {
  183. self.create_element("pre", None, id);
  184. self.set_attribute("hidden", "", None);
  185. }
  186. fn create_text_node(&mut self, text: &str, id: u64) {
  187. let textnode = self
  188. .document
  189. .create_text_node(text)
  190. .dyn_into::<Node>()
  191. .unwrap();
  192. self.stack.push(textnode.clone());
  193. self.nodes[(id as usize)] = Some(textnode);
  194. }
  195. fn create_element(&mut self, tag: &str, ns: Option<&'static str>, id: u64) {
  196. let tag = wasm_bindgen::intern(tag);
  197. let el = match ns {
  198. Some(ns) => self
  199. .document
  200. .create_element_ns(Some(ns), tag)
  201. .unwrap()
  202. .dyn_into::<Node>()
  203. .unwrap(),
  204. None => self
  205. .document
  206. .create_element(tag)
  207. .unwrap()
  208. .dyn_into::<Node>()
  209. .unwrap(),
  210. };
  211. self.stack.push(el.clone());
  212. self.nodes[(id as usize)] = Some(el);
  213. }
  214. fn new_event_listener(&mut self, event: &'static str, scope: ScopeId, real_id: u64) {
  215. let event = wasm_bindgen::intern(event);
  216. // attach the correct attributes to the element
  217. // these will be used by accessing the event's target
  218. // This ensures we only ever have one handler attached to the root, but decide
  219. // dynamically when we want to call a listener.
  220. let el = self.stack.top();
  221. let el = el
  222. .dyn_ref::<Element>()
  223. .expect(&format!("not an element: {:?}", el));
  224. // let scope_id = scope.data().as_ffi();
  225. let scope_id = scope.0 as u64;
  226. el.set_attribute(
  227. &format!("dioxus-event-{}", event),
  228. &format!("{}.{}", scope_id, real_id),
  229. )
  230. .unwrap();
  231. // el.set_attribute(&format!("dioxus-event"), &format!("{}", event))
  232. // .unwrap();
  233. // Register the callback to decode
  234. if let Some(entry) = self.listeners.get_mut(event) {
  235. entry.0 += 1;
  236. } else {
  237. let trigger = self.sender_callback.clone();
  238. let handler = Closure::wrap(Box::new(move |event: &web_sys::Event| {
  239. // "Result" cannot be received from JS
  240. // Instead, we just build and immediately execute a closure that returns result
  241. match decode_trigger(event) {
  242. Ok(synthetic_event) => trigger.as_ref()(SchedulerMsg::UiEvent(synthetic_event)),
  243. Err(e) => log::error!("Error decoding Dioxus event attribute. {:#?}", e),
  244. };
  245. }) as Box<dyn FnMut(&Event)>);
  246. self.root
  247. .add_event_listener_with_callback(event, (&handler).as_ref().unchecked_ref())
  248. .unwrap();
  249. // Increment the listeners
  250. self.listeners.insert(event.into(), (1, handler));
  251. }
  252. }
  253. fn remove_event_listener(&mut self, event: &str) {
  254. todo!()
  255. }
  256. fn set_text(&mut self, text: &str) {
  257. self.stack.top().set_text_content(Some(text))
  258. }
  259. fn set_attribute(&mut self, name: &str, value: &str, ns: Option<&str>) {
  260. let node = self.stack.top();
  261. if ns == Some("style") {
  262. if let Some(el) = node.dyn_ref::<Element>() {
  263. let el = el.dyn_ref::<HtmlElement>().unwrap();
  264. let style_dc: CssStyleDeclaration = el.style();
  265. style_dc.set_property(name, value).unwrap();
  266. }
  267. } else {
  268. let fallback = || {
  269. let el = node.dyn_ref::<Element>().unwrap();
  270. el.set_attribute(name, value).unwrap()
  271. };
  272. match name {
  273. "value" => {
  274. if let Some(input) = node.dyn_ref::<HtmlInputElement>() {
  275. /*
  276. if the attribute being set is the same as the value of the input, then don't bother setting it.
  277. This is used in controlled components to keep the cursor in the right spot.
  278. this logic should be moved into the virtualdom since we have the notion of "volatile"
  279. */
  280. if input.value() != value {
  281. input.set_value(value);
  282. }
  283. } else if let Some(node) = node.dyn_ref::<HtmlTextAreaElement>() {
  284. if name == "value" {
  285. node.set_value(value);
  286. }
  287. } else {
  288. fallback();
  289. }
  290. }
  291. "checked" => {
  292. if let Some(input) = node.dyn_ref::<HtmlInputElement>() {
  293. input.set_checked(true);
  294. } else {
  295. fallback();
  296. }
  297. }
  298. "selected" => {
  299. if let Some(node) = node.dyn_ref::<HtmlOptionElement>() {
  300. node.set_selected(true);
  301. } else {
  302. fallback();
  303. }
  304. }
  305. _ => fallback(),
  306. }
  307. }
  308. }
  309. fn remove_attribute(&mut self, name: &str) {
  310. let node = self.stack.top();
  311. if let Some(node) = node.dyn_ref::<web_sys::Element>() {
  312. node.remove_attribute(name).unwrap();
  313. }
  314. if let Some(node) = node.dyn_ref::<HtmlInputElement>() {
  315. // Some attributes are "volatile" and don't work through `removeAttribute`.
  316. if name == "value" {
  317. node.set_value("");
  318. }
  319. if name == "checked" {
  320. node.set_checked(false);
  321. }
  322. }
  323. if let Some(node) = node.dyn_ref::<HtmlOptionElement>() {
  324. if name == "selected" {
  325. node.set_selected(true);
  326. }
  327. }
  328. }
  329. fn insert_after(&mut self, n: u32, root: u64) {
  330. let old = self.nodes[root as usize].as_ref().unwrap();
  331. let arr: js_sys::Array = self
  332. .stack
  333. .list
  334. .drain((self.stack.list.len() - n as usize)..)
  335. .collect();
  336. if let Some(el) = old.dyn_ref::<Element>() {
  337. el.after_with_node(&arr).unwrap();
  338. } else if let Some(el) = old.dyn_ref::<web_sys::CharacterData>() {
  339. el.after_with_node(&arr).unwrap();
  340. } else if let Some(el) = old.dyn_ref::<web_sys::DocumentType>() {
  341. el.after_with_node(&arr).unwrap();
  342. }
  343. }
  344. fn insert_before(&mut self, n: u32, root: u64) {
  345. let after = self.nodes[root as usize].as_ref().unwrap();
  346. if n == 1 {
  347. let before = self.stack.pop();
  348. after
  349. .parent_node()
  350. .unwrap()
  351. .insert_before(&before, Some(&after))
  352. .unwrap();
  353. after.insert_before(&before, None).unwrap();
  354. } else {
  355. let arr: js_sys::Array = self
  356. .stack
  357. .list
  358. .drain((self.stack.list.len() - n as usize)..)
  359. .collect();
  360. if let Some(el) = after.dyn_ref::<Element>() {
  361. el.before_with_node(&arr).unwrap();
  362. } else if let Some(el) = after.dyn_ref::<web_sys::CharacterData>() {
  363. el.before_with_node(&arr).unwrap();
  364. } else if let Some(el) = after.dyn_ref::<web_sys::DocumentType>() {
  365. el.before_with_node(&arr).unwrap();
  366. }
  367. }
  368. }
  369. }
  370. #[derive(Debug, Default)]
  371. struct Stack {
  372. list: Vec<Node>,
  373. }
  374. impl Stack {
  375. #[inline]
  376. fn with_capacity(cap: usize) -> Self {
  377. Stack {
  378. list: Vec::with_capacity(cap),
  379. }
  380. }
  381. #[inline]
  382. fn push(&mut self, node: Node) {
  383. self.list.push(node);
  384. }
  385. #[inline]
  386. fn pop(&mut self) -> Node {
  387. self.list.pop().unwrap()
  388. }
  389. fn top(&self) -> &Node {
  390. match self.list.last() {
  391. Some(a) => a,
  392. None => panic!("Called 'top' of an empty stack, make sure to push the root first"),
  393. }
  394. }
  395. }
  396. // todo: some of these events are being casted to the wrong event type.
  397. // We need tests that simulate clicks/etc and make sure every event type works.
  398. fn virtual_event_from_websys_event(event: web_sys::Event) -> SyntheticEvent {
  399. use crate::events::*;
  400. use dioxus_core::events::on::*;
  401. match event.type_().as_str() {
  402. "copy" | "cut" | "paste" => {
  403. SyntheticEvent::ClipboardEvent(ClipboardEvent(Arc::new(WebsysClipboardEvent(event))))
  404. }
  405. "compositionend" | "compositionstart" | "compositionupdate" => {
  406. let evt: web_sys::CompositionEvent = event.dyn_into().unwrap();
  407. SyntheticEvent::CompositionEvent(CompositionEvent(Arc::new(WebsysCompositionEvent(
  408. evt,
  409. ))))
  410. }
  411. "keydown" | "keypress" | "keyup" => {
  412. let evt: web_sys::KeyboardEvent = event.dyn_into().unwrap();
  413. SyntheticEvent::KeyboardEvent(KeyboardEvent(Arc::new(WebsysKeyboardEvent(evt))))
  414. }
  415. "focus" | "blur" => {
  416. let evt: web_sys::FocusEvent = event.dyn_into().unwrap();
  417. SyntheticEvent::FocusEvent(FocusEvent(Arc::new(WebsysFocusEvent(evt))))
  418. }
  419. "change" => {
  420. let evt = event.dyn_into().unwrap();
  421. SyntheticEvent::GenericEvent(GenericEvent(Arc::new(WebsysGenericUiEvent(evt))))
  422. }
  423. "input" | "invalid" | "reset" | "submit" => {
  424. let evt: web_sys::Event = event.dyn_into().unwrap();
  425. SyntheticEvent::FormEvent(FormEvent(Arc::new(WebsysFormEvent(evt))))
  426. }
  427. "click" | "contextmenu" | "doubleclick" | "drag" | "dragend" | "dragenter" | "dragexit"
  428. | "dragleave" | "dragover" | "dragstart" | "drop" | "mousedown" | "mouseenter"
  429. | "mouseleave" | "mousemove" | "mouseout" | "mouseover" | "mouseup" => {
  430. let evt: web_sys::MouseEvent = event.dyn_into().unwrap();
  431. SyntheticEvent::MouseEvent(MouseEvent(Arc::new(WebsysMouseEvent(evt))))
  432. }
  433. "pointerdown" | "pointermove" | "pointerup" | "pointercancel" | "gotpointercapture"
  434. | "lostpointercapture" | "pointerenter" | "pointerleave" | "pointerover" | "pointerout" => {
  435. let evt: web_sys::PointerEvent = event.dyn_into().unwrap();
  436. SyntheticEvent::PointerEvent(PointerEvent(Arc::new(WebsysPointerEvent(evt))))
  437. }
  438. "select" => {
  439. let evt: web_sys::UiEvent = event.dyn_into().unwrap();
  440. SyntheticEvent::SelectionEvent(SelectionEvent(Arc::new(WebsysGenericUiEvent(evt))))
  441. }
  442. "touchcancel" | "touchend" | "touchmove" | "touchstart" => {
  443. let evt: web_sys::TouchEvent = event.dyn_into().unwrap();
  444. SyntheticEvent::TouchEvent(TouchEvent(Arc::new(WebsysTouchEvent(evt))))
  445. }
  446. "scroll" => {
  447. let evt: web_sys::UiEvent = event.dyn_into().unwrap();
  448. SyntheticEvent::GenericEvent(GenericEvent(Arc::new(WebsysGenericUiEvent(evt))))
  449. }
  450. "wheel" => {
  451. let evt: web_sys::WheelEvent = event.dyn_into().unwrap();
  452. SyntheticEvent::WheelEvent(WheelEvent(Arc::new(WebsysWheelEvent(evt))))
  453. }
  454. "animationstart" | "animationend" | "animationiteration" => {
  455. let evt: web_sys::AnimationEvent = event.dyn_into().unwrap();
  456. SyntheticEvent::AnimationEvent(AnimationEvent(Arc::new(WebsysAnimationEvent(evt))))
  457. }
  458. "transitionend" => {
  459. let evt: web_sys::TransitionEvent = event.dyn_into().unwrap();
  460. SyntheticEvent::TransitionEvent(TransitionEvent(Arc::new(WebsysTransitionEvent(evt))))
  461. }
  462. "abort" | "canplay" | "canplaythrough" | "durationchange" | "emptied" | "encrypted"
  463. | "ended" | "error" | "loadeddata" | "loadedmetadata" | "loadstart" | "pause" | "play"
  464. | "playing" | "progress" | "ratechange" | "seeked" | "seeking" | "stalled" | "suspend"
  465. | "timeupdate" | "volumechange" | "waiting" => {
  466. let evt: web_sys::UiEvent = event.dyn_into().unwrap();
  467. SyntheticEvent::MediaEvent(MediaEvent(Arc::new(WebsysMediaEvent(evt))))
  468. }
  469. "toggle" => {
  470. let evt: web_sys::UiEvent = event.dyn_into().unwrap();
  471. SyntheticEvent::ToggleEvent(ToggleEvent(Arc::new(WebsysToggleEvent(evt))))
  472. }
  473. _ => {
  474. let evt: web_sys::UiEvent = event.dyn_into().unwrap();
  475. SyntheticEvent::GenericEvent(GenericEvent(Arc::new(WebsysGenericUiEvent(evt))))
  476. }
  477. }
  478. }
  479. /// This function decodes a websys event and produces an EventTrigger
  480. /// With the websys implementation, we attach a unique key to the nodes
  481. fn decode_trigger(event: &web_sys::Event) -> anyhow::Result<UserEvent> {
  482. let target = event
  483. .target()
  484. .expect("missing target")
  485. .dyn_into::<Element>()
  486. .expect("not a valid element");
  487. let typ = event.type_();
  488. // TODO: clean this up
  489. if cfg!(debug_assertions) {
  490. let attrs = target.attributes();
  491. for x in 0..attrs.length() {
  492. let attr: Attr = attrs.item(x).unwrap();
  493. // log::debug!("attrs include: {:#?}, {:#?}", attr.name(), attr.value());
  494. }
  495. }
  496. use anyhow::Context;
  497. // The error handling here is not very descriptive and needs to be replaced with a zero-cost error system
  498. let val: String = target
  499. .get_attribute(&format!("dioxus-event-{}", typ))
  500. .context(format!("wrong format - received {:#?}", typ))?;
  501. let mut fields = val.splitn(3, ".");
  502. let gi_id = fields
  503. .next()
  504. .and_then(|f| f.parse::<u64>().ok())
  505. .context("failed to parse gi id")?;
  506. let real_id = fields
  507. .next()
  508. .and_then(|raw_id| raw_id.parse::<u64>().ok())
  509. .context("failed to parse real id")?;
  510. let triggered_scope = gi_id;
  511. Ok(UserEvent {
  512. name: event_name_from_typ(&typ),
  513. event: virtual_event_from_websys_event(event.clone()),
  514. mounted_dom_id: Some(ElementId(real_id as usize)),
  515. scope: ScopeId(triggered_scope as usize),
  516. })
  517. }
  518. pub(crate) fn load_document() -> Document {
  519. web_sys::window()
  520. .expect("should have access to the Window")
  521. .document()
  522. .expect("should have access to the Document")
  523. }
  524. fn event_name_from_typ(typ: &str) -> &'static str {
  525. match typ {
  526. "copy" => "copy",
  527. "cut" => "cut",
  528. "paste" => "paste",
  529. "compositionend" => "compositionend",
  530. "compositionstart" => "compositionstart",
  531. "compositionupdate" => "compositionupdate",
  532. "keydown" => "keydown",
  533. "keypress" => "keypress",
  534. "keyup" => "keyup",
  535. "focus" => "focus",
  536. "blur" => "blur",
  537. "change" => "change",
  538. "input" => "input",
  539. "invalid" => "invalid",
  540. "reset" => "reset",
  541. "submit" => "submit",
  542. "click" => "click",
  543. "contextmenu" => "contextmenu",
  544. "doubleclick" => "doubleclick",
  545. "drag" => "drag",
  546. "dragend" => "dragend",
  547. "dragenter" => "dragenter",
  548. "dragexit" => "dragexit",
  549. "dragleave" => "dragleave",
  550. "dragover" => "dragover",
  551. "dragstart" => "dragstart",
  552. "drop" => "drop",
  553. "mousedown" => "mousedown",
  554. "mouseenter" => "mouseenter",
  555. "mouseleave" => "mouseleave",
  556. "mousemove" => "mousemove",
  557. "mouseout" => "mouseout",
  558. "mouseover" => "mouseover",
  559. "mouseup" => "mouseup",
  560. "pointerdown" => "pointerdown",
  561. "pointermove" => "pointermove",
  562. "pointerup" => "pointerup",
  563. "pointercancel" => "pointercancel",
  564. "gotpointercapture" => "gotpointercapture",
  565. "lostpointercapture" => "lostpointercapture",
  566. "pointerenter" => "pointerenter",
  567. "pointerleave" => "pointerleave",
  568. "pointerover" => "pointerover",
  569. "pointerout" => "pointerout",
  570. "select" => "select",
  571. "touchcancel" => "touchcancel",
  572. "touchend" => "touchend",
  573. "touchmove" => "touchmove",
  574. "touchstart" => "touchstart",
  575. "scroll" => "scroll",
  576. "wheel" => "wheel",
  577. "animationstart" => "animationstart",
  578. "animationend" => "animationend",
  579. "animationiteration" => "animationiteration",
  580. "transitionend" => "transitionend",
  581. "abort" => "abort",
  582. "canplay" => "canplay",
  583. "canplaythrough" => "canplaythrough",
  584. "durationchange" => "durationchange",
  585. "emptied" => "emptied",
  586. "encrypted" => "encrypted",
  587. "ended" => "ended",
  588. "error" => "error",
  589. "loadeddata" => "loadeddata",
  590. "loadedmetadata" => "loadedmetadata",
  591. "loadstart" => "loadstart",
  592. "pause" => "pause",
  593. "play" => "play",
  594. "playing" => "playing",
  595. "progress" => "progress",
  596. "ratechange" => "ratechange",
  597. "seeked" => "seeked",
  598. "seeking" => "seeking",
  599. "stalled" => "stalled",
  600. "suspend" => "suspend",
  601. "timeupdate" => "timeupdate",
  602. "volumechange" => "volumechange",
  603. "waiting" => "waiting",
  604. "toggle" => "toggle",
  605. _ => {
  606. panic!("unsupported event type")
  607. }
  608. }
  609. }