1use std::borrow::Cow;
7use wasm_bindgen::closure::Closure;
8use wasm_bindgen::JsCast;
9
10pub use gloo_utils::{document, history, window};
11
12#[deprecated(
13 since = "0.8.0",
14 note = "see [`request_animation_frame`](fn.request_animation_frame.html)"
15)]
16pub type RequestAnimationFrameTime = f64;
17
18#[must_use]
19#[deprecated(
20 since = "0.8.0",
21 note = "see [`request_animation_frame`](fn.request_animation_frame.html)"
22)]
23pub struct RequestAnimationFrameHandle {
24 request_id: i32,
25 _closure: Closure<dyn FnMut(RequestAnimationFrameTime)>,
26}
27
28impl Drop for RequestAnimationFrameHandle {
29 fn drop(&mut self) {
30 window()
31 .cancel_animation_frame(self.request_id)
32 .expect("Problem cancelling animation frame request");
33 }
34}
35
36pub fn body() -> web_sys::HtmlElement {
38 document().body().expect("Can't find the document's body")
39}
40
41pub fn html_document() -> web_sys::HtmlDocument {
43 wasm_bindgen::JsValue::from(document()).unchecked_into::<web_sys::HtmlDocument>()
44}
45pub fn canvas(id: &str) -> Option<web_sys::HtmlCanvasElement> {
48 document()
49 .get_element_by_id(id)
50 .and_then(|element| element.dyn_into::<web_sys::HtmlCanvasElement>().ok())
51}
52
53pub fn canvas_context_2d(canvas: &web_sys::HtmlCanvasElement) -> web_sys::CanvasRenderingContext2d {
55 canvas
56 .get_context("2d")
57 .expect("Problem getting canvas context")
58 .expect("The canvas context is empty")
59 .dyn_into::<web_sys::CanvasRenderingContext2d>()
60 .expect("Problem casting as web_sys::CanvasRenderingContext2d")
61}
62
63#[deprecated(
64 since = "0.8.0",
65 note = "use [`Orders::after_next_render`](../../app/orders/trait.Orders.html#method.after_next_render) instead"
66)]
67pub fn request_animation_frame(
69 f: Closure<dyn FnMut(RequestAnimationFrameTime)>,
70) -> RequestAnimationFrameHandle {
71 let request_id = window()
72 .request_animation_frame(f.as_ref().unchecked_ref())
73 .expect("Problem requesting animation frame");
74
75 RequestAnimationFrameHandle {
76 request_id,
77 _closure: f,
78 }
79}
80
81pub fn get_value(target: &web_sys::EventTarget) -> Result<String, &'static str> {
88 use web_sys::*;
89
90 macro_rules! get {
91 ($element:ty) => {
92 get!($element, |_| Ok(()))
93 };
94 ($element:ty, $result_callback:expr) => {
95 if let Some(input) = target.dyn_ref::<$element>() {
96 return $result_callback(input).map(|_| input.value().to_string());
97 }
98 };
99 }
100 get!(HtmlInputElement, |input: &HtmlInputElement| {
105 match input.type_().as_str() {
107 "file" => Err(r#"The value attribute cannot be used with <input type="file">."#),
108 _ => Ok(()),
109 }
110 });
111 get!(HtmlTextAreaElement);
112 get!(HtmlSelectElement);
113 get!(HtmlProgressElement);
114 get!(HtmlOptionElement);
115 get!(HtmlButtonElement);
116 get!(HtmlDataElement);
117 get!(HtmlMeterElement);
118 get!(HtmlLiElement);
119 get!(HtmlOutputElement);
120 get!(HtmlParamElement);
121
122 Err("Can't use function `get_value` for given element.")
123}
124
125#[allow(clippy::missing_errors_doc)]
126pub fn set_value(target: &web_sys::EventTarget, value: &str) -> Result<(), Cow<'static, str>> {
128 use web_sys::*;
129
130 macro_rules! set {
131 ($element:ty) => {
132 set!($element, |_| Ok(value))
133 };
134 ($element:ty, $value_result_callback:expr) => {
135 if let Some(input) = target.dyn_ref::<$element>() {
136 return $value_result_callback(input).map(|value| input.set_value(value));
137 }
138 };
139 }
140 if let Some(input) = target.dyn_ref::<HtmlInputElement>() {
145 return set_html_input_element_value(input, value);
146 }
147 set!(HtmlTextAreaElement);
148 set!(HtmlSelectElement);
149 set!(HtmlProgressElement, |_| value.parse().map_err(|error| {
150 Cow::from(format!(
151 "Can't parse value to `f64` for `HtmlProgressElement`. Error: {error:?}",
152 ))
153 }));
154 set!(HtmlOptionElement);
155 set!(HtmlButtonElement);
156 set!(HtmlDataElement);
157 set!(HtmlMeterElement, |_| value.parse().map_err(|error| {
158 Cow::from(format!(
159 "Can't parse value to `f64` for `HtmlMeterElement`. Error: {error:?}"
160 ))
161 }));
162 set!(HtmlLiElement, |_| value.parse().map_err(|error| {
163 Cow::from(format!(
164 "Can't parse value to `i32` for `HtmlLiElement`. Error: {error:?}"
165 ))
166 }));
167 set!(HtmlOutputElement);
168 set!(HtmlParamElement);
169
170 Err(Cow::from(
171 "Can't use function `set_value` for given element.",
172 ))
173}
174
175fn set_html_input_element_value(
176 input: &web_sys::HtmlInputElement,
177 value: &str,
178) -> Result<(), Cow<'static, str>> {
179 if value == input.value() {
181 return Ok(());
182 }
183
184 let selection_update_required = match input.type_().as_str() {
195 "file" => {
197 return Err(Cow::from(
198 r#"The value attribute cannot be used with <input type="file">."#,
199 ))
200 }
201 "text" | "password" | "search" | "tel" | "url" | "week" | "month" => true,
202 _ => false,
203 };
204
205 if selection_update_required && is_active(input) {
208 let selection_start = input
209 .selection_start()
210 .expect("get `HtmlInputElement` selection start");
211 let selection_end = input
212 .selection_end()
213 .expect("get `HtmlInputElement` selection end");
214
215 input.set_value(value);
216
217 input
218 .set_selection_start(selection_start)
219 .expect("set `HtmlInputElement` selection start");
220 input
221 .set_selection_end(selection_end)
222 .expect("set `HtmlInputElement` selection end");
223 } else {
224 input.set_value(value);
225 }
226
227 Ok(())
228}
229
230fn is_active(element: &web_sys::Element) -> bool {
232 document().active_element().as_ref() == Some(element)
233}
234
235#[allow(clippy::missing_errors_doc)]
236#[allow(dead_code)]
238pub fn get_checked(target: &web_sys::EventTarget) -> Result<bool, Cow<str>> {
239 if let Some(input) = target.dyn_ref::<web_sys::HtmlInputElement>() {
240 return match input.type_().as_str() {
242 "file" => Err(Cow::from(
243 r#"The checked attribute can be used with <input type="checkbox"> and <input type="radio">."#,
244 )),
245 _ => Ok(input.checked()),
246 };
247 }
248 if let Some(input) = target.dyn_ref::<web_sys::HtmlMenuItemElement>() {
249 return Ok(input.checked());
250 }
251 Err(Cow::from(
252 "Only `HtmlInputElement` and `HtmlMenuItemElement` can be used in function `get_checked`.",
253 ))
254}
255
256#[allow(clippy::missing_errors_doc)]
257#[allow(clippy::unit_arg)]
259pub fn set_checked(target: &web_sys::EventTarget, value: bool) -> Result<(), Cow<str>> {
260 if let Some(input) = target.dyn_ref::<web_sys::HtmlInputElement>() {
261 return match input.type_().as_str() {
263 "file" => Err(Cow::from(
264 r#"The checked attribute can be used with <input type="checkbox"> and <input type="radio">."#,
265 )),
266 _ => Ok(input.set_checked(value)),
267 };
268 }
269 if let Some(input) = target.dyn_ref::<web_sys::HtmlMenuItemElement>() {
270 return Ok(input.set_checked(value));
271 }
272 Err(Cow::from(
273 "Only `HtmlInputElement` and `HtmlMenuItemElement` can be used in function `set_checked`.",
274 ))
275}
276
277#[deprecated(note = "Use something like https://crates.io/crates/gloo-console instead")]
280pub fn log<T: std::fmt::Debug>(object: T) -> T {
281 web_sys::console::log_1(&format!("{:#?}", &object).into());
282 object
283}
284
285#[deprecated(note = "Use something like https://crates.io/crates/gloo-console instead")]
287pub fn error<T: std::fmt::Debug>(object: T) -> T {
288 web_sys::console::error_1(&format!("{:#?}", &object).into());
289 object
290}