interpreter.js 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881
  1. export function main() {
  2. let root = window.document.getElementById("main");
  3. if (root != null) {
  4. window.interpreter = new Interpreter(root);
  5. window.ipc.postMessage(serializeIpcMessage("initialize"));
  6. }
  7. }
  8. class ListenerMap {
  9. constructor(root) {
  10. // bubbling events can listen at the root element
  11. this.global = {};
  12. // non bubbling events listen at the element the listener was created at
  13. this.local = {};
  14. this.root = root;
  15. }
  16. create(event_name, element, handler, bubbles) {
  17. if (bubbles) {
  18. if (this.global[event_name] === undefined) {
  19. this.global[event_name] = {};
  20. this.global[event_name].active = 1;
  21. this.global[event_name].callback = handler;
  22. this.root.addEventListener(event_name, handler);
  23. } else {
  24. this.global[event_name].active++;
  25. }
  26. }
  27. else {
  28. const id = element.getAttribute("data-dioxus-id");
  29. if (!this.local[id]) {
  30. this.local[id] = {};
  31. }
  32. this.local[id][event_name] = handler;
  33. element.addEventListener(event_name, handler);
  34. }
  35. }
  36. remove(element, event_name, bubbles) {
  37. if (bubbles) {
  38. this.global[event_name].active--;
  39. if (this.global[event_name].active === 0) {
  40. this.root.removeEventListener(event_name, this.global[event_name].callback);
  41. delete this.global[event_name];
  42. }
  43. }
  44. else {
  45. const id = element.getAttribute("data-dioxus-id");
  46. delete this.local[id][event_name];
  47. if (this.local[id].length === 0) {
  48. delete this.local[id];
  49. }
  50. element.removeEventListener(event_name, handler);
  51. }
  52. }
  53. removeAllNonBubbling(element) {
  54. const id = element.getAttribute("data-dioxus-id");
  55. delete this.local[id];
  56. }
  57. }
  58. export class Interpreter {
  59. constructor(root) {
  60. this.root = root;
  61. this.listeners = new ListenerMap(root);
  62. this.nodes = [root];
  63. this.stack = [root];
  64. this.handlers = {};
  65. this.templates = {};
  66. this.lastNodeWasText = false;
  67. }
  68. top() {
  69. return this.stack[this.stack.length - 1];
  70. }
  71. pop() {
  72. return this.stack.pop();
  73. }
  74. SaveTemplate(nodes, name) {
  75. this.templates[name] = nodes;
  76. }
  77. MountToRoot() {
  78. this.AppendChildren(this.stack.length - 1);
  79. }
  80. SetNode(id, node) {
  81. this.nodes[id] = node;
  82. }
  83. PushRoot(root) {
  84. const node = this.nodes[root];
  85. this.stack.push(node);
  86. }
  87. PopRoot() {
  88. this.stack.pop();
  89. }
  90. AppendChildren(many) {
  91. let root = this.stack[this.stack.length - (1 + many)];
  92. let to_add = this.stack.splice(this.stack.length - many);
  93. for (let i = 0; i < many; i++) {
  94. root.appendChild(to_add[i]);
  95. }
  96. }
  97. ReplaceWith(root_id, m) {
  98. let root = this.nodes[root_id];
  99. let els = this.stack.splice(this.stack.length - m);
  100. if (is_element_node(root.nodeType)) {
  101. this.listeners.removeAllNonBubbling(root);
  102. }
  103. root.replaceWith(...els);
  104. }
  105. InsertAfter(root, n) {
  106. let old = this.nodes[root];
  107. let new_nodes = this.stack.splice(this.stack.length - n);
  108. old.after(...new_nodes);
  109. }
  110. InsertBefore(root, n) {
  111. let old = this.nodes[root];
  112. let new_nodes = this.stack.splice(this.stack.length - n);
  113. old.before(...new_nodes);
  114. }
  115. Remove(root) {
  116. let node = this.nodes[root];
  117. if (node !== undefined) {
  118. if (is_element_node(node)) {
  119. this.listeners.removeAllNonBubbling(node);
  120. }
  121. node.remove();
  122. }
  123. }
  124. CreateRawText(text) {
  125. this.stack.push(document.createTextNode(text));
  126. }
  127. CreateTextNode(text, root) {
  128. const node = document.createTextNode(text);
  129. this.nodes[root] = node;
  130. this.stack.push(node);
  131. }
  132. CreatePlaceholder(root) {
  133. let el = document.createElement("pre");
  134. el.hidden = true;
  135. this.stack.push(el);
  136. this.nodes[root] = el;
  137. }
  138. NewEventListener(event_name, root, handler, bubbles) {
  139. const element = this.nodes[root];
  140. element.setAttribute("data-dioxus-id", `${root}`);
  141. this.listeners.create(event_name, element, handler, bubbles);
  142. }
  143. RemoveEventListener(root, event_name, bubbles) {
  144. const element = this.nodes[root];
  145. element.removeAttribute(`data-dioxus-id`);
  146. this.listeners.remove(element, event_name, bubbles);
  147. }
  148. SetText(root, text) {
  149. this.nodes[root].textContent = text;
  150. }
  151. SetAttribute(id, field, value, ns) {
  152. const node = this.nodes[id];
  153. this.SetAttributeInner(node, field, value, ns);
  154. }
  155. SetAttributeInner(node, field, value, ns) {
  156. const name = field;
  157. if (ns === "style") {
  158. // ????? why do we need to do this
  159. if (node.style === undefined) {
  160. node.style = {};
  161. }
  162. node.style[name] = value;
  163. } else if (ns != null && ns != undefined) {
  164. node.setAttributeNS(ns, name, value);
  165. } else {
  166. switch (name) {
  167. case "value":
  168. if (value !== node.value) {
  169. node.value = value;
  170. }
  171. break;
  172. case "checked":
  173. node.checked = value === "true";
  174. break;
  175. case "selected":
  176. node.selected = value === "true";
  177. break;
  178. case "dangerous_inner_html":
  179. node.innerHTML = value;
  180. break;
  181. default:
  182. // https://github.com/facebook/react/blob/8b88ac2592c5f555f315f9440cbb665dd1e7457a/packages/react-dom/src/shared/DOMProperty.js#L352-L364
  183. if (value === "false" && bool_attrs.hasOwnProperty(name)) {
  184. node.removeAttribute(name);
  185. } else {
  186. node.setAttribute(name, value);
  187. }
  188. }
  189. }
  190. }
  191. RemoveAttribute(root, field, ns) {
  192. const name = field;
  193. const node = this.nodes[root];
  194. if (ns == "style") {
  195. node.style.removeProperty(name);
  196. } else if (ns !== null || ns !== undefined) {
  197. node.removeAttributeNS(ns, name);
  198. } else if (name === "value") {
  199. node.value = "";
  200. } else if (name === "checked") {
  201. node.checked = false;
  202. } else if (name === "selected") {
  203. node.selected = false;
  204. } else if (name === "dangerous_inner_html") {
  205. node.innerHTML = "";
  206. } else {
  207. node.removeAttribute(name);
  208. }
  209. }
  210. handleEdits(edits) {
  211. for (let edit of edits) {
  212. this.handleEdit(edit);
  213. }
  214. }
  215. AssignId(path, id) {
  216. this.nodes[id] = this.LoadChild(path);
  217. }
  218. LoadChild(path) {
  219. // iterate through each number and get that child
  220. let node = this.stack[this.stack.length - 1];
  221. for (let i = 0; i < path.length; i++) {
  222. node = node.childNodes[path[i]];
  223. }
  224. return node;
  225. }
  226. HydrateText(path, value, id) {
  227. let node = this.LoadChild(path);
  228. node.textContent = value;
  229. this.nodes[id] = node;
  230. }
  231. ReplacePlaceholder(path, m) {
  232. let els = this.stack.splice(this.stack.length - m);
  233. let node = this.LoadChild(path);
  234. node.replaceWith(...els);
  235. }
  236. LoadTemplate(name, index, id) {
  237. let node = this.templates[name][index].cloneNode(true);
  238. this.nodes[id] = node;
  239. this.stack.push(node);
  240. }
  241. handleEdit(edit) {
  242. switch (edit.type) {
  243. case "AppendChildren":
  244. this.AppendChildren(edit.m);
  245. break;
  246. case "AssignId":
  247. this.AssignId(edit.path, edit.id);
  248. break;
  249. case "CreatePlaceholder":
  250. this.CreatePlaceholder(edit.id);
  251. break;
  252. case "CreateTextNode":
  253. this.CreateTextNode(edit.value);
  254. break;
  255. case "HydrateText":
  256. this.HydrateText(edit.path, edit.value, edit.id);
  257. break;
  258. case "LoadTemplate":
  259. this.LoadTemplate(edit.name, edit.index, edit.id);
  260. break;
  261. case "PushRoot":
  262. this.PushRoot(edit.id);
  263. break;
  264. case "ReplaceWith":
  265. this.ReplaceWith(edit.id, edit.m);
  266. break;
  267. case "ReplacePlaceholder":
  268. this.ReplacePlaceholder(edit.path, edit.m);
  269. break;
  270. case "InsertAfter":
  271. this.InsertAfter(edit.id, edit.m);
  272. break;
  273. case "InsertBefore":
  274. this.InsertBefore(edit.id, edit.m);
  275. break;
  276. case "Remove":
  277. this.Remove(edit.id);
  278. break;
  279. case "SetText":
  280. this.SetText(edit.id, edit.value);
  281. break;
  282. case "SetAttribute":
  283. this.SetAttribute(edit.id, edit.name, edit.value, edit.ns);
  284. break;
  285. case "SetBoolAttribute":
  286. this.SetAttribute(edit.id, edit.name, edit.value, edit.ns);
  287. break;
  288. case "RemoveAttribute":
  289. this.RemoveAttribute(edit.id, edit.name, edit.ns);
  290. break;
  291. case "RemoveEventListener":
  292. this.RemoveEventListener(edit.id, edit.event_name);
  293. break;
  294. case "NewEventListener":
  295. // console.log("creating listener! ", edit);
  296. // this handler is only provided on desktop implementations since this
  297. // method is not used by the web implementation
  298. let handler = (event) => {
  299. console.log("event", event);
  300. let target = event.target;
  301. if (target != null) {
  302. let realId = target.getAttribute(`data-dioxus-id`);
  303. let shouldPreventDefault = target.getAttribute(
  304. `dioxus-prevent-default`
  305. );
  306. if (event.type === "click") {
  307. // todo call prevent default if it's the right type of event
  308. if (shouldPreventDefault !== `onclick`) {
  309. if (target.tagName === "A") {
  310. event.preventDefault();
  311. const href = target.getAttribute("href");
  312. if (href !== "" && href !== null && href !== undefined) {
  313. window.ipc.postMessage(
  314. serializeIpcMessage("browser_open", { href })
  315. );
  316. }
  317. }
  318. }
  319. // also prevent buttons from submitting
  320. if (target.tagName === "BUTTON" && event.type == "submit") {
  321. event.preventDefault();
  322. }
  323. }
  324. // walk the tree to find the real element
  325. while (realId == null) {
  326. // we've reached the root we don't want to send an event
  327. if (target.parentElement === null) {
  328. return;
  329. }
  330. target = target.parentElement;
  331. realId = target.getAttribute(`data-dioxus-id`);
  332. }
  333. shouldPreventDefault = target.getAttribute(
  334. `dioxus-prevent-default`
  335. );
  336. let contents = serialize_event(event);
  337. if (shouldPreventDefault === `on${event.type}`) {
  338. event.preventDefault();
  339. }
  340. if (event.type === "submit") {
  341. event.preventDefault();
  342. }
  343. if (
  344. target.tagName === "FORM" &&
  345. (event.type === "submit" || event.type === "input")
  346. ) {
  347. for (let x = 0; x < target.elements.length; x++) {
  348. let element = target.elements[x];
  349. let name = element.getAttribute("name");
  350. if (name != null) {
  351. if (element.getAttribute("type") === "checkbox") {
  352. // @ts-ignore
  353. contents.values[name] = element.checked ? "true" : "false";
  354. } else if (element.getAttribute("type") === "radio") {
  355. if (element.checked) {
  356. contents.values[name] = element.value;
  357. }
  358. } else {
  359. // @ts-ignore
  360. contents.values[name] =
  361. element.value ?? element.textContent;
  362. }
  363. }
  364. }
  365. }
  366. if (realId === null) {
  367. return;
  368. }
  369. window.ipc.postMessage(
  370. serializeIpcMessage("user_event", {
  371. event: edit.event_name,
  372. mounted_dom_id: parseInt(realId),
  373. contents: contents,
  374. })
  375. );
  376. }
  377. };
  378. console.log("adding event listener", edit);
  379. this.NewEventListener(edit.event_name, edit.id, handler, event_bubbles(edit.event_name));
  380. break;
  381. }
  382. }
  383. }
  384. export function serialize_event(event) {
  385. switch (event.type) {
  386. case "copy":
  387. case "cut":
  388. case "past": {
  389. return {};
  390. }
  391. case "compositionend":
  392. case "compositionstart":
  393. case "compositionupdate": {
  394. let { data } = event;
  395. return {
  396. data,
  397. };
  398. }
  399. case "keydown":
  400. case "keypress":
  401. case "keyup": {
  402. let {
  403. charCode,
  404. key,
  405. altKey,
  406. ctrlKey,
  407. metaKey,
  408. keyCode,
  409. shiftKey,
  410. location,
  411. repeat,
  412. which,
  413. code,
  414. } = event;
  415. return {
  416. char_code: charCode,
  417. key: key,
  418. alt_key: altKey,
  419. ctrl_key: ctrlKey,
  420. meta_key: metaKey,
  421. key_code: keyCode,
  422. shift_key: shiftKey,
  423. location: location,
  424. repeat: repeat,
  425. which: which,
  426. code,
  427. };
  428. }
  429. case "focus":
  430. case "blur": {
  431. return {};
  432. }
  433. case "change": {
  434. let target = event.target;
  435. let value;
  436. if (target.type === "checkbox" || target.type === "radio") {
  437. value = target.checked ? "true" : "false";
  438. } else {
  439. value = target.value ?? target.textContent;
  440. }
  441. return {
  442. value: value,
  443. values: {},
  444. };
  445. }
  446. case "input":
  447. case "invalid":
  448. case "reset":
  449. case "submit": {
  450. let target = event.target;
  451. let value = target.value ?? target.textContent;
  452. if (target.type === "checkbox") {
  453. value = target.checked ? "true" : "false";
  454. }
  455. return {
  456. value: value,
  457. values: {},
  458. };
  459. }
  460. case "click":
  461. case "contextmenu":
  462. case "doubleclick":
  463. case "dblclick":
  464. case "drag":
  465. case "dragend":
  466. case "dragenter":
  467. case "dragexit":
  468. case "dragleave":
  469. case "dragover":
  470. case "dragstart":
  471. case "drop":
  472. case "mousedown":
  473. case "mouseenter":
  474. case "mouseleave":
  475. case "mousemove":
  476. case "mouseout":
  477. case "mouseover":
  478. case "mouseup": {
  479. const {
  480. altKey,
  481. button,
  482. buttons,
  483. clientX,
  484. clientY,
  485. ctrlKey,
  486. metaKey,
  487. offsetX,
  488. offsetY,
  489. pageX,
  490. pageY,
  491. screenX,
  492. screenY,
  493. shiftKey,
  494. } = event;
  495. return {
  496. alt_key: altKey,
  497. button: button,
  498. buttons: buttons,
  499. client_x: clientX,
  500. client_y: clientY,
  501. ctrl_key: ctrlKey,
  502. meta_key: metaKey,
  503. offset_x: offsetX,
  504. offset_y: offsetY,
  505. page_x: pageX,
  506. page_y: pageY,
  507. screen_x: screenX,
  508. screen_y: screenY,
  509. shift_key: shiftKey,
  510. };
  511. }
  512. case "pointerdown":
  513. case "pointermove":
  514. case "pointerup":
  515. case "pointercancel":
  516. case "gotpointercapture":
  517. case "lostpointercapture":
  518. case "pointerenter":
  519. case "pointerleave":
  520. case "pointerover":
  521. case "pointerout": {
  522. const {
  523. altKey,
  524. button,
  525. buttons,
  526. clientX,
  527. clientY,
  528. ctrlKey,
  529. metaKey,
  530. pageX,
  531. pageY,
  532. screenX,
  533. screenY,
  534. shiftKey,
  535. pointerId,
  536. width,
  537. height,
  538. pressure,
  539. tangentialPressure,
  540. tiltX,
  541. tiltY,
  542. twist,
  543. pointerType,
  544. isPrimary,
  545. } = event;
  546. return {
  547. alt_key: altKey,
  548. button: button,
  549. buttons: buttons,
  550. client_x: clientX,
  551. client_y: clientY,
  552. ctrl_key: ctrlKey,
  553. meta_key: metaKey,
  554. page_x: pageX,
  555. page_y: pageY,
  556. screen_x: screenX,
  557. screen_y: screenY,
  558. shift_key: shiftKey,
  559. pointer_id: pointerId,
  560. width: width,
  561. height: height,
  562. pressure: pressure,
  563. tangential_pressure: tangentialPressure,
  564. tilt_x: tiltX,
  565. tilt_y: tiltY,
  566. twist: twist,
  567. pointer_type: pointerType,
  568. is_primary: isPrimary,
  569. };
  570. }
  571. case "select": {
  572. return {};
  573. }
  574. case "touchcancel":
  575. case "touchend":
  576. case "touchmove":
  577. case "touchstart": {
  578. const { altKey, ctrlKey, metaKey, shiftKey } = event;
  579. return {
  580. // changed_touches: event.changedTouches,
  581. // target_touches: event.targetTouches,
  582. // touches: event.touches,
  583. alt_key: altKey,
  584. ctrl_key: ctrlKey,
  585. meta_key: metaKey,
  586. shift_key: shiftKey,
  587. };
  588. }
  589. case "scroll": {
  590. return {};
  591. }
  592. case "wheel": {
  593. const { deltaX, deltaY, deltaZ, deltaMode } = event;
  594. return {
  595. delta_x: deltaX,
  596. delta_y: deltaY,
  597. delta_z: deltaZ,
  598. delta_mode: deltaMode,
  599. };
  600. }
  601. case "animationstart":
  602. case "animationend":
  603. case "animationiteration": {
  604. const { animationName, elapsedTime, pseudoElement } = event;
  605. return {
  606. animation_name: animationName,
  607. elapsed_time: elapsedTime,
  608. pseudo_element: pseudoElement,
  609. };
  610. }
  611. case "transitionend": {
  612. const { propertyName, elapsedTime, pseudoElement } = event;
  613. return {
  614. property_name: propertyName,
  615. elapsed_time: elapsedTime,
  616. pseudo_element: pseudoElement,
  617. };
  618. }
  619. case "abort":
  620. case "canplay":
  621. case "canplaythrough":
  622. case "durationchange":
  623. case "emptied":
  624. case "encrypted":
  625. case "ended":
  626. case "error":
  627. case "loadeddata":
  628. case "loadedmetadata":
  629. case "loadstart":
  630. case "pause":
  631. case "play":
  632. case "playing":
  633. case "progress":
  634. case "ratechange":
  635. case "seeked":
  636. case "seeking":
  637. case "stalled":
  638. case "suspend":
  639. case "timeupdate":
  640. case "volumechange":
  641. case "waiting": {
  642. return {};
  643. }
  644. case "toggle": {
  645. return {};
  646. }
  647. default: {
  648. return {};
  649. }
  650. }
  651. }
  652. function serializeIpcMessage(method, params = {}) {
  653. return JSON.stringify({ method, params });
  654. }
  655. const bool_attrs = {
  656. allowfullscreen: true,
  657. allowpaymentrequest: true,
  658. async: true,
  659. autofocus: true,
  660. autoplay: true,
  661. checked: true,
  662. controls: true,
  663. default: true,
  664. defer: true,
  665. disabled: true,
  666. formnovalidate: true,
  667. hidden: true,
  668. ismap: true,
  669. itemscope: true,
  670. loop: true,
  671. multiple: true,
  672. muted: true,
  673. nomodule: true,
  674. novalidate: true,
  675. open: true,
  676. playsinline: true,
  677. readonly: true,
  678. required: true,
  679. reversed: true,
  680. selected: true,
  681. truespeed: true,
  682. };
  683. function is_element_node(node) {
  684. return node.nodeType == 1;
  685. }
  686. function event_bubbles(event) {
  687. switch (event) {
  688. case "copy":
  689. return true;
  690. case "cut":
  691. return true;
  692. case "paste":
  693. return true;
  694. case "compositionend":
  695. return true;
  696. case "compositionstart":
  697. return true;
  698. case "compositionupdate":
  699. return true;
  700. case "keydown":
  701. return true;
  702. case "keypress":
  703. return true;
  704. case "keyup":
  705. return true;
  706. case "focus":
  707. return false;
  708. case "focusout":
  709. return true;
  710. case "focusin":
  711. return true;
  712. case "blur":
  713. return false;
  714. case "change":
  715. return true;
  716. case "input":
  717. return true;
  718. case "invalid":
  719. return true;
  720. case "reset":
  721. return true;
  722. case "submit":
  723. return true;
  724. case "click":
  725. return true;
  726. case "contextmenu":
  727. return true;
  728. case "doubleclick":
  729. return true;
  730. case "dblclick":
  731. return true;
  732. case "drag":
  733. return true;
  734. case "dragend":
  735. return true;
  736. case "dragenter":
  737. return false;
  738. case "dragexit":
  739. return false;
  740. case "dragleave":
  741. return true;
  742. case "dragover":
  743. return true;
  744. case "dragstart":
  745. return true;
  746. case "drop":
  747. return true;
  748. case "mousedown":
  749. return true;
  750. case "mouseenter":
  751. return false;
  752. case "mouseleave":
  753. return false;
  754. case "mousemove":
  755. return true;
  756. case "mouseout":
  757. return true;
  758. case "scroll":
  759. return false;
  760. case "mouseover":
  761. return true;
  762. case "mouseup":
  763. return true;
  764. case "pointerdown":
  765. return true;
  766. case "pointermove":
  767. return true;
  768. case "pointerup":
  769. return true;
  770. case "pointercancel":
  771. return true;
  772. case "gotpointercapture":
  773. return true;
  774. case "lostpointercapture":
  775. return true;
  776. case "pointerenter":
  777. return false;
  778. case "pointerleave":
  779. return false;
  780. case "pointerover":
  781. return true;
  782. case "pointerout":
  783. return true;
  784. case "select":
  785. return true;
  786. case "touchcancel":
  787. return true;
  788. case "touchend":
  789. return true;
  790. case "touchmove":
  791. return true;
  792. case "touchstart":
  793. return true;
  794. case "wheel":
  795. return true;
  796. case "abort":
  797. return false;
  798. case "canplay":
  799. return false;
  800. case "canplaythrough":
  801. return false;
  802. case "durationchange":
  803. return false;
  804. case "emptied":
  805. return false;
  806. case "encrypted":
  807. return true;
  808. case "ended":
  809. return false;
  810. case "error":
  811. return false;
  812. case "loadeddata":
  813. return false;
  814. case "loadedmetadata":
  815. return false;
  816. case "loadstart":
  817. return false;
  818. case "pause":
  819. return false;
  820. case "play":
  821. return false;
  822. case "playing":
  823. return false;
  824. case "progress":
  825. return false;
  826. case "ratechange":
  827. return false;
  828. case "seeked":
  829. return false;
  830. case "seeking":
  831. return false;
  832. case "stalled":
  833. return false;
  834. case "suspend":
  835. return false;
  836. case "timeupdate":
  837. return false;
  838. case "volumechange":
  839. return false;
  840. case "waiting":
  841. return false;
  842. case "animationstart":
  843. return true;
  844. case "animationend":
  845. return true;
  846. case "animationiteration":
  847. return true;
  848. case "transitionend":
  849. return true;
  850. case "toggle":
  851. return true;
  852. }
  853. return true;
  854. }