use std::{borrow::Cow, cell::OnceCell, collections::HashMap};
use serde::{Deserialize, Serialize};
use crate::element::definition::ElementDefinition;
use crate::error::{BodyError, WebDynproError};
use crate::event::Event;
use crate::element::utils::{children_element, parse_lsdata, parse_lsevents};
use crate::element::{Element, ElementWrapper, EventParameterMap, Interactable};
#[derive(Debug)]
pub struct ClientInspector<'a> {
id: Cow<'static, str>,
element_ref: scraper::ElementRef<'a>,
lsdata: OnceCell<ClientInspectorLSData>,
lsevents: OnceCell<Option<EventParameterMap>>,
}
#[derive(Serialize, Deserialize, Debug, Default)]
#[serde(rename_all(serialize = "PascalCase"))]
#[allow(unused)]
pub struct ClientInspectorLSData {
#[serde(rename(deserialize = "0"), skip_serializing)]
notification_trigger: Option<String>,
#[serde(rename(deserialize = "1"))]
client_width: Option<String>,
#[serde(rename(deserialize = "2"))]
client_height: Option<String>,
#[serde(rename(deserialize = "3"))]
screen_width: Option<String>,
#[serde(rename(deserialize = "4"))]
screen_height: Option<String>,
#[serde(rename(deserialize = "5"))]
screen_orientation: Option<String>,
#[serde(rename(serialize = "QME", deserialize = "6"))]
qme: Option<String>,
#[serde(rename(deserialize = "7"))]
rendering_mode_compatibility: Option<String>,
#[serde(rename(serialize = "ThemeID", deserialize = "8"))]
theme_id: Option<String>,
#[serde(rename(serialize = "SapThemeID", deserialize = "9"))]
sap_theme_id: Option<String>,
#[serde(rename(deserialize = "10"))]
theme_scope: Option<String>,
#[serde(rename(deserialize = "11"))]
themed_table_row_height: Option<String>,
#[serde(rename(deserialize = "12"))]
themed_form_layout_row_height: Option<String>,
#[serde(rename(deserialize = "13"))]
themed_scrollbar_dimension: Option<String>,
#[serde(rename(deserialize = "14"))]
themed_document_background_color: Option<String>,
#[serde(rename(deserialize = "15"))]
themed_svg_libs: Option<String>,
#[serde(rename(deserialize = "16"))]
themed_svg_lib_urls: Option<String>,
#[serde(rename(deserialize = "17"))]
themed_raster_height: Option<String>,
#[serde(rename(deserialize = "18"))]
themed_raster_width: Option<String>,
#[serde(rename(deserialize = "19"))]
themed_layout_padding_top: Option<String>,
#[serde(rename(deserialize = "20"))]
themed_layout_padding_left: Option<String>,
#[serde(rename(deserialize = "21"))]
themed_layout_padding_bottom: Option<String>,
#[serde(rename(deserialize = "22"))]
themed_layout_padding_right: Option<String>,
#[serde(rename(deserialize = "23"))]
themed_abap_list_raster_height: Option<String>,
#[serde(rename(deserialize = "24"))]
themed_abap_list_raster_width: Option<String>,
#[serde(rename(deserialize = "25"))]
themed_value_help_height: Option<String>,
#[serde(rename(deserialize = "26"))]
themed_value_help_width: Option<String>,
#[serde(rename(deserialize = "27"))]
theme_tags: Option<String>,
#[serde(rename(deserialize = "28"))]
word_enabled: Option<String>,
#[serde(rename(deserialize = "29"))]
excel_enabled: Option<String>,
#[serde(rename(deserialize = "30"))]
flash_enabled: Option<String>,
#[serde(rename(deserialize = "31"))]
acrobat_enabled: Option<String>,
#[serde(rename(deserialize = "32"))]
silverlight_enabled: Option<String>,
#[serde(rename(deserialize = "33"))]
java_enabled: Option<String>,
#[serde(rename(deserialize = "34"))]
java_version: Option<String>,
#[serde(rename(deserialize = "35"))]
web_sockets_enabled: Option<String>,
#[serde(rename(deserialize = "36"))]
device_type: Option<String>,
#[serde(rename(deserialize = "37"))]
css_matches_html_version: Option<String>,
#[serde(rename(deserialize = "38"))]
custom_data: Option<String>,
#[serde(rename(deserialize = "39"))]
platform: Option<String>,
#[serde(rename(deserialize = "40"))]
window_opener_exists: Option<String>,
#[serde(rename(serialize = "ClientURL", deserialize = "41"))]
client_url: Option<String>,
#[serde(rename(deserialize = "42"))]
document_domain: Option<String>,
#[serde(rename(deserialize = "43"))]
is_top_window: Option<String>,
#[serde(rename(deserialize = "44"))]
parent_accessible: Option<String>,
}
#[doc = "[`ClientInspector`]의 정의"]
#[derive(Clone, Debug)]
pub struct ClientInspectorDef {
id: Cow<'static, str>,
}
impl ClientInspectorDef {
pub const fn new(id: &'static str) -> Self {
Self {
id: Cow::Borrowed(id),
}
}
}
impl<'body> ElementDefinition<'body> for ClientInspectorDef {
type Element = ClientInspector<'body>;
fn new_dynamic(id: String) -> Self {
Self { id: id.into() }
}
fn from_ref(element_ref: scraper::ElementRef<'_>) -> Result<Self, WebDynproError> {
let id = element_ref.value().id().ok_or(BodyError::InvalidElement)?;
Ok(Self {
id: id.to_string().into(),
})
}
fn id(&self) -> &str {
&self.id
}
fn id_cow(&self) -> Cow<'static, str> {
self.id.clone()
}
}
impl<'a> Element<'a> for ClientInspector<'a> {
const CONTROL_ID: &'static str = "CI";
const ELEMENT_NAME: &'static str = "ClientInspector";
type ElementLSData = ClientInspectorLSData;
type Def = ClientInspectorDef;
fn from_ref(
elem_def: &impl ElementDefinition<'a>,
element: scraper::ElementRef<'a>,
) -> Result<Self, WebDynproError> {
Ok(Self::new(elem_def.id_cow(), element))
}
fn children(&self) -> Vec<ElementWrapper<'a>> {
children_element(*self.element_ref())
}
fn lsdata(&self) -> &Self::ElementLSData {
self.lsdata.get_or_init(|| {
let Ok(lsdata_obj) =
parse_lsdata(self.element_ref.value().attr("lsdata").unwrap_or(""))
else {
return ClientInspectorLSData::default();
};
serde_json::from_value::<Self::ElementLSData>(lsdata_obj)
.unwrap_or(ClientInspectorLSData::default())
})
}
fn id(&self) -> &str {
&self.id
}
fn element_ref(&self) -> &scraper::ElementRef<'a> {
&self.element_ref
}
fn wrap(self) -> ElementWrapper<'a> {
ElementWrapper::ClientInspector(self)
}
}
impl<'a> Interactable<'a> for ClientInspector<'a> {
fn lsevents(&self) -> Option<&EventParameterMap> {
self.lsevents
.get_or_init(|| parse_lsevents(self.element_ref.attr("lsevents")?).ok())
.as_ref()
}
}
inventory::submit! {
crate::element::registry::ElementRegistration::new(
"CI",
|id, element_ref| {
use crate::element::Element;
use crate::element::definition::ElementDefinition;
let def = ClientInspectorDef::new_dynamic(id);
Ok(ClientInspector::from_ref(&def, element_ref)?.wrap())
},
)
}
impl<'a> ClientInspector<'a> {
pub const fn new(id: Cow<'static, str>, element_ref: scraper::ElementRef<'a>) -> Self {
Self {
id,
element_ref,
lsdata: OnceCell::new(),
lsevents: OnceCell::new(),
}
}
pub fn notify(&self, data: &str) -> Result<Event, WebDynproError> {
let mut parameters: HashMap<String, String> = HashMap::new();
parameters.insert("Id".to_string(), self.id.clone().to_string());
parameters.insert("Data".to_string(), data.to_string());
self.fire_event("Notify".to_string(), parameters)
}
}