use super::*;
pub(crate) fn bind_signal_to_element<T, F>(element: &Element, write: F, signal: Signal<T>)
where
T: Clone + PartialEq + Display + 'static,
F: Fn(&Element, &str) + 'static,
{
let initial_value: String = signal.get().to_string();
write(element, &initial_value);
let euv_id: usize = element.ensure_euv_id();
let element_clone: Element = element.clone();
let subscription_id: u64 = signal.subscribe(move || {
if !element_clone.is_connected() {
return;
}
let new_value: String = signal.get().to_string();
write(&element_clone, &new_value);
});
Registry::push_binding_cleanup(
euv_id,
Box::new(move || signal.unsubscribe(subscription_id)),
);
}
pub(crate) fn lis_indices<T: Ord>(keys: &[T]) -> Vec<usize> {
let n: usize = keys.len();
if n == 0 {
return Vec::new();
}
let mut tails: Vec<usize> = Vec::with_capacity(n);
let mut tail_min: Vec<&T> = Vec::with_capacity(n);
let mut predecessors: Vec<usize> = vec![0_usize; n];
for (i, key) in keys.iter().enumerate() {
let pos: usize = match tail_min.binary_search(&key) {
Ok(idx) => idx,
Err(idx) => idx,
};
if pos == tails.len() {
tails.push(i);
tail_min.push(key);
} else {
tails[pos] = i;
tail_min[pos] = key;
}
predecessors[i] = if pos == 0 { usize::MAX } else { tails[pos - 1] };
}
let mut result: Vec<usize> = Vec::with_capacity(tails.len());
let mut k: usize = match tails.last() {
Some(last) => *last,
None => return result,
};
while k != usize::MAX {
result.push(k);
match predecessors.get(k) {
Some(&next) if next != usize::MAX => k = next,
_ => break,
}
}
result.reverse();
result
}
pub(crate) fn cached_document() -> Option<Document> {
DOCUMENT_CACHE.with(|cell: &UnsafeCell<Option<Document>>| {
let cached_ptr: *mut Option<Document> = cell.get();
unsafe {
if let Some(doc) = &*cached_ptr {
return Some(doc.clone());
}
}
let window_value: Window = window()?;
let document: Document = window_value.document()?;
DOCUMENT_CACHE.with(|cell: &UnsafeCell<Option<Document>>| unsafe {
*cell.get() = Some(document.clone());
});
Some(document)
})
}
pub(crate) fn append_nodes(parent: &Element, nodes: impl IntoIterator<Item = Node>) {
if !parent.is_connected() {
for node in nodes {
let _: Result<Node, JsValue> = parent.append_child(&node);
}
return;
}
let mut iter = nodes.into_iter();
let Some(first) = iter.next() else {
return;
};
let Some(second) = iter.next() else {
let _: Result<Node, JsValue> = parent.append_child(&first);
return;
};
let document: Document = match cached_document() {
Some(doc) => doc,
None => {
let _: Result<Node, JsValue> = parent.append_child(&first);
let _: Result<Node, JsValue> = parent.append_child(&second);
for node in iter {
let _: Result<Node, JsValue> = parent.append_child(&node);
}
return;
}
};
let fragment: DocumentFragment = document.create_document_fragment();
let _: Result<Node, JsValue> = fragment.append_child(&first);
let _: Result<Node, JsValue> = fragment.append_child(&second);
for node in iter {
let _: Result<Node, JsValue> = fragment.append_child(&node);
}
let fragment_node: Node = fragment.into();
let _: Result<Node, JsValue> = parent.append_child(&fragment_node);
}
pub(crate) fn compute_child_ops_plan<'a>(
old_keys: &[Option<&'a str>],
new_keys: &[Option<&'a str>],
) -> Vec<ChildOpPlan> {
let old_len: usize = old_keys.len();
let new_len: usize = new_keys.len();
let mut plan: Vec<ChildOpPlan> = Vec::with_capacity(old_len.saturating_add(new_len));
let mut old_key_to_pos: HashMap<&str, usize> = HashMap::with_capacity(old_len);
for (old_index, old_key_opt) in old_keys.iter().enumerate() {
if let Some(key) = old_key_opt.as_deref() {
old_key_to_pos.insert(key, old_index);
}
}
let mut new_key_set: HashSet<&str> = HashSet::with_capacity(new_len);
for new_key_opt in new_keys.iter() {
if let Some(key) = new_key_opt.as_deref() {
new_key_set.insert(key);
}
}
for (old_index, old_key_opt) in old_keys.iter().enumerate() {
match old_key_opt.as_deref() {
Some(key) if new_key_set.contains(key) => {
}
_ => {
plan.push(ChildOpPlan::Remove { old_index });
}
}
}
let mut kept_old_indices: Vec<usize> = Vec::with_capacity(new_len);
let mut kept_pos_for_new: Vec<Option<usize>> = Vec::with_capacity(new_len);
for new_key_opt in new_keys.iter() {
let Some(new_key) = new_key_opt.as_deref() else {
kept_pos_for_new.push(None);
continue;
};
match old_key_to_pos.get(new_key) {
Some(&old_idx) => {
let kept_pos: usize = kept_old_indices.len();
kept_old_indices.push(old_idx);
kept_pos_for_new.push(Some(kept_pos));
}
None => {
kept_pos_for_new.push(None);
}
}
}
let lis: Vec<usize> = lis_indices(&kept_old_indices);
let mut in_lis_at_kept_pos: Vec<bool> = vec![false; kept_old_indices.len()];
for &lis_pos in lis.iter() {
in_lis_at_kept_pos[lis_pos] = true;
}
let mut next_lis_for: Vec<Option<usize>> = vec![None; new_len];
let mut next: Option<usize> = None;
for new_index in (0..new_len).rev() {
next_lis_for[new_index] = next;
if let Some(kept_pos) = kept_pos_for_new[new_index]
&& in_lis_at_kept_pos[kept_pos]
{
next = Some(new_index);
}
}
for new_index in 0..new_len {
match kept_pos_for_new[new_index] {
Some(kept_pos) if in_lis_at_kept_pos[kept_pos] => {
plan.push(ChildOpPlan::Keep { new_index });
}
Some(_kept_pos) => {
plan.push(ChildOpPlan::MoveBefore {
new_index,
before: next_lis_for[new_index],
});
}
None => {
plan.push(ChildOpPlan::InsertBefore {
new_index,
before: next_lis_for[new_index],
});
}
}
}
plan
}