1use std::{cell::RefCell, collections::HashMap, rc::Rc};
2use url::Url;
3use wasm_bindgen::{closure::Closure, JsCast};
4use wasm_bindgen_futures::spawn_local;
5use web_sys::{window, Document, Event, FormData, HtmlFormElement};
6
7use crate::{enums::request_method::RequestMethod, router::Router};
8
9pub type SharedRouter = Rc<RefCell<Router>>;
10
11pub trait Controller {
12 type View;
13 fn new(router: SharedRouter, view: Self::View) -> Self;
14}
15
16pub trait View {
17 fn new(router: SharedRouter) -> Self;
18}
19
20pub trait Template {
21 type Data;
22
23 fn new(router: SharedRouter) -> Self;
24 fn router(&self) -> SharedRouter;
25 fn body(&self, data: Self::Data) -> String;
26
27 fn render(&self, data: Self::Data) {
28 self.render_html(&self.body(data));
29 self.register_forms();
30 }
31
32 fn render_html(&self, html: &str) {
33 let doc = match self.get_document() {
34 Some(doc) => doc,
35 None => return,
36 };
37
38 let body = match doc.body() {
39 Some(body) => body,
40 None => return,
41 };
42
43 body.set_inner_html(html);
44 }
45
46 fn register_forms(&self) {
47 let doc = match self.get_document() {
48 Some(doc) => doc,
49 None => return,
50 };
51
52 let forms = match self.get_all_forms(&doc) {
53 Some(forms) => forms,
54 None => return,
55 };
56
57 for i in 0..forms.length() {
58 if let Some(form_node) = forms.get(i) {
59 if let Some(form) = self.reset_form(form_node) {
60 self.attach_form_listener(&form);
61 }
62 }
63 }
64 }
65
66 fn get_document(&self) -> Option<Document> {
67 window()?.document()
68 }
69
70 fn get_all_forms(&self, doc: &Document) -> Option<web_sys::NodeList> {
71 doc.query_selector_all("form").ok()
72 }
73
74 fn reset_form(&self, form_node: web_sys::Node) -> Option<HtmlFormElement> {
75 let parent = form_node.parent_node()?;
76 let new_node = form_node.clone_node_with_deep(true).ok()?;
77 parent.replace_child(&new_node, &form_node).ok()?;
78 new_node.dyn_into::<HtmlFormElement>().ok()
79 }
80
81 fn attach_form_listener(&self, form: &HtmlFormElement) {
82 let router_for_closure = self.router().clone();
83
84 let closure = Closure::wrap(Box::new(move |e: Event| {
85 e.prevent_default();
86
87 let mut args: HashMap<String, String> = HashMap::new();
88
89 let element: HtmlFormElement = match e
90 .current_target()
91 .and_then(|t| t.dyn_into::<HtmlFormElement>().ok())
92 {
93 Some(el) => el,
94 None => return,
95 };
96
97 let form_data = match FormData::new_with_form(&element) {
98 Ok(data) => data,
99 Err(_) => return,
100 };
101
102 let entries = form_data.entries();
103
104 for entry in entries {
105 if let Ok(entry) = entry {
106 let entry_array = js_sys::Array::from(&entry);
107 if entry_array.length() == 2 {
108 let key = entry_array.get(0).as_string().unwrap_or_default();
109 let value = entry_array.get(1).as_string().unwrap_or_default();
110 args.insert(key, value);
111 }
112 }
113 }
114
115 if let Ok(url) = Url::parse(&element.action()) {
116 let router = router_for_closure.clone();
117 spawn_local(async move {
118 let method = RequestMethod::from(
119 &element
120 .get_attribute("method")
121 .unwrap_or(String::from("post")),
122 );
123
124 router
125 .borrow()
126 .resolve(method, url.path(), Some(args))
127 .await;
128 });
129 }
130 }) as Box<dyn FnMut(Event)>);
131
132 form.add_event_listener_with_callback("submit", closure.as_ref().unchecked_ref())
133 .expect("Failed to add submit event listeners");
134 closure.forget();
135 }
136}