webru/
global.rs

1use wasm_bindgen::closure::Closure;
2use wasm_bindgen::JsCast;
3use web_sys::{window, Document, Element, HtmlElement, Location};
4
5/// Get the [`web_sys::Document`](https://docs.rs/web-sys/0.3.56/web_sys/struct.Document.html) Object
6///
7/// This function is equivalent to javascript's `document` property
8///
9///     
10/// # Panics
11///
12/// This function will panic if you try to call this outside of the web such as `node.js` runtime
13///
14///
15/// # Example    
16///
17/// ```rust
18/// use weblog::console_log;
19/// use webru::document;
20///
21/// // Get the `web_sys::Document` object by calling this function
22/// let document = document();
23///
24/// // Get the url of the page
25/// let url = document.url().unwrap();
26///
27/// // print the url in console
28/// console_log!(url.clone());
29///
30/// assert_eq!(
31///     url,
32///     web_sys::window()
33///         .unwrap()
34///         .document()
35///         .unwrap()
36///         .url()
37///         .unwrap()
38/// )
39/// ```
40///
41///    
42pub fn document() -> Document {
43    let window = window().unwrap();
44    window.document().unwrap()
45}
46
47/// Get the [`web_sys::Location`](https://docs.rs/web-sys/0.3.56/web_sys/struct.Location.html) object
48///
49/// This function is equivalent to javascript's `location` property        
50///
51///        
52/// # Panics
53///
54/// This function will panic if you try to call this outside of the web such as `node.js` runtime
55///
56///
57/// # Example
58///
59/// ```rust
60/// use weblog::console_log;
61/// use webru::location;
62///
63/// // Get the `web_sys::Location` object
64/// let location = location();
65///
66/// // print the full path of your webpage
67/// console_log!(location.href().unwrap());
68///
69/// // print the host/domain of your webpage
70/// console_log!(location.host().unwrap());
71///
72/// ```
73///
74pub fn location() -> Location {
75    document().location().unwrap()
76}
77
78/// Get the full url of the web page.
79///
80/// This function is equivalent to javascript's [`location.href`](https://developer.mozilla.org/en-US/docs/Web/API/Location/href) property
81///
82/// It returns the full url of the web page
83///
84/// If your web page's url is `https://www.example.com/path/name?key=2345` then it will return `https://www.example.com/path/name?key=2345`
85///
86///      
87/// # Panics
88///
89/// This function will panic if you try to call this outside of the web such as `node.js` runtime    
90///
91pub fn url() -> String {
92    location().href().unwrap()
93}
94
95/// Get the [`body`] property.
96///
97/// This function is equivalent to javascript's [`document.body`](https://developer.mozilla.org/en-US/docs/Web/API/Document/body) property
98///
99///
100/// # Panics
101///
102/// This function will panic if you try to call this outside of the web such as `node.js` runtime
103///
104///
105/// # Example
106///
107/// ```rust
108/// use webru::create_element;
109/// use webru::body;
110///
111/// // Create an element with `create_element` function
112/// let h1: web_sys::Element = create_element("h1");
113///
114/// // get the `body` field
115/// let body = body();
116///
117/// // insert the element `h1` into the <body>
118/// body.append_child(&h1).unwrap();
119///
120/// assert_eq!(
121///     body,
122///     web_sys::window()
123///         .unwrap()
124///         .document()
125///         .unwrap()
126///         .body()
127///         .unwrap()
128/// );
129///    
130/// ```
131///
132/// [`body`]: <https://developer.mozilla.org/en-US/docs/Web/API/Document/body>
133pub fn body() -> HtmlElement {
134    document().body().unwrap()
135}
136
137/// Get the domain name of the website
138///
139/// This function is equivalent to javascript's [`location.hostname`](https://developer.mozilla.org/en-US/docs/Web/API/Location/hostname) property
140///
141/// If your website's url is `https://www.example.com/path/name?key=2345` then it will return `www.example.com`
142///
143/// If your website's url is `https://sub.example.org` then it will return `sub.example.org`
144///
145///     
146/// # Panics
147///
148/// This function will panic if you try to call this outside of the web such as `node.js` runtime
149///
150pub fn domain_name() -> String {
151    location().hostname().unwrap()
152}
153
154/// Get the the path of the website
155///
156/// This function is equivalent to javascript's [`location.pathname`](https://developer.mozilla.org/en-US/docs/Web/API/Location/pathname) property
157///
158/// If your website's url is `https://www.example.com/path/name?key=234` then it will return `/path/name`
159///
160/// If your website's url is `https://www.example.com` then it will return `/`
161///
162///
163/// # Panics
164///
165/// This function will panic if you try to call this outside of the web such as `node.js` runtime
166///
167pub fn path_name() -> String {
168    location().pathname().unwrap()
169}
170
171/// Reloads the page
172///
173/// This function is equivalent to javascript's `location.reload()` function
174///    
175///
176/// # Panics
177///
178/// This function will panic if you try to call this outside of the web such as `node.js` runtime
179///
180pub fn reload() {
181    location().reload().unwrap()
182}
183
184/// Javascript [`alert`](https://developer.mozilla.org/en-US/docs/Web/API/Window/alert) method
185///
186///
187/// # Arguments
188///
189/// * `msg` - The text to display in the alert box
190///
191///
192/// # Panics
193///
194/// This function will panic if you try to call this outside of the web such as `node.js` runtime
195///
196///
197/// # Example
198///
199/// ```rust
200/// use webru::alert;
201///
202/// // show an popup/alert message
203/// alert("You do not have access to this site!");
204/// ```
205///
206pub fn alert(msg: &str) {
207    window().unwrap().alert_with_message(msg).unwrap()
208}
209
210/// Javascript [`prompt`](https://developer.mozilla.org/en-US/docs/Web/API/Window/prompt) method
211///
212/// It returns the text the user entered in the dialog box
213///
214/// If the user clicks on the `Okay` button and if the user typed anything in the prompt, then it will return `Some("whatever user typed")`, else if the user didn't typed anything it will return `Some("")`
215///
216/// If the user clicks on the `Cancel` button then this function will return `None`  
217///
218///
219/// # Arguments
220///
221/// * `msg` - The text to display in the dialog box    
222///
223///
224/// # Panics
225///
226/// This function will panic if you try to call this outside of the web such as `node.js` runtime
227///
228///
229/// # Example
230///
231/// ```rust
232/// use webru::prompt;
233/// use weblog::console_log;
234///
235/// // taking user's name
236/// let name: Option<String> = prompt("What is your name?");
237///
238/// if let Some(name) = name {
239///     console_log!("The name of the user is: ", name);
240/// } else {
241///     console_log!("The user didn't specify his/her name")
242/// }
243///
244/// ```
245pub fn prompt(msg: &str) -> Option<String> {
246    window().unwrap().prompt_with_message(msg).unwrap() // if None: it means the user clicked "cancel" button, else it means the user clicked "OK" button
247}
248
249/// Javascript [`Callback`](https://developer.mozilla.org/en-US/docs/Glossary/Callback_function)
250///
251/// [`web-sys`](https://crates.io/crates/web-sys) uses [`Function`] for taking closures from Rust and pass it to javascript.
252///
253/// So you can create a [`Closure`] by calling this function
254/// and then call the `.as_ref().dyn_ref().unwrap()` method to convert it from [`Closure<dyn Fn()>`] to [`Function`]
255///
256/// It returns a [`Closure`] which can be used in any JavaScript function that takes a callback/closure as argument
257///
258/// You can see more info about using rust closures with wasm-bindgen [here](https://rustwasm.github.io/docs/wasm-bindgen/examples/closures.html)
259///
260///
261/// # Arguments
262///
263/// * `handler` - A Rust closure
264///
265///
266/// # Examples
267///
268/// Javascript's `setTimeout` function
269///
270/// ```rust
271/// use wasm_bindgen::JsCast;
272/// use webru::callback;
273/// use weblog::console_log;
274///
275/// // Getting the `Window` object
276/// let window = web_sys::window().unwrap();
277///
278/// // Getting the `wasm_bindgen::closure::Closure` by passing a Rust closure
279/// let callback = callback(|| {
280///     console_log!("Hello world");
281/// });
282///
283/// // The first argument of `set_timeout_with_callback_and_timeout_and_arguments_0` is a `&::js_sys::Function`
284/// // which can be used by converting the `Closure` into the `&::js_sys::Function`
285/// // by calling `.as_ref().unchecked_ref()` method on it.
286///
287/// // The secend argument of `set_timeout_with_callback_and_timeout_and_arguments_0` is a timer,
288/// // how much time do I want to delay
289/// window
290///     .set_timeout_with_callback_and_timeout_and_arguments_0(
291///         callback.as_ref().unchecked_ref(),
292///         2000,
293///     )
294///     .unwrap();
295///
296/// // The call of `.forget()` is necessary. If we don't call this method, it will create a memory leak :(
297/// callback.forget();
298/// ```
299///
300///
301/// `onclick` property
302///
303/// ```rust
304/// use webru::{body, callback, create_element};
305/// use web_sys::HtmlElement;
306/// use wasm_bindgen::JsCast;
307/// use weblog::console_log;
308///     
309///
310/// // Creating a <button>
311/// let button: HtmlElement = create_element("button")
312///     .dyn_ref::<HtmlElement>()
313///     .unwrap()
314///     .clone();
315///
316/// // setting the innerHTML field
317/// button.set_inner_html("click me");
318///
319/// // This callback will be called the button will be clicked
320/// let callback = callback(|| {
321///     console_log!("You clicked the button");
322/// });
323///
324/// // setting `onclick` property to the <button>
325/// // converting the `Closure<dyn Fn()>` to `&js_sys::Function` by calling `.as_ref().dyn_ref().unwrap()` method
326/// button.set_onclick(Some(callback.as_ref().dyn_ref().unwrap()));
327///
328/// // The call of `.forget()` is necessary. If we don't call this method, it will create a memory leak :(
329/// callback.forget();
330///
331/// // inserting the button into the <body>
332/// body().append_child(&button).unwrap();
333///
334/// // Now you the button is clicked, the text `You clicked the button` will be in console
335/// ```
336///
337/// [`Closure`]: <https://docs.rs/wasm-bindgen/0.2.79/wasm_bindgen/closure/struct.Closure.html>
338/// [`Closure<dyn Fn()>`]: <https://docs.rs/wasm-bindgen/0.2.79/wasm_bindgen/closure/struct.Closure.html>
339/// [`Function`]: <https://docs.rs/js-sys/0.3.56/js_sys/struct.Function.html>
340///
341pub fn callback<T: 'static>(handler: T) -> Closure<dyn Fn()>
342where
343    T: Fn(),
344{
345    Closure::wrap(Box::new(handler) as Box<dyn Fn()>)
346}
347
348/// Javascript [`document.createElement`](https://developer.mozilla.org/en-US/docs/Web/API/Document/createElement) method
349///
350/// This function will create a new [`Element`](https://docs.rs/web-sys/0.3.56/web_sys/struct.Element.html) and return it.
351///
352/// Note that this function will create and return the element, not push to the DOM
353///
354///
355/// # Arguments
356///
357/// * `element_name` - A string that specifies the type of element to be created.
358///
359///
360/// # Panics
361///
362/// * This function will panic if you try to call this outside of the web such as `node.js` runtime
363///  
364/// * This function will panic if the `element_name` is not a valid tag name. For example if you pass `1h` as argument, it will `panic`. But if you pass `h1` as argument, it will not `panic`
365///
366///
367/// # Example
368///
369/// ```
370/// use webru::{body, create_element};
371///
372/// // Create a <p> tag
373/// let p = create_element("p");
374///
375/// // Create a <h1> tag
376/// let h1 = create_element("h1");
377///
378/// // Create a <button> tag
379/// let button = create_element("button");
380///
381/// // Create a <div> tag
382/// let div = create_element("div");
383///
384/// // Pushing these elements into the DOM
385/// body().append_child(&p).unwrap();
386/// body().append_child(&h1).unwrap();
387/// body().append_child(&button).unwrap();
388/// body().append_child(&div).unwrap();
389///```
390pub fn create_element(element_name: &str) -> Element {
391    document().create_element(element_name).unwrap()
392}
393
394/// Call a closure in a specific window width with media query
395///
396/// # Panics
397///
398/// This function will panic if you try to call this outside of the web such as `node.js` runtime
399///
400/// # Example
401///
402/// ```rust
403/// use webru::{body, create_element, media_query};
404///
405/// let p = create_element("p");
406/// p.set_inner_html("You cannot see me if you are less then 1000px");
407///
408/// // render the "p" tag into the DOM
409/// body().append_child(&p).unwrap();
410///
411/// media_query(
412///     // if the window width is less then 1000px
413///     {
414///         let p = p.clone();
415///         move || {
416///             p.set_inner_html("");
417///         }
418///     },
419///     // if the window width is more then 1000px
420///     move || {
421///         p.clone()
422///             .set_inner_html("You cannot see me if you are less then 1000px");
423///     },
424///     // the maxium width when the callback will be called
425///     1000,
426/// );
427/// ```
428pub fn media_query<T1: Fn() + 'static, T2: Fn() + 'static>(
429    if_media_matched: T1,
430    if_media_doesnt_matched: T2,
431    max_width: u128,
432) {
433    let query = window()
434        .unwrap()
435        .match_media(&format!("(max-width: {}px)", max_width))
436        .unwrap()
437        .unwrap();
438
439    let cb = callback({
440        let query = query.clone();
441
442        move || {
443            if query.matches() {
444                if_media_matched();
445            } else {
446                if_media_doesnt_matched();
447            }
448        }
449    });
450
451    query
452        .add_listener_with_opt_callback(Some(cb.as_ref().dyn_ref().unwrap()))
453        .unwrap();
454
455    cb.forget();
456}