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, ¤t)
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(¤t)
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}