EN605.607.81.SP22_ASDM_Project/front_end/src/html_helper.rs

176 lines
5.9 KiB
Rust

//Four Line Dropper Frontend - A webapp that allows one to play a game of Four Line Dropper
//Copyright (C) 2022 Stephen Seo
//
//This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
//
//This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
//
//You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
use js_sys::{Function, JsString, Promise};
use std::collections::HashMap;
use wasm_bindgen::{JsCast, JsValue};
use wasm_bindgen_futures::JsFuture;
use web_sys::{window, Document, Window};
use crate::constants::BACKEND_URL;
pub fn get_window_document() -> Result<(Window, Document), String> {
let window = window().ok_or_else(|| String::from("Failed to get window"))?;
let document = window
.document()
.ok_or_else(|| String::from("Failed to get document"))?;
Ok((window, document))
}
pub fn append_to_info_text(
document: &Document,
id: &str,
msg: &str,
limit: u32,
) -> Result<(), String> {
let info_text = document
.get_element_by_id(id)
.ok_or_else(|| format!("Failed to get info_text \"{}\"", id))?;
let height = info_text.client_height();
// create the new text to be appended in the text
let p = document
.create_element("p")
.map_err(|e| format!("{:?}", e))?;
p.set_inner_html(msg);
// check if scrolled to top
let at_top: bool = info_text.scroll_top() <= height - info_text.scroll_height();
// append text to output
info_text
.append_with_node_1(&p)
.map_err(|e| format!("{:?}", e))?;
while info_text.child_element_count() > limit {
info_text
.remove_child(
&info_text.first_child().ok_or_else(|| {
format!("Failed to get first_child() of info_text \"{}\"", id)
})?,
)
.map_err(|e| format!("{:?}", e))?;
}
if at_top {
info_text.set_scroll_top(height - info_text.scroll_height());
}
Ok(())
}
pub fn element_append_class(document: &Document, id: &str, class: &str) -> Result<(), String> {
let element = document
.get_element_by_id(id)
.ok_or_else(|| format!("Failed to get element with id \"{}\"", id))?;
let new_class = format!("{} {}", element.class_name(), class);
element.set_class_name(&string_remove_extra_whitespace(&new_class));
Ok(())
}
pub fn element_remove_class(document: &Document, id: &str, class: &str) -> Result<(), String> {
let element = document
.get_element_by_id(id)
.ok_or_else(|| format!("Failed to get element with id \"{}\"", id))?;
let mut element_class: String = element.class_name();
let idx_opt = element_class.find(class);
if let Some(idx) = idx_opt {
let mut remaining = element_class.split_off(idx);
element_class += &remaining.split_off(class.len());
}
element.set_class_name(&string_remove_extra_whitespace(&element_class));
Ok(())
}
fn string_remove_extra_whitespace(s: &str) -> String {
let mut replacement_string = String::with_capacity(s.len());
let mut is_space = false;
for c in s.chars() {
if c != ' ' {
replacement_string.push(c);
is_space = false;
} else if is_space {
continue;
} else {
replacement_string.push(c);
is_space = true;
}
}
replacement_string
}
pub fn element_has_class(document: &Document, id: &str, class: &str) -> Result<bool, String> {
let element = document
.get_element_by_id(id)
.ok_or_else(|| format!("Failed to get element with id \"{}\"", id))?;
let element_class: String = element.class_name();
Ok(element_class.contains(class))
}
pub async fn send_to_backend(entries: HashMap<String, String>) -> Result<String, String> {
let mut send_json_string = String::from("{");
for (key, value) in entries {
send_json_string.push('"');
send_json_string.push_str(&key);
send_json_string.push_str("\":");
if key == "id" || key == "position" {
send_json_string.push_str(&value);
} else {
send_json_string.push('"');
send_json_string.push_str(&value);
send_json_string.push('"');
}
send_json_string.push(',');
}
send_json_string.truncate(send_json_string.len() - 1);
send_json_string.push('}');
// TODO check usage of "no-cors"
let function = Function::new_no_args(&format!(
"
let fetch_settings = {{}};
fetch_settings.method = 'POST';
fetch_settings.headers = {{}};
fetch_settings.headers['Content-Type'] = 'application/json';
fetch_settings.headers['Accept'] = 'text/html,application/json';
//fetch_settings.mode = 'no-cors';
fetch_settings.body = '{}';
return fetch('{}', fetch_settings)
.then((response) => {{
return response.text();
}});
",
send_json_string, BACKEND_URL,
));
let jsvalue: JsValue = function
.call0(&function)
.map_err(|e| format!("Failed to POST to backend: {:?}", e))?;
let promise: Promise = jsvalue.dyn_into().map_err(|e| {
format!(
"Failed to get Promise out of JsValue when POSTing to backend: {:?}",
e
)
})?;
let future_result: JsValue = JsFuture::from(promise)
.await
.map_err(|e| format!("Failed to await promise when POSTing to backend: {:?}", e))?;
let json_string = String::from(JsString::from(future_result));
Ok(json_string)
}