1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
//! utility functions
//!
use js_sys::Promise;
use wasm_bindgen::{closure::Closure, JsCast, JsValue};
pub use wasm_bindgen_futures::spawn_local;
use wasm_bindgen_futures::JsFuture;

thread_local!(static WINDOW: web_sys::Window = web_sys::window().expect("no global `window` exists"));
thread_local!(static DOCUMENT: web_sys::Document = window().document().expect("should have a document on window"));

/// utility function which returns the Window element
pub fn window() -> web_sys::Window {
    WINDOW.with(|window| window.clone())
}

/// provides access to the document element
pub fn document() -> web_sys::Document {
    DOCUMENT.with(|document| document.clone())
}

/// utility function which returns the history api of the browser
pub fn history() -> web_sys::History {
    window().history().expect("should have a history object")
}

/// utility function which a closure in request animation frame
pub fn request_animation_frame<F>(f: F) -> Result<i32, JsValue>
where
    F: FnMut() + 'static,
{
    let closure_raf: Closure<dyn FnMut() + 'static> = Closure::once(f);
    let handle = window().request_animation_frame(closure_raf.as_ref().unchecked_ref())?;
    closure_raf.forget();
    Ok(handle)
}

/// cancel the animation frame with handle
pub fn cancel_animation_frame(handle: i32) -> Result<(), JsValue>{
    window().cancel_animation_frame(handle)
}


/// request and idle callback
pub fn request_idle_callback<F>(f: F) -> Result<u32, JsValue>
where
    F: Fn(web_sys::IdleDeadline) + 'static,
{
    let closure = Closure::once(move |v: JsValue| {
        let deadline = v
            .dyn_into::<web_sys::IdleDeadline>()
            .expect("must have an idle deadline");
        f(deadline);
    });

    let handle = window().request_idle_callback(closure.as_ref().unchecked_ref())?;
    closure.forget();
    Ok(handle)
}

/// cancel the idle callback with handle
pub fn cancel_idle_callback(handle: u32) -> Result<(), JsValue>{
    window().cancel_idle_callback(handle);
    Ok(())
}

/// request and idle callback
pub fn request_timeout_callback<F>(f: F, timeout: i32) -> Result<i32, JsValue>
where
    F: FnMut() + 'static,
{
    let closure = Closure::once(f);
    let handle = window().set_timeout_with_callback_and_timeout_and_arguments_0(closure.as_ref().unchecked_ref(), timeout)?;
    closure.forget();
    Ok(handle)
}

/// cancel the timeout callback with handle
pub fn cancel_timeout_callback(handle: i32) -> Result<(), JsValue>{
    window().clear_timeout_with_handle(handle);
    Ok(())
}

/// execute the function at a certain specified timeout in ms
pub fn delay_exec<F>(f: F, timeout: i32) -> Result<i32, JsValue>
where
    F: FnMut() + 'static,
{
    let closure_delay = Closure::once(f);
    let timeout_id = window().set_timeout_with_callback_and_timeout_and_arguments_0(
        closure_delay.as_ref().unchecked_ref(),
        timeout,
    );
    closure_delay.forget();
    timeout_id
}


/// simulate a delay using promise in js
pub async fn async_delay(timeout: i32) {
    let promise = Promise::new(&mut |resolve, _reject| {
        let _handle = delay_exec(
            move || {
                resolve
                    .call0(&JsValue::NULL)
                    .expect("must be able to call resolve");
            },
            timeout,
        ).expect("must schedule it");
    });
    JsFuture::from(promise).await.expect("must not error");
}



/// provides access to the document body
pub fn body() -> web_sys::HtmlElement {
    document().body().expect("document should have a body")
}

/// provides access to the window Performance api
pub fn performance() -> web_sys::Performance {
    window()
        .performance()
        .expect("should have performance on window")
}

/// return the instantaneous time
pub fn now() -> f64 {
    performance().now()
}