added the tags "hierarchy"
This commit is contained in:
@@ -27,3 +27,11 @@ type HTMLChecker interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// var NotYetFinished = errors.New("the HTML checker has not yet been finished")
|
// var NotYetFinished = errors.New("the HTML checker has not yet been finished")
|
||||||
|
|
||||||
|
type htmlCheckerBackend interface {
|
||||||
|
getCheckerAttrValue(string) string
|
||||||
|
sendTagMessage(string)
|
||||||
|
getCheckerContextValue(string) any
|
||||||
|
addExternalRef(*url.URL)
|
||||||
|
addInternalRef(string)
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,205 @@
|
|||||||
|
/*
|
||||||
|
* Amsterdam Web Communities System
|
||||||
|
* Copyright (c) 2025 Erbosoft Metaverse Design Solutions, All Rights Reserved
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
// The htmlcheck package contains the HTML Checker.
|
||||||
|
package htmlcheck
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/url"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type causeLineBreakFunc func(*tag, bool) bool
|
||||||
|
type closingTagFunc func(*tag) string
|
||||||
|
type rewriteContentsFunc func(*tag, string, bool, htmlCheckerBackend) string
|
||||||
|
|
||||||
|
type tag struct {
|
||||||
|
name string
|
||||||
|
index int
|
||||||
|
lineBreak bool
|
||||||
|
allowClose bool
|
||||||
|
balanceTags bool
|
||||||
|
clb causeLineBreakFunc
|
||||||
|
ct closingTagFunc
|
||||||
|
rwc rewriteContentsFunc
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *tag) causeLineBreak(isClosing bool) bool {
|
||||||
|
if t.clb == nil {
|
||||||
|
return t.lineBreak
|
||||||
|
}
|
||||||
|
return t.clb(t, isClosing)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *tag) makeClosingTag() string {
|
||||||
|
if t.ct == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return t.ct(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *tag) rewriteContents(contents string, isClosing bool, ctxt htmlCheckerBackend) string {
|
||||||
|
if t.rwc == nil {
|
||||||
|
return contents
|
||||||
|
}
|
||||||
|
return t.rwc(t, contents, isClosing, ctxt)
|
||||||
|
}
|
||||||
|
|
||||||
|
func createSimpleTag(name string, brk bool) *tag {
|
||||||
|
return &tag{
|
||||||
|
name: strings.ToUpper(name),
|
||||||
|
index: -1,
|
||||||
|
lineBreak: brk,
|
||||||
|
allowClose: false,
|
||||||
|
balanceTags: false,
|
||||||
|
clb: nil,
|
||||||
|
ct: nil,
|
||||||
|
rwc: nil,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func createWBRTag() *tag {
|
||||||
|
return &tag{
|
||||||
|
name: "WBR",
|
||||||
|
index: -1,
|
||||||
|
lineBreak: false,
|
||||||
|
allowClose: false,
|
||||||
|
balanceTags: false,
|
||||||
|
clb: nil,
|
||||||
|
ct: nil,
|
||||||
|
rwc: func(t *tag, contents string, isClosing bool, ctxt htmlCheckerBackend) string {
|
||||||
|
ctxt.sendTagMessage("WBR")
|
||||||
|
return contents
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func stdClosingTag(tag *tag) string {
|
||||||
|
return fmt.Sprintf("</%s>", tag.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func createOpenCloseTag(name string, brk bool) *tag {
|
||||||
|
return &tag{
|
||||||
|
name: strings.ToUpper(name),
|
||||||
|
index: -1,
|
||||||
|
lineBreak: brk,
|
||||||
|
allowClose: true,
|
||||||
|
balanceTags: false,
|
||||||
|
clb: nil,
|
||||||
|
ct: stdClosingTag,
|
||||||
|
rwc: nil,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func createListElementTag(name string) *tag {
|
||||||
|
return &tag{
|
||||||
|
name: strings.ToUpper(name),
|
||||||
|
index: -1,
|
||||||
|
lineBreak: true,
|
||||||
|
allowClose: true,
|
||||||
|
balanceTags: false,
|
||||||
|
clb: func(t *tag, isClosing bool) bool {
|
||||||
|
return !isClosing
|
||||||
|
},
|
||||||
|
ct: stdClosingTag,
|
||||||
|
rwc: nil,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func createBalancedTag(name string, brk bool) *tag {
|
||||||
|
return &tag{
|
||||||
|
name: strings.ToUpper(name),
|
||||||
|
index: -1,
|
||||||
|
lineBreak: brk,
|
||||||
|
allowClose: true,
|
||||||
|
balanceTags: true,
|
||||||
|
clb: nil,
|
||||||
|
ct: stdClosingTag,
|
||||||
|
rwc: nil,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func createNOBRTag() *tag {
|
||||||
|
return &tag{
|
||||||
|
name: "NOBR",
|
||||||
|
index: -1,
|
||||||
|
lineBreak: false,
|
||||||
|
allowClose: true,
|
||||||
|
balanceTags: true,
|
||||||
|
clb: nil,
|
||||||
|
ct: stdClosingTag,
|
||||||
|
rwc: func(t *tag, contents string, isClosing bool, ctxt htmlCheckerBackend) string {
|
||||||
|
if isClosing {
|
||||||
|
ctxt.sendTagMessage("/NOBR")
|
||||||
|
} else {
|
||||||
|
ctxt.sendTagMessage("NOBR")
|
||||||
|
}
|
||||||
|
return contents
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var hrefPattern = regexp.MustCompile(`(?i:href\s*=)`)
|
||||||
|
var targetPattern = regexp.MustCompile(`(?i:target\s*=)`)
|
||||||
|
|
||||||
|
func extractAttribute(s string) string {
|
||||||
|
s = strings.TrimSpace(s)
|
||||||
|
if s[0] == '\'' || s[0] == '"' {
|
||||||
|
p := strings.IndexByte(s[1:], s[0])
|
||||||
|
if p < 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return s[1 : p+1]
|
||||||
|
}
|
||||||
|
return strings.Fields(s)[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
func rewriteATagContents(t *tag, contents string, isClosing bool, ctxt htmlCheckerBackend) string {
|
||||||
|
if isClosing {
|
||||||
|
return contents // don't bother checking close tag
|
||||||
|
}
|
||||||
|
bounds := hrefPattern.FindStringIndex(contents)
|
||||||
|
if bounds != nil {
|
||||||
|
s := extractAttribute(contents[bounds[1]:])
|
||||||
|
if s != "" && (strings.HasPrefix(s, "http:") || strings.HasPrefix(s, "https:")) {
|
||||||
|
ref, err := url.Parse(s)
|
||||||
|
if err != nil {
|
||||||
|
ctxt.addExternalRef(ref)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
targetSeen := false
|
||||||
|
bounds = targetPattern.FindStringIndex(contents)
|
||||||
|
if bounds != nil {
|
||||||
|
s := extractAttribute(contents[bounds[1]:])
|
||||||
|
if s != "" {
|
||||||
|
targetSeen = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if targetSeen {
|
||||||
|
return contents
|
||||||
|
}
|
||||||
|
tail := ctxt.getCheckerAttrValue("ANCHORTAIL")
|
||||||
|
return contents + " " + tail
|
||||||
|
}
|
||||||
|
|
||||||
|
func createATag() *tag {
|
||||||
|
return &tag{
|
||||||
|
name: "A",
|
||||||
|
index: -1,
|
||||||
|
lineBreak: false,
|
||||||
|
allowClose: true,
|
||||||
|
balanceTags: true,
|
||||||
|
clb: nil,
|
||||||
|
ct: stdClosingTag,
|
||||||
|
rwc: rewriteATagContents,
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user