1use super::*;
6
7pub struct NodeMut<'a, Ext: 'static = ()> {
14 pub(crate) dom: &'a mut Dom<Ext>,
15 pub(crate) id: NodeId,
16}
17
18impl<'a, Ext> NodeMut<'a, Ext> {
19 pub fn id(&self) -> NodeId {
20 self.id
21 }
22
23 pub fn as_ref(&self) -> NodeRef<'_, Ext> {
24 NodeRef {
25 dom: self.dom,
26 id: self.id,
27 }
28 }
29
30 pub fn dom_mut(&mut self) -> &mut Dom<Ext> {
36 self.dom
37 }
38
39 pub fn into_dom_mut(self) -> &'a mut Dom<Ext> {
44 self.dom
45 }
46
47 pub fn ext_mut(&mut self) -> Option<&mut Ext> {
50 match &mut self.dom.get_node_mut(self.id)?.data {
51 NodeData::Element { ext, .. } => Some(ext),
52 _ => None,
53 }
54 }
55}
56
57impl<'a, Ext: 'static> NodeMut<'a, Ext> {
58 pub fn set_attribute(&mut self, key: &str, value: &str) -> Result<()> {
61 self.dom.set_attribute(self.id, key, value)
62 }
63
64 pub fn remove_attribute(&mut self, key: &str) -> Result<bool> {
65 self.dom.remove_attribute(self.id, key)
66 }
67
68 pub fn toggle_attribute(&mut self, key: &str) -> Result<bool> {
69 self.dom.toggle_attribute(self.id, key)
70 }
71
72 pub fn set_id(&mut self, value: &str) -> Result<()> {
73 self.dom.set_id(self.id, value)
74 }
75
76 pub fn add_class(&mut self, class: &str) -> Result<()> {
77 self.dom.add_class(self.id, class)
78 }
79
80 pub fn remove_class(&mut self, class: &str) -> Result<bool> {
81 self.dom.remove_class(self.id, class)
82 }
83
84 pub fn toggle_class(&mut self, class: &str) -> Result<bool> {
85 self.dom.toggle_class(self.id, class)
86 }
87
88 pub fn replace_class(&mut self, old: &str, new: &str) -> Result<bool> {
89 self.dom.replace_class(self.id, old, new)
90 }
91
92 pub fn set_class_name(&mut self, value: &str) -> Result<()> {
101 self.dom.set_attribute(self.id, "class", value)
105 }
106
107 pub fn class_list_mut(&mut self) -> DomTokenListMut<'_, Ext> {
114 DomTokenListMut::new(NodeMut {
115 dom: &mut *self.dom,
116 id: self.id,
117 })
118 }
119
120 pub fn dataset_mut(&mut self) -> DomStringMapMut<'_, Ext> {
125 DomStringMapMut::new(NodeMut {
126 dom: &mut *self.dom,
127 id: self.id,
128 })
129 }
130
131 pub fn set_tab_index(&mut self, value: i32) -> Result<()> {
134 self.set_attribute("tabindex", &value.to_string())
135 }
136
137 pub fn set_hidden(&mut self, value: bool) -> Result<()> {
140 if value {
141 self.set_attribute("hidden", "")
142 } else {
143 self.remove_attribute("hidden").map(|_| ())
144 }
145 }
146
147 pub fn set_content_editable(&mut self, value: &str) -> Result<()> {
153 if value.eq_ignore_ascii_case("inherit") {
154 return self.remove_attribute("contenteditable").map(|_| ());
155 }
156 match crate::ContentEditableState::from_attribute(value) {
157 Some(state) if !value.is_empty() => {
158 self.set_attribute("contenteditable", state.keyword())
159 }
160 _ => Err(DomError::Syntax(
161 "contentEditable must be true, false, plaintext-only or inherit",
162 )),
163 }
164 }
165
166 pub fn toggle_attribute_force(&mut self, name: &str, force: Option<bool>) -> Result<bool> {
174 match force {
175 Some(true) => {
176 if !self.as_ref().has_attribute(name) {
177 self.set_attribute(name, "")?;
178 }
179 Ok(true)
180 }
181 Some(false) => {
182 self.remove_attribute(name)?;
183 Ok(false)
184 }
185 None => self.toggle_attribute(name),
186 }
187 }
188
189 pub fn append_child(&mut self, child: NodeId) -> Result<()> {
192 self.dom.append_child(self.id, child)
193 }
194
195 pub fn prepend_child(&mut self, child: NodeId) -> Result<()> {
196 self.dom.prepend_child(self.id, child)
197 }
198
199 pub fn remove_child(&mut self, child: NodeId) -> Result<()> {
200 self.dom.remove_child(self.id, child)
201 }
202
203 pub fn replace_child(&mut self, old: NodeId, new: NodeId) -> Result<()> {
204 self.dom.replace_child(self.id, old, new)
205 }
206
207 pub fn insert_before(&mut self, new: NodeId, reference: Option<NodeId>) -> Result<()> {
208 self.dom.insert_before(self.id, new, reference)
209 }
210
211 pub fn insert_adjacent(&mut self, position: AdjacentPosition, new: NodeId) -> Result<()> {
212 self.dom.insert_adjacent(self.id, position, new)
213 }
214
215 pub fn clear_children(&mut self) -> Result<()> {
216 self.dom.clear_children(self.id)
217 }
218
219 pub fn append(&mut self, children: impl IntoIterator<Item = NodeOrString>) -> Result<()> {
225 let parent = self.id;
226 for item in children {
227 let new_id = match item {
228 NodeOrString::Node(n) => n,
229 NodeOrString::Text(s) => self.dom.create_text_node(&s),
230 };
231 self.dom.append_child(parent, new_id)?;
232 }
233 Ok(())
234 }
235
236 pub fn prepend(&mut self, children: impl IntoIterator<Item = NodeOrString>) -> Result<()> {
240 let parent = self.id;
241 let reference = self.dom.get_node(parent).and_then(|n| n.first_child);
242 for item in children {
243 let new_id = match item {
244 NodeOrString::Node(n) => n,
245 NodeOrString::Text(s) => self.dom.create_text_node(&s),
246 };
247 self.dom.insert_before(parent, new_id, reference)?;
248 }
249 Ok(())
250 }
251
252 pub fn before(&mut self, siblings: impl IntoIterator<Item = NodeOrString>) -> Result<()> {
259 let id = self.id;
260 let parent = match self.dom.get_node(id).and_then(|n| n.parent) {
261 Some(p) => p,
262 None => return Ok(()),
263 };
264 for item in siblings {
265 let new_id = match item {
266 NodeOrString::Node(n) => n,
267 NodeOrString::Text(s) => self.dom.create_text_node(&s),
268 };
269 self.dom.insert_before(parent, new_id, Some(id))?;
270 }
271 Ok(())
272 }
273
274 pub fn after(&mut self, siblings: impl IntoIterator<Item = NodeOrString>) -> Result<()> {
280 let id = self.id;
281 if self.dom.get_node(id).and_then(|n| n.parent).is_none() {
282 return Ok(());
283 }
284 let mut cursor = id;
285 for item in siblings {
286 let new_id = match item {
287 NodeOrString::Node(n) => n,
288 NodeOrString::Text(s) => self.dom.create_text_node(&s),
289 };
290 self.dom
291 .insert_adjacent(cursor, AdjacentPosition::AfterEnd, new_id)?;
292 cursor = new_id;
293 }
294 Ok(())
295 }
296
297 pub fn replace_children(
300 &mut self,
301 children: impl IntoIterator<Item = NodeOrString>,
302 ) -> Result<()> {
303 let parent = self.id;
304 self.dom.clear_children(parent)?;
305 for item in children {
306 let new_id = match item {
307 NodeOrString::Node(n) => n,
308 NodeOrString::Text(s) => self.dom.create_text_node(&s),
309 };
310 self.dom.append_child(parent, new_id)?;
311 }
312 Ok(())
313 }
314
315 pub fn replace_with(self, siblings: impl IntoIterator<Item = NodeOrString>) -> Result<()> {
334 let NodeMut { dom, id } = self;
335 let parent = match dom.get_node(id).and_then(|n| n.parent) {
336 Some(p) => p,
337 None => return Ok(()),
338 };
339 for item in siblings {
340 let new_id = match item {
341 NodeOrString::Node(n) => n,
342 NodeOrString::Text(s) => dom.create_text_node(&s),
343 };
344 dom.insert_before(parent, new_id, Some(id))?;
345 }
346 dom.remove_child(parent, id)?;
347 Ok(())
348 }
349
350 pub fn remove_self(self) -> Result<()> {
366 let NodeMut { dom, id } = self;
367 if let Some(parent) = dom.get_node(id).and_then(|n| n.parent) {
368 dom.remove_child(parent, id)?;
369 }
370 Ok(())
371 }
372
373 pub fn set_node_value(&mut self, data: &str) -> Result<()> {
376 let id = self.id;
377 let old = match &self.dom.node_or_err(id)?.data {
378 NodeData::Text { data: d } | NodeData::Comment { data: d } => d.clone(),
379 NodeData::Element { .. } => {
380 return Err(DomError::WrongNodeType {
381 expected: "Text or Comment",
382 got: NodeType::Element,
383 });
384 }
385 NodeData::Fragment => {
386 return Err(DomError::WrongNodeType {
387 expected: "Text or Comment",
388 got: NodeType::Fragment,
389 });
390 }
391 };
392 if old == data {
393 return Ok(());
394 }
395 match &mut self.dom.node_mut_or_err(id)?.data {
396 NodeData::Text { data: d } | NodeData::Comment { data: d } => {
397 *d = data.to_string();
398 }
399 _ => unreachable!("type-checked above"),
400 }
401 self.dom
402 .fire_mutation(crate::Mutation::CharacterDataChanged {
403 id,
404 old,
405 new: data.to_string(),
406 });
407 Ok(())
408 }
409
410 pub fn set_data(&mut self, data: &str) -> Result<()> {
413 self.set_node_value(data)
414 }
415
416 pub fn edit_text(&mut self, start: usize, end: usize, replacement: &str) -> Result<()> {
429 let id = self.id;
430 let data = match &self.dom.node_or_err(id)?.data {
431 NodeData::Text { data: d } | NodeData::Comment { data: d } => d.clone(),
432 NodeData::Element { .. } => {
433 return Err(DomError::WrongNodeType {
434 expected: "Text or Comment",
435 got: NodeType::Element,
436 });
437 }
438 NodeData::Fragment => {
439 return Err(DomError::WrongNodeType {
440 expected: "Text or Comment",
441 got: NodeType::Fragment,
442 });
443 }
444 };
445 if start > data.len() || !data.is_char_boundary(start) {
446 return Err(DomError::InvalidOffset {
447 node: id,
448 offset: start,
449 });
450 }
451 let end = end.max(start);
452 if end > data.len() || !data.is_char_boundary(end) {
453 return Err(DomError::InvalidOffset {
454 node: id,
455 offset: end,
456 });
457 }
458 let mut new_data = String::with_capacity(data.len() - (end - start) + replacement.len());
459 new_data.push_str(&data[..start]);
460 new_data.push_str(replacement);
461 new_data.push_str(&data[end..]);
462 self.set_node_value(&new_data)
463 }
464}
465
466impl<'a, Ext: Default> NodeMut<'a, Ext> {
467 pub fn set_text_content(&mut self, text: &str) -> Result<()> {
470 self.dom.set_text_content(self.id, text)
471 }
472}