func PreviousElementSibling(node *html.Node) *html.Node {
	for sibling := node.PrevSibling; sibling != nil; sibling = sibling.PrevSibling {
		if sibling.Type == html.ElementNode {
			return sibling
		}
	}
	return nil
}
PreviousElementSibling returns the the Element immediately prior
to the specified one in its parent's children list, or null if
the specified element is the first one in the list.
package dom
import (
	"bytes"
	"regexp"
	"strings"
	"github.com/andybalholm/cascadia"
	"golang.org/x/net/html"
)
var (
	rxPunctuation      = regexp.MustCompile(`\s+([.?!,;])\s*(\S*)`)
	rxTempNewline      = regexp.MustCompile(`\s*\|\\/\|\s*`)
	rxDisplayNone      = regexp.MustCompile(`(?i)display:\s*none`)
	rxVisibilityHidden = regexp.MustCompile(`(?i)visibility:\s*(:?hidden|collapse)`)
)
func QuerySelectorAll(doc *html.Node, selectors string) []*html.Node {
	matcher, err := cascadia.ParseGroup(selectors)
	if err != nil {
		return nil
	}
	return cascadia.QueryAll(doc, matcher)
}
func QuerySelector(doc *html.Node, selectors string) *html.Node {
	matcher, err := cascadia.ParseGroup(selectors)
	if err != nil {
		return nil
	}
	return cascadia.Query(doc, matcher)
}
func GetElementByID(doc *html.Node, id string) *html.Node {
	if id == "" {
		return nil
	}
	var results []*html.Node
	var finder func(*html.Node)
	finder = func(node *html.Node) {
		nodeID := GetAttribute(node, "id")
		nodeID = strings.TrimSpace(nodeID)
		if node.Type == html.ElementNode && nodeID == id {
			results = append(results, node)
		}
		for child := node.FirstChild; child != nil; child = child.NextSibling {
			finder(child)
		}
	}
	for child := doc.FirstChild; child != nil; child = child.NextSibling {
		finder(child)
		if len(results) > 0 {
			return results[0]
		}
	}
	return nil
}
func GetElementsByClassName(doc *html.Node, classNames string) []*html.Node {
	classes := map[string]struct{}{}
	for _, class := range strings.Fields(classNames) {
		classes[class] = struct{}{}
	}
	nClasses := len(classes)
	if nClasses == 0 {
		return nil
	}
	var results []*html.Node
	var finder func(*html.Node)
	var allClassExist func(*html.Node) bool
	allClassExist = func(node *html.Node) bool {
		matchCount := 0
		nodeClasses := GetAttribute(node, "class")
		for _, nodeClass := range strings.Fields(nodeClasses) {
			if _, exist := classes[nodeClass]; exist {
				matchCount++
			}
		}
		return matchCount == nClasses
	}
	finder = func(node *html.Node) {
		if node.Type == html.ElementNode && allClassExist(node) {
			results = append(results, node)
		}
		for child := node.FirstChild; child != nil; child = child.NextSibling {
			finder(child)
		}
	}
	for child := doc.FirstChild; child != nil; child = child.NextSibling {
		finder(child)
	}
	return results
}
func GetElementsByTagName(doc *html.Node, tagName string) []*html.Node {
	var results []*html.Node
	var finder func(*html.Node)
	finder = func(node *html.Node) {
		if node.Type == html.ElementNode && (tagName == "*" || node.Data == tagName) {
			results = append(results, node)
		}
		for child := node.FirstChild; child != nil; child = child.NextSibling {
			finder(child)
		}
	}
	for child := doc.FirstChild; child != nil; child = child.NextSibling {
		finder(child)
	}
	return results
}
func CreateElement(tagName string) *html.Node {
	return &html.Node{
		Type: html.ElementNode,
		Data: tagName,
	}
}
func CreateTextNode(data string) *html.Node {
	return &html.Node{
		Type: html.TextNode,
		Data: data,
	}
}
func TagName(node *html.Node) string {
	if node == nil {
		return ""
	}
	if node.Type != html.ElementNode {
		return ""
	}
	return node.Data
}
func GetAttribute(node *html.Node, attrName string) string {
	for i := 0; i < len(node.Attr); i++ {
		if node.Attr[i].Key == attrName {
			return node.Attr[i].Val
		}
	}
	return ""
}
func SetAttribute(node *html.Node, attrName string, attrValue string) {
	attrIdx := -1
	for i := 0; i < len(node.Attr); i++ {
		if node.Attr[i].Key == attrName {
			attrIdx = i
			break
		}
	}
	if attrIdx >= 0 {
		node.Attr[attrIdx].Val = attrValue
	} else {
		node.Attr = append(node.Attr, html.Attribute{
			Key: attrName,
			Val: attrValue,
		})
	}
}
func RemoveAttribute(node *html.Node, attrName string) {
	attrIdx := -1
	for i := 0; i < len(node.Attr); i++ {
		if node.Attr[i].Key == attrName {
			attrIdx = i
			break
		}
	}
	if attrIdx >= 0 {
		a := node.Attr
		a = append(a[:attrIdx], a[attrIdx+1:]...)
		node.Attr = a
	}
}
func HasAttribute(node *html.Node, attrName string) bool {
	for i := 0; i < len(node.Attr); i++ {
		if node.Attr[i].Key == attrName {
			return true
		}
	}
	return false
}
func TextContent(node *html.Node) string {
	var buffer bytes.Buffer
	var finder func(*html.Node)
	finder = func(n *html.Node) {
		if n.Type == html.TextNode {
			buffer.WriteString(n.Data)
		}
		for child := n.FirstChild; child != nil; child = child.NextSibling {
			finder(child)
		}
	}
	finder(node)
	return buffer.String()
}
func InnerText(node *html.Node) string {
	var buffer bytes.Buffer
	var finder func(*html.Node)
	finder = func(n *html.Node) {
		switch n.Type {
		case html.TextNode:
			buffer.WriteString(" " + n.Data + " ")
		case html.ElementNode:
			if n.Data == "br" {
				buffer.WriteString(`|\/|`)
				return
			}
			if HasAttribute(n, "hidden") {
				return
			}
			styleAttr := GetAttribute(n, "style")
			if rxDisplayNone.MatchString(styleAttr) || rxVisibilityHidden.MatchString(styleAttr) {
				return
			}
		}
		for child := n.FirstChild; child != nil; child = child.NextSibling {
			finder(child)
		}
	}
	finder(node)
	text := buffer.String()
	text = strings.Join(strings.Fields(text), " ")
	text = rxPunctuation.ReplaceAllString(text, "$1 $2")
	text = rxTempNewline.ReplaceAllString(text, "\n")
	return text
}
func OuterHTML(node *html.Node) string {
	if node == nil {
		return ""
	}
	var buffer bytes.Buffer
	err := html.Render(&buffer, node)
	if err != nil {
		return ""
	}
	return buffer.String()
}
func InnerHTML(node *html.Node) string {
	var err error
	var buffer bytes.Buffer
	if node == nil {
		return ""
	}
	for child := node.FirstChild; child != nil; child = child.NextSibling {
		err = html.Render(&buffer, child)
		if err != nil {
			return ""
		}
	}
	return strings.TrimSpace(buffer.String())
}
func DocumentElement(doc *html.Node) *html.Node {
	if nodes := GetElementsByTagName(doc, "html"); len(nodes) > 0 {
		return nodes[0]
	}
	return nil
}
func ID(node *html.Node) string {
	id := GetAttribute(node, "id")
	id = strings.TrimSpace(id)
	return id
}
func ClassName(node *html.Node) string {
	className := GetAttribute(node, "class")
	className = strings.TrimSpace(className)
	className = strings.Join(strings.Fields(className), " ")
	return className
}
func Children(node *html.Node) []*html.Node {
	var children []*html.Node
	if node == nil {
		return nil
	}
	for child := node.FirstChild; child != nil; child = child.NextSibling {
		if child.Type == html.ElementNode {
			children = append(children, child)
		}
	}
	return children
}
func ChildNodes(node *html.Node) []*html.Node {
	var childNodes []*html.Node
	for child := node.FirstChild; child != nil; child = child.NextSibling {
		childNodes = append(childNodes, child)
	}
	return childNodes
}
func FirstElementChild(node *html.Node) *html.Node {
	for child := node.FirstChild; child != nil; child = child.NextSibling {
		if child.Type == html.ElementNode {
			return child
		}
	}
	return nil
}