index.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463
  1. const bool_attrs = {
  2. allowfullscreen: true,
  3. allowpaymentrequest: true,
  4. async: true,
  5. autofocus: true,
  6. autoplay: true,
  7. checked: true,
  8. controls: true,
  9. default: true,
  10. defer: true,
  11. disabled: true,
  12. formnovalidate: true,
  13. hidden: true,
  14. ismap: true,
  15. itemscope: true,
  16. loop: true,
  17. multiple: true,
  18. muted: true,
  19. nomodule: true,
  20. novalidate: true,
  21. open: true,
  22. playsinline: true,
  23. readonly: true,
  24. required: true,
  25. reversed: true,
  26. selected: true,
  27. truespeed: true,
  28. };
  29. function serialize_event(event) {
  30. switch (event.type) {
  31. case "copy":
  32. case "cut":
  33. case "past":
  34. return {};
  35. case "compositionend":
  36. case "compositionstart":
  37. case "compositionupdate":
  38. return {
  39. data: event.data,
  40. };
  41. case "keydown":
  42. case "keypress":
  43. case "keyup":
  44. return {
  45. char_code: event.charCode,
  46. key: event.key,
  47. alt_key: event.altKey,
  48. ctrl_key: event.ctrlKey,
  49. meta_key: event.metaKey,
  50. key_code: event.keyCode,
  51. shift_key: event.shiftKey,
  52. locale: "locale",
  53. location: event.location,
  54. repeat: event.repeat,
  55. which: event.which,
  56. // locale: event.locale,
  57. };
  58. case "focus":
  59. case "blur":
  60. return {};
  61. case "change":
  62. let target = event.target;
  63. let value;
  64. if (target.type === "checkbox" || target.type === "radio") {
  65. value = target.checked ? "true" : "false";
  66. } else {
  67. value = target.value ?? target.textContent;
  68. }
  69. return {
  70. value: value,
  71. };
  72. case "input":
  73. case "invalid":
  74. case "reset":
  75. case "submit": {
  76. let target = event.target;
  77. let value = target.value ?? target.textContent;
  78. if (target.type == "checkbox") {
  79. value = target.checked ? "true" : "false";
  80. }
  81. return {
  82. value: value,
  83. };
  84. }
  85. case "click":
  86. case "contextmenu":
  87. case "doubleclick":
  88. case "drag":
  89. case "dragend":
  90. case "dragenter":
  91. case "dragexit":
  92. case "dragleave":
  93. case "dragover":
  94. case "dragstart":
  95. case "drop":
  96. case "mousedown":
  97. case "mouseenter":
  98. case "mouseleave":
  99. case "mousemove":
  100. case "mouseout":
  101. case "mouseover":
  102. case "mouseup":
  103. return {
  104. alt_key: event.altKey,
  105. button: event.button,
  106. buttons: event.buttons,
  107. client_x: event.clientX,
  108. client_y: event.clientY,
  109. ctrl_key: event.ctrlKey,
  110. meta_key: event.metaKey,
  111. page_x: event.pageX,
  112. page_y: event.pageY,
  113. screen_x: event.screenX,
  114. screen_y: event.screenY,
  115. shift_key: event.shiftKey,
  116. };
  117. case "pointerdown":
  118. case "pointermove":
  119. case "pointerup":
  120. case "pointercancel":
  121. case "gotpointercapture":
  122. case "lostpointercapture":
  123. case "pointerenter":
  124. case "pointerleave":
  125. case "pointerover":
  126. case "pointerout":
  127. return {
  128. alt_key: event.altKey,
  129. button: event.button,
  130. buttons: event.buttons,
  131. client_x: event.clientX,
  132. client_y: event.clientY,
  133. ctrl_key: event.ctrlKey,
  134. meta_key: event.metaKey,
  135. page_x: event.pageX,
  136. page_y: event.pageY,
  137. screen_x: event.screenX,
  138. screen_y: event.screenY,
  139. shift_key: event.shiftKey,
  140. pointer_id: event.pointerId,
  141. width: event.width,
  142. height: event.height,
  143. pressure: event.pressure,
  144. tangential_pressure: event.tangentialPressure,
  145. tilt_x: event.tiltX,
  146. tilt_y: event.tiltY,
  147. twist: event.twist,
  148. pointer_type: event.pointerType,
  149. is_primary: event.isPrimary,
  150. };
  151. case "select":
  152. return {};
  153. case "touchcancel":
  154. case "touchend":
  155. case "touchmove":
  156. case "touchstart":
  157. return {
  158. alt_key: event.altKey,
  159. ctrl_key: event.ctrlKey,
  160. meta_key: event.metaKey,
  161. shift_key: event.shiftKey,
  162. // changed_touches: event.changedTouches,
  163. // target_touches: event.targetTouches,
  164. // touches: event.touches,
  165. };
  166. case "scroll":
  167. return {};
  168. case "wheel":
  169. return {
  170. delta_x: event.deltaX,
  171. delta_y: event.deltaY,
  172. delta_z: event.deltaZ,
  173. delta_mode: event.deltaMode,
  174. };
  175. case "animationstart":
  176. case "animationend":
  177. case "animationiteration":
  178. return {
  179. animation_name: event.animationName,
  180. elapsed_time: event.elapsedTime,
  181. pseudo_element: event.pseudoElement,
  182. };
  183. case "transitionend":
  184. return {
  185. property_name: event.propertyName,
  186. elapsed_time: event.elapsedTime,
  187. pseudo_element: event.pseudoElement,
  188. };
  189. case "abort":
  190. case "canplay":
  191. case "canplaythrough":
  192. case "durationchange":
  193. case "emptied":
  194. case "encrypted":
  195. case "ended":
  196. case "error":
  197. case "loadeddata":
  198. case "loadedmetadata":
  199. case "loadstart":
  200. case "pause":
  201. case "play":
  202. case "playing":
  203. case "progress":
  204. case "ratechange":
  205. case "seeked":
  206. case "seeking":
  207. case "stalled":
  208. case "suspend":
  209. case "timeupdate":
  210. case "volumechange":
  211. case "waiting":
  212. return {};
  213. case "toggle":
  214. return {};
  215. default:
  216. return {};
  217. }
  218. }
  219. class Interpreter {
  220. constructor(root) {
  221. this.root = root;
  222. this.stack = [root];
  223. this.listeners = {
  224. onclick: {},
  225. };
  226. this.lastNodeWasText = false;
  227. this.nodes = [root];
  228. }
  229. top() {
  230. return this.stack[this.stack.length - 1];
  231. }
  232. pop() {
  233. return this.stack.pop();
  234. }
  235. PushRoot(edit) {
  236. const id = edit.root;
  237. const node = this.nodes[id];
  238. this.stack.push(node);
  239. }
  240. AppendChildren(edit) {
  241. let root = this.stack[this.stack.length - (1 + edit.many)];
  242. let to_add = this.stack.splice(this.stack.length - edit.many);
  243. for (let i = 0; i < edit.many; i++) {
  244. root.appendChild(to_add[i]);
  245. }
  246. }
  247. ReplaceWith(edit) {
  248. let root = this.nodes[edit.root];
  249. let els = this.stack.splice(this.stack.length - edit.m);
  250. root.replaceWith(...els);
  251. }
  252. InsertAfter(edit) {
  253. let old = this.nodes[edit.root];
  254. let new_nodes = this.stack.splice(this.stack.length - edit.n);
  255. old.after(...new_nodes);
  256. }
  257. InsertBefore(edit) {
  258. let old = this.nodes[edit.root];
  259. let new_nodes = this.stack.splice(this.stack.length - edit.n);
  260. old.before(...new_nodes);
  261. }
  262. Remove(edit) {
  263. let node = this.nodes[edit.root];
  264. if (node !== undefined) {
  265. node.remove();
  266. }
  267. }
  268. CreateTextNode(edit) {
  269. const node = document.createTextNode(edit.text);
  270. this.nodes[edit.root] = node;
  271. this.stack.push(node);
  272. }
  273. CreateElement(edit) {
  274. const tagName = edit.tag;
  275. const el = document.createElement(tagName);
  276. this.nodes[edit.root] = el;
  277. el.setAttribute("dioxus-id", edit.root);
  278. this.stack.push(el);
  279. }
  280. CreateElementNs(edit) {
  281. let el = document.createElementNS(edit.ns, edit.tag);
  282. this.stack.push(el);
  283. this.nodes[edit.root] = el;
  284. }
  285. CreatePlaceholder(edit) {
  286. let el = document.createElement("pre");
  287. el.hidden = true;
  288. this.stack.push(el);
  289. this.nodes[edit.root] = el;
  290. }
  291. RemoveEventListener(edit) {}
  292. NewEventListener(edit) {
  293. const event_name = edit.event_name;
  294. const mounted_node_id = edit.root;
  295. const scope = edit.scope;
  296. const element = this.nodes[edit.root];
  297. element.setAttribute(
  298. `dioxus-event-${event_name}`,
  299. `${scope}.${mounted_node_id}`
  300. );
  301. if (this.listeners[event_name] === undefined) {
  302. this.listeners[event_name] = true;
  303. this.root.addEventListener(event_name, (event) => {
  304. const target = event.target;
  305. const real_id = target.getAttribute(`dioxus-id`);
  306. const should_prevent_default = target.getAttribute(
  307. `dioxus-prevent-default`
  308. );
  309. let contents = serialize_event(event);
  310. if (should_prevent_default === `on${event.type}`) {
  311. event.preventDefault();
  312. }
  313. console.log(event);
  314. if (event.type == "click") {
  315. event.preventDefault();
  316. if (should_prevent_default !== `onclick`) {
  317. console.log(event.target.getAttribute("href"));
  318. if(element.tagName == "A") {
  319. rpc.call("browser_open", {
  320. mounted_dom_id: parseInt(real_id),
  321. href: event.target.getAttribute("href")
  322. })
  323. }
  324. }
  325. }
  326. if (real_id == null) {
  327. return;
  328. }
  329. rpc.call("user_event", {
  330. event: event_name,
  331. mounted_dom_id: parseInt(real_id),
  332. contents: contents,
  333. });
  334. });
  335. }
  336. }
  337. SetText(edit) {
  338. this.nodes[edit.root].textContent = edit.text;
  339. }
  340. SetAttribute(edit) {
  341. // console.log("setting attr", edit);
  342. const name = edit.field;
  343. const value = edit.value;
  344. const ns = edit.ns;
  345. const node = this.nodes[edit.root];
  346. if (ns == "style") {
  347. node.style[name] = value;
  348. } else if (ns != null || ns != undefined) {
  349. node.setAttributeNS(ns, name, value);
  350. } else {
  351. switch (name) {
  352. case "value":
  353. if (value != node.value) {
  354. node.value = value;
  355. }
  356. break;
  357. case "checked":
  358. node.checked = value === "true";
  359. break;
  360. case "selected":
  361. node.selected = value === "true";
  362. break;
  363. case "dangerous_inner_html":
  364. node.innerHTML = value;
  365. break;
  366. default:
  367. // https://github.com/facebook/react/blob/8b88ac2592c5f555f315f9440cbb665dd1e7457a/packages/react-dom/src/shared/DOMProperty.js#L352-L364
  368. if (value == "false" && bool_attrs.hasOwnProperty(name)) {
  369. node.removeAttribute(name);
  370. } else {
  371. node.setAttribute(name, value);
  372. }
  373. }
  374. }
  375. }
  376. RemoveAttribute(edit) {
  377. const name = edit.field;
  378. const node = this.nodes[edit.root];
  379. node.removeAttribute(name);
  380. if (name === "value") {
  381. node.value = null;
  382. }
  383. if (name === "checked") {
  384. node.checked = false;
  385. }
  386. if (name === "selected") {
  387. node.selected = false;
  388. }
  389. }
  390. handleEdits(edits) {
  391. this.stack.push(this.root);
  392. for (let x = 0; x < edits.length; x++) {
  393. let edit = edits[x];
  394. let f = this[edit.type];
  395. f.call(this, edit);
  396. }
  397. }
  398. }
  399. function main() {
  400. let root = window.document.getElementById("main");
  401. window.interpreter = new Interpreter(root);
  402. rpc.call("initialize");
  403. }
  404. main();