1#![allow(unused_unsafe)]
2
3extern crate libc;
4
5use std::ffi::{CString, CStr};
6use std::{mem, fmt};
7use std::str;
8use std::borrow::ToOwned;
9use std::ops::Deref;
10use std::cell::RefCell;
11use std::clone::Clone;
12use std::rc::Rc;
13use std::collections::HashSet;
14use std::char;
15use std::iter::IntoIterator;
16
17mod webplatform {
18 pub use emscripten_asm_const;
19 pub use emscripten_asm_const_int;
20}
21
22trait Interop {
23 fn as_int(self, _:&mut Vec<CString>) -> libc::c_int;
24}
25
26impl Interop for i32 {
27 fn as_int(self, _:&mut Vec<CString>) -> libc::c_int {
28 return self;
29 }
30}
31
32impl<'a> Interop for &'a str {
33 fn as_int(self, arena:&mut Vec<CString>) -> libc::c_int {
34 let c = CString::new(self).unwrap();
35 let ret = c.as_ptr() as libc::c_int;
36 arena.push(c);
37 return ret;
38 }
39}
40
41impl<'a> Interop for *const libc::c_void {
42 fn as_int(self, _:&mut Vec<CString>) -> libc::c_int {
43 return self as libc::c_int;
44 }
45}
46
47#[macro_export]
48macro_rules! js {
49 ( ($( $x:expr ),*) $y:expr ) => {
50 {
51 let mut arena:Vec<CString> = Vec::new();
52 const LOCAL: &'static [u8] = $y;
53 unsafe { ::webplatform::emscripten_asm_const_int(&LOCAL[0] as *const _ as *const libc::c_char, $(Interop::as_int($x, &mut arena)),*) }
54 }
55 };
56 ( $y:expr ) => {
57 {
58 const LOCAL: &'static [u8] = $y;
59 unsafe { ::webplatform::emscripten_asm_const_int(&LOCAL[0] as *const _ as *const libc::c_char) }
60 }
61 };
62}
63
64extern "C" {
65 pub fn emscripten_asm_con(s: *const libc::c_char);
66 pub fn emscripten_asm_const(s: *const libc::c_char);
67 pub fn emscripten_asm_const_int(s: *const libc::c_char, ...) -> libc::c_int;
68 pub fn emscripten_pause_main_loop();
69 pub fn emscripten_set_main_loop(m: extern fn(), fps: libc::c_int, infinite: libc::c_int);
70}
71
72pub struct HtmlNode<'a> {
73 id: libc::c_int,
74 doc: *const Document<'a>,
75}
76
77impl<'a> fmt::Debug for HtmlNode<'a> {
78 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
79 write!(f, "HtmlNode({:?})", self.id)
80 }
81}
82
83impl<'a> Drop for HtmlNode<'a> {
84 fn drop(&mut self) {
85 println!("dropping HTML NODE {:?}", self.id);
86 }
87}
88
89pub struct JSRef<'a> {
90 ptr: *const HtmlNode<'a>,
91}
92
93impl<'a> fmt::Debug for JSRef<'a> {
94 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
95 write!(f, "JSRef(HtmlNode({:?}))", self.id)
96 }
97}
98
99impl<'a> Clone for JSRef<'a> {
100 fn clone(&self) -> JSRef<'a> {
101 JSRef {
102 ptr: self.ptr,
103 }
104 }
105}
106
107impl<'a> HtmlNode<'a> {
108 pub fn root_ref(&self) -> JSRef<'a> {
109 JSRef {
110 ptr: &*self,
111 }
112 }
113}
114
115impl<'a> Deref for JSRef<'a> {
116 type Target = HtmlNode<'a>;
117
118 fn deref(&self) -> &HtmlNode<'a> {
119 unsafe {
120 &*self.ptr
121 }
122 }
123}
124
125pub struct Event<'a> {
126 pub target: Option<HtmlNode<'a>>
127}
128
129extern fn rust_caller<F: FnMut(Event)>(a: *const libc::c_void, docptr: *const libc::c_void, id: i32) {
130 let v:&mut F = unsafe { mem::transmute(a) };
131 v(Event {
132 target: if id == -1 {
133 None
134 } else {
135 Some(HtmlNode {
136 id: id,
137 doc: unsafe { mem::transmute(docptr) },
138 })
139 }
140 });
142}
143
144impl<'a> HtmlNode<'a> {
145 pub fn tagname(&self) -> String {
146 let a = js! { (self.id) b"\
147 var str = WEBPLATFORM.rs_refs[$0].tagName.toLowerCase();\
148 return allocate(intArrayFromString(str), 'i8', ALLOC_STACK);\
149 \0" };
150 unsafe {
151 str::from_utf8(CStr::from_ptr(a as *const libc::c_char).to_bytes()).unwrap().to_owned()
152 }
153 }
154
155 pub fn focus(&self) {
156 js! { (self.id) b"\
157 WEBPLATFORM.rs_refs[$0].focus();\
158 \0" };
159 }
160
161 pub fn html_set(&self, s: &str) {
162 js! { (self.id, s) b"\
163 WEBPLATFORM.rs_refs[$0].innerHTML = UTF8ToString($1);\
164 \0" };
165 }
166
167 pub fn html_get(&self) -> String {
168 let a = js! { (self.id) b"\
169 return allocate(intArrayFromString(WEBPLATFORM.rs_refs[$0].innerHTML), 'i8', ALLOC_STACK);\
170 \0" };
171 unsafe {
172 str::from_utf8(CStr::from_ptr(a as *const libc::c_char).to_bytes()).unwrap().to_owned()
173 }
174 }
175
176 pub fn class_get(&self) -> HashSet<String> {
177 let a = js! { (self.id) b"\
178 return allocate(intArrayFromString(WEBPLATFORM.rs_refs[$0].className), 'i8', ALLOC_STACK);\
179 \0" };
180 let class = unsafe {
181 str::from_utf8(CStr::from_ptr(a as *const libc::c_char).to_bytes()).unwrap().to_owned()
182 };
183 class.trim().split(char::is_whitespace).map(|x| x.to_string()).collect()
184 }
185
186 pub fn class_add(&self, s: &str) {
187 js! { (self.id, s) b"\
188 WEBPLATFORM.rs_refs[$0].classList.add(UTF8ToString($1));\
189 \0" };
190 }
191
192 pub fn class_remove(&self, s: &str) {
193 js! { (self.id, s) b"\
194 WEBPLATFORM.rs_refs[$0].classList.remove(UTF8ToString($1));\
195 \0" };
196 }
197
198 pub fn parent(&self) -> Option<HtmlNode<'a>> {
199 let id = js! { (self.id) b"\
200 var value = WEBPLATFORM.rs_refs[$0].parentNode;\
201 if (!value) {\
202 return -1;\
203 }\
204 return WEBPLATFORM.rs_refs.push(value) - 1;\
205 \0" };
206 if id < 0 {
207 None
208 } else {
209 Some(HtmlNode {
210 id: id,
211 doc: self.doc,
212 })
213 }
214 }
215
216 pub fn data_set(&self, s: &str, v: &str) {
217 js! { (self.id, s, v) b"\
218 WEBPLATFORM.rs_refs[$0].dataset[UTF8ToString($1)] = UTF8ToString($2);\
219 \0" };
220 }
221
222 pub fn data_get(&self, s: &str) -> Option<String> {
223 let a = js! { (self.id, s) b"\
224 var str = WEBPLATFORM.rs_refs[$0].dataset[UTF8ToString($1)];\
225 if (str == null) return -1;\
226 return allocate(intArrayFromString(str), 'i8', ALLOC_STACK);\
227 \0" };
228 if a == -1 {
229 None
230 } else {
231 Some(unsafe {
232 str::from_utf8(CStr::from_ptr(a as *const libc::c_char).to_bytes()).unwrap().to_owned()
233 })
234 }
235 }
236
237 pub fn style_set_str(&self, s: &str, v: &str) {
238 js! { (self.id, s, v) b"\
239 WEBPLATFORM.rs_refs[$0].style[UTF8ToString($1)] = UTF8ToString($2);\
240 \0" };
241 }
242
243 pub fn style_get_str(&self, s: &str) -> String {
244 let a = js! { (self.id, s) b"\
245 return allocate(intArrayFromString(WEBPLATFORM.rs_refs[$0].style[UTF8ToString($1)]), 'i8', ALLOC_STACK);\
246 \0" };
247 unsafe {
248 str::from_utf8(CStr::from_ptr(a as *const libc::c_char).to_bytes()).unwrap().to_owned()
249 }
250 }
251
252 pub fn prop_set_i32(&self, s: &str, v: i32) {
253 js! { (self.id, s, v) b"\
254 WEBPLATFORM.rs_refs[$0][UTF8ToString($1)] = $2;\
255 \0" };
256 }
257
258 pub fn prop_set_str(&self, s: &str, v: &str) {
259 js! { (self.id, s, v) b"\
260 WEBPLATFORM.rs_refs[$0][UTF8ToString($1)] = UTF8ToString($2);\
261 \0" };
262 }
263
264 pub fn prop_get_i32(&self, s: &str) -> i32 {
265 return js! { (self.id, s) b"\
266 return Number(WEBPLATFORM.rs_refs[$0][UTF8ToString($1)])\
267 \0" };
268 }
269
270 pub fn prop_get_str(&self, s: &str) -> String {
271 let a = js! { (self.id, s) b"\
272 var a = allocate(intArrayFromString(WEBPLATFORM.rs_refs[$0][UTF8ToString($1)]), 'i8', ALLOC_STACK); console.log(WEBPLATFORM.rs_refs[$0]); return a;\
273 \0" };
274 unsafe {
275 str::from_utf8(CStr::from_ptr(a as *const libc::c_char).to_bytes()).unwrap().to_owned()
276 }
277 }
278
279 pub fn append(&self, s: &HtmlNode) {
280 js! { (self.id, s.id) b"\
281 WEBPLATFORM.rs_refs[$0].appendChild(WEBPLATFORM.rs_refs[$1]);\
282 \0" };
283 }
284
285 pub fn html_append(&self, s: &str) {
286 js! { (self.id, s) b"\
287 WEBPLATFORM.rs_refs[$0].insertAdjacentHTML('beforeEnd', UTF8ToString($1));\
288 \0" };
289 }
290
291 pub fn html_prepend(&self, s: &str) {
292 js! { (self.id, s) b"\
293 WEBPLATFORM.rs_refs[$0].insertAdjacentHTML('afterBegin', UTF8ToString($1));\
294 \0" };
295 }
296
297 pub fn on<F: FnMut(Event) + 'a>(&self, s: &str, f: F) {
298 unsafe {
299 let b = Box::new(f);
300 let a = &*b as *const _;
301 js! { (self.id, s, a as *const libc::c_void,
302 rust_caller::<F> as *const libc::c_void,
303 self.doc as *const libc::c_void)
304 b"\
305 WEBPLATFORM.rs_refs[$0].addEventListener(UTF8ToString($1), function (e) {\
306 Runtime.dynCall('viii', $3, [$2, $4, e.target ? WEBPLATFORM.rs_refs.push(e.target) - 1 : -1]);\
307 }, false);\
308 \0" };
309 (&*self.doc).refs.borrow_mut().push(b);
310 }
311 }
312
313 pub fn captured_on<F: FnMut(Event) + 'a>(&self, s: &str, f: F) {
314 unsafe {
315 let b = Box::new(f);
316 let a = &*b as *const _;
317 js! { (self.id, s, a as *const libc::c_void,
318 rust_caller::<F> as *const libc::c_void,
319 self.doc as *const libc::c_void)
320 b"\
321 WEBPLATFORM.rs_refs[$0].addEventListener(UTF8ToString($1), function (e) {\
322 Runtime.dynCall('viii', $3, [$2, $4, e.target ? WEBPLATFORM.rs_refs.push(e.target) - 1 : -1]);\
323 }, true);\
324 \0" };
325 (&*self.doc).refs.borrow_mut().push(b);
326 }
327 }
328
329 pub fn remove_self(&self) {
330 js! { (self.id) b"\
331 var s = WEBPLATFORM.rs_refs[$0];\
332 s.parentNode.removeChild(s);\
333 \0" };
334 }
335}
336
337pub fn alert(s: &str) {
338 js! { (s) b"\
339 alert(UTF8ToString($0));\
340 \0" };
341}
342
343pub struct Document<'a> {
344 refs: Rc<RefCell<Vec<Box<FnMut(Event<'a>) + 'a>>>>,
345}
346
347impl<'a> Document<'a> {
348 pub fn element_create<'b>(&'b self, s: &str) -> Option<HtmlNode<'a>> {
349 let id = js! { (s) b"\
350 var value = document.createElement(UTF8ToString($0));\
351 if (!value) {\
352 return -1;\
353 }\
354 return WEBPLATFORM.rs_refs.push(value) - 1;\
355 \0" };
356
357 if id < 0 {
358 None
359 } else {
360 Some(HtmlNode {
361 id: id,
362 doc: &*self,
363 })
364 }
365 }
366
367 pub fn location_hash_get(&self) -> String {
368 let a = js! { b"\
369 return allocate(intArrayFromString(window.location.hash), 'i8', ALLOC_STACK);\
370 \0" };
371 unsafe {
372 str::from_utf8(CStr::from_ptr(a as *const libc::c_char).to_bytes()).unwrap().to_owned()
373 }
374 }
375
376 pub fn on<F: FnMut(Event) + 'a>(&self, s: &str, f: F) {
377 unsafe {
378 let b = Box::new(f);
379 let a = &*b as *const _;
380 js! { (0, s, a as *const libc::c_void,
381 rust_caller::<F> as *const libc::c_void,
382 &*self as *const _ as *const libc::c_void)
383 b"\
384 window.addEventListener(UTF8ToString($1), function (e) {\
385 Runtime.dynCall('viii', $3, [$2, $4, e.target ? WEBPLATFORM.rs_refs.push(e.target) - 1 : -1]);\
386 }, false);\
387 \0" };
388 self.refs.borrow_mut().push(b);
389 }
390 }
391
392 pub fn element_query<'b>(&'b self, s: &str) -> Option<HtmlNode<'a>> {
393 let id = js! { (s) b"\
394 var value = document.querySelector(UTF8ToString($0));\
395 if (!value) {\
396 return -1;\
397 }\
398 return WEBPLATFORM.rs_refs.push(value) - 1;\
399 \0" };
400
401 if id < 0 {
402 None
403 } else {
404 Some(HtmlNode {
405 id: id,
406 doc: self,
407 })
408 }
409 }
410}
411
412pub struct LocalStorageInterface;
413
414pub struct LocalStorageIterator {
415 index: i32,
416}
417
418impl LocalStorageInterface {
419 pub fn len(&self) -> i32 {
420 js! { b"\
421 return window.localStorage.length;\
422 \0" }
423 }
424
425 pub fn clear(&self) {
426 js! { b"\
427 window.localStorage.clear();\
428 \0" };
429 }
430
431 pub fn remove(&self, s: &str) {
432 js! { (s) b"\
433 window.localStorage.removeItem(UTF8ToString($0));\
434 \0" };
435 }
436
437 pub fn set(&self, s: &str, v: &str) {
438 js! { (s, v) b"\
439 window.localStorage.setItem(UTF8ToString($0), UTF8ToString($1));\
440 \0" };
441 }
442
443 pub fn get(&self, name: &str) -> Option<String> {
444 let a = js! { (name) b"\
445 var str = window.localStorage.getItem(UTF8ToString($0));\
446 if (str == null) {\
447 return -1;\
448 }\
449 return allocate(intArrayFromString(str), 'i8', ALLOC_STACK);\
450 \0" };
451 if a == -1 {
452 None
453 } else {
454 Some(unsafe {
455 str::from_utf8(CStr::from_ptr(a as *const libc::c_char).to_bytes()).unwrap().to_owned()
456 })
457 }
458 }
459
460 pub fn key(&self, index: i32) -> String {
461 let a = js! { (index) b"\
462 var key = window.localStorage.key($0);\
463 return allocate(intArrayFromString(str), 'i8', ALLOC_STACK);\
464 \0" };
465 unsafe {
466 str::from_utf8(CStr::from_ptr(a as *const libc::c_char).to_bytes()).unwrap().to_owned()
467 }
468 }
469}
470
471impl IntoIterator for LocalStorageInterface {
472 type Item = String;
473 type IntoIter = LocalStorageIterator;
474
475 fn into_iter(self) -> LocalStorageIterator {
476 LocalStorageIterator { index: 0 }
477 }
478}
479
480impl Iterator for LocalStorageIterator {
481 type Item = String;
482 fn next(&mut self) -> Option<String> {
483 if self.index >= LocalStorage.len() {
484 None
485 } else {
486 LocalStorage.get(&LocalStorage.key(self.index))
487 }
488 }
489}
490
491#[allow(non_upper_case_globals)]
492pub const LocalStorage: LocalStorageInterface = LocalStorageInterface;
493
494pub fn init<'a>() -> Document<'a> {
495 js! { b"\
496 console.log('hi');\
497 window.WEBPLATFORM || (window.WEBPLATFORM = {\
498 rs_refs: [],\
499 });\
500 \0" };
501 Document {
502 refs: Rc::new(RefCell::new(Vec::new())),
503 }
504}
505
506extern fn leavemebe() {
507 unsafe {
508 emscripten_pause_main_loop();
509 }
510}
511
512pub fn spin() {
513 unsafe {
514 emscripten_set_main_loop(leavemebe, 0, 1);
515
516 }
517}
518
519#[no_mangle]
520pub extern "C" fn syscall(a: i32) -> i32 {
521 if a == 355 {
522 return 55
523 }
524 return -1
525}