2020-08-09 05:13:34 +03:00
|
|
|
module html
|
|
|
|
|
|
|
|
import os
|
|
|
|
|
2020-12-09 22:08:15 +03:00
|
|
|
// The W3C Document Object Model (DOM) is a platform and language-neutral
|
|
|
|
// interface that allows programs and scripts to dynamically access and
|
|
|
|
// update the content, structure, and style of a document.
|
|
|
|
//
|
|
|
|
// https://www.w3.org/TR/WD-DOM/introduction.html
|
2020-08-09 05:13:34 +03:00
|
|
|
pub struct DocumentObjectModel {
|
|
|
|
mut:
|
2022-09-15 07:59:31 +03:00
|
|
|
root &Tag = unsafe { nil }
|
2020-09-09 16:34:41 +03:00
|
|
|
constructed bool
|
2020-08-09 05:13:34 +03:00
|
|
|
btree BTree
|
|
|
|
all_tags []&Tag
|
|
|
|
all_attributes map[string][]&Tag
|
|
|
|
close_tags map[string]bool // add a counter to see count how many times is closed and parse correctly
|
|
|
|
attributes map[string][]string
|
|
|
|
tag_attributes map[string][][]&Tag
|
|
|
|
tag_type map[string][]&Tag
|
|
|
|
debug_file os.File
|
|
|
|
}
|
|
|
|
|
2022-05-25 11:23:56 +03:00
|
|
|
[if debug_html ?]
|
2020-08-09 05:13:34 +03:00
|
|
|
fn (mut dom DocumentObjectModel) print_debug(data string) {
|
2022-05-25 11:23:56 +03:00
|
|
|
if data.len > 0 {
|
|
|
|
dom.debug_file.writeln(data) or { eprintln(err) }
|
2020-08-09 05:13:34 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-09 22:08:15 +03:00
|
|
|
[inline]
|
2020-08-09 05:13:34 +03:00
|
|
|
fn is_close_tag(tag &Tag) bool {
|
2020-12-09 22:08:15 +03:00
|
|
|
return tag.name.len > 0 && tag.name[0] == `/`
|
2020-08-09 05:13:34 +03:00
|
|
|
}
|
|
|
|
|
2020-10-21 12:23:03 +03:00
|
|
|
fn (mut dom DocumentObjectModel) where_is(item_name string, attribute_name string) int {
|
2020-12-09 22:08:15 +03:00
|
|
|
if attribute_name !in dom.attributes {
|
|
|
|
dom.attributes[attribute_name] = []string{}
|
2020-08-09 05:13:34 +03:00
|
|
|
}
|
|
|
|
mut string_array := dom.attributes[attribute_name]
|
|
|
|
mut counter := 0
|
|
|
|
for value in string_array {
|
|
|
|
if value == item_name {
|
|
|
|
return counter
|
|
|
|
}
|
|
|
|
counter++
|
|
|
|
}
|
|
|
|
string_array << item_name
|
|
|
|
dom.attributes[attribute_name] = string_array
|
|
|
|
return string_array.len - 1
|
|
|
|
}
|
|
|
|
|
|
|
|
fn (mut dom DocumentObjectModel) add_tag_attribute(tag &Tag) {
|
2020-12-09 22:08:15 +03:00
|
|
|
for attribute_name, _ in tag.attributes {
|
2020-08-09 05:13:34 +03:00
|
|
|
attribute_value := tag.attributes[attribute_name]
|
|
|
|
location := dom.where_is(attribute_value, attribute_name)
|
2020-12-09 22:08:15 +03:00
|
|
|
if attribute_name !in dom.tag_attributes {
|
2020-08-09 05:13:34 +03:00
|
|
|
dom.tag_attributes[attribute_name] = []
|
|
|
|
}
|
|
|
|
for {
|
|
|
|
mut temp_array := dom.tag_attributes[attribute_name]
|
|
|
|
temp_array << []&Tag{}
|
|
|
|
dom.tag_attributes[attribute_name] = temp_array
|
|
|
|
if location < dom.tag_attributes[attribute_name].len + 1 {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
mut temp_array := dom.tag_attributes[attribute_name][location]
|
|
|
|
temp_array << tag
|
|
|
|
dom.tag_attributes[attribute_name][location] = temp_array
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn (mut dom DocumentObjectModel) add_tag_by_type(tag &Tag) {
|
|
|
|
tag_name := tag.name
|
2021-08-29 11:55:18 +03:00
|
|
|
if tag_name !in dom.tag_type {
|
2020-08-09 05:13:34 +03:00
|
|
|
dom.tag_type[tag_name] = [tag]
|
|
|
|
} else {
|
|
|
|
mut temp_array := dom.tag_type[tag_name]
|
|
|
|
temp_array << tag
|
|
|
|
dom.tag_type[tag_name] = temp_array
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn (mut dom DocumentObjectModel) add_tag_by_attribute(tag &Tag) {
|
|
|
|
for attribute_name in tag.attributes.keys() {
|
2020-12-09 22:08:15 +03:00
|
|
|
if attribute_name !in dom.all_attributes {
|
2020-08-09 05:13:34 +03:00
|
|
|
dom.all_attributes[attribute_name] = [tag]
|
|
|
|
} else {
|
|
|
|
mut temp_array := dom.all_attributes[attribute_name]
|
|
|
|
temp_array << tag
|
|
|
|
dom.all_attributes[attribute_name] = temp_array
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-21 12:23:03 +03:00
|
|
|
fn (mut dom DocumentObjectModel) construct(tag_list []&Tag) {
|
2020-08-09 05:13:34 +03:00
|
|
|
dom.constructed = true
|
|
|
|
mut temp_map := map[string]int{}
|
2020-12-09 22:08:15 +03:00
|
|
|
mut temp_int := null_element
|
2020-08-09 05:13:34 +03:00
|
|
|
mut temp_string := ''
|
|
|
|
mut stack := Stack{}
|
|
|
|
dom.btree = BTree{}
|
|
|
|
dom.root = tag_list[0]
|
|
|
|
dom.all_tags = [tag_list[0]]
|
|
|
|
temp_map['0'] = dom.btree.add_children(tag_list[0])
|
|
|
|
stack.push(0)
|
|
|
|
root_index := 0
|
|
|
|
for index := 1; index < tag_list.len; index++ {
|
|
|
|
mut tag := tag_list[index]
|
|
|
|
dom.print_debug(tag.str())
|
|
|
|
if is_close_tag(tag) {
|
|
|
|
temp_int = stack.peek()
|
2020-12-09 22:08:15 +03:00
|
|
|
temp_string = tag.name[1..]
|
2021-02-25 15:24:30 +03:00
|
|
|
for !is_null(temp_int) && temp_string != tag_list[temp_int].name
|
|
|
|
&& !tag_list[temp_int].closed {
|
|
|
|
dom.print_debug(temp_string + ' >> ' + tag_list[temp_int].name + ' ' +
|
|
|
|
(temp_string == tag_list[temp_int].name).str())
|
2020-08-09 05:13:34 +03:00
|
|
|
stack.pop()
|
|
|
|
temp_int = stack.peek()
|
|
|
|
}
|
|
|
|
temp_int = stack.peek()
|
2020-12-09 22:08:15 +03:00
|
|
|
temp_int = if !is_null(temp_int) { stack.pop() } else { root_index }
|
|
|
|
if is_null(temp_int) {
|
2020-08-09 05:13:34 +03:00
|
|
|
stack.push(root_index)
|
|
|
|
}
|
|
|
|
dom.print_debug('Removed ' + temp_string + ' -- ' + tag_list[temp_int].name)
|
|
|
|
} else if tag.name.len > 0 {
|
|
|
|
dom.add_tag_attribute(tag) // error here
|
|
|
|
dom.add_tag_by_attribute(tag)
|
|
|
|
dom.add_tag_by_type(tag)
|
|
|
|
dom.all_tags << tag
|
|
|
|
temp_int = stack.peek()
|
2020-12-09 22:08:15 +03:00
|
|
|
if !is_null(temp_int) {
|
2020-08-09 05:13:34 +03:00
|
|
|
dom.btree.move_pointer(temp_map[temp_int.str()])
|
|
|
|
temp_map[index.str()] = dom.btree.add_children(tag)
|
|
|
|
mut temp_tag := tag_list[temp_int]
|
|
|
|
position_in_parent := temp_tag.add_child(tag) // tag_list[temp_int] = temp_tag
|
|
|
|
tag.add_parent(temp_tag, position_in_parent)
|
|
|
|
/*
|
|
|
|
dom.print_debug("Added ${tag.name} as child of '" + tag_list[temp_int].name +
|
|
|
|
"' which now has ${dom.btree.get_children().len} childrens")
|
|
|
|
*/
|
2022-11-15 16:53:13 +03:00
|
|
|
dom.print_debug("Added ${tag.name} as child of '" + temp_tag.name +
|
|
|
|
"' which now has ${temp_tag.children.len} childrens")
|
2020-08-09 05:13:34 +03:00
|
|
|
} else { // dom.new_root(tag)
|
|
|
|
stack.push(root_index)
|
|
|
|
}
|
|
|
|
temp_string = '/' + tag.name
|
|
|
|
if temp_string in dom.close_tags && !tag.closed { // if tag ends with />
|
|
|
|
dom.print_debug('Pushed ' + temp_string)
|
|
|
|
stack.push(index)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} // println(tag_list[root_index]) for debug purposes
|
|
|
|
dom.root = tag_list[0]
|
|
|
|
}
|
|
|
|
|
2020-12-09 22:08:15 +03:00
|
|
|
// get_tag_by_attribute_value retrieves all the tags in the document that has the given attribute name and value.
|
|
|
|
pub fn (mut dom DocumentObjectModel) get_tag_by_attribute_value(name string, value string) []&Tag {
|
2020-08-09 05:13:34 +03:00
|
|
|
location := dom.where_is(value, name)
|
2020-12-09 22:08:15 +03:00
|
|
|
return if dom.tag_attributes[name].len > location {
|
|
|
|
dom.tag_attributes[name][location]
|
|
|
|
} else {
|
|
|
|
[]&Tag{}
|
2020-08-09 05:13:34 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-09 22:08:15 +03:00
|
|
|
// get_tag retrieves all the tags in the document that has the given tag name.
|
|
|
|
pub fn (dom DocumentObjectModel) get_tag(name string) []&Tag {
|
2021-02-25 15:24:30 +03:00
|
|
|
return if name in dom.tag_type { dom.tag_type[name] } else { []&Tag{} }
|
2020-08-09 05:13:34 +03:00
|
|
|
}
|
|
|
|
|
2020-12-09 22:08:15 +03:00
|
|
|
// get_tag_by_attribute retrieves all the tags in the document that has the given attribute name.
|
|
|
|
pub fn (dom DocumentObjectModel) get_tag_by_attribute(name string) []&Tag {
|
2021-02-25 15:24:30 +03:00
|
|
|
return if name in dom.all_attributes { dom.all_attributes[name] } else { []&Tag{} }
|
2020-08-09 05:13:34 +03:00
|
|
|
}
|
|
|
|
|
2020-12-09 22:08:15 +03:00
|
|
|
// get_root returns the root of the document.
|
2020-08-09 05:13:34 +03:00
|
|
|
pub fn (dom DocumentObjectModel) get_root() &Tag {
|
|
|
|
return dom.root
|
|
|
|
}
|
|
|
|
|
2020-12-09 22:08:15 +03:00
|
|
|
// get_tags returns all of the tags stored in the document.
|
|
|
|
pub fn (dom DocumentObjectModel) get_tags() []&Tag {
|
2020-08-09 05:13:34 +03:00
|
|
|
return dom.all_tags
|
|
|
|
}
|
2023-01-18 20:00:46 +03:00
|
|
|
|
|
|
|
// get_tags_by_class_name retrieves all the tags recursively in the document that has the given class name(s).
|
|
|
|
pub fn (dom DocumentObjectModel) get_tags_by_class_name(names ...string) []&Tag {
|
|
|
|
return dom.root.get_tags_by_class_name(...names)
|
|
|
|
}
|