use crate::events;
use wasm_bindgen::closure::Closure;
use wasm_bindgen::JsCast;
use web_sys;
pub type RequestAnimationFrameTime = f64;
#[must_use]
pub struct RequestAnimationFrameHandle {
request_id: i32,
_closure: Closure<dyn FnMut(RequestAnimationFrameTime)>,
}
impl Drop for RequestAnimationFrameHandle {
fn drop(&mut self) {
window()
.cancel_animation_frame(self.request_id)
.expect("Problem cancelling animation frame request")
}
}
pub fn window() -> web_sys::Window {
web_sys::window().expect("Can't find the global Window")
}
pub fn document() -> web_sys::Document {
window()
.document()
.expect("Can't find the window's document")
}
pub fn body() -> web_sys::HtmlElement {
document().body().expect("Can't find the document's body")
}
pub fn history() -> web_sys::History {
window().history().expect("Can't find history")
}
pub fn html_document() -> web_sys::HtmlDocument {
wasm_bindgen::JsValue::from(document()).unchecked_into::<web_sys::HtmlDocument>()
}
pub fn cookies() -> Option<cookie::CookieJar> {
let cookies_str = html_document().cookie().ok()?;
let mut jar = cookie::CookieJar::new();
for cookie_str in cookies_str.split(';') {
let cookie = cookie::Cookie::parse_encoded(cookie_str).ok()?;
jar.add(cookie.into_owned());
}
let jar_is_empty = jar.iter().next().is_none();
if jar_is_empty {
None
} else {
Some(jar)
}
}
pub fn request_animation_frame(
f: Closure<dyn FnMut(RequestAnimationFrameTime)>,
) -> RequestAnimationFrameHandle {
let request_id = window()
.request_animation_frame(f.as_ref().unchecked_ref())
.expect("Problem requesting animation frame");
RequestAnimationFrameHandle {
request_id,
_closure: f,
}
}
pub fn get_value(target: &web_sys::EventTarget) -> Result<String, &'static str> {
use web_sys::*;
macro_rules! get {
($element:ty) => {
get!($element, |_| Ok(()))
};
($element:ty, $result_callback:expr) => {
if let Some(input) = target.dyn_ref::<$element>() {
return $result_callback(input).map(|_| input.value().to_string());
}
};
}
get!(HtmlInputElement, |input: &HtmlInputElement| {
match input.type_().as_str() {
"file" => Err(r#"The value attribute cannot be used with <input type="file">."#),
_ => Ok(()),
}
});
get!(HtmlTextAreaElement);
get!(HtmlSelectElement);
get!(HtmlProgressElement);
get!(HtmlOptionElement);
get!(HtmlButtonElement);
get!(HtmlDataElement);
get!(HtmlMeterElement);
get!(HtmlLiElement);
get!(HtmlOutputElement);
get!(HtmlParamElement);
Err("Can't use function `get_value` for given element.")
}
pub fn set_value(target: &web_sys::EventTarget, value: &str) -> Result<(), &'static str> {
use web_sys::*;
macro_rules! set {
($element:ty) => {
set!($element, |_| Ok(value))
};
($element:ty, $value_result_callback:expr) => {
if let Some(input) = target.dyn_ref::<$element>() {
return $value_result_callback(input).map(|value| input.set_value(value));
}
};
}
set!(HtmlInputElement, |input: &HtmlInputElement| {
match input.type_().as_str() {
"file" => Err(r#"The value attribute cannot be used with <input type="file">."#),
_ => Ok(value),
}
});
set!(HtmlTextAreaElement);
set!(HtmlSelectElement);
set!(HtmlProgressElement, |_| value.parse().map_err(|_| {
"Can't parse value to `f64` for `HtmlProgressElement`."
}));
set!(HtmlOptionElement);
set!(HtmlButtonElement);
set!(HtmlDataElement);
set!(HtmlMeterElement, |_| value.parse().map_err(|_| {
"Can't parse value to `f64` for `HtmlMeterElement`."
}));
set!(HtmlLiElement, |_| value.parse().map_err(|_| {
"Can't parse value to `i32` for `HtmlLiElement`."
}));
set!(HtmlOutputElement);
set!(HtmlParamElement);
Err("Can't use function `set_value` for given element.")
}
#[allow(dead_code)]
pub fn get_checked(target: &web_sys::EventTarget) -> Result<bool, &'static str> {
if let Some(input) = target.dyn_ref::<web_sys::HtmlInputElement>() {
return match input.type_().as_str() {
"file" => Err(r#"The checked attribute can be used with <input type="checkbox"> and <input type="radio">."#),
_ => Ok(input.checked())
};
}
if let Some(input) = target.dyn_ref::<web_sys::HtmlMenuItemElement>() {
return Ok(input.checked());
}
Err("Only `HtmlInputElement` and `HtmlMenuItemElement` can be used in function `get_checked`.")
}
#[allow(clippy::unit_arg)]
pub fn set_checked(target: &web_sys::EventTarget, value: bool) -> Result<(), &'static str> {
if let Some(input) = target.dyn_ref::<web_sys::HtmlInputElement>() {
return match input.type_().as_str() {
"file" => Err(r#"The checked attribute can be used with <input type="checkbox"> and <input type="radio">."#),
_ => Ok(input.set_checked(value))
};
}
if let Some(input) = target.dyn_ref::<web_sys::HtmlMenuItemElement>() {
return Ok(input.set_checked(value));
}
Err("Only `HtmlInputElement` and `HtmlMenuItemElement` can be used in function `set_checked`.")
}
pub trait ClosureNew<T> {
#[allow(clippy::new_ret_no_self)]
fn new(inner: impl FnMut(T) + 'static) -> Closure<dyn FnMut(T)>
where
T: wasm_bindgen::convert::FromWasmAbi + 'static;
}
impl<T> ClosureNew<T> for Closure<T> {
#[allow(clippy::new_ret_no_self)]
fn new(inner: impl FnMut(T) + 'static) -> Closure<dyn FnMut(T)>
where
T: wasm_bindgen::convert::FromWasmAbi + 'static,
{
Closure::wrap(Box::new(inner))
}
}
#[cfg(use_nightly)]
pub fn log<T>(object: T) -> T {
web_sys::console::log_1(&format!("{:#?}", dbg::WrapDebug(&object)).into());
object
}
#[cfg(not(use_nightly))]
pub fn log<T: std::fmt::Debug>(object: T) -> T {
web_sys::console::log_1(&format!("{:#?}", &object).into());
object
}
#[cfg(use_nightly)]
pub fn error<T>(object: T) -> T {
web_sys::console::error_1(&format!("{:#?}", dbg::WrapDebug(&object)).into());
object
}
#[cfg(not(use_nightly))]
pub fn error<T: std::fmt::Debug>(object: T) -> T {
web_sys::console::error_1(&format!("{:#?}", &object).into());
object
}
#[deprecated]
pub fn update<Ms>(msg: Ms)
where
Ms: 'static + serde::Serialize,
{
let msg_as_js_value = wasm_bindgen::JsValue::from_serde(&msg)
.expect("Error: TriggerUpdate - can't serialize given msg!");
let mut custom_event_config = web_sys::CustomEventInit::new();
custom_event_config.detail(&msg_as_js_value);
let event = web_sys::CustomEvent::new_with_event_init_dict(
events::UPDATE_TRIGGER_EVENT_ID,
&custom_event_config,
)
.expect("Error: TriggerUpdate - create event failed!");
window()
.dispatch_event(&event)
.expect("Error: TriggerUpdate - dispatch)event failed!");
}