Skip to content
This repository was archived by the owner on May 25, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion FUTURE-CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
## v0.0.0-beta.3 <- current

## v0.0.0
Delete deletes the branch without connecting sibling nodes.
Delete deletes the node without connecting sibling nodes.
* Delete

QuerySelector takes attribute name and regexp for the value and returns the first node that matches the regexp.
Expand Down
17 changes: 1 addition & 16 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ func DeepCloneNode(node *Node) *Node{
return &newNode
}

//CloneNode copy the node.
//CloneNode copy the node. But have one way connections to it's parent, next and previous nodes.
func CloneNode(node *Node) *Node{
newNode := DeepCloneNode(node)
newNode.setParentNode(node.getParentNode())
Expand All @@ -59,21 +59,6 @@ func CloneNode(node *Node) *Node{
return newNode
}

//ApplySaveChanges replaces the nodes previous and parent node with the given node.
func ApplySaveChanges(node *Node){
previousNode := node.GetPreviousNode()
if previousNode != nil{
previousNode.SetNextNode(node)
}

parentNode := node.getParentNode()
if parentNode != nil{
parentNode.rwMutex.Lock()
parentNode.childNode = node
parentNode.rwMutex.Unlock()
}
}

func isQuote(chr string) bool {
return chr == `"` || chr == `'` || chr == "`"
}
Expand Down
108 changes: 58 additions & 50 deletions node-tree.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import (
"sync"
)

//The DOM Node struct is an abstract base class upon which many other DOM API objects are based, thus letting
// The DOM Node struct is an abstract base class upon which many other DOM API objects are based, thus letting
// those object types to be used similarly and often interchangeably.
type Node struct {
nextNode *Node
Expand All @@ -16,136 +16,137 @@ type Node struct {
tagName string
attributes map[string]string
text string
rwMutex sync.Mutex
rwMutex sync.Mutex
}

//GetNextNode returns node next to the node.
func (node *Node) GetNextNode() *Node{
// GetNextNode returns node next to the node.
func (node *Node) GetNextNode() *Node {
node.rwMutex.Lock()
defer node.rwMutex.Unlock()

return node.nextNode
}

//SetNextNode make nodes next node as nextNode.
func (node *Node) SetNextNode(nextNode *Node){
// SetNextNode make nodes next node as nextNode.
func (node *Node) SetNextNode(nextNode *Node) {
node.rwMutex.Lock()
defer node.rwMutex.Unlock()

node.nextNode = nextNode
}

//GetPreviousNode returns the previous node.
func (node *Node) GetPreviousNode() *Node{
// GetPreviousNode returns the previous node.
func (node *Node) GetPreviousNode() *Node {
node.rwMutex.Lock()
defer node.rwMutex.Unlock()

return node.previousNode
}

//SetPreviousNode sets nodes previous node to previousNode.
func (node *Node) SetPreviousNode(previousNode *Node){
// SetPreviousNode sets nodes previous node to previousNode.
func (node *Node) SetPreviousNode(previousNode *Node) {
node.rwMutex.Lock()
defer node.rwMutex.Unlock()

node.previousNode = previousNode
}

//GetChildNode returns the first child elements of this node.
func (node *Node) GetChildNode() *Node{
// GetChildNode returns the first child elements of this node.
func (node *Node) GetChildNode() *Node {
node.rwMutex.Lock()
defer node.rwMutex.Unlock()

return node.childNode
}

//getParentNode returns parent node.
func (node *Node) getParentNode() *Node{
// getParentNode returns parent node.
func (node *Node) getParentNode() *Node {
node.rwMutex.Lock()
defer node.rwMutex.Unlock()

return node.parentNode
}

func (node *Node) setParentNode(parentNode *Node){
func (node *Node) setParentNode(parentNode *Node) {
node.rwMutex.Lock()
defer node.rwMutex.Unlock()

node.parentNode = parentNode
}

//Returns a string with the name of the tag for the given node.
func (node *Node) GetTagName() string{
// Returns a string with the name of the tag for the given node.
func (node *Node) GetTagName() string {
node.rwMutex.Lock()
defer node.rwMutex.Unlock()

if strings.ToUpper(node.tagName) == DOCTYPEDTD{
if strings.ToUpper(node.tagName) == DOCTYPEDTD {
return strings.ToUpper(node.tagName)
}

return node.tagName
}

//SetTagName changes the html tag name to the tagName.
func (node *Node) SetTagName(tagName string){
// SetTagName changes the html tag name to the tagName.
func (node *Node) SetTagName(tagName string) {
node.rwMutex.Lock()
defer node.rwMutex.Unlock()

node.tagName = strings.TrimSpace(strings.ToLower(tagName))
}

//GetAttribute returns the specified attribute value form the node.
func (node *Node) GetAttribute(attributeName string) (string, bool){
// GetAttribute returns the specified attribute value form the node. If the specified attribute doesn't exists GetAttribute returns a empty string and false.
func (node *Node) GetAttribute(attributeName string) (string, bool) {
node.rwMutex.Lock()
defer node.rwMutex.Unlock()

v, ok := node.attributes[attributeName]
return v, ok
}

//RemoveAttribute remove or delete the specified attribute.
func (node *Node) RemoveAttribute(attributeName string){
// RemoveAttribute remove or delete the specified attribute.
func (node *Node) RemoveAttribute(attributeName string) {
node.rwMutex.Lock()
defer node.rwMutex.Unlock()

delete(node.attributes, attributeName)
}

//IterateAttributes calls callback at every attribute in the node by passing attribute and value of the each node to the callback.
func (node *Node) IterateAttributes(callback func(attribute, value string)){
// IterateAttributes calls callback at every attribute in the node by passing attribute and value of the node.
func (node *Node) IterateAttributes(callback func(attribute, value string)) {
node.rwMutex.Lock()
defer node.rwMutex.Unlock()
attributes := node.attributes
node.rwMutex.Unlock()

for k, v := range node.attributes{
for k, v := range attributes {
callback(k, v)
}
}

//SetAttribute add a attribute to the node.
func (node *Node) SetAttribute(attribute, value string){
// SetAttribute add a attribute to the node.
func (node *Node) SetAttribute(attribute, value string) {
node.rwMutex.Lock()
defer node.rwMutex.Unlock()

node.attributes[strings.TrimSpace(attribute)] = strings.TrimSpace(value)
}

//GetText returns text on the node. This does not returns text on it's child nodes. If you also wants child nodes text use GetInnerText method on the node.
func (node *Node) GetText() string{
// GetText returns text on the node. This does not returns text on it's child nodes. If you also wants child nodes text use GetInnerText method on the node.
func (node *Node) GetText() string {
node.rwMutex.Lock()
defer node.rwMutex.Unlock()

return node.text
}

//SetText add text to the node.
func (node *Node) SetText(text string){
// SetText add text to the node.
func (node *Node) SetText(text string) {
node.rwMutex.Lock()
defer node.rwMutex.Unlock()

node.text = text
}

//The AppendChild() method of the Node adds a node to the end of the list of children of a specified parent node.
// The AppendChild() method of the Node adds a node to the end of the list of children of a specified parent node.
func (node *Node) AppendChild(childNode *Node) {
if node.GetChildNode() == nil {
node.rwMutex.Lock()
Expand All @@ -161,50 +162,50 @@ func (node *Node) AppendChild(childNode *Node) {
lastNode.SetNextNode(childNode)
}

//Append inserts the newNode to end of the node chain.
// Append inserts the newNode to end of the node chain.
func (node *Node) Append(newNode *Node) {
lastNode := node.GetLastNode()
newNode.SetPreviousNode(lastNode)
lastNode.SetNextNode(newNode)
}

//GetParent returns a pointer to the parent node.
// GetParent returns a pointer to the parent node.
func (node *Node) GetParent() *Node {
return node.GetFirstNode().getParentNode()
}

//GetLastNode returns the last node in the node branch.
func (node *Node) GetLastNode() *Node{
// GetLastNode returns the last node in the node branch.
func (node *Node) GetLastNode() *Node {
traverser := GetTraverser(node)
for traverser.GetCurrentNode().GetNextNode() != nil {
traverser.Next()
}
return traverser.GetCurrentNode()
}

//GetFirstNode returns the first node of the node branch.
func (node *Node) GetFirstNode() *Node{
// GetFirstNode returns the first node of the node branch.
func (node *Node) GetFirstNode() *Node {
traverser := GetTraverser(node)
for traverser.GetCurrentNode().GetPreviousNode() != nil{
for traverser.GetCurrentNode().GetPreviousNode() != nil {
traverser.Previous()
}
return traverser.GetCurrentNode()
}

//AppendText add text to the node.
func (node *Node) AppendText(text string){
// AppendText append text to the node.
func (node *Node) AppendText(text string) {
textNode := CreateNode("")
textNode.SetText(text)

if node.GetTagName() == "" || IsVoidTag(node.GetTagName()){
if node.GetTagName() == "" || IsVoidTag(node.GetTagName()) {
node.GetLastNode().Append(textNode)
return
}
node.GetLastNode().AppendChild(textNode)
}

//GetInnerText returns all of the text inside the node.
func (node *Node) GetInnerText() string{
// GetInnerText returns all of the text inside the node.
func (node *Node) GetInnerText() string {
text := ""
traverser := GetTraverser(node.childNode)
traverser.Walkthrough(func(node *Node) TraverseCondition {
Expand All @@ -218,25 +219,32 @@ func (node *Node) GetInnerText() string{
return text
}

//RemoveNode removes the node from the branch safely.
func (node *Node) RemoveNode(){
// RemoveNode removes the node from the branch safely by connecting sibling nodes.
func (node *Node) RemoveNode() {
node.rwMutex.Lock()
defer node.rwMutex.Unlock()

previousNode := node.previousNode
nextNode := node.nextNode
parentNode := node.parentNode

node.previousNode = nil
node.nextNode = nil

node.rwMutex.Unlock()

if previousNode != nil {
previousNode.SetNextNode(nextNode)
}
if nextNode != nil {
nextNode.SetPreviousNode(previousNode)
}

if nextNode != nil{
nextNode.setParentNode(parentNode)
}
}

// IsTextNode returns a boolean value indicating node is a text node or not.
func (node *Node) IsTextNode() bool {
return node.GetTagName() == ""
}
11 changes: 11 additions & 0 deletions test-files/3.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Simple Page</title>
</head>
<body>
<h1>Hello, World!</h1>
<p>This is a simple HTML page.</p>
</body>
</html>
4 changes: 2 additions & 2 deletions traverser.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,8 @@ const (
ContinueWalkthrough TraverseCondition = false
)

//TODO: use a linked stack
//Walkthrough traverse the node tree from the current node to the end of the node tree by visiting every node. If callback returned StopWalkthrough walkthrough function will stop else if it returned ContinueWalkthrough it continues walkthrough until every node is visited.
//Walkthrough traverse the node tree from the current node to the end of the node tree by visiting every node. If callback returned StopWalkthrough walkthrough function will stop else if it returned ContinueWalkthrough it advanced to the next node.
//Walkthrough calls callback at every node and pass that node. Walkthrough traverse the node tree similar to DFS without visiting visited nodes iteratively.
func (t *Traverser) Walkthrough(callback func(node *Node) TraverseCondition) {
stack := linkedliststack.New()
stack.Push(t.GetCurrentNode())
Expand Down