Skip to content
Snippets Groups Projects
Verified Commit c588de0d authored by Volker Schukai's avatar Volker Schukai :alien:
Browse files

feat: implement cut, sync and template

parent 31731b28
No related branches found
No related tags found
No related merge requests found
Showing
with 1625 additions and 4 deletions
......@@ -11,6 +11,34 @@ To check out docs and examples, visit [gitlab.schukai.com/oss/bob](https://gitla
## Usage
### Template
#### Prepare
```bash
bob template prepare --input ./templates/ --output ./output/
```
This will create a `./output/` directory with all parsed templates from `./templates/` directory.
Also, a data YAML. This data YAML is used to generate the final files.
This command prepares the title, description, keywords, and other metadata for the templates.
Also, it will parse the templates for images, anchors, and text.
| Original | Parsed |
|-------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------|
| `<html lang="en"><head>` | `<html lang="en" data-attributes="lang path:lang"><head>` |
| `<title>Bob</title>` | `<title data-attributes="title path:title">Bob</title>` |
| `<meta name="description" content="Bob is a html and html fragment builder">` | `<meta name="description" content="Bob is a html and html fragment builder" data-attributes="description path:meta.description">` |
| `<img alt="alt text" title="my title" src="..." ` | `<img alt="alt text" title="my title" src="..." data-attributes="alt path:img.id1003.alt title path:img.id1003.title src path:img.id1003.src">` |
| `<a href="https://gitlab.schukai.com/oss/bob">` | `<a href="https://gitlab.schukai.com/oss/bob" data-attributes="href path:a.id1004.href">` |
| `<p>Bob is a html and html fragment builder</p>` | `<p><span data-attributes="text path:p.id1005.text">Bob is a html and html fragment builder</span></p>` |
## Questions
......
package main
import (
"fmt"
html2 "gitlab.schukai.com/oss/bob.git/html"
template2 "gitlab.schukai.com/oss/bob.git/template"
"gitlab.schukai.com/oss/bob.git/types"
"gitlab.schukai.com/oss/libraries/go/application/xflags"
"gopkg.in/yaml.v3"
"os"
"path"
"path/filepath"
)
type Definition struct {
Help struct {
} `command:"help" call:"PrintHelp" description:"Prints this help message"`
Verbose bool `short:"v" long:"verbose" description:"Show verbose debug information"`
Template struct {
Prepare struct {
Input string `short:"i" long:"input" description:"Directory with html files to prepare" required:"true"`
Output string `short:"o" long:"output" description:"Directory to save prepared html files" required:"true"`
} `command:"prepare" description:"Prepare content from a file" call:"PrepareTemplate"`
} `command:"template" description:"Template commands"`
HTML struct {
Generate struct {
Input string `short:"i" long:"input" description:"Directory with prepared html files" required:"true"`
Output string `short:"o" long:"output" description:"Directory to save generated template files" required:"true"`
} `command:"generate" description:"Generate html files from a file" call:"GenerateHTML"`
Sync struct {
Specification string `short:"s" long:"specification" description:"Specification file" required:"true"`
} `command:"sync" description:"Sync html" call:"SyncHTML"`
Cut struct {
Specification string `short:"s" long:"specification" description:"Specification file" required:"true"`
} `command:"cut" description:"Cut html" call:"CutHTML"`
} `command:"html" description:"HTML related commands"`
}
func (d *Definition) CutHTML(s *xflags.Settings[Definition]) {
err := html2.CutHtml(d.HTML.Cut.Specification)
if err != nil {
s.AddError(err)
}
}
func (d *Definition) SyncHTML(s *xflags.Settings[Definition]) {
err := html2.SyncHtml(d.HTML.Sync.Specification)
if err != nil {
s.AddError(err)
}
}
func (d *Definition) GenerateHTML(s *xflags.Settings[Definition]) {
err := filepath.Walk(d.HTML.Generate.Input, func(p string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if info.IsDir() {
return nil
}
ext := filepath.Ext(p)
if ext != ".yaml" {
return nil
}
return html2.GenerateFiles(p, d.HTML.Generate.Output)
})
if err != nil {
s.AddError(err)
}
}
func (d *Definition) PrepareTemplate(s *xflags.Settings[Definition]) {
storage := types.NewPageDataStorage()
err := filepath.Walk(d.Template.Prepare.Input, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if info.IsDir() {
return nil
}
return template2.PrepareHtmlFile(path, d.Template.Prepare.Output, storage)
})
if err != nil {
s.AddError(err)
}
data, err := yaml.Marshal(storage)
if err != nil {
s.AddError(err)
} else {
o := path.Join(d.Template.Prepare.Output, "data.yaml")
os.WriteFile(o, data, os.ModePerm)
}
if err != nil {
s.AddError(err)
}
}
func (d *Definition) PrintHelp(s *xflags.Settings[Definition]) {
fmt.Println(s.Help())
}
package main
import (
"fmt"
"os"
)
func checkError(e error) {
if e != nil {
fmt.Println(e)
os.Exit(1)
}
}
......@@ -2,11 +2,33 @@ module gitlab.schukai.com/oss/bob.git
go 1.19
require (
github.com/andybalholm/cascadia v1.3.1
gitlab.schukai.com/oss/libraries/go/application/xflags v1.13.0
gitlab.schukai.com/oss/libraries/go/markup/html v0.2.1
golang.org/x/crypto v0.4.0
golang.org/x/net v0.4.0
gopkg.in/yaml.v3 v3.0.1
)
require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/fsnotify/fsnotify v1.6.0 // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/imdario/mergo v0.3.13 // indirect
github.com/magiconair/properties v1.8.6 // indirect
github.com/pelletier/go-toml/v2 v2.0.5 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/stretchr/testify v1.8.0 // indirect
gitlab.schukai.com/oss/libraries/go/application/xflags v1.13.0 // indirect
gitlab.schukai.com/oss/libraries/go/utilities/pathfinder v0.3.1 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
github.com/r3labs/diff/v3 v3.0.0 // indirect
github.com/stretchr/testify v1.8.1 // indirect
github.com/vmihailenco/msgpack v4.0.4+incompatible // indirect
github.com/volker-schukai/tokenizer v1.0.0 // indirect
gitlab.schukai.com/oss/libraries/go/application/configuration v1.14.0 // indirect
gitlab.schukai.com/oss/libraries/go/network/http-negotiation v1.3.0 // indirect
gitlab.schukai.com/oss/libraries/go/utilities/data.git v0.2.0 // indirect
gitlab.schukai.com/oss/libraries/go/utilities/pathfinder v0.5.0 // indirect
golang.org/x/exp v0.0.0-20221023144134-a1e5550cf13e // indirect
golang.org/x/sys v0.3.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/protobuf v1.28.1 // indirect
)
github.com/andybalholm/cascadia v1.3.1 h1:nhxRkql1kdYCc8Snf7D5/D3spOX+dBgjA6u8x004T2c=
github.com/andybalholm/cascadia v1.3.1/go.mod h1:R4bJ1UQfqADjvDa4P6HZHLh/3OxWWEqc0Sk8XGwHqvA=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk=
github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg=
github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo=
github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
github.com/pelletier/go-toml/v2 v2.0.5 h1:ipoSadvV8oGUjnUbMub59IDPPwfxF694nG/jwbMiyQg=
github.com/pelletier/go-toml/v2 v2.0.5/go.mod h1:OMHamSCAODeSsVrwwvcJOaoN0LIUIaFVNZzmWyNfXas=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/r3labs/diff/v3 v3.0.0 h1:ZhPwNxn9gW5WLPBV9GCYaVbMdLOSmJ0DeKdCiSbOLUI=
github.com/r3labs/diff/v3 v3.0.0/go.mod h1:wCkTySAiDnZao1sZrVTDIzuzgLZ+cNPGn3LC8DlIg5g=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/vmihailenco/msgpack v4.0.4+incompatible h1:dSLoQfGFAo3F6OoNhwUmLwVgaUXK79GlxNBwueZn0xI=
github.com/vmihailenco/msgpack v4.0.4+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk=
github.com/volker-schukai/tokenizer v1.0.0 h1:wF4haFoCodq7lgAk8c+th/DZmpFpL2WVD8wDzAGU1mA=
github.com/volker-schukai/tokenizer v1.0.0/go.mod h1:LPw7lLIxUnZgeg96818N7IvwLE1x8ya31J/Aa0aCq9M=
gitlab.schukai.com/oss/libraries/go/application/configuration v1.14.0 h1:cuS7BxTe5pS5iUD6eJKNcErRx6NHGNAGeZx8tOED9pk=
gitlab.schukai.com/oss/libraries/go/application/configuration v1.14.0/go.mod h1:me1BS/adhucf6h8t2ew0ifaNVio2vunQIYfpofUBbF8=
gitlab.schukai.com/oss/libraries/go/application/xflags v1.13.0 h1:nTnuGzAOzUTlmrst8tPrLp5EFOwC+6vozsxOcnGVfQk=
gitlab.schukai.com/oss/libraries/go/application/xflags v1.13.0/go.mod h1:EEYPy5RYR4ahK9J6CTKSLFHUhP+ndHBWVdQMtGCFqDo=
gitlab.schukai.com/oss/libraries/go/markup/html v0.2.1 h1:d08SBGYHXHWOCJqaLh1Cfh6rOYmHoRJxM63GhF2klCo=
gitlab.schukai.com/oss/libraries/go/markup/html v0.2.1/go.mod h1:h0+dJUwXfF1QeaBxFiidgxA2I3A9qIPE+zhfs88pKV8=
gitlab.schukai.com/oss/libraries/go/network/http-negotiation v1.3.0 h1:SZG0BW5ll3WK5ZIOTogjqX8oVHCTxANTDLPxUs7Rnx8=
gitlab.schukai.com/oss/libraries/go/network/http-negotiation v1.3.0/go.mod h1:RS2rKf5O+rmSBshHLOgjG7dxg5N2MhNYokZOBcuXdX8=
gitlab.schukai.com/oss/libraries/go/utilities/data.git v0.2.0 h1:JVxMHiA8zFVjJDhNl65XeYrhdMkzB+5dyrBUEZ982WU=
gitlab.schukai.com/oss/libraries/go/utilities/data.git v0.2.0/go.mod h1:BsR4Y9jsvISplkW6UoLFRGxQX69/AUmP1SXRwWhx31o=
gitlab.schukai.com/oss/libraries/go/utilities/pathfinder v0.3.1 h1:oyElaqEiyr2XgaE1CYwD8LoeHsuR/vQD/p6k3jYbJFs=
gitlab.schukai.com/oss/libraries/go/utilities/pathfinder v0.3.1/go.mod h1:UvdD4NAf3gLKYafabJD7e9ZCOetzM9JZ9y4GkZukPVU=
gitlab.schukai.com/oss/libraries/go/utilities/pathfinder v0.5.0 h1:LsKHjuiEzOS3W5UqtqLmHEDYGVQ7qddf88rmD1HmI4M=
gitlab.schukai.com/oss/libraries/go/utilities/pathfinder v0.5.0/go.mod h1:UvdD4NAf3gLKYafabJD7e9ZCOetzM9JZ9y4GkZukPVU=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.4.0 h1:UVQgzMY87xqpKNgb+kDsll2Igd33HszWHFLmpaRMq/8=
golang.org/x/crypto v0.4.0/go.mod h1:3quD/ATkf6oY+rnes5c3ExXTbLc8mueNue5/DoinL80=
golang.org/x/exp v0.0.0-20221023144134-a1e5550cf13e h1:SkwG94eNiiYJhbeDE018Grw09HIN/KB9NlRmZsrzfWs=
golang.org/x/exp v0.0.0-20221023144134-a1e5550cf13e/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20210916014120-12bc252f5db8/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.1.0 h1:hZ/3BUoy5aId7sCpA/Tc5lt8DkFgdVS2onTpJsZ/fl0=
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=
golang.org/x/net v0.4.0 h1:Q5QPcMlvfxFTAPV0+07Xz/MpK9NTXu2VDUuy0FeMfaU=
golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
package html
import (
"github.com/andybalholm/cascadia"
"gitlab.schukai.com/oss/bob.git/types"
"gitlab.schukai.com/oss/libraries/go/markup/html/engine"
"golang.org/x/net/html"
"gopkg.in/yaml.v3"
"os"
"path/filepath"
"strings"
)
func CutHtml(p string) error {
content, err := os.ReadFile(p)
if err != nil {
return err
}
currentDir, _ := os.Getwd()
os.Chdir(filepath.Dir(p))
defer os.Chdir(currentDir)
specification := types.SnippetsSpecification{}
if err := yaml.Unmarshal(content, &specification); err != nil {
return err
}
sourceFiles := make(map[string]*html.Node)
for _, r := range specification.Snippets {
source := r.Source
absSource, err := filepath.Abs(source)
if err != nil {
return err
}
if sourceFiles[absSource], err = readHTML(absSource); err != nil {
return err
}
}
for _, r := range specification.Snippets {
source := r.Source
destination := r.Destination
absSource, err := filepath.Abs(source)
if err != nil {
return err
}
sourceNode, ok := sourceFiles[absSource]
if !ok {
continue
}
replacedNodes, err := replaceNodes(sourceNode, r.Replacement)
if err != nil {
return err
}
err = setAttributes(replacedNodes, r.Attributes)
if err != nil {
return err
}
selectors := r.Selector
query, err := cascadia.Compile(selectors)
if err != nil {
return err
}
snippetNode := query.MatchFirst(replacedNodes)
absDestination, err := filepath.Abs(destination)
if err != nil {
return err
}
fp, err := os.Create(absDestination)
if err != nil {
return err
}
err = html.Render(fp, snippetNode)
err2 := fp.Close()
if err2 != nil {
return err2
}
if err != nil {
return err
}
}
return nil
}
func setAttributes(n *html.Node, attributes []types.Attributes) error {
for _, a := range attributes {
sel, err := cascadia.Compile(a.Selector)
if err != nil {
return err
}
for _, h := range sel.MatchAll(n) {
removeAttribute(h.Attr, a.Name)
h.Attr = append(h.Attr, html.Attribute{Key: a.Name, Val: a.Value})
}
}
return nil
}
func removeAttribute(attrs []html.Attribute, key string) []html.Attribute {
var result []html.Attribute
for _, attr := range attrs {
if attr.Key == key {
continue
}
result = append(result, attr)
}
return result
}
func replaceNodes(n *html.Node, replacements []types.ContentReplacement) (*html.Node, error) {
ret := engine.CloneNode(n)
for _, r := range replacements {
doc, err := html.Parse(strings.NewReader(r.Content))
if err != nil {
return nil, err
}
nodeList := []html.Node{}
sel, err := cascadia.Compile("head")
if err != nil {
return nil, err
}
head := sel.MatchAll(doc)
for _, h := range head {
if h.FirstChild == nil {
continue
}
for c := h.FirstChild; c != nil; c = c.NextSibling {
nodeList = append(nodeList, *c)
}
}
sel, err = cascadia.Compile("body")
if err != nil {
return nil, err
}
for _, h := range sel.MatchAll(doc) {
if h.FirstChild == nil {
continue
}
for c := h.FirstChild; c != nil; c = c.NextSibling {
nodeList = append(nodeList, *c)
}
}
sel, err = cascadia.Compile(r.Selector)
if err != nil {
return nil, err
}
for _, h := range sel.MatchAll(ret) {
for _, node := range nodeList {
h.Parent.InsertBefore(engine.CloneNode(&node), h)
}
h.Parent.RemoveChild(h)
}
}
return ret, nil
}
package html
import (
"gitlab.schukai.com/oss/bob.git/types"
"gitlab.schukai.com/oss/libraries/go/markup/html/engine"
"gopkg.in/yaml.v3"
"os"
"path"
"strings"
)
func GenerateFiles(dataPath, out string) error {
dir := path.Dir(dataPath)
yamlFile, err := os.ReadFile(dataPath)
if err != nil {
return err
}
storage := types.NewPageDataStorage()
if err := yaml.Unmarshal(yamlFile, storage); err != nil {
return err
}
for name, page := range storage {
p := path.Join(dir, name)
html, err := Generate(page, p)
if err != nil {
return err
}
outFile := path.Join(out, name)
err = os.WriteFile(outFile, []byte(html), 0644)
if err != nil {
return err
}
}
return nil
}
func Generate(data *types.PageData, name string) (string, error) {
dataset := make(map[any]any)
dataset["lang"] = data.Lang
dataset["title"] = data.Title
dataset["meta"] = make(map[any]any)
for k, v := range data.Meta {
dataset["meta"].(map[any]any)[k] = v
}
dataset["images"] = make(map[any]any, 0)
for _, v := range data.Images {
dataset["images"].(map[any]any)[v.Id] = map[any]any{
"source": v.Source,
"alt": v.Alt,
"title": v.Title,
"id": v.Id,
}
}
dataset["anchors"] = make(map[any]any, 0)
for _, v := range data.Anchors {
dataset["anchors"].(map[any]any)[v.Id] = map[any]any{
"href": v.Href,
"hreflang": v.HrefLang,
"title": v.Title,
"id": v.Id,
}
}
dataset["text"] = make(map[any]any, 0)
for _, v := range data.Text {
dataset["text"].(map[any]any)[v.Id] = map[any]any{"text": v.Text}
}
e := engine.New(dataset)
template, err := os.ReadFile(name)
if err != nil {
return "", err
}
stringWriter := &strings.Builder{}
e.ProcessHtml(stringWriter, strings.NewReader(string(template)))
return stringWriter.String(), nil
}
package html
import (
"fmt"
"github.com/andybalholm/cascadia"
"gitlab.schukai.com/oss/bob.git/types"
"gitlab.schukai.com/oss/libraries/go/markup/html/engine"
"golang.org/x/net/html"
"gopkg.in/yaml.v3"
"os"
"path/filepath"
)
type Specification struct {
Selector string
}
func readHTML(p string) (*html.Node, error) {
htmlFile, err := os.Open(p)
if err != nil {
return nil, err
}
defer htmlFile.Close()
return html.Parse(htmlFile)
}
func SyncHtml(p string) error {
content, err := os.ReadFile(p)
if err != nil {
return err
}
currentDir, _ := os.Getwd()
os.Chdir(filepath.Dir(p))
defer os.Chdir(currentDir)
specification := types.SyncSpecification{}
if err := yaml.Unmarshal(content, &specification); err != nil {
return err
}
sourceFiles := make(map[string]*html.Node)
destinationFiles := make(map[string]*html.Node)
sourceDestination := make(map[string][]string)
for _, r := range specification.Sync {
source := r.Source
destination := r.Destination
exclude := r.Destination.Exclude
absSource, err := filepath.Abs(source.Path)
if err != nil {
return err
}
if err != nil {
return err
}
if sourceFiles[absSource], err = readHTML(absSource); err != nil {
return err
}
for _, d := range destination.Path {
d, err := filepath.Abs(d)
if err != nil {
return err
}
fileinfo, err := os.Stat(d)
if err != nil {
return err
}
if fileinfo.IsDir() {
filepath.Walk(d, func(pp string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if info.IsDir() {
return nil
}
if exclude != nil {
for _, e := range exclude {
e, err := filepath.Abs(e)
if err != nil {
return err
}
if e == pp {
return nil
}
}
}
ext := filepath.Ext(pp)
if ext == ".html" {
var dd *html.Node
if dd, err = readHTML(pp); err != nil {
return err
}
destinationFiles[pp] = dd
sourceDestination[absSource] = append(sourceDestination[absSource], pp)
}
return nil
})
} else if filepath.Ext(d) == ".html" {
if exclude != nil {
for _, e := range exclude {
e, err := filepath.Abs(e)
if err != nil {
return err
}
if e == d {
return nil
}
}
}
var dd *html.Node
if dd, err = readHTML(d); err != nil {
return err
}
destinationFiles[d] = dd
sourceDestination[absSource] = append(sourceDestination[absSource], d)
}
}
for _, r := range specification.Sync {
source := r.Source
absSource, err := filepath.Abs(source.Path)
if err != nil {
return err
}
sourceSelector := source.Selector
query, err := cascadia.Compile(sourceSelector)
if err != nil {
return err
}
sourceNode := query.MatchFirst(sourceFiles[absSource])
dp := sourceDestination[absSource]
for _, d := range dp {
destinationSelector := r.Destination.Selector
if destinationSelector == "" {
destinationSelector = sourceSelector
}
query, err := cascadia.Compile(destinationSelector)
if err != nil {
return err
}
destinationData := query.MatchFirst(destinationFiles[d])
if destinationData == nil {
return fmt.Errorf("could not find selector %s in %s", destinationSelector, d)
}
n := engine.CloneNode(sourceNode)
destinationData.Parent.InsertBefore(n, destinationData)
destinationData.Parent.RemoveChild(destinationData)
}
}
for p, d := range destinationFiles {
fp, err := os.Create(p)
if err != nil {
return err
}
err = html.Render(fp, d)
err2 := fp.Close()
if err2 != nil {
return err2
}
if err != nil {
return err
}
}
}
return nil
}
package main
import (
"fmt"
"gitlab.schukai.com/oss/libraries/go/application/xflags"
"os"
)
func main() {
var settings *xflags.Settings[Definition]
settings = xflags.New(os.Args[0], Definition{})
settings.Parse(os.Args[1:])
settings.Execute()
if settings.HasErrors() {
for _, err := range settings.Errors() {
fmt.Println(err)
}
os.Exit(1)
} else {
if settings.WasExecuted() {
os.Exit(0)
}
os.Exit(1)
}
}
package template
import (
"github.com/andybalholm/cascadia"
"gitlab.schukai.com/oss/bob.git/types"
"gitlab.schukai.com/oss/bob.git/util"
"golang.org/x/net/html"
"golang.org/x/net/html/atom"
"path"
"strings"
)
const attributePrefix = "data-"
const attributeAttributes = "data-attributes"
const attributeReplace = "data-replace"
func removeAttribute(attrs []html.Attribute, key string) []html.Attribute {
var result []html.Attribute
for _, attr := range attrs {
if attr.Key == key {
continue
}
result = append(result, attr)
}
return result
}
//func setDataAttribute(node *html.Node, name, attribute, instruction string) {
// node.Attr = removeAttribute(node.Attr, name)
// node.Attr = append(node.Attr, html.Attribute{Key: name, Val: attribute + " " + instruction})
//}
func setDataAttributesAttribute(node *html.Node, name, attribute, instruction string) {
value := util.GetAttribute(node.Attr, attributeAttributes)
if value != "" {
l := strings.Split(value, ",")
m := make(map[string]string)
for _, v := range l {
v = strings.TrimSpace(v)
x := strings.Index(v, " ")
if x > 0 {
a := v[:x]
b := v[x+1:]
if a != attribute {
m[a] = b
}
}
}
value = ""
for k, v := range m {
if value != "" {
value += ","
}
value += k + " " + v
}
}
node.Attr = removeAttribute(node.Attr, attributeAttributes)
if value != "" {
value += ","
}
value += attribute + " " + instruction
node.Attr = append(node.Attr, html.Attribute{Key: name, Val: value})
}
func prepateMeta(node *html.Node, attrKey, attrValue string, storage *types.PageData) {
sel, err := cascadia.Parse("meta[" + attrKey + "=" + attrValue + "]")
if err != nil {
return
}
meta := cascadia.Query(node, sel)
if meta == nil {
return
}
setDataAttributesAttribute(meta, attributeAttributes, "content", "path:meta."+attrValue)
storage.Meta[attrValue] = util.GetAttribute(meta.Attr, "content")
}
func prepareLanguage(node *html.Node, storage *types.PageData) {
selector, err := cascadia.Parse("html")
if err != nil {
return
}
n := cascadia.Query(node, selector)
if n == nil {
return
}
setDataAttributesAttribute(n, attributeAttributes, "lang", "path:lang")
lang := util.GetAttribute(n.Attr, "lang")
if lang == "" {
lang = "en"
}
storage.Lang = lang
}
func prepareTitle(node *html.Node, storage *types.PageData) {
selector, err := cascadia.Parse("title")
if err != nil {
return
}
n := cascadia.Query(node, selector)
if n == nil {
return
}
n.Attr = removeAttribute(node.Attr, attributeReplace)
n.Attr = append(node.Attr, html.Attribute{Key: attributeReplace, Val: "path:title"})
title := ""
for c := n.FirstChild; c != nil; c = c.NextSibling {
if c.Type == html.TextNode {
title += c.Data
}
}
storage.Title = title
}
func prepareAnchors(node *html.Node, storage *types.PageData) {
selector, err := cascadia.Parse("a")
if err != nil {
return
}
list := cascadia.QueryAll(node, selector)
if list == nil {
return
}
for _, n := range list {
title := util.GetAttribute(n.Attr, "title")
hreflang := util.GetAttribute(n.Attr, "hreflang")
href := util.GetAttribute(n.Attr, "href")
id := util.GetOrCreateId(n, title+hreflang+href)
if id == "" {
id, _ = util.RandomString(8)
n.Attr = removeAttribute(n.Attr, "id")
n.Attr = append(n.Attr, html.Attribute{Key: "id", Val: id})
}
setDataAttributesAttribute(n, attributeAttributes, "href", "path:anchors."+id+".href")
setDataAttributesAttribute(n, attributeAttributes, "title", "path:anchors."+id+".title")
setDataAttributesAttribute(n, attributeAttributes, "hreflang", "path:anchors."+id+".hreflang")
storage.Anchors = append(storage.Anchors, types.Anchor{
Id: id,
Title: title,
HrefLang: hreflang,
Href: href,
})
}
}
func prepareImages(node *html.Node, storage *types.PageData) {
selector, err := cascadia.Parse("img")
if err != nil {
return
}
list := cascadia.QueryAll(node, selector)
if list == nil {
return
}
for _, n := range list {
alt := util.GetAttribute(n.Attr, "alt")
title := util.GetAttribute(n.Attr, "title")
source := util.GetAttribute(n.Attr, "src")
id := util.GetOrCreateId(n, alt+title+source)
if id == "" {
id, _ = util.RandomString(8)
n.Attr = removeAttribute(n.Attr, "id")
n.Attr = append(n.Attr, html.Attribute{Key: "id", Val: id})
}
setDataAttributesAttribute(n, attributeAttributes, "src", "path:content."+id+".src")
setDataAttributesAttribute(n, attributeAttributes, "alt", "path:content."+id+".alt")
setDataAttributesAttribute(n, attributeAttributes, "title", "path:content."+id+".title")
storage.Images = append(storage.Images, types.Image{
Id: id,
Alt: alt,
Title: title,
Source: source,
})
}
}
func prepareTextNodes(node *html.Node, storage *types.PageData) {
selector, err := cascadia.Parse("body")
if err != nil {
return
}
body := cascadia.Query(node, selector)
if body == nil {
return
}
runNodes(body, storage)
}
func runNodes(n *html.Node, storage *types.PageData) {
if n.Type == html.TextNode {
content := strings.TrimSpace(n.Data)
if content == "" {
return
}
id, err := util.BuildTextKey(content)
if err != nil {
id = util.GetNextId()
}
parent := n.Parent
span := &html.Node{
Type: html.ElementNode,
Data: atom.Span.String(),
Attr: []html.Attribute{
{
Key: "id",
Val: id,
},
{
Key: "data-replace-self",
Val: "path:text." + id + ".text",
},
},
}
parent.InsertBefore(span, n)
parent.RemoveChild(n)
span.AppendChild(n)
storage.Text = append(storage.Text, types.Text{
Id: id,
Text: content,
})
return
}
for c := n.FirstChild; c != nil; c = c.NextSibling {
runNodes(c, storage)
}
}
func PrepareHtmlFile(from, to string, storage types.PageDataStorage) error {
node, err := util.LoadHtml(from)
if err != nil {
return err
}
p := path.Base(from)
pd := types.NewPageData()
storage[p] = pd
prepareLanguage(node, pd)
prepareTitle(node, pd)
prepateMeta(node, "name", "description", pd)
prepateMeta(node, "name", "keywords", pd)
prepateMeta(node, "name", "author", pd)
prepareImages(node, pd)
prepareAnchors(node, pd)
prepareTextNodes(node, pd)
to = path.Join(to, path.Base(from))
return util.SaveHtml(to, node)
}
package types
type Text struct {
Text string `yaml:"text"`
Id string `yaml:"id"`
}
type Image struct {
Id string `yaml:"id"`
Source string `yaml:"source"`
Alt string `yaml:"alt"`
Title string `yaml:"title"`
}
type Anchor struct {
Id string `yaml:"id"`
Href string `yaml:"href"`
HrefLang string `yaml:"hreflang"`
Title string
}
type PageData struct {
Lang string `yaml:"lang"`
Title string `yaml:"title"`
Meta map[string]string `yaml:"meta"`
Images []Image `yaml:"images"`
Anchors []Anchor `yaml:"anchors"`
Text []Text `yaml:"text"`
}
func NewPageData() *PageData {
pd := &PageData{}
pd.Meta = make(map[string]string)
return pd
}
type PageDataStorage map[string]*PageData
func NewPageDataStorage() PageDataStorage {
var storage PageDataStorage
storage = PageDataStorage{}
return storage
}
package types
type Snippet struct {
Selector string `yaml:"selector"`
Source string `yaml:"source"`
Destination string `yaml:"destination"`
Attributes []Attributes `yaml:"attribute"`
Replacement []ContentReplacement `yaml:"replacement"`
}
type ContentReplacement struct {
Selector string `yaml:"selector"`
Content string `yaml:"content"`
}
type SnippetsSpecification struct {
Snippets []Snippet `yaml:"snippet"`
}
type Attributes struct {
Selector string `yaml:"selector"`
Name string `yaml:"name"`
Value string `yaml:"value"`
}
package types
import "gopkg.in/yaml.v3"
type PathOrList []string
// func (p PathOrList) MarshalYAML() (interface{}, error) {
// return yaml.Marshal(p)
// }
func (p *PathOrList) UnmarshalYAML(value *yaml.Node) error {
var multi []string
err := value.Decode(&multi)
if err != nil {
var single string
err := value.Decode(&single)
if err != nil {
return err
}
*p = []string{single}
} else {
*p = multi
}
return nil
}
//func (p *PathOrList) UnmarshalYAML(b interface{}) error {
// var s string
// if err := yaml.Unmarshal(b, &s); err != nil {
// return err
// }
//
// return nil
//}
type Sync struct {
Source struct {
Selector string `yaml:"selector"`
Path string `yaml:"path"`
} `yaml:"source"`
Destination struct {
Selector string `yaml:"selector"`
Path PathOrList `yaml:"path"`
Exclude []string `yaml:"exclude"`
} `yaml:"destination"`
}
type SyncSpecification struct {
Sync []Sync `yaml:"sync"`
}
package util
import (
"errors"
"golang.org/x/net/html"
"os"
"strconv"
)
func LoadHtml(p string) (*html.Node, error) {
f, err := os.Open(p)
if err != nil {
return nil, err
}
defer f.Close()
return html.Parse(f)
}
func GetAttribute(attrs []html.Attribute, key string) string {
for _, attr := range attrs {
if attr.Key == key {
return attr.Val
}
}
return ""
}
var internalIDCounter int = 999
func GetNextId() string {
internalIDCounter++
return "id" + strconv.Itoa(internalIDCounter)
}
func GetOrCreateId(node *html.Node, text string) string {
var err error
nodeId := GetAttribute(node.Attr, "id")
if nodeId != "" {
return nodeId
}
nodeId, err = BuildTextKey(text)
if err != nil {
nodeId = GetNextId()
}
node.Attr = append(node.Attr, html.Attribute{Key: "id", Val: nodeId})
return nodeId
}
func ReplaceAttribute(node *html.Node, key, value string) {
for i, attr := range node.Attr {
if attr.Key != key {
continue
}
node.Attr[i].Val = value
return
}
node.Attr = append(node.Attr, html.Attribute{Key: key, Val: value})
}
func SaveHtml(p string, node *html.Node) error {
var f *os.File
var err error
_, err = os.Stat(p)
if errors.Is(err, os.ErrNotExist) {
f, err = os.Create(p)
} else if err != nil {
return err
} else {
os.Remove(p)
f, err = os.Create(p)
if err != nil {
return err
}
}
defer f.Close()
return html.Render(f, node)
}
package util
import (
"encoding/base64"
"golang.org/x/crypto/bcrypt"
"hash/fnv"
"math/rand"
"regexp"
"strconv"
"strings"
)
const tempStringMask = "GAEKEEPHIENOHJOHEOJH" // random string to avoid collision with other text nodes
const keyDelimiter = "-"
// NewSecret creates a new secret
func NewSecret() (string, error) {
return NewSecretWithLength(32)
}
// NewSecretWithLength creates a new secret for a given length
func NewSecretWithLength(length int) (string, error) {
return RandomString(length)
}
func randomBytes(len int) ([]byte, error) {
b := make([]byte, len)
if _, err := rand.Read(b); err != nil {
return nil, err
}
return b, nil
}
func RandomString(length int) (string, error) {
b, err := randomBytes(length)
if err != nil {
return "", err
}
s := base64.URLEncoding.EncodeToString(b)
return cryptID(s, length)
}
func cryptID(t string, length int) (string, error) {
hash, err := bcrypt.GenerateFromPassword([]byte(t), bcrypt.DefaultCost)
if err != nil {
return "", err
}
r := strings.ToUpper(string(hash[len(hash)-length-1 : len(hash)-1]))
return r, nil
}
func hashString(s string) string {
h := fnv.New32a()
h.Write([]byte(s))
return strconv.Itoa(int(h.Sum32()))
}
func BuildTextKey(txt string) (string, error) {
txt = strings.TrimSpace(txt)
txt = strings.ToLower(txt)
txt = strings.ReplaceAll(txt, keyDelimiter, tempStringMask)
reg, err := regexp.Compile("[^a-z0-9]+")
if err != nil {
return "", err
}
newStr := reg.ReplaceAllString(txt, keyDelimiter)
newStr = strings.ReplaceAll(newStr, tempStringMask, keyDelimiter)
newStr = strings.Trim(newStr, keyDelimiter)
if len(newStr) > 20 {
newStr = newStr[:20] + keyDelimiter + hashString(newStr)
}
c := strings.TrimSpace(newStr)
if c == "" {
return "", nil
}
if len(c) < 2 {
return "", nil
}
return c, nil
}
snippet:
-
source: ../template/test.html
selector: 'monster-state'
destination: ../snippets/meta/container.html
attribute:
- selector: 'li'
name: 'data-state'
value: 'monster'
replacement:
-
selector: 'li>span'
content: '!!!!!!!!!!!!!!!!!!'
sync:
- source:
path: '../original/test1.html'
selector: '#mainscript'
destination:
path: '../original/'
exclude:
- ../original/test1.html
- source:
path: '../original/test1.html'
selector: '.deco'
destination:
path: '../original/'
exclude:
- ../original/test1.html
<!DOCTYPE html><html lang="en"><head>
<style>
*:not(:defined) {
visibility: hidden;
}
</style>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<meta name="robots" content="noindex"/>
<link rel="icon" type="image/x-icon" href="/asset/icon/favicon.svg"/>
<meta name="theme-color" content="#c10000"/>
<link rel="apple-touch-icon" sizes="180x180" href="/asset/icon/apple-touch-icon.png"/>
<link rel="icon" type="image/png" sizes="32x32" href="/asset/icon/favicon-32x32.png"/>
<link rel="icon" type="image/png" sizes="16x16" href="/asset/icon/favicon-16x16.png"/>
<link rel="mask-icon" href="/asset/icon/safari-pinned-tab.svg" color="#fd1d1d"/>
<meta name="msapplication-TileColor" content="#fd1d1d"/>
<meta name="msapplication-TileImage" content="/asset/icon/mstile-144x144.png"/>
<title>Bad Request</title>
<meta name="description" content="The request was malformed or invalid."/>
<meta name="author" content="schukai GmbH"/>
<link rel="icon" href="/favicon.ico"/>
<link rel="icon" href="/favicon.svg" type="image/svg+xml"/>
<link rel="apple-touch-icon" href="/apple-touch-icon.png"/>
<script src="/script/main.mjs" type="module"></script>
</head>
<body>
<header>
<div class="gradient"></div>
</header>
<monster-state>
<img width="16" height="16" alt="tick" title="yes" src="data:image/gif;base64,R0lGODdhEAAQAMwAAPj7+FmhUYjNfGuxYYDJdYTIeanOpT+DOTuANXi/bGOrWj6CONzv2sPjv2Cm
V1unU4zPgI/Sg6DJnJ3ImTh8Mtbs00aNP1CZSGy0YqLEn47RgXW8amasW7XWsmmvX2iuXiwAAAAAEAAQAAAFVyAgjmRpnihqGCkpDQ
PbGkNUOFk6DZqgHCNGg2T4QAQBoIiRSAwBE4VA4FACKgkB5NGReASFZEmxsQ0whPDi9BiACYQAInXhwOUtgCUQoORFCGt/g4QAIQA7" id="tickyesdata-image-gi-4013311193"/>
<h1 class="deco">Bad Request</h1>
<p>The request was incorrect, the server could not process it.</p><p>
</p><div class="infobox">
<div>
<ul>
<li>Try submitting your<a id="yes-a-html" href="/b.html" title="Yes" hreflang="">request</a> again (sometimes this helps).</li>
<li>Contact support if you continue to have problems.</li>
<li>If you received this message as a result of a request to the server API, then check the structure
against the documentation.</li>
</ul>
<p>You can try the following steps:</p>
</div>
</div>
</monster-state>
</body></html>
\ No newline at end of file
<!DOCTYPE html><html lang="de"><head>
<style>
*:not(:defined) {
visibility: hidden;
}
</style>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<meta name="robots" content="noindex,noarchive"/>
<link rel="icon" type="image/x-icon" href="/asset/icon/favicon.svg"/>
<meta name="theme-color" content="#c10000"/>
<link rel="apple-touch-icon" sizes="180x180" href="/asset/icon/apple-touch-icon.png"/>
<link rel="icon" type="image/png" sizes="32x32" href="/asset/icon/favicon-32x32.png"/>
<link rel="icon" type="image/png" sizes="16x16" href="/asset/icon/favicon-16x16.png"/>
<link rel="mask-icon" href="/asset/icon/safari-pinned-tab.svg" color="#fd1d1d"/>
<meta name="msapplication-TileColor" content="#fd1d1d"/>
<meta name="msapplication-TileImage" content="/asset/icon/mstile-144x144.png"/>
<title>Bad Request ME</title>
<meta name="description" content="The request was malformed or invalid."/>
<meta name="author" content="schukai GmbH"/>
<link rel="icon" href="/favicon.ico"/>
<link rel="icon" href="/favicon.svg" type="image/svg+xml"/>
<link rel="apple-touch-icon" href="/apple-touch-icon.png"/>
<script id="mainscript" src="/script/main22.mjs" type="module"></script>
</head>
<body>
<header>
<div class="gradient"></div>
</header>
<monster-state>
<img width="16" height="16" alt="tick" title="yes" src="data:image/gif;base64,R0lGODdhEAAQAMwAAPj7+FmhUYjNfGuxYYDJdYTIeanOpT+DOTuANXi/bGOrWj6CONzv2sPjv2Cm
V1unU4zPgI/Sg6DJnJ3ImTh8Mtbs00aNP1CZSGy0YqLEn47RgXW8amasW7XWsmmvX2iuXiwAAAAAEAAQAAAFVyAgjmRpnihqGCkpDQ
PbGkNUOFk6DZqgHCNGg2T4QAQBoIiRSAwBE4VA4FACKgkB5NGReASFZEmxsQ0whPDi9BiACYQAInXhwOUtgCUQoORFCGt/g4QAIQA7">
<h1 class="deco">Bad xxxx</h1>
<p>The request was incorrect, the server could not process it.
</p><p>
</p><div class="infobox">
<div>
<ul>
<li>Try submitting your <a href="/a.html" title="Yes">request</a> again (sometimes this helps).</li>
<li>Contact support if you continue to have problems.</li>
<li>If you received this message as a result of a request to the server API, then check the structure
against the documentation.
</li>
</ul>
<p>You can try the following steps:</p>
</div>
</div>
</monster-state>
</body></html>
\ No newline at end of file
<!DOCTYPE html><html lang="en"><head>
<style>
*:not(:defined) {
visibility: hidden;
}
</style>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<meta name="robots" content="noindex,noarchive"/>
<link rel="icon" type="image/x-icon" href="/asset/icon/favicon.svg"/>
<meta name="theme-color" content="#c10000"/>
<link rel="apple-touch-icon" sizes="180x180" href="/asset/icon/apple-touch-icon.png"/>
<link rel="icon" type="image/png" sizes="32x32" href="/asset/icon/favicon-32x32.png"/>
<link rel="icon" type="image/png" sizes="16x16" href="/asset/icon/favicon-16x16.png"/>
<link rel="mask-icon" href="/asset/icon/safari-pinned-tab.svg" color="#fd1d1d"/>
<meta name="msapplication-TileColor" content="#fd1d1d"/>
<meta name="msapplication-TileImage" content="/asset/icon/mstile-144x144.png"/>
<title>Bad Request ME</title>
<meta name="description" content="The request was malformed or invalid."/>
<meta name="author" content="schukai GmbH"/>
<link rel="icon" href="/favicon.ico"/>
<link rel="icon" href="/favicon.svg" type="image/svg+xml"/>
<link rel="apple-touch-icon" href="/apple-touch-icon.png"/>
<script id="mainscript" src="/script/main22.mjs" type="module"></script>
</head>
<body>
<header>
<div class="gradient"></div>
</header>
<monster-state>
<img width="16" height="16" alt="tick" title="yes" src="data:image/gif;base64,R0lGODdhEAAQAMwAAPj7+FmhUYjNfGuxYYDJdYTIeanOpT+DOTuANXi/bGOrWj6CONzv2sPjv2Cm
V1unU4zPgI/Sg6DJnJ3ImTh8Mtbs00aNP1CZSGy0YqLEn47RgXW8amasW7XWsmmvX2iuXiwAAAAAEAAQAAAFVyAgjmRpnihqGCkpDQ
PbGkNUOFk6DZqgHCNGg2T4QAQBoIiRSAwBE4VA4FACKgkB5NGReASFZEmxsQ0whPDi9BiACYQAInXhwOUtgCUQoORFCGt/g4QAIQA7"/>
<h1 class="deco">Bad xxxx</h1>
<p>The request was incorrect, the server could not process it.
</p><p>
</p><div class="infobox">
<div>
<ul>
<li>Try submitting your <a href="/a.html" title="Yes">request</a> again (sometimes this helps).</li>
<li>Contact support if you continue to have problems.</li>
<li>If you received this message as a result of a request to the server API, then check the structure
against the documentation.
</li>
</ul>
<p>You can try the following steps:</p>
</div>
</div>
</monster-state>
</body></html>
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment