use super::*;
const EVENT_ID_CHAIN_JS: &str = r#"(globalThis.__euvEventIdChain = function (event, maxDepth) {
const path = (typeof event.composedPath === 'function')
? event.composedPath()
: (function () { const out = []; let n = event.target; while (n) { out.push(n); n = n.parentNode; } return out; })();
const ids = [];
for (let i = 0; i < path.length; i++) {
if (maxDepth !== 0 && i >= maxDepth) { break; }
const el = path[i];
if (el.nodeType !== 1) { continue; }
const id = el.getAttribute('data-euv-id');
if (id !== null && id !== '') { ids.push(parseFloat(id)); }
}
return ids;
});
"#;
fn global_this() -> Option<JsValue> {
if let Ok(value) = js_sys::eval(GLOBAL_THIS_NAME)
&& !value.is_undefined()
{
return Some(value);
}
let window_value: web_sys::Window = window()?;
Some(window_value.into())
}
pub(crate) fn ensure_event_id_chain_global() -> bool {
let Some(global_value) = global_this() else {
return false;
};
let already_present: bool = js_sys::Reflect::get(
&global_value,
&JsValue::from_str(EVENT_ID_CHAIN_GLOBAL_NAME),
)
.map(|value: JsValue| value.is_function())
.unwrap_or(false);
if already_present {
return true;
}
js_sys::eval(EVENT_ID_CHAIN_JS).is_ok()
}
fn event_id_chain_fn() -> Option<Function> {
EVENT_ID_CHAIN_FN.with(|cell: &RefCell<Option<Option<Function>>>| {
if let Some(resolved) = cell.borrow().clone() {
return resolved;
}
let resolved: Option<Function> = global_this()
.and_then(|global_value: JsValue| {
js_sys::Reflect::get(
&global_value,
&JsValue::from_str(EVENT_ID_CHAIN_GLOBAL_NAME),
)
.ok()
})
.filter(|value: &JsValue| value.is_function())
.and_then(|value: JsValue| value.dyn_into::<Function>().ok());
let mut slot: std::cell::RefMut<'_, Option<Option<Function>>> = cell.borrow_mut();
*slot = Some(resolved.clone());
resolved
})
}
pub(crate) fn euv_event_collect_id_chain(event: &JsValue, max_depth: usize) -> Float64Array {
if let Some(walker) = event_id_chain_fn()
&& let Some(global_value) = global_this()
&& let Ok(result) = walker.call2(&global_value, event, &JsValue::from_f64(max_depth as f64))
&& let Some(array) = result.dyn_ref::<js_sys::Array>()
{
let length: u32 = array.length();
if length > 0 {
let out: Float64Array = Float64Array::new_with_length(length);
let mut ids: Vec<f64> = Vec::with_capacity(length as usize);
for index in 0..length {
match array.get(index).as_f64() {
Some(value) => ids.push(value),
None => return collect_id_chain_rust(event, max_depth),
}
}
out.copy_from(&ids);
return out;
}
return Float64Array::new_with_length(0);
}
collect_id_chain_rust(event, max_depth)
}
fn collect_id_chain_rust(event: &JsValue, max_depth: usize) -> Float64Array {
let event_target: Option<web_sys::EventTarget> =
js_sys::Reflect::get(event, &JsValue::from_str(EVENT_TARGET_PROP))
.ok()
.and_then(|value: JsValue| value.dyn_into::<web_sys::EventTarget>().ok());
let Some(target) = event_target else {
return Float64Array::new_with_length(0);
};
let mut ids: Vec<f64> = Vec::new();
let mut node: Option<web_sys::Node> = Some(target.unchecked_into::<web_sys::Node>());
let mut depth: usize = 0;
while let Some(current) = node {
if max_depth != 0 && depth >= max_depth {
break;
}
let element: Option<&web_sys::Element> = current.dyn_ref::<web_sys::Element>();
if let Some(el) = element
&& let Some(id_str) = el.get_attribute(DATA_EUV_ID)
&& let Ok(parsed) = id_str.parse::<f64>()
{
ids.push(parsed);
}
depth += 1;
node = current.parent_node();
}
let out: Float64Array = Float64Array::new_with_length(ids.len() as u32);
out.copy_from(&ids);
out
}