Skip to content

Commit

Permalink
feat: add method replace_with for elements, fix #10
Browse files Browse the repository at this point in the history
  • Loading branch information
fefit committed Aug 2, 2022
1 parent a0e8570 commit a89c0fe
Show file tree
Hide file tree
Showing 8 changed files with 172 additions and 20 deletions.
24 changes: 17 additions & 7 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,28 @@

0.2.1 版本后各个接口方法已经基本趋于稳定,后面将不会做大的调整。

## [0.5.4] - 2022-08-02

### 修复

- 修复 `insert_before` `before` `insert_after` `after` 等操作错误过滤了空标签等不能插入子节点的标签、导致无法正确插入节点的问题。

### 增加

- 增加 `replace_with` 操作,方便快速替换节点。

## [0.5.3] - 2022-07-31

### 修复

- 修复 `:contains` 选择器包含中文字符、因为在正则匹配中没有使用chars长度引起 panic 的问题。
- 修复 `:contains` 选择器包含中文字符、因为在正则匹配中没有使用 chars 长度引起 panic 的问题。

## [0.5.2] - 2022-04-26

### 增加

- 增加 `:has` 伪类选择器,以配合 `:not` 伪类能组成 `:not(:has(p))` 等实现 `has` 反选逻辑。

## [0.5.1] - 2022-02-22

### 修改
Expand All @@ -23,15 +34,15 @@

### 变更

