squark_stdweb/
lib.rs

1#[macro_use]
2extern crate serde_json;
3extern crate squark;
4#[macro_use]
5extern crate stdweb;
6
7use std::collections::{BTreeMap, HashMap};
8use std::rc::Rc;
9use std::cell::RefCell;
10use std::collections::Bound::{Excluded, Included};
11
12use squark::{App, AttributeValue, Diff, Element, Env, HandlerArg, Node, Runtime};
13use stdweb::traits::*;
14use stdweb::unstable::TryFrom;
15use stdweb::web;
16use stdweb::web::{document, window, EventListenerHandle};
17use stdweb::web::html_element::InputElement;
18use stdweb::web::event::{BlurEvent, ChangeEvent, ClickEvent, ConcreteEvent, DoubleClickEvent,
19                         InputEvent, KeyDownEvent};
20
21type Position = Vec<usize>;
22type AttachedMadp = BTreeMap<Position, HashMap<String, EventListenerHandle>>;
23
24trait ToHandlerArg {
25    fn to_handler_arg(&self) -> HandlerArg;
26}
27
28impl ToHandlerArg for ClickEvent {
29    fn to_handler_arg(&self) -> HandlerArg {
30        json!{null}
31    }
32}
33
34impl ToHandlerArg for BlurEvent {
35    fn to_handler_arg(&self) -> HandlerArg {
36        json!{null}
37    }
38}
39
40impl ToHandlerArg for DoubleClickEvent {
41    fn to_handler_arg(&self) -> HandlerArg {
42        json!{null}
43    }
44}
45
46impl ToHandlerArg for InputEvent {
47    fn to_handler_arg(&self) -> HandlerArg {
48        json!{self.target().map(|t| InputElement::try_from(t).expect("Failed to convert InputEvent to HandlerArg").raw_value())}
49    }
50}
51
52impl ToHandlerArg for KeyDownEvent {
53    fn to_handler_arg(&self) -> HandlerArg {
54        json!{self.key()}
55    }
56}
57
58impl ToHandlerArg for ChangeEvent {
59    fn to_handler_arg(&self) -> HandlerArg {
60        json!{null}
61    }
62}
63
64#[derive(Clone)]
65pub struct StdwebRuntime<A: App> {
66    env: Env<A>,
67    attached_map: Rc<RefCell<AttachedMadp>>,
68    root: web::Element,
69}
70
71fn insert_at<N: INode>(parent: &web::Element, i: usize, node: N) {
72    match parent.child_nodes().into_iter().nth(i) {
73        Some(ref_node) => {
74            parent
75                .insert_before(&node, &ref_node)
76                .expect("Failed to insert at given position");
77        }
78        None => {
79            parent.append_child(&node);
80        }
81    }
82}
83
84fn replace_at<N: INode>(parent: &web::Element, i: usize, node: N) {
85    let current = parent
86        .child_nodes()
87        .into_iter()
88        .nth(i)
89        .expect("Could not find node by given index");
90    parent
91        .replace_child(&node, &current)
92        .expect("Failed to replace child");
93}
94
95fn set_attribute(el: &web::Element, name: &str, value: &AttributeValue) {
96    match value {
97        &AttributeValue::Bool(ref b) => {
98            js! { @{el.clone()}[@{name}] = @{b} };
99            el.set_attribute(name, &b.to_string())
100                .expect("Failet to set bool attirbute");
101        }
102        &AttributeValue::String(ref s) => {
103            js! { @{el.clone()}[@{name}] = @{s} };
104            el.set_attribute(name, s)
105                .expect("Failed to set string attribute");
106        }
107    }
108}
109
110impl<A: App> StdwebRuntime<A> {
111    pub fn new(root: web::Element, state: A::State) -> StdwebRuntime<A> {
112        StdwebRuntime {
113            env: Env::new(state),
114            attached_map: Rc::new(RefCell::new(BTreeMap::new())),
115            root,
116        }
117    }
118
119    fn handle_diff_inner(&self, el: &web::Element, diff: Diff, pos: &mut Position) {
120        match diff {
121            Diff::AddChild(i, node) => self.add_child(el, i, node, pos),
122            Diff::PatchChild(i, diffs) => {
123                let child = web::Element::try_from(
124                    el.child_nodes()
125                        .iter()
126                        .nth(i)
127                        .expect("Failed to find child for patching"),
128                ).expect("Failed to convert Node to Element");
129                pos.push(i);
130                for diff in diffs {
131                    self.handle_diff_inner(&child, diff, pos);
132                }
133                pos.pop();
134            }
135            Diff::ReplaceChild(i, node) => self.replace_child(el, i, node, pos),
136            Diff::SetAttribute(name, value) => set_attribute(el, &name, &value),
137            Diff::RemoveAttribute(name) => {
138                js! { @{el}[@{name.clone()}] = undefined };
139                el.remove_attribute(&name);
140            }
141            Diff::RemoveChild(i) => self.remove_child(el, i, pos),
142            Diff::SetHandler(name, id) => self.set_handler(el, &name, &id, pos),
143            Diff::RemoveHandler(name, _) => {
144                let attached = self.attached_map
145                    .borrow_mut()
146                    .get_mut(pos)
147                    .and_then(|m| m.remove(&name));
148                if let Some(attached) = attached {
149                    attached.remove();
150                }
151            }
152        }
153    }
154
155    fn create_element(&self, el: Element, pos: &mut Position) -> web::Element {
156        let web_el = document()
157            .create_element(el.name.as_str())
158            .expect("Failed to create element");
159
160        for &(ref name, ref value) in el.attributes.iter() {
161            set_attribute(&web_el, name, value);
162        }
163
164        for &(ref name, ref id) in el.handlers.iter() {
165            self.set_handler(&web_el, name, &id, pos);
166        }
167
168        let mut i = 0;
169        for child in el.children {
170            pos.push(i.clone());
171            match child {
172                Node::Element(el) => {
173                    let child = self.create_element(el, pos);
174                    web_el.append_child(&child);
175                }
176                Node::Text(s) => {
177                    let child = document().create_text_node(s.as_str());
178                    web_el.append_child(&child);
179                }
180                _ => (),
181            };
182            pos.pop();
183            i += 1;
184        }
185
186        web_el
187    }
188
189    fn add_child(&self, parent: &web::Element, i: usize, node: Node, pos: &mut Position) {
190        pos.push(i);
191        match node {
192            Node::Element(el) => {
193                let child = self.create_element(el, pos);
194                insert_at(parent, i, child);
195            }
196            Node::Text(s) => {
197                let child = document().create_text_node(s.as_str());
198                insert_at(parent, i, child);
199            }
200            _ => (),
201        };
202        pos.pop();
203    }
204
205    fn replace_child(&self, parent: &web::Element, i: usize, node: Node, pos: &mut Position) {
206        pos.push(i);
207        self.remove_attached(pos);
208        match node {
209            Node::Element(el) => {
210                let child = self.create_element(el, pos);
211                replace_at(parent, i, child);
212            }
213            Node::Text(s) => {
214                let child = document().create_text_node(s.as_str());
215                replace_at(parent, i, child);
216            }
217            _ => (),
218        };
219        pos.pop();
220    }
221
222    fn remove_attached(&self, pos: &Position) {
223        let mut max = pos.clone();
224        let i = max.pop()
225            .expect("Failed to pop index from posion to use max range") + 1;
226        max.push(i);
227        let range = (Included(pos.clone()), Excluded(max));
228        let mut map = self.attached_map.borrow_mut();
229
230        let vec: Vec<Position> = map.range(range).map(|(k, _)| k.clone()).collect();
231        for k in vec {
232            map.remove(&k);
233        }
234    }
235
236    fn remove_child(&self, parent: &web::Element, i: usize, pos: &mut Position) {
237        pos.push(i);
238        self.remove_attached(pos);
239        let current = parent
240            .child_nodes()
241            .into_iter()
242            .nth(i)
243            .expect("Could not find node for removing");
244        parent
245            .remove_child(&current)
246            .expect("Failed to remove child");
247        pos.pop();
248    }
249
250    fn set_handler(&self, el: &web::Element, name: &str, id: &str, pos: &Position) {
251        let handle = match name {
252            "click" => self._set_handler::<ClickEvent>(&el, id),
253            "dblclick" => self._set_handler::<DoubleClickEvent>(&el, id),
254            "blur" => self._set_handler::<BlurEvent>(&el, id),
255            "change" => self._set_handler::<ChangeEvent>(&el, id),
256            "input" => self._set_handler::<InputEvent>(&el, id),
257            "keydown" => self._set_handler::<KeyDownEvent>(&el, id),
258            "render" => {
259                let handler = self.pop_handler(&id)
260                    .expect("Could not find handler by given id");
261                window().request_animation_frame(move |_| {
262                    handler(json!{null});
263                });
264                return;
265            }
266
267            _ => return,
268        };
269
270        let mut map = self.attached_map.borrow_mut();
271        let attached = map.get_mut(pos).and_then(|m| m.remove(name));
272        if let Some(attached) = attached {
273            attached.remove();
274        }
275        map.entry(pos.clone())
276            .or_insert(HashMap::new())
277            .insert(name.to_owned(), handle);
278    }
279
280    fn _set_handler<E: ConcreteEvent + ToHandlerArg>(
281        &self,
282        el: &web::Element,
283        id: &str,
284    ) -> EventListenerHandle {
285        let handler = self.pop_handler(id)
286            .expect("Could not find handler by given id");
287        el.clone().add_event_listener(move |e: E| {
288            e.stop_propagation();
289            let arg = e.to_handler_arg();
290            handler(arg);
291        })
292    }
293}
294
295impl<A: App> Runtime<A> for StdwebRuntime<A> {
296    fn get_env<'a>(&'a self) -> &'a Env<A> {
297        &self.env
298    }
299
300    fn schedule_render(&self) {
301        let this = self.clone();
302        window().request_animation_frame(move |_| {
303            this.run();
304        });
305    }
306
307    fn handle_diff(&self, diff: Diff) {
308        self.handle_diff_inner(&self.root, diff, &mut vec![]);
309    }
310}