sledgehammer_bindings.rs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436
  1. #[cfg(feature = "web")]
  2. use js_sys::Function;
  3. use sledgehammer_bindgen::bindgen;
  4. #[cfg(feature = "web")]
  5. use web_sys::Node;
  6. pub const SLEDGEHAMMER_JS: &str = GENERATED_JS;
  7. #[cfg(feature = "web")]
  8. #[bindgen(module)]
  9. mod js {
  10. const JS: &str = r#"
  11. class ListenerMap {
  12. constructor(root) {
  13. // bubbling events can listen at the root element
  14. this.global = {};
  15. // non bubbling events listen at the element the listener was created at
  16. this.local = {};
  17. this.root = null;
  18. this.handler = null;
  19. }
  20. create(event_name, element, bubbles) {
  21. if (bubbles) {
  22. if (this.global[event_name] === undefined) {
  23. this.global[event_name] = {};
  24. this.global[event_name].active = 1;
  25. this.root.addEventListener(event_name, this.handler);
  26. } else {
  27. this.global[event_name].active++;
  28. }
  29. }
  30. else {
  31. const id = element.getAttribute("data-dioxus-id");
  32. if (!this.local[id]) {
  33. this.local[id] = {};
  34. }
  35. element.addEventListener(event_name, this.handler);
  36. }
  37. }
  38. remove(element, event_name, bubbles) {
  39. if (bubbles) {
  40. this.global[event_name].active--;
  41. if (this.global[event_name].active === 0) {
  42. this.root.removeEventListener(event_name, this.global[event_name].callback);
  43. delete this.global[event_name];
  44. }
  45. }
  46. else {
  47. const id = element.getAttribute("data-dioxus-id");
  48. delete this.local[id][event_name];
  49. if (this.local[id].length === 0) {
  50. delete this.local[id];
  51. }
  52. element.removeEventListener(event_name, this.handler);
  53. }
  54. }
  55. removeAllNonBubbling(element) {
  56. const id = element.getAttribute("data-dioxus-id");
  57. delete this.local[id];
  58. }
  59. }
  60. function SetAttributeInner(node, field, value, ns) {
  61. const name = field;
  62. if (ns === "style") {
  63. // ????? why do we need to do this
  64. if (node.style === undefined) {
  65. node.style = {};
  66. }
  67. node.style[name] = value;
  68. } else if (ns !== null && ns !== undefined && ns !== "") {
  69. node.setAttributeNS(ns, name, value);
  70. } else {
  71. switch (name) {
  72. case "value":
  73. if (value !== node.value) {
  74. node.value = value;
  75. }
  76. break;
  77. case "initial_value":
  78. node.defaultValue = value;
  79. break;
  80. case "checked":
  81. node.checked = truthy(value);
  82. break;
  83. case "selected":
  84. node.selected = truthy(value);
  85. break;
  86. case "dangerous_inner_html":
  87. node.innerHTML = value;
  88. break;
  89. default:
  90. // https://github.com/facebook/react/blob/8b88ac2592c5f555f315f9440cbb665dd1e7457a/packages/react-dom/src/shared/DOMProperty.js#L352-L364
  91. if (!truthy(value) && bool_attrs.hasOwnProperty(name)) {
  92. node.removeAttribute(name);
  93. } else {
  94. node.setAttribute(name, value);
  95. }
  96. }
  97. }
  98. }
  99. function LoadChild(ptr, len) {
  100. // iterate through each number and get that child
  101. node = stack[stack.length - 1];
  102. ptr_end = ptr + len;
  103. for (; ptr < ptr_end; ptr++) {
  104. end = m.getUint8(ptr);
  105. for (node = node.firstChild; end > 0; end--) {
  106. node = node.nextSibling;
  107. }
  108. }
  109. return node;
  110. }
  111. const listeners = new ListenerMap();
  112. let nodes = [];
  113. let stack = [];
  114. let root;
  115. const templates = {};
  116. let node, els, end, ptr_end, k;
  117. export function save_template(nodes, tmpl_id) {
  118. templates[tmpl_id] = nodes;
  119. }
  120. export function set_node(id, node) {
  121. nodes[id] = node;
  122. }
  123. export function get_node(id) {
  124. return nodes[id];
  125. }
  126. export function initialize(root, handler) {
  127. listeners.handler = handler;
  128. nodes = [root];
  129. stack = [root];
  130. listeners.root = root;
  131. }
  132. function AppendChildren(id, many){
  133. root = nodes[id];
  134. els = stack.splice(stack.length-many);
  135. for (k = 0; k < many; k++) {
  136. root.appendChild(els[k]);
  137. }
  138. }
  139. const bool_attrs = {
  140. allowfullscreen: true,
  141. allowpaymentrequest: true,
  142. async: true,
  143. autofocus: true,
  144. autoplay: true,
  145. checked: true,
  146. controls: true,
  147. default: true,
  148. defer: true,
  149. disabled: true,
  150. formnovalidate: true,
  151. hidden: true,
  152. ismap: true,
  153. itemscope: true,
  154. loop: true,
  155. multiple: true,
  156. muted: true,
  157. nomodule: true,
  158. novalidate: true,
  159. open: true,
  160. playsinline: true,
  161. readonly: true,
  162. required: true,
  163. reversed: true,
  164. selected: true,
  165. truespeed: true,
  166. webkitdirectory: true,
  167. };
  168. function truthy(val) {
  169. return val === "true" || val === true;
  170. }
  171. "#;
  172. extern "C" {
  173. #[wasm_bindgen]
  174. pub fn save_template(nodes: Vec<Node>, tmpl_id: u16);
  175. #[wasm_bindgen]
  176. pub fn set_node(id: u32, node: Node);
  177. #[wasm_bindgen]
  178. pub fn get_node(id: u32) -> Node;
  179. #[wasm_bindgen]
  180. pub fn initialize(root: Node, handler: &Function);
  181. }
  182. fn mount_to_root() {
  183. "{AppendChildren(root, stack.length-1);}"
  184. }
  185. fn push_root(root: u32) {
  186. "{stack.push(nodes[$root$]);}"
  187. }
  188. fn append_children(id: u32, many: u16) {
  189. "{AppendChildren($id$, $many$);}"
  190. }
  191. fn pop_root() {
  192. "{stack.pop();}"
  193. }
  194. fn replace_with(id: u32, n: u16) {
  195. "{root = nodes[$id$]; els = stack.splice(stack.length-$n$); if (root.listening) { listeners.removeAllNonBubbling(root); } root.replaceWith(...els);}"
  196. }
  197. fn insert_after(id: u32, n: u16) {
  198. "{nodes[$id$].after(...stack.splice(stack.length-$n$));}"
  199. }
  200. fn insert_before(id: u32, n: u16) {
  201. "{nodes[$id$].before(...stack.splice(stack.length-$n$));}"
  202. }
  203. fn remove(id: u32) {
  204. "{node = nodes[$id$]; if (node !== undefined) { if (node.listening) { listeners.removeAllNonBubbling(node); } node.remove(); }}"
  205. }
  206. fn create_raw_text(text: &str) {
  207. "{stack.push(document.createTextNode($text$));}"
  208. }
  209. fn create_text_node(text: &str, id: u32) {
  210. "{node = document.createTextNode($text$); nodes[$id$] = node; stack.push(node);}"
  211. }
  212. fn create_placeholder(id: u32) {
  213. "{node = document.createElement('pre'); node.hidden = true; stack.push(node); nodes[$id$] = node;}"
  214. }
  215. fn new_event_listener(event_name: &str<u8, evt>, id: u32, bubbles: u8) {
  216. r#"node = nodes[id]; if(node.listening){node.listening += 1;}else{node.listening = 1;} node.setAttribute('data-dioxus-id', `\${id}`); listeners.create($event_name$, node, $bubbles$);"#
  217. }
  218. fn remove_event_listener(event_name: &str<u8, evt>, id: u32, bubbles: u8) {
  219. "{node = nodes[$id$]; node.listening -= 1; node.removeAttribute('data-dioxus-id'); listeners.remove(node, $event_name$, $bubbles$);}"
  220. }
  221. fn set_text(id: u32, text: &str) {
  222. "{nodes[$id$].textContent = $text$;}"
  223. }
  224. fn set_attribute(id: u32, field: &str<u8, attr>, value: &str, ns: &str<u8, ns_cache>) {
  225. "{node = nodes[$id$]; SetAttributeInner(node, $field$, $value$, $ns$);}"
  226. }
  227. fn remove_attribute(id: u32, field: &str<u8, attr>, ns: &str<u8, ns_cache>) {
  228. r#"{
  229. node = nodes[$id$];
  230. if (!ns) {
  231. switch (field) {
  232. case "value":
  233. node.value = "";
  234. break;
  235. case "checked":
  236. node.checked = false;
  237. break;
  238. case "selected":
  239. node.selected = false;
  240. break;
  241. case "dangerous_inner_html":
  242. node.innerHTML = "";
  243. break;
  244. default:
  245. node.removeAttribute(field);
  246. break;
  247. }
  248. } else if (ns == "style") {
  249. node.style.removeProperty(name);
  250. } else {
  251. node.removeAttributeNS(ns, field);
  252. }
  253. }"#
  254. }
  255. fn assign_id(ptr: u32, len: u8, id: u32) {
  256. "{nodes[$id$] = LoadChild($ptr$, $len$);}"
  257. }
  258. fn hydrate_text(ptr: u32, len: u8, value: &str, id: u32) {
  259. r#"{
  260. node = LoadChild($ptr$, $len$);
  261. if (node.nodeType == Node.TEXT_NODE) {
  262. node.textContent = value;
  263. } else {
  264. let text = document.createTextNode(value);
  265. node.replaceWith(text);
  266. node = text;
  267. }
  268. nodes[$id$] = node;
  269. }"#
  270. }
  271. fn replace_placeholder(ptr: u32, len: u8, n: u16) {
  272. "{els = stack.splice(stack.length - $n$); node = LoadChild($ptr$, $len$); node.replaceWith(...els);}"
  273. }
  274. fn load_template(tmpl_id: u16, index: u16, id: u32) {
  275. "{node = templates[$tmpl_id$][$index$].cloneNode(true); nodes[$id$] = node; stack.push(node);}"
  276. }
  277. }
  278. #[cfg(not(feature = "web"))]
  279. #[bindgen]
  280. mod js {
  281. const JS_FILE: &str = "./packages/interpreter/src/interpreter.js";
  282. fn mount_to_root() {
  283. "{AppendChildren(root, stack.length-1);}"
  284. }
  285. fn push_root(root: u32) {
  286. "{stack.push(nodes[$root$]);}"
  287. }
  288. fn append_children(id: u32, many: u16) {
  289. "{AppendChildren($id$, $many$);}"
  290. }
  291. fn append_children_to_top(many: u16) {
  292. "{
  293. root = stack[stack.length-many-1];
  294. els = stack.splice(stack.length-many);
  295. for (k = 0; k < many; k++) {
  296. root.appendChild(els[k]);
  297. }
  298. }"
  299. }
  300. fn pop_root() {
  301. "{stack.pop();}"
  302. }
  303. fn replace_with(id: u32, n: u16) {
  304. "{root = nodes[$id$]; els = stack.splice(stack.length-$n$); if (root.listening) { listeners.removeAllNonBubbling(root); } root.replaceWith(...els);}"
  305. }
  306. fn insert_after(id: u32, n: u16) {
  307. "{nodes[$id$].after(...stack.splice(stack.length-$n$));}"
  308. }
  309. fn insert_before(id: u32, n: u16) {
  310. "{nodes[$id$].before(...stack.splice(stack.length-$n$));}"
  311. }
  312. fn remove(id: u32) {
  313. "{node = nodes[$id$]; if (node !== undefined) { if (node.listening) { listeners.removeAllNonBubbling(node); } node.remove(); }}"
  314. }
  315. fn create_raw_text(text: &str) {
  316. "{stack.push(document.createTextNode($text$));}"
  317. }
  318. fn create_text_node(text: &str, id: u32) {
  319. "{node = document.createTextNode($text$); nodes[$id$] = node; stack.push(node);}"
  320. }
  321. fn create_element(element: &'static str<u8, el>) {
  322. "{stack.push(document.createElement($element$))}"
  323. }
  324. fn create_element_ns(element: &'static str<u8, el>, ns: &'static str<u8, namespace>) {
  325. "{stack.push(document.createElementNS($ns$, $element$))}"
  326. }
  327. fn create_placeholder(id: u32) {
  328. "{node = document.createElement('pre'); node.hidden = true; stack.push(node); nodes[$id$] = node;}"
  329. }
  330. fn add_placeholder() {
  331. "{node = document.createElement('pre'); node.hidden = true; stack.push(node);}"
  332. }
  333. fn new_event_listener(event_name: &str<u8, evt>, id: u32, bubbles: u8) {
  334. r#"
  335. bubbles = bubbles == 1;
  336. node = nodes[id];
  337. if(node.listening){
  338. node.listening += 1;
  339. } else {
  340. node.listening = 1;
  341. }
  342. node.setAttribute('data-dioxus-id', `\${id}`);
  343. // if this is a mounted listener, we send the event immediately
  344. if (event_name === "mounted") {
  345. window.ipc.postMessage(
  346. serializeIpcMessage("user_event", {
  347. name: event_name,
  348. element: edit.id,
  349. data: null,
  350. bubbles,
  351. })
  352. );
  353. } else {
  354. listeners.create(event_name, node, bubbles, (event) => {
  355. handler(event, event_name, bubbles, config);
  356. });
  357. }"#
  358. }
  359. fn remove_event_listener(event_name: &str<u8, evt>, id: u32, bubbles: u8) {
  360. "{node = nodes[$id$]; node.listening -= 1; node.removeAttribute('data-dioxus-id'); listeners.remove(node, $event_name$, $bubbles$);}"
  361. }
  362. fn set_text(id: u32, text: &str) {
  363. "{nodes[$id$].textContent = $text$;}"
  364. }
  365. fn set_attribute(id: u32, field: &str<u8, attr>, value: &str, ns: &str<u8, ns_cache>) {
  366. "{node = nodes[$id$]; SetAttributeInner(node, $field$, $value$, $ns$);}"
  367. }
  368. fn set_top_attribute(field: &str<u8, attr>, value: &str, ns: &str<u8, ns_cache>) {
  369. "{SetAttributeInner(stack[stack.length-1], $field$, $value$, $ns$);}"
  370. }
  371. fn remove_attribute(id: u32, field: &str<u8, attr>, ns: &str<u8, ns_cache>) {
  372. r#"{
  373. node = nodes[$id$];
  374. if (!ns) {
  375. switch (field) {
  376. case "value":
  377. node.value = "";
  378. break;
  379. case "checked":
  380. node.checked = false;
  381. break;
  382. case "selected":
  383. node.selected = false;
  384. break;
  385. case "dangerous_inner_html":
  386. node.innerHTML = "";
  387. break;
  388. default:
  389. node.removeAttribute(field);
  390. break;
  391. }
  392. } else if (ns == "style") {
  393. node.style.removeProperty(name);
  394. } else {
  395. node.removeAttributeNS(ns, field);
  396. }
  397. }"#
  398. }
  399. fn assign_id(array: &[u8], id: u32) {
  400. "{nodes[$id$] = LoadChild($array$);}"
  401. }
  402. fn hydrate_text(array: &[u8], value: &str, id: u32) {
  403. r#"{
  404. node = LoadChild($array$);
  405. if (node.nodeType == Node.TEXT_NODE) {
  406. node.textContent = value;
  407. } else {
  408. let text = document.createTextNode(value);
  409. node.replaceWith(text);
  410. node = text;
  411. }
  412. nodes[$id$] = node;
  413. }"#
  414. }
  415. fn replace_placeholder(array: &[u8], n: u16) {
  416. "{els = stack.splice(stack.length - $n$); node = LoadChild($array$); node.replaceWith(...els);}"
  417. }
  418. fn load_template(tmpl_id: u16, index: u16, id: u32) {
  419. "{node = templates[$tmpl_id$][$index$].cloneNode(true); nodes[$id$] = node; stack.push(node);}"
  420. }
  421. fn add_templates(tmpl_id: u16, len: u16) {
  422. "{templates[$tmpl_id$] = stack.splice(stack.length-$len$);}"
  423. }
  424. }