#![feature(use_extern_macros)] extern crate wasm_bindgen; extern crate js_sys; use js_sys::Date; use js_sys::JsString; use wasm_bindgen::prelude::*; #[wasm_bindgen] extern "C" { // Binding for the `setInverval` method in JS. This function takes a "long // lived" closure as the first argument so we use `Closure` instead of // a bare `&Fn()` which only surives for that one stack frame. // // The second argument is then the interval and the return value is how we // clear this interval. We're not going to clear our interval in this // example though so the return value is ignored. #[wasm_bindgen(js_name = setInterval)] fn set_interval(cb: &Closure, delay: u32) -> f64; // Bindings for `document` and various methods of updating HTML elements. // Like with the `dom` example these'll ideally be upstream in a generated // crate one day but for now we manually define them. type HTMLDocument; static document: HTMLDocument; #[wasm_bindgen(method, js_name = getElementById)] fn get_element_by_id(this: &HTMLDocument, id: &str) -> Element; #[wasm_bindgen(method, js_name = getElementById)] fn get_html_element_by_id(this: &HTMLDocument, id: &str) -> HTMLElement; type Element; #[wasm_bindgen(method, setter = innerHTML)] fn set_inner_html(this: &Element, html: &str); type HTMLElement; #[wasm_bindgen(method, setter)] fn set_onclick(this: &HTMLElement, cb: &Closure); #[wasm_bindgen(method, getter)] fn style(this: &HTMLElement) -> CSS2Properties; type CSS2Properties; #[wasm_bindgen(method, setter)] fn set_display(this: &CSS2Properties, display: &str); } #[wasm_bindgen] pub fn run() { // Set up a clock on our page and update it each second to ensure it's got // an accurate date. let a = Closure::new(update_time); set_interval(&a, 1000); update_time(); fn update_time() { document .get_element_by_id("current-time") .set_inner_html(&String::from( Date::new(JsValue::undefined()) .to_locale_string("en-GB".into(), JsValue::undefined()), )); } // We also want to count the number of times that our green square has been // clicked. Our callback will update the `#num-clicks` div let square = document.get_html_element_by_id("green-square"); let mut clicks = 0; let b = Closure::new(move || { clicks += 1; document .get_element_by_id("num-clicks") .set_inner_html(&clicks.to_string()); }); square.set_onclick(&b); // The instances of `Closure` that we created will invalidate their // corresponding JS callback whenever they're dropped, so if we were to // normally return from `run` then both of our registered closures will // raise exceptions when invoked. // // Normally we'd store these handles to later get dropped at an appropriate // time but for now we want these to be global handlers so we use the // `forget` method to drop them without invalidating the closure. Note that // this is leaking memory in Rust, so this should be done judiciously! a.forget(); b.forget(); // And finally now that our demo is ready to go let's switch things up so // everything is displayed and our loading prompt is hidden. document .get_html_element_by_id("loading") .style() .set_display("none"); document .get_html_element_by_id("script") .style() .set_display("block"); }