diff --git a/README.md b/README.md index 4bd8d8e549deb24effee7c342ca28ea43fd3b790..aba8a961dcfcd4801be5d82309e5dc45448f2391 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/application/source/command.go b/application/source/command.go new file mode 100644 index 0000000000000000000000000000000000000000..aa2efd03b66bcfa2b2d8112c54e008edaab441fc --- /dev/null +++ b/application/source/command.go @@ -0,0 +1,116 @@ +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()) +} diff --git a/application/source/error.go b/application/source/error.go new file mode 100644 index 0000000000000000000000000000000000000000..81f4ce57e70a83635d57ea81a4f9aa0cba595022 --- /dev/null +++ b/application/source/error.go @@ -0,0 +1,13 @@ +package main + +import ( + "fmt" + "os" +) + +func checkError(e error) { + if e != nil { + fmt.Println(e) + os.Exit(1) + } +} diff --git a/application/source/go.mod b/application/source/go.mod index 0167c86ce779dd9f092c87643e51491203065149..6c8b8ed1aa5fe61648a6c313a43f421d4a414a22 100644 --- a/application/source/go.mod +++ b/application/source/go.mod @@ -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 ) diff --git a/application/source/go.sum b/application/source/go.sum index c7a0b0d7d274b2098b80a508b7233a5293b7761a..734260ea1eeccaefb87bd84d6aa94b647f8166a5 100644 --- a/application/source/go.sum +++ b/application/source/go.sum @@ -1,18 +1,88 @@ +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= diff --git a/application/source/html/cut.go b/application/source/html/cut.go new file mode 100644 index 0000000000000000000000000000000000000000..7f0bfe32414f0aecc375f0f85646bd9b4e2d7a8b --- /dev/null +++ b/application/source/html/cut.go @@ -0,0 +1,200 @@ +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 + +} diff --git a/application/source/html/generate.go b/application/source/html/generate.go new file mode 100644 index 0000000000000000000000000000000000000000..a8a5c6541401950ae13eae0b251f35de858e1968 --- /dev/null +++ b/application/source/html/generate.go @@ -0,0 +1,90 @@ +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 +} diff --git a/application/source/html/sync.go b/application/source/html/sync.go new file mode 100644 index 0000000000000000000000000000000000000000..f4486e38bee3001e1c525b1925f821ac965d18de --- /dev/null +++ b/application/source/html/sync.go @@ -0,0 +1,201 @@ +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 +} diff --git a/application/source/main.go b/application/source/main.go index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..48433a389bae8c8517ab41c265337cd4f74de90a 100644 --- a/application/source/main.go +++ b/application/source/main.go @@ -0,0 +1,30 @@ +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) + } + +} diff --git a/application/source/template/prepare.go b/application/source/template/prepare.go new file mode 100644 index 0000000000000000000000000000000000000000..a887056f3644d273fc140b05dcda287aec400988 --- /dev/null +++ b/application/source/template/prepare.go @@ -0,0 +1,304 @@ +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) + +} diff --git a/application/source/types/page-data.go b/application/source/types/page-data.go new file mode 100644 index 0000000000000000000000000000000000000000..0432fea76c4fcb3c410960ce8f1fb71d8d73d4da --- /dev/null +++ b/application/source/types/page-data.go @@ -0,0 +1,44 @@ +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 + +} diff --git a/application/source/types/snippets-specification.go b/application/source/types/snippets-specification.go new file mode 100644 index 0000000000000000000000000000000000000000..0ff0c8ea43e6f96817171a0ca9ba839dad4dda3c --- /dev/null +++ b/application/source/types/snippets-specification.go @@ -0,0 +1,24 @@ +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"` +} diff --git a/application/source/types/sync-specification.go b/application/source/types/sync-specification.go new file mode 100644 index 0000000000000000000000000000000000000000..bafd4b8cbd7199022e658a6170fc41cbd5d3038b --- /dev/null +++ b/application/source/types/sync-specification.go @@ -0,0 +1,50 @@ +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"` +} diff --git a/application/source/util/html.go b/application/source/util/html.go new file mode 100644 index 0000000000000000000000000000000000000000..1c264710278d05f96dd0e17c4f50898c34accc61 --- /dev/null +++ b/application/source/util/html.go @@ -0,0 +1,93 @@ +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) + +} diff --git a/application/source/util/id.go b/application/source/util/id.go new file mode 100644 index 0000000000000000000000000000000000000000..cef7728aa3a7994f149b81bbecb6ec9ca3c0d596 --- /dev/null +++ b/application/source/util/id.go @@ -0,0 +1,92 @@ +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 +} diff --git a/development/examples/example1/config/snippet.yaml b/development/examples/example1/config/snippet.yaml new file mode 100644 index 0000000000000000000000000000000000000000..bb21f81000bdbae6dfdb8f1260594e3536451d84 --- /dev/null +++ b/development/examples/example1/config/snippet.yaml @@ -0,0 +1,13 @@ +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: '!!!!!!!!!!!!!!!!!!' diff --git a/development/examples/example1/config/spec1.yaml b/development/examples/example1/config/spec1.yaml new file mode 100644 index 0000000000000000000000000000000000000000..b55ca45c847df4ec2101aab84dd1a5066e1e1957 --- /dev/null +++ b/development/examples/example1/config/spec1.yaml @@ -0,0 +1,20 @@ +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 + + + diff --git a/development/examples/example1/html/test.html b/development/examples/example1/html/test.html new file mode 100644 index 0000000000000000000000000000000000000000..b697ce002ebbc78cc42b154ddf246828bc25fa61 --- /dev/null +++ b/development/examples/example1/html/test.html @@ -0,0 +1,69 @@ +<!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 diff --git a/development/examples/example1/original/test1.html b/development/examples/example1/original/test1.html new file mode 100644 index 0000000000000000000000000000000000000000..5b7e9acad8157b903b19feb079248e71a1c0b3e2 --- /dev/null +++ b/development/examples/example1/original/test1.html @@ -0,0 +1,71 @@ +<!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 diff --git a/development/examples/example1/original/test2.html b/development/examples/example1/original/test2.html new file mode 100644 index 0000000000000000000000000000000000000000..3a4f24aa88a66a038aec5489009b3c393cae39fe --- /dev/null +++ b/development/examples/example1/original/test2.html @@ -0,0 +1,71 @@ +<!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 diff --git a/development/examples/example1/original/test3.html b/development/examples/example1/original/test3.html new file mode 100644 index 0000000000000000000000000000000000000000..3a4f24aa88a66a038aec5489009b3c393cae39fe --- /dev/null +++ b/development/examples/example1/original/test3.html @@ -0,0 +1,71 @@ +<!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 diff --git a/development/examples/example1/snippets/meta/container.html b/development/examples/example1/snippets/meta/container.html new file mode 100644 index 0000000000000000000000000000000000000000..2424762369803336a154c2d11af0f787e9c8df13 --- /dev/null +++ b/development/examples/example1/snippets/meta/container.html @@ -0,0 +1,27 @@ +<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" data-attributes="alt path:content.tickyesdata-image-gi-4013311193.alt,src path:content.tickyesdata-image-gi-4013311193.src,title path:content.tickyesdata-image-gi-4013311193.title"/> + <h1 class="deco"><span id="bad-request" data-replace-self="path:text.bad-request.text">Bad Request</span></h1> + <p><span id="the-request-was-inco-2640993422" data-replace-self="path:text.the-request-was-inco-2640993422.text">The request was incorrect, the server could not process it. + </span></p><p> +</p><div class="infobox"> + <div> + + <ul> + + <li data-state="monster">!!!!!!!!!!!!!!!!!!<a href="/a.html" title="Yes" id="yes-a-html" data-attributes="href path:anchors.yes-a-html.href,title path:anchors.yes-a-html.title,hreflang path:anchors.yes-a-html.hreflang">request</a> again (sometimes this helps).</li> + <li data-state="monster">!!!!!!!!!!!!!!!!!!</li> + + <li data-state="monster">!!!!!!!!!!!!!!!!!!</li> + + + </ul> + + <p><span id="you-can-try-the-foll-3363859033" data-replace-self="path:text.you-can-try-the-foll-3363859033.text">You can try the following steps:</span></p> + + </div> +</div> + + +</monster-state> \ No newline at end of file diff --git a/development/examples/example1/template/data.yaml b/development/examples/example1/template/data.yaml new file mode 100755 index 0000000000000000000000000000000000000000..518cba7fe81273a1271b3e56d00390de3e0b97f6 --- /dev/null +++ b/development/examples/example1/template/data.yaml @@ -0,0 +1,34 @@ +test.html: + lang: en + title: Bad Request + meta: + author: schukai GmbH + description: The request was malformed or invalid. + images: + - id: tickyesdata-image-gi-4013311193 + source: |- + data:image/gif;base64,R0lGODdhEAAQAMwAAPj7+FmhUYjNfGuxYYDJdYTIeanOpT+DOTuANXi/bGOrWj6CONzv2sPjv2Cm + V1unU4zPgI/Sg6DJnJ3ImTh8Mtbs00aNP1CZSGy0YqLEn47RgXW8amasW7XWsmmvX2iuXiwAAAAAEAAQAAAFVyAgjmRpnihqGCkpDQ + PbGkNUOFk6DZqgHCNGg2T4QAQBoIiRSAwBE4VA4FACKgkB5NGReASFZEmxsQ0whPDi9BiACYQAInXhwOUtgCUQoORFCGt/g4QAIQA7 + alt: tick + title: "yes" + anchors: + - id: yes-a-html + href: /b.html + hreflang: "" + title: "Yes" + text: + - text: Bad Request + id: bad-request + - text: The request was incorrect, the server could not process it. + id: the-request-was-inco-2640993422 + - text: Try submitting your + id: try-submitting-your + - text: Contact support if you continue to have problems. + id: contact-support-if-y-3404332025 + - text: |- + If you received this message as a result of a request to the server API, then check the structure + against the documentation. + id: if-you-received-this-423958995 + - text: 'You can try the following steps:' + id: you-can-try-the-foll-3363859033 diff --git a/development/examples/example1/template/test.html b/development/examples/example1/template/test.html new file mode 100644 index 0000000000000000000000000000000000000000..ab678048edbb1e41bb68cf5c18d65e7381b1378f --- /dev/null +++ b/development/examples/example1/template/test.html @@ -0,0 +1,72 @@ +<!DOCTYPE html><html lang="en" data-attributes="lang path:lang"><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 data-replace="path:title">Bad Request</title> + <meta name="description" content="The request was malformed or invalid." data-attributes="content path:meta.description"/> + <meta name="author" content="schukai GmbH" data-attributes="content path:meta.author"/> + + <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" data-attributes="alt path:content.tickyesdata-image-gi-4013311193.alt,src path:content.tickyesdata-image-gi-4013311193.src,title path:content.tickyesdata-image-gi-4013311193.title"/> + <h1 class="deco"><span id="bad-request" data-replace-self="path:text.bad-request.text">Bad Request</span></h1> + <p><span id="the-request-was-inco-2640993422" data-replace-self="path:text.the-request-was-inco-2640993422.text">The request was incorrect, the server could not process it. + </span></p><p> +</p><div class="infobox"> + <div> + + <ul> + + <li><span id="try-submitting-your" data-replace-self="path:text.try-submitting-your.text">Try submitting your </span><a href="/a.html" title="Yes" id="yes-a-html" +data-attributes="href path:anchors.yes-a-html.href,title path:anchors.yes-a-html.title,hreflang path:anchors.yes-a-html.hreflang">request</a> again (sometimes this helps).</li> + <li><span id="contact-support-if-y-3404332025" data-replace-self="path:text.contact-support-if-y-3404332025.text">Contact support if you continue to have problems.</span></li> + + <li><span id="if-you-received-this-423958995" data-replace-self="path:text.if-you-received-this-423958995.text">If you received this message as a result of a request to the server API, then check the structure + against the documentation. + </span></li> + + + </ul> + + <p><span id="you-can-try-the-foll-3363859033" data-replace-self="path:text.you-can-try-the-foll-3363859033.text">You can try the following steps:</span></p> + + </div> +</div> + + +</monster-state> + + + +</body></html> \ No newline at end of file