use std::borrow::Cow;
use wasm_bindgen::closure::Closure;
use wasm_bindgen::JsCast;
pub use gloo_utils::{document, history, window};
#[deprecated(
since = "0.8.0",
note = "see [`request_animation_frame`](fn.request_animation_frame.html)"
)]
pub type RequestAnimationFrameTime = f64;
#[must_use]
#[deprecated(
since = "0.8.0",
note = "see [`request_animation_frame`](fn.request_animation_frame.html)"
)]
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 body() -> web_sys::HtmlElement {
document().body().expect("Can't find the document's body")
}
pub fn html_document() -> web_sys::HtmlDocument {
wasm_bindgen::JsValue::from(document()).unchecked_into::<web_sys::HtmlDocument>()
}
pub fn canvas(id: &str) -> Option<web_sys::HtmlCanvasElement> {
document()
.get_element_by_id(id)
.and_then(|element| element.dyn_into::<web_sys::HtmlCanvasElement>().ok())
}
pub fn canvas_context_2d(canvas: &web_sys::HtmlCanvasElement) -> web_sys::CanvasRenderingContext2d {
canvas
.get_context("2d")
.expect("Problem getting canvas context")
.expect("The canvas context is empty")
.dyn_into::<web_sys::CanvasRenderingContext2d>()
.expect("Problem casting as web_sys::CanvasRenderingContext2d")
}
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)
}
}
#[deprecated(
since = "0.8.0",
note = "use [`Orders::after_next_render`](../../app/orders/trait.Orders.html#method.after_next_render) instead"
)]
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.")
}
#[allow(clippy::missing_errors_doc)]
pub fn set_value(target: &web_sys::EventTarget, value: &str) -> Result<(), Cow<'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));
}
};
}
if let Some(input) = target.dyn_ref::<HtmlInputElement>() {
return set_html_input_element_value(input, value);
}
set!(HtmlTextAreaElement);
set!(HtmlSelectElement);
set!(HtmlProgressElement, |_| value.parse().map_err(|error| {
Cow::from(format!(
"Can't parse value to `f64` for `HtmlProgressElement`. Error: {:?}",
error
))
}));
set!(HtmlOptionElement);
set!(HtmlButtonElement);
set!(HtmlDataElement);
set!(HtmlMeterElement, |_| value.parse().map_err(|error| {
Cow::from(format!(
"Can't parse value to `f64` for `HtmlMeterElement`. Error: {:?}",
error
))
}));
set!(HtmlLiElement, |_| value.parse().map_err(|error| {
Cow::from(format!(
"Can't parse value to `i32` for `HtmlLiElement`. Error: {:?}",
error
))
}));
set!(HtmlOutputElement);
set!(HtmlParamElement);
Err(Cow::from(
"Can't use function `set_value` for given element.",
))
}
fn set_html_input_element_value(
input: &web_sys::HtmlInputElement,
value: &str,
) -> Result<(), Cow<'static, str>> {
if value == input.value() {
return Ok(());
}
let selection_update_required = match input.type_().as_str() {
"file" => {
return Err(Cow::from(
r#"The value attribute cannot be used with <input type="file">."#,
))
}
"text" | "password" | "search" | "tel" | "url" | "week" | "month" => true,
_ => false,
};
if selection_update_required && is_active(input) {
let selection_start = input
.selection_start()
.expect("get `HtmlInputElement` selection start");
let selection_end = input
.selection_end()
.expect("get `HtmlInputElement` selection end");
input.set_value(value);
input
.set_selection_start(selection_start)
.expect("set `HtmlInputElement` selection start");
input
.set_selection_end(selection_end)
.expect("set `HtmlInputElement` selection end");
} else {
input.set_value(value);
}
Ok(())
}
fn is_active(element: &web_sys::Element) -> bool {
document().active_element().as_ref() == Some(element)
}
#[allow(clippy::missing_errors_doc)]
#[allow(dead_code)]
pub fn get_checked(target: &web_sys::EventTarget) -> Result<bool, Cow<str>> {
if let Some(input) = target.dyn_ref::<web_sys::HtmlInputElement>() {
return match input.type_().as_str() {
"file" => Err(Cow::from(
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(Cow::from(
"Only `HtmlInputElement` and `HtmlMenuItemElement` can be used in function `get_checked`.",
))
}
#[allow(clippy::missing_errors_doc)]
#[allow(clippy::unit_arg)]
pub fn set_checked(target: &web_sys::EventTarget, value: bool) -> Result<(), Cow<str>> {
if let Some(input) = target.dyn_ref::<web_sys::HtmlInputElement>() {
return match input.type_().as_str() {
"file" => Err(Cow::from(
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(Cow::from(
"Only `HtmlInputElement` and `HtmlMenuItemElement` can be used in function `set_checked`.",
))
}
#[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
}