- (break change) 新增了 `features` 的条件编译,将可能不会用到的DOM节点操作API分离出来,具体可以参见[README里的Feature flags](./README.md#feature-flags)如果仍想使用全部API,可以在 `features` 中使用 `full`
- (break change) 新增了 `features` 的条件编译,将可能不会用到的 DOM 节点操作 API 分离出来,具体可以参见[README 里的 Feature flags](./README.md#feature-flags)如果仍想使用全部 API,可以在 `features` 中使用 `full`

### 修复

- 修复 `has_class` 当查找的class为空格分隔的列表时,找到一个就返回`true`,实际逻辑应该是全部包含才能返回 `true`
- 修复 `has_class` 当查找的 class 为空格分隔的列表时,找到一个就返回`true`,实际逻辑应该是全部包含才能返回 `true`

### 增加

- 增加了主要API的doc文档
- 增加了主要 API 的 doc 文档

## [0.4.13] - 2022-02-08

Expand All @@ -42,9 +53,8 @@

### 修复

- 修复 `:root` 伪类在非子类查找时候丢失查询handle的问题
- 删除冗余的 `println!` debug信息

- 修复 `:root` 伪类在非子类查找时候丢失查询 handle 的问题
- 删除冗余的 `println!` debug 信息

## [0.4.12] - 2022-02-07

Expand Down
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "visdom"
version = "0.5.3"
version = "0.5.4"
edition = "2018"
description = "A html document syntax and operation library, use APIs similar to jquery, easy to use for web scraping and confused html."
keywords = ["html", "scrape", "jquery", "query", "selector"]
Expand Down
61 changes: 60 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -758,7 +758,7 @@ impl IElementTrait for Rc<RefCell<Node>> {
}
// when the feature `insertion` is open
cfg_feat_insertion! {
// append child
// append child, insert siblings
fn insert_adjacent(&mut self, position: &InsertPosition, node: &BoxDynElement) {
// base validate
let action = position.action();
Expand Down Expand Up @@ -879,6 +879,65 @@ impl IElementTrait for Rc<RefCell<Node>> {
);
}
}
// replace with
fn replace_with(&mut self, node: &BoxDynElement){
let node_type = node.node_type();
let specified: Box<dyn Any> = node.cloned().to_node();
if let Ok(dom) = specified.downcast::<RefNode>() {
// get the nodes
let nodes = match node_type {
INodeType::DocumentFragement => {
if let Some(childs) = &dom.borrow().childs {
childs.iter().map(Rc::clone).collect::<Vec<RefNode>>()
} else {
vec![]
}
}
_ => {
// remove current node from parent's childs
if let Some(parent) = &mut node.parent() {
parent.remove_child(node.cloned());
}
vec![*dom]
}
};
// check if has nodes
if nodes.is_empty(){
return;
}
// get index first, for borrow check
let index = self.index();
let insert_len = nodes.len();
// replace with nodes
if let Some(parent) = &self.borrow_mut().parent {
if let Some(parent) = &parent.upgrade() {
if let Some(childs) = &mut parent.borrow_mut().childs {
let next_index = index + 1;
// reset node indexs
reset_next_siblings_index(index, &nodes);
// reset next childs indexs
if index < childs.len(){
reset_next_siblings_index(index + insert_len, &childs[next_index..]);
}
// set node parent
for node in &nodes {
node.borrow_mut().parent = Some(Rc::downgrade(parent));
}
// delete current index node
// insert replace nodes
childs.splice(index..next_index, nodes);
}
}
}
} else {
let action = "replace with";
Dom::halt(
self,
action,
&format!("Can't {} that not implemented 'Dom'", action),
);
}
}
}

// when the feature `text` is open
Expand Down
6 changes: 4 additions & 2 deletions src/mesdoc/interface/element.rs
Original file line number Diff line number Diff line change
Expand Up @@ -301,11 +301,13 @@ pub trait IElementTrait: INodeTrait {
fn inner_html(&self) -> String;
fn outer_html(&self) -> String;

// append child, insert before, remove child
// append child, insert before
cfg_feat_insertion! {
fn insert_adjacent(&mut self, position: &InsertPosition, ele: &BoxDynElement);
fn insert_adjacent(&mut self, position: &InsertPosition, node: &BoxDynElement);
fn replace_with(&mut self, node: &BoxDynElement);
}
cfg_feat_mutation! {
// remove child
fn remove_child(&mut self, ele: BoxDynElement);
}
// texts
Expand Down
57 changes: 55 additions & 2 deletions src/mesdoc/interface/elements.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3544,13 +3544,20 @@ impl<'a> Elements<'a> {
// when feature 'insertion' is open
cfg_feat_insertion! {
// `insert`
fn insert(&mut self, dest: &Elements, position: &InsertPosition) -> &mut Self {
fn insert(&mut self, dest: &Elements, position: &InsertPosition) {
for ele in self.get_mut_ref() {
for inserted in dest.get_ref().iter().rev() {
ele.insert_adjacent(position, inserted);
}
}
self
}
// `replace`
fn replace(&mut self, dest: &Elements){
for ele in self.get_mut_ref() {
for inserted in dest.get_ref().iter().rev() {
ele.replace_with(inserted);
}
}
}
/// Append the parameter Elements to the child before the tag end of the current Elements set.
///
Expand Down Expand Up @@ -3672,6 +3679,52 @@ impl<'a> Elements<'a> {
self.insert(elements, &InsertPosition::AfterEnd);
self
}
/// Replace each element in the set of matched elements with the provided new set of elements.
///
/// ```
/// use visdom::Vis;
/// use visdom::types::BoxDynError;
/// fn main()-> Result<(), BoxDynError>{
/// let html = r##"
/// <html>
/// <head>
/// <title>document</title>
/// </head>
/// <body>
/// <dl>
/// <dt>Title</dt>
/// <dd><span>item1</span></dd>
/// <dd class="item2"><span>item2</span></dd>
/// <dd class="item3"><!--comment-->item3</dd>
/// </dl>
/// </body>
/// </html>
/// "##;
/// let doc = Vis::load(html)?;
/// let mut dl = doc.find("dl");
/// let mut dt = dl.children("dt");
/// assert_eq!(dt.length(), 1);
/// assert_eq!(dl.children("dd").length(), 3);
/// // now replace dt with dd
/// let mut new_dd = Vis::load("<dd>replace</dd>")?;
/// dt.replace_with(&mut new_dd);
/// assert_eq!(dl.children("dd").length(), 4);
/// assert_eq!(dl.children("dt").length(), 0);
/// assert_eq!(dl.children("dd").eq(0).text(), "replace");
/// // replace with exist dd
/// let dds = dl.children("");
/// let mut first_dd = dds.first();
/// let mut last_dd = dds.last();
/// last_dd.replace_with(&mut first_dd);
/// assert_eq!(dl.children("").length(), 3);
/// assert_eq!(dl.children("").last().text(), "replace");
/// Ok(())
/// }
/// ```
pub fn replace_with(&mut self, elements: &mut Elements) -> &mut Self{
self.replace(elements);
self
}
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/mesdoc/interface/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ pub trait INodeTrait {
fn to_node(self: Box<Self>) -> Box<dyn Any>;
// clone a ele
fn clone_node<'b>(&self) -> BoxDynNode<'b>;
// typed,whether element or text
// typed, whether element or text
fn typed<'b>(self: Box<Self>) -> IEnumTyped<'b>;
// get ele type
fn node_type(&self) -> INodeType;
Expand Down
10 changes: 5 additions & 5 deletions src/mesdoc/selector/pattern.rs
Original file line number Diff line number Diff line change
Expand Up @@ -485,16 +485,16 @@ mod tests {
vec![2, 4, 6, 8]
);
assert_eq!(Nth::get_allowed_indexs(&None, &Some("3"), 9), vec![2]);
assert_eq!(Nth::get_allowed_indexs(&None, &Some("3"), 2), vec![]);
assert!(Nth::get_allowed_indexs(&None, &Some("3"), 2).is_empty());
assert_eq!(Nth::get_allowed_indexs(&Some("0"), &Some("3"), 9), vec![2]);
assert_eq!(Nth::get_allowed_indexs(&Some("0"), &Some("-3"), 9), vec![]);
assert_eq!(Nth::get_allowed_indexs(&Some("1"), &Some("6"), 5), vec![]);
assert!(Nth::get_allowed_indexs(&Some("0"), &Some("-3"), 9).is_empty());
assert!(Nth::get_allowed_indexs(&Some("1"), &Some("6"), 5).is_empty());
assert_eq!(
Nth::get_allowed_indexs(&Some("2"), &None, 9),
vec![1, 3, 5, 7]
);
assert_eq!(Nth::get_allowed_indexs(&Some("-2"), &None, 9), vec![]);
assert_eq!(Nth::get_allowed_indexs(&Some("-4"), &Some("3"), 2), vec![]);
assert!(Nth::get_allowed_indexs(&Some("-2"), &None, 9).is_empty());
assert!(Nth::get_allowed_indexs(&Some("-4"), &Some("3"), 2).is_empty());
}

#[test]
Expand Down
30 changes: 29 additions & 1 deletion tests/mutation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ fn test_insert() -> Result {
</div>
"#;
let fragement = Vis::load(html)?;
let mut root_div = fragement.children("div");
let root_div = fragement.children("div");
let mut img_list = root_div.find("img[src]");
img_list.for_each(|_, ele| {
let attr_src = ele.get_attribute("src").unwrap().to_string();
Expand All @@ -94,3 +94,31 @@ fn test_insert() -> Result {
assert_eq!(now_svg.length(), 1);
Ok(())
}

#[test]
fn test_replace_with() -> Result {
let html = r#"
<div>
<img src="a.png" />
<img src="b.jpg" />
<img src="c.webp" />
</div>
"#;
let fragement = Vis::load(html)?;
let root_div = fragement.children("div");
let mut img_list = root_div.find("img[src]");
img_list.for_each(|_, ele| {
let attr_src = ele.get_attribute("src").unwrap().to_string();
if attr_src.ends_with(".png") {
let mut img = Vis::dom(ele);
let mut svg = Vis::load("<svg></svg>").unwrap();
img.replace_with(&mut svg);
}
true
});
let now_img_list = root_div.find("img[src]");
assert_eq!(now_img_list.length(), 2);
let now_svg = root_div.find("svg");
assert_eq!(now_svg.length(), 1);
Ok(())
}

0 comments on commit a89c0fe

Please sign in to comment.