diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..6cffaaf --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,3 @@ +# These are supported funding model platforms + +github: [maddalax] diff --git a/.github/workflows/release-auth-example.yml b/.github/workflows/release-auth-example.yml new file mode 100644 index 0000000..ae84089 --- /dev/null +++ b/.github/workflows/release-auth-example.yml @@ -0,0 +1,52 @@ +name: Build and Deploy htmgo auth example + +on: + workflow_run: + workflows: [ "Update HTMGO Framework Dependency" ] # The name of the first workflow + types: + - completed + workflow_dispatch: # Trigger on manual workflow_dispatch + push: + branches: + - master # Trigger on pushes to master + paths: + - 'examples/simple-auth/**' # Trigger only if files in this directory change + - "framework-ui/**" + - "cli/**" + +jobs: + build-and-push: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2 + + - name: Log in to GitHub Container Registry + uses: docker/login-action@v2 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Get short commit hash + id: vars + run: echo "::set-output name=short_sha::$(echo $GITHUB_SHA | cut -c1-7)" + + - name: Build Docker image + run: | + cd ./examples/simple-auth && docker build -t ghcr.io/${{ github.repository_owner }}/simple-auth:${{ steps.vars.outputs.short_sha }} . + + - name: Tag as latest Docker image + run: | + docker tag ghcr.io/${{ github.repository_owner }}/simple-auth:${{ steps.vars.outputs.short_sha }} ghcr.io/${{ github.repository_owner }}/simple-auth:latest + + - name: Log in to GitHub Container Registry + run: echo "${{ secrets.CR_PAT }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin + + - name: Push Docker image + run: | + docker push ghcr.io/${{ github.repository_owner }}/simple-auth:latest diff --git a/.github/workflows/release-chat-example.yml b/.github/workflows/release-chat-example.yml index 0302bd9..6efb434 100644 --- a/.github/workflows/release-chat-example.yml +++ b/.github/workflows/release-chat-example.yml @@ -1,9 +1,10 @@ name: Build and Deploy htmgo.dev chat example on: - pull_request: - branches: - - master + workflow_run: + workflows: [ "Update HTMGO Framework Dependency" ] # The name of the first workflow + types: + - completed workflow_dispatch: # Trigger on manual workflow_dispatch push: branches: diff --git a/.github/workflows/release-hn-clone.yml b/.github/workflows/release-hn-clone.yml new file mode 100644 index 0000000..f25e942 --- /dev/null +++ b/.github/workflows/release-hn-clone.yml @@ -0,0 +1,52 @@ +name: Build and Deploy htmgo hackernews clone + +on: + workflow_run: + workflows: [ "Update HTMGO Framework Dependency" ] # The name of the first workflow + types: + - completed + workflow_dispatch: # Trigger on manual workflow_dispatch + push: + branches: + - master # Trigger on pushes to master + paths: + - 'examples/hackernews/**' # Trigger only if files in this directory change + - "framework-ui/**" + - "cli/**" + +jobs: + build-and-push: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2 + + - name: Log in to GitHub Container Registry + uses: docker/login-action@v2 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Get short commit hash + id: vars + run: echo "::set-output name=short_sha::$(echo $GITHUB_SHA | cut -c1-7)" + + - name: Build Docker image + run: | + cd ./examples/hackernews && docker build -t ghcr.io/${{ github.repository_owner }}/hackernews:${{ steps.vars.outputs.short_sha }} . + + - name: Tag as latest Docker image + run: | + docker tag ghcr.io/${{ github.repository_owner }}/hackernews:${{ steps.vars.outputs.short_sha }} ghcr.io/${{ github.repository_owner }}/hackernews:latest + + - name: Log in to GitHub Container Registry + run: echo "${{ secrets.CR_PAT }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin + + - name: Push Docker image + run: | + docker push ghcr.io/${{ github.repository_owner }}/hackernews:latest diff --git a/.github/workflows/release-site.yml b/.github/workflows/release-site.yml index 83b9c46..f3ab2ab 100644 --- a/.github/workflows/release-site.yml +++ b/.github/workflows/release-site.yml @@ -1,13 +1,16 @@ name: Build and Deploy htmgo.dev on: + workflow_run: + workflows: [ "Update HTMGO Framework Dependency" ] # The name of the first workflow + types: + - completed workflow_dispatch: # Trigger on manual workflow_dispatch push: branches: - master # Trigger on pushes to master paths: - 'htmgo-site/**' # Trigger only if files in this directory change - - "framework/**" - "framework-ui/**" - "cli/**" @@ -46,4 +49,4 @@ jobs: - name: Push Docker image run: | - docker push ghcr.io/${{ github.repository_owner }}/htmgo-site:latest \ No newline at end of file + docker push ghcr.io/${{ github.repository_owner }}/htmgo-site:latest diff --git a/.github/workflows/release-starter-template.yml b/.github/workflows/release-starter-template.yml index da3167c..1d22898 100644 --- a/.github/workflows/release-starter-template.yml +++ b/.github/workflows/release-starter-template.yml @@ -1,6 +1,10 @@ name: Build and Deploy starter template on: + workflow_run: + workflows: [ "Update HTMGO Framework Dependency" ] # The name of the first workflow + types: + - completed workflow_dispatch: # Trigger on manual workflow_dispatch push: branches: @@ -43,4 +47,4 @@ jobs: - name: Push Docker image run: | - docker push ghcr.io/${{ github.repository_owner }}/starter-template:latest \ No newline at end of file + docker push ghcr.io/${{ github.repository_owner }}/starter-template:latest diff --git a/.github/workflows/release-todo-example.yml b/.github/workflows/release-todo-example.yml index 4fce8fb..a81c2af 100644 --- a/.github/workflows/release-todo-example.yml +++ b/.github/workflows/release-todo-example.yml @@ -1,6 +1,10 @@ name: Build and Deploy htmgo.dev todo example on: + workflow_run: + workflows: [ "Update HTMGO Framework Dependency" ] # The name of the first workflow + types: + - completed workflow_dispatch: # Trigger on manual workflow_dispatch push: branches: @@ -43,4 +47,4 @@ jobs: - name: Push Docker image run: | - docker push ghcr.io/${{ github.repository_owner }}/htmgo-todo-example:latest \ No newline at end of file + docker push ghcr.io/${{ github.repository_owner }}/htmgo-todo-example:latest diff --git a/.github/workflows/update-framework-dep.yml b/.github/workflows/update-framework-dep.yml index 1f93441..a59ff30 100644 --- a/.github/workflows/update-framework-dep.yml +++ b/.github/workflows/update-framework-dep.yml @@ -6,7 +6,8 @@ on: branches: - master # Trigger on pushes to master paths: - - 'framework/**' # Trigger only if files in this directory change + - 'framework/**' + - 'tools/html-to-htmgo/**' jobs: update-htmgo-dep: diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..7edfd10 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,128 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, religion, or sexual identity +and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +* Focusing on what is best not just for us as individuals, but for the + overall community + +Examples of unacceptable behavior include: + +* The use of sexualized language or imagery, and sexual attention or + advances of any kind +* Trolling, insulting or derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or email + address, without their explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official e-mail address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement at +maddox@htmgo.dev. +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series +of actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or +permanent ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within +the community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.0, available at +https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. + +Community Impact Guidelines were inspired by [Mozilla's code of conduct +enforcement ladder](https://github.com/mozilla/diversity). + +[homepage]: https://www.contributor-covenant.org + +For answers to common questions about this code of conduct, see the FAQ at +https://www.contributor-covenant.org/faq. Translations are available at +https://www.contributor-covenant.org/translations. diff --git a/README.md b/README.md index f8181e6..d9b1bae 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,3 @@ -> [!WARNING] -> htmgo is in alpha release. Please report any issues on GitHub. - ## **htmgo** ### build simple and scalable systems with go + htmx @@ -34,9 +31,12 @@ func IndexPage(ctx *h.RequestContext) *h.Page { 2. live reload (rebuilds css, go, ent schema, and routes upon change) 3. automatic page and partial registration based on file path 4. built in tailwindcss support, no need to configure anything by default -5. plugin architecture to include optional plugins to streamline development, such as http://entgo.io -6. custom [htmx extensions](https://github.com/maddalax/htmgo/tree/b610aefa36e648b98a13823a6f8d87566120cfcc/framework/assets/js/htmxextensions) to reduce boilerplate with common tasks +5. custom [htmx extensions](https://github.com/maddalax/htmgo/tree/b610aefa36e648b98a13823a6f8d87566120cfcc/framework/assets/js/htmxextensions) to reduce boilerplate with common tasks **get started:** View documentation on [htmgo.dev](https://htmgo.dev/docs). + +## Star History + +[![Star History Chart](https://api.star-history.com/svg?repos=maddalax/htmgo&type=Date)](https://star-history.com/#maddalax/htmgo&Date) diff --git a/cli/htmgo/go.mod b/cli/htmgo/go.mod index 6bad2bb..ba191b9 100644 --- a/cli/htmgo/go.mod +++ b/cli/htmgo/go.mod @@ -11,3 +11,5 @@ require ( golang.org/x/sys v0.25.0 golang.org/x/tools v0.25.0 ) + +require github.com/bmatcuk/doublestar/v4 v4.7.1 // indirect diff --git a/cli/htmgo/go.sum b/cli/htmgo/go.sum index b8b03a7..87e7902 100644 --- a/cli/htmgo/go.sum +++ b/cli/htmgo/go.sum @@ -1,3 +1,5 @@ +github.com/bmatcuk/doublestar/v4 v4.7.1 h1:fdDeAqgT47acgwd9bd9HxJRDmc9UAmPpc+2m0CXv75Q= +github.com/bmatcuk/doublestar/v4 v4.7.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc= github.com/dave/jennifer v1.7.1 h1:B4jJJDHelWcDhlRQxWeo0Npa/pYKBLrirAQoTN45txo= github.com/dave/jennifer v1.7.1/go.mod h1:nXbxhEmQfOZhWml3D1cDK5M1FLnMSozpbFN/m3RmGZc= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= diff --git a/cli/htmgo/htmltogo/entry.go b/cli/htmgo/htmltogo/entry.go deleted file mode 100644 index 9406939..0000000 --- a/cli/htmgo/htmltogo/entry.go +++ /dev/null @@ -1,119 +0,0 @@ -package main - -import ( - "bytes" - "fmt" - "log" - "strings" - - "github.com/dave/jennifer/jen" - "golang.org/x/net/html" -) - -func main() { - // Example HTML input - htmlData := ` -

Manage Patients

Name: Sydne

Reason for visit: arm hurts

- ` - - // Parse the HTML - doc, err := html.Parse(bytes.NewReader([]byte(htmlData))) - if err != nil { - log.Fatal(err) - } - - // Create a new Jennifer file - f := jen.NewFile("main") - - // Generate Jennifer code for the parsed HTML tree - generatedCode := processNode(doc.FirstChild) - - // Add the generated code to the file - f.Func().Id("Render").Params().Block(generatedCode...) - - // Render the generated code - var buf bytes.Buffer - err = f.Render(&buf) - if err != nil { - log.Fatal(err) - } - - //// Format the generated code - //formattedCode, err := format.Source(buf.Bytes()) - //if err != nil { - // log.Fatal(err) - //} - - // Output the formatted code - fmt.Println(string(buf.Bytes())) -} - -// Recursively process the HTML nodes and generate Jennifer code -func processNode(n *html.Node) []jen.Code { - var code []jen.Code - - // Only process element nodes - if n.Type == html.ElementNode { - // Create a dynamic method call based on the tag name - tagMethod := strings.Title(n.Data) // Capitalize the first letter of the tag - - // Add dynamic method call for the tag (e.g., h.Div(), h.Button(), etc.) - code = append(code, jen.Id("h").Dot(tagMethod).Call(mergeArgs(n)...)) - } - - return code -} - -// Merge attributes and children into a single slice for Call() -func mergeArgs(n *html.Node) []jen.Code { - // Process attributes - attrs := processAttributes(n.Attr) - - // Process children - children := processChildren(n) - - // Combine attributes and children into one slice - return append(attrs, children...) -} - -// Process child nodes of a given HTML node -func processChildren(n *html.Node) []jen.Code { - var children []jen.Code - - for c := n.FirstChild; c != nil; c = c.NextSibling { - children = append(children, processNode(c)...) - } - - return children -} - -func FormatFieldName(name string) string { - split := strings.Split(name, "_") - if strings.Contains(name, "-") { - split = strings.Split(name, "-") - } - parts := make([]string, 0) - for _, s := range split { - parts = append(parts, PascalCase(s)) - } - return strings.Join(parts, "") -} - -func PascalCase(s string) string { - if s == "" { - return s - } - // Convert the first rune (character) to uppercase and concatenate with the rest of the string - return strings.ToUpper(string(s[0])) + s[1:] -} - -// Process the attributes of an HTML node and return Jennifer code -func processAttributes(attrs []html.Attribute) []jen.Code { - var args []jen.Code - for _, attr := range attrs { - // Dynamically handle all attributes - attrMethod := FormatFieldName(attr.Key) // E.g., convert "data-role" to "DataRole" - args = append(args, jen.Id("h").Dot(attrMethod).Call(jen.Lit(attr.Val))) - } - return args -} diff --git a/cli/htmgo/internal/dirutil/dir.go b/cli/htmgo/internal/dirutil/dir.go index 6817ac9..7063e83 100644 --- a/cli/htmgo/internal/dirutil/dir.go +++ b/cli/htmgo/internal/dirutil/dir.go @@ -4,6 +4,7 @@ import ( "errors" "fmt" "github.com/maddalax/htmgo/cli/htmgo/tasks/process" + "github.com/maddalax/htmgo/framework/config" "io" "log/slog" "os" @@ -17,6 +18,10 @@ func HasFileFromRoot(file string) bool { return err == nil } +func GetConfig() *config.ProjectConfig { + return config.FromConfigFile(process.GetWorkingDir()) +} + func CreateHtmgoDir() { if !HasFileFromRoot("__htmgo") { CreateDirFromRoot("__htmgo") @@ -71,7 +76,7 @@ func MoveFile(src, dst string) error { if err != nil { return fmt.Errorf("failed to copy file: %v", err) } - // Disconnect the source file. + // Remove the source file. err = os.Remove(src) if err != nil { return fmt.Errorf("failed to remove source file: %v", err) diff --git a/cli/htmgo/internal/dirutil/glob.go b/cli/htmgo/internal/dirutil/glob.go new file mode 100644 index 0000000..1315c66 --- /dev/null +++ b/cli/htmgo/internal/dirutil/glob.go @@ -0,0 +1,31 @@ +package dirutil + +import ( + "fmt" + "github.com/bmatcuk/doublestar/v4" +) + +func matchesAny(patterns []string, path string) bool { + for _, pattern := range patterns { + matched, err := doublestar.Match(pattern, path) + if err != nil { + fmt.Printf("Error matching pattern: %v\n", err) + return false + } + if matched { + return true + } + } + return false +} + +func IsGlobExclude(path string, excludePatterns []string) bool { + return matchesAny(excludePatterns, path) +} + +func IsGlobMatch(path string, patterns []string, excludePatterns []string) bool { + if matchesAny(excludePatterns, path) { + return false + } + return matchesAny(patterns, path) +} diff --git a/cli/htmgo/runner.go b/cli/htmgo/runner.go index ab27c44..0cfd586 100644 --- a/cli/htmgo/runner.go +++ b/cli/htmgo/runner.go @@ -9,6 +9,7 @@ import ( "github.com/maddalax/htmgo/cli/htmgo/tasks/copyassets" "github.com/maddalax/htmgo/cli/htmgo/tasks/css" "github.com/maddalax/htmgo/cli/htmgo/tasks/downloadtemplate" + "github.com/maddalax/htmgo/cli/htmgo/tasks/formatter" "github.com/maddalax/htmgo/cli/htmgo/tasks/process" "github.com/maddalax/htmgo/cli/htmgo/tasks/reloader" "github.com/maddalax/htmgo/cli/htmgo/tasks/run" @@ -19,10 +20,10 @@ import ( ) func main() { - done := RegisterSignals() + needsSignals := true commandMap := make(map[string]*flag.FlagSet) - commands := []string{"template", "run", "watch", "build", "setup", "css", "schema", "generate"} + commands := []string{"template", "run", "watch", "build", "setup", "css", "schema", "generate", "format"} for _, command := range commands { commandMap[command] = flag.NewFlagSet(command, flag.ExitOnError) @@ -56,6 +57,15 @@ func main() { slog.Debug("Running task:", slog.String("task", taskName)) slog.Debug("working dir:", slog.String("dir", process.GetWorkingDir())) + if taskName == "format" { + needsSignals = false + } + + done := make(chan bool, 1) + if needsSignals { + done = RegisterSignals() + } + if taskName == "watch" { fmt.Printf("Running in watch mode\n") os.Setenv("ENV", "development") @@ -90,7 +100,18 @@ func main() { }() startWatcher(reloader.OnFileChange) } else { - if taskName == "schema" { + if taskName == "format" { + if len(os.Args) < 3 { + fmt.Println(fmt.Sprintf("Usage: htmgo format ")) + os.Exit(1) + } + file := os.Args[2] + if file == "." { + formatter.FormatDir(process.GetWorkingDir()) + } else { + formatter.FormatFile(os.Args[2]) + } + } else if taskName == "schema" { reader := bufio.NewReader(os.Stdin) fmt.Print("Enter entity name:") text, _ := reader.ReadString('\n') @@ -106,8 +127,7 @@ func main() { } else if taskName == "ast" { _ = astgen.GenAst(process.ExitOnError) } else if taskName == "run" { - _ = astgen.GenAst(process.ExitOnError) - _ = css.GenerateCss(process.ExitOnError) + run.MakeBuildable() _ = run.Server(process.ExitOnError) } else if taskName == "template" { name := "" diff --git a/cli/htmgo/tasks/astgen/entry.go b/cli/htmgo/tasks/astgen/entry.go index 7b4d13b..8472c03 100644 --- a/cli/htmgo/tasks/astgen/entry.go +++ b/cli/htmgo/tasks/astgen/entry.go @@ -2,7 +2,9 @@ package astgen import ( "fmt" + "github.com/maddalax/htmgo/cli/htmgo/internal/dirutil" "github.com/maddalax/htmgo/cli/htmgo/tasks/process" + "github.com/maddalax/htmgo/framework/h" "go/ast" "go/parser" "go/token" @@ -24,6 +26,7 @@ type Partial struct { FuncName string Package string Import string + Path string } const GeneratedDirName = "__htmgo" @@ -53,7 +56,7 @@ func sliceCommonPrefix(dir1, dir2 string) string { slicedDir1 := strings.TrimPrefix(dir1, commonPrefix) slicedDir2 := strings.TrimPrefix(dir2, commonPrefix) - // Disconnect leading slashes + // Remove leading slashes slicedDir1 = strings.TrimPrefix(slicedDir1, string(filepath.Separator)) slicedDir2 = strings.TrimPrefix(slicedDir2, string(filepath.Separator)) @@ -103,6 +106,7 @@ func findPublicFuncsReturningHPartial(dir string, predicate func(partial Partial if selectorExpr.Sel.Name == "Partial" { p := Partial{ Package: node.Name.Name, + Path: sliceCommonPrefix(cwd, path), Import: sliceCommonPrefix(cwd, strings.ReplaceAll(filepath.Dir(path), `\`, `/`)), FuncName: funcDecl.Name.Name, } @@ -254,12 +258,18 @@ func buildGetPartialFromContext(builder *CodeBuilder, partials []Partial) { } func writePartialsFile() { + config := dirutil.GetConfig() + cwd := process.GetWorkingDir() partialPath := filepath.Join(cwd, "partials") partials, err := findPublicFuncsReturningHPartial(partialPath, func(partial Partial) bool { return partial.FuncName != "GetPartialFromContext" }) + partials = h.Filter(partials, func(partial Partial) bool { + return !dirutil.IsGlobExclude(partial.Path, config.AutomaticPartialRoutingIgnore) + }) + if err != nil { fmt.Println(err) return @@ -317,6 +327,7 @@ func formatRoute(path string) string { } func writePagesFile() { + config := dirutil.GetConfig() builder := NewCodeBuilder(nil) builder.AppendLine(GeneratedFileLine) @@ -326,6 +337,10 @@ func writePagesFile() { pages, _ := findPublicFuncsReturningHPage("pages") + pages = h.Filter(pages, func(page Page) bool { + return !dirutil.IsGlobExclude(page.Path, config.AutomaticPageRoutingIgnore) + }) + if len(pages) > 0 { builder.AddImport(ModuleName) } diff --git a/cli/htmgo/tasks/astgen/map.go b/cli/htmgo/tasks/astgen/map.go index 73eb3e4..201ccea 100644 --- a/cli/htmgo/tasks/astgen/map.go +++ b/cli/htmgo/tasks/astgen/map.go @@ -68,10 +68,10 @@ func (om *OrderedMap[K, V]) Values() []V { // Delete removes a key-value pair from the OrderedMap. func (om *OrderedMap[K, V]) Delete(key K) { if _, exists := om.values[key]; exists { - // Disconnect the key from the map + // Remove the key from the map delete(om.values, key) - // Disconnect the key from the keys slice + // Remove the key from the keys slice for i, k := range om.keys { if k == key { om.keys = append(om.keys[:i], om.keys[i+1:]...) diff --git a/cli/htmgo/tasks/copyassets/bundle.go b/cli/htmgo/tasks/copyassets/bundle.go index a24da76..f0f7699 100644 --- a/cli/htmgo/tasks/copyassets/bundle.go +++ b/cli/htmgo/tasks/copyassets/bundle.go @@ -92,7 +92,7 @@ func CopyAssets() { }) } - if !dirutil.HasFileFromRoot("tailwind.config.js") { + if dirutil.GetConfig().Tailwind && !dirutil.HasFileFromRoot("tailwind.config.js") { err = dirutil.CopyFile( filepath.Join(assetCssDir, "tailwind.config.js"), filepath.Join(process.GetWorkingDir(), "tailwind.config.js"), diff --git a/cli/htmgo/tasks/css/css.go b/cli/htmgo/tasks/css/css.go index 0bdaf58..1c07ec2 100644 --- a/cli/htmgo/tasks/css/css.go +++ b/cli/htmgo/tasks/css/css.go @@ -12,7 +12,7 @@ import ( ) func IsTailwindEnabled() bool { - return dirutil.HasFileFromRoot("tailwind.config.js") + return dirutil.GetConfig().Tailwind && dirutil.HasFileFromRoot("tailwind.config.js") } func Setup() bool { diff --git a/cli/htmgo/tasks/formatter/formatter.go b/cli/htmgo/tasks/formatter/formatter.go new file mode 100644 index 0000000..61a3205 --- /dev/null +++ b/cli/htmgo/tasks/formatter/formatter.go @@ -0,0 +1,50 @@ +package formatter + +import ( + "fmt" + "github.com/maddalax/htmgo/tools/html-to-htmgo/htmltogo" + "os" + "path/filepath" + "strings" +) + +func FormatDir(dir string) { + files, err := os.ReadDir(dir) + if err != nil { + fmt.Printf("error reading dir: %s\n", err.Error()) + return + } + for _, file := range files { + if file.IsDir() { + FormatDir(filepath.Join(dir, file.Name())) + } else { + FormatFile(filepath.Join(dir, file.Name())) + } + } +} + +func FormatFile(file string) { + if !strings.HasSuffix(file, ".go") { + return + } + + fmt.Printf("formatting file: %s\n", file) + + source, err := os.ReadFile(file) + if err != nil { + fmt.Printf("error reading file: %s\n", err.Error()) + return + } + + str := string(source) + + if !strings.Contains(str, "github.com/maddalax/htmgo/framework/h") { + return + } + + parsed := htmltogo.Indent(str) + + os.WriteFile(file, []byte(parsed), 0644) + + return +} diff --git a/cli/htmgo/tasks/process/process.go b/cli/htmgo/tasks/process/process.go index a31cd19..f3c1c41 100644 --- a/cli/htmgo/tasks/process/process.go +++ b/cli/htmgo/tasks/process/process.go @@ -115,7 +115,7 @@ func OnShutdown() { } } // give it a second - time.Sleep(time.Second * 2) + time.Sleep(time.Second * 1) // force kill KillAll() } diff --git a/cli/htmgo/tasks/reloader/reloader.go b/cli/htmgo/tasks/reloader/reloader.go index 48fefc4..7133b7a 100644 --- a/cli/htmgo/tasks/reloader/reloader.go +++ b/cli/htmgo/tasks/reloader/reloader.go @@ -108,6 +108,11 @@ func OnFileChange(version string, events []*fsnotify.Event) { //tasks.Run = true } + // something in public folder changed + if c.HasAnyPrefix("assets/public/") { + copyassets.CopyAssets() + } + if hasTask { slog.Info("file changed", slog.String("version", version), slog.String("file", c.Name())) } diff --git a/cli/htmgo/tasks/run/build.go b/cli/htmgo/tasks/run/build.go index e468072..724e885 100644 --- a/cli/htmgo/tasks/run/build.go +++ b/cli/htmgo/tasks/run/build.go @@ -9,10 +9,14 @@ import ( "os" ) -func Build() { +func MakeBuildable() { copyassets.CopyAssets() astgen.GenAst(process.ExitOnError) css.GenerateCss(process.ExitOnError) +} + +func Build() { + MakeBuildable() process.RunOrExit(process.NewRawCommand("", "mkdir -p ./dist")) diff --git a/cli/htmgo/tasks/run/setup.go b/cli/htmgo/tasks/run/setup.go index d48d42d..0dd27d6 100644 --- a/cli/htmgo/tasks/run/setup.go +++ b/cli/htmgo/tasks/run/setup.go @@ -1,19 +1,12 @@ package run import ( - "github.com/maddalax/htmgo/cli/htmgo/tasks/astgen" - "github.com/maddalax/htmgo/cli/htmgo/tasks/copyassets" - "github.com/maddalax/htmgo/cli/htmgo/tasks/css" "github.com/maddalax/htmgo/cli/htmgo/tasks/process" ) func Setup() { process.RunOrExit(process.NewRawCommand("", "go mod download")) process.RunOrExit(process.NewRawCommand("", "go mod tidy")) - - copyassets.CopyAssets() - astgen.GenAst(process.ExitOnError) - css.GenerateCss(process.ExitOnError) - + MakeBuildable() EntGenerate() } diff --git a/cli/htmgo/tasks/util/file.go b/cli/htmgo/tasks/util/file.go index 3cb9f70..5ddd06c 100644 --- a/cli/htmgo/tasks/util/file.go +++ b/cli/htmgo/tasks/util/file.go @@ -19,7 +19,7 @@ func ReplaceTextInFile(file string, text string, replacement string) error { func ReplaceTextInDirRecursive(dir string, text string, replacement string, filter func(file string) bool) error { return filepath.WalkDir(dir, func(path string, d fs.DirEntry, err error) error { - if filter(path) { + if filter(filepath.Base(path)) { _ = ReplaceTextInFile(path, text, replacement) } return nil diff --git a/cli/htmgo/watcher.go b/cli/htmgo/watcher.go index f2a0ee8..4838d14 100644 --- a/cli/htmgo/watcher.go +++ b/cli/htmgo/watcher.go @@ -4,6 +4,7 @@ import ( "github.com/fsnotify/fsnotify" "github.com/google/uuid" "github.com/maddalax/htmgo/cli/htmgo/internal" + "github.com/maddalax/htmgo/cli/htmgo/internal/dirutil" "github.com/maddalax/htmgo/cli/htmgo/tasks/module" "log" "log/slog" @@ -13,11 +14,10 @@ import ( "time" ) -var ignoredDirs = []string{".git", ".idea", "node_modules", "vendor"} - func startWatcher(cb func(version string, file []*fsnotify.Event)) { events := make([]*fsnotify.Event, 0) debouncer := internal.NewDebouncer(500 * time.Millisecond) + config := dirutil.GetConfig() defer func() { if r := recover(); r != nil { @@ -38,8 +38,38 @@ func startWatcher(cb func(version string, file []*fsnotify.Event)) { if !ok { return } - slog.Debug("event:", slog.String("name", event.Name), slog.String("op", event.Op.String())) + + if event.Has(fsnotify.Remove) { + if dirutil.IsGlobMatch(event.Name, config.WatchFiles, config.WatchIgnore) { + watcher.Remove(event.Name) + continue + } + } + + if event.Has(fsnotify.Create) { + if dirutil.IsGlobMatch(event.Name, config.WatchFiles, config.WatchIgnore) { + watcher.Add(event.Name) + continue + } + info, err := os.Stat(event.Name) + if err != nil { + slog.Error("Error getting file info:", slog.String("path", event.Name), slog.String("error", err.Error())) + continue + } + if info.IsDir() { + err = watcher.Add(event.Name) + if err != nil { + slog.Error("Error adding directory to watcher:", slog.String("path", event.Name), slog.String("error", err.Error())) + } else { + slog.Debug("Watching directory:", slog.String("path", event.Name)) + } + } + } + if event.Has(fsnotify.Write) || event.Has(fsnotify.Remove) || event.Has(fsnotify.Rename) { + if !dirutil.IsGlobMatch(event.Name, config.WatchFiles, config.WatchIgnore) { + continue + } events = append(events, &event) debouncer.Do(func() { seen := make(map[string]bool) @@ -54,6 +84,7 @@ func startWatcher(cb func(version string, file []*fsnotify.Event)) { events = make([]*fsnotify.Event, 0) }) } + case err, ok := <-watcher.Errors: if !ok { return @@ -79,11 +110,10 @@ func startWatcher(cb func(version string, file []*fsnotify.Event)) { return err } // Ignore directories in the ignoredDirs list - for _, ignoredDir := range ignoredDirs { - if ignoredDir == info.Name() { - return filepath.SkipDir - } + if dirutil.IsGlobExclude(path, config.WatchIgnore) { + return filepath.SkipDir } + // Only watch directories if info.IsDir() { err = watcher.Add(path) @@ -95,6 +125,7 @@ func startWatcher(cb func(version string, file []*fsnotify.Event)) { } return nil }) + if err != nil { log.Fatal(err) } diff --git a/examples/chat/Dockerfile b/examples/chat/Dockerfile index 5f9bc42..eae23dd 100644 --- a/examples/chat/Dockerfile +++ b/examples/chat/Dockerfile @@ -14,7 +14,7 @@ RUN go mod download COPY . . # Build the Go binary for Linux -RUN CGO_ENABLED=0 GOPRIVATE=github.com/maddalax LOG_LEVEL=debug go run github.com/maddalax/htmgo/cli/htmgo@8b816e956692683337d9fff6416ccc31f5047b59 build +RUN CGO_ENABLED=0 GOPRIVATE=github.com/maddalax LOG_LEVEL=debug go run github.com/maddalax/htmgo/cli/htmgo@latest build RUN CGO_ENABLED=1 GOOS=linux go build -tags prod -o ./dist -a -ldflags '-linkmode external -extldflags "-static"' . diff --git a/examples/chat/chat/broadcast.go b/examples/chat/chat/broadcast.go index 312eb7b..394c1cd 100644 --- a/examples/chat/chat/broadcast.go +++ b/examples/chat/chat/broadcast.go @@ -2,7 +2,7 @@ package chat import ( "chat/internal/db" - "chat/ws" + "chat/sse" "context" "fmt" "github.com/maddalax/htmgo/framework/h" @@ -11,79 +11,86 @@ import ( ) type Manager struct { - socketManager *ws.SocketManager + socketManager *sse.SocketManager queries *db.Queries service *Service } func NewManager(locator *service.Locator) *Manager { return &Manager{ - socketManager: service.Get[ws.SocketManager](locator), + socketManager: service.Get[sse.SocketManager](locator), queries: service.Get[db.Queries](locator), service: NewService(locator), } } func (m *Manager) StartListener() { - c := make(chan ws.SocketEvent) + c := make(chan sse.SocketEvent, 1) m.socketManager.Listen(c) for { select { case event := <-c: switch event.Type { - case ws.ConnectedEvent: + case sse.ConnectedEvent: m.OnConnected(event) - case ws.DisconnectedEvent: + case sse.DisconnectedEvent: m.OnDisconnected(event) - case ws.MessageEvent: + case sse.MessageEvent: m.onMessage(event) + default: + fmt.Printf("Unknown event type: %s\n", event.Type) } } } } -func (m *Manager) OnConnected(e ws.SocketEvent) { +func (m *Manager) dispatchConnectedUsers(roomId string, predicate func(conn sse.SocketConnection) bool) { + + connectedUsers := make([]db.User, 0) + + // backfill all existing clients to the connected client + m.socketManager.ForEachSocket(roomId, func(conn sse.SocketConnection) { + if !predicate(conn) { + return + } + user, err := m.queries.GetUserBySessionId(context.Background(), conn.Id) + if err != nil { + return + } + connectedUsers = append(connectedUsers, user) + }) + + m.socketManager.ForEachSocket(roomId, func(conn sse.SocketConnection) { + m.socketManager.SendText(conn.Id, h.Render(ConnectedUsers(connectedUsers, conn.Id))) + }) +} + +func (m *Manager) OnConnected(e sse.SocketEvent) { room, _ := m.service.GetRoom(e.RoomId) if room == nil { - m.socketManager.CloseWithError(e.Id, 1008, "invalid room") + m.socketManager.CloseWithMessage(e.Id, "invalid room") return } user, err := m.queries.GetUserBySessionId(context.Background(), e.Id) if err != nil { - m.socketManager.CloseWithError(e.Id, 1008, "invalid user") + m.socketManager.CloseWithMessage(e.Id, "invalid user") return } fmt.Printf("User %s connected to %s\n", user.Name, e.RoomId) - // backfill all existing clients to the connected client - m.socketManager.ForEachSocket(e.RoomId, func(conn ws.SocketConnection) { - user, err := m.queries.GetUserBySessionId(context.Background(), conn.Id) - if err != nil { - return - } - isMe := conn.Id == e.Id - fmt.Printf("Sending connected user %s to %s\n", user.Name, e.Id) - m.socketManager.SendText(e.Id, h.Render(ConnectedUsers(user.Name, isMe))) + m.dispatchConnectedUsers(e.RoomId, func(conn sse.SocketConnection) bool { + return true }) - // send the connected user to all existing clients - m.socketManager.BroadcastText( - e.RoomId, - h.Render(ConnectedUsers(user.Name, false)), - func(conn ws.SocketConnection) bool { - return conn.Id != e.Id - }, - ) - - go m.backFill(e.Id, e.RoomId) + m.backFill(e.Id, e.RoomId) } -func (m *Manager) OnDisconnected(e ws.SocketEvent) { +func (m *Manager) OnDisconnected(e sse.SocketEvent) { user, err := m.queries.GetUserBySessionId(context.Background(), e.Id) if err != nil { return @@ -93,7 +100,7 @@ func (m *Manager) OnDisconnected(e ws.SocketEvent) { return } fmt.Printf("User %s disconnected from %s\n", user.Name, room.ID) - m.socketManager.BroadcastText(room.ID, h.Render(ConnectedUser(user.Name, true, false)), func(conn ws.SocketConnection) bool { + m.dispatchConnectedUsers(e.RoomId, func(conn sse.SocketConnection) bool { return conn.Id != e.Id }) } @@ -116,7 +123,7 @@ func (m *Manager) backFill(socketId string, roomId string) { } } -func (m *Manager) onMessage(e ws.SocketEvent) { +func (m *Manager) onMessage(e sse.SocketEvent) { message := e.Payload["message"].(string) if message == "" { @@ -140,7 +147,7 @@ func (m *Manager) onMessage(e ws.SocketEvent) { m.socketManager.BroadcastText( e.RoomId, h.Render(MessageRow(saved)), - func(conn ws.SocketConnection) bool { + func(conn sse.SocketConnection) bool { return true }, ) diff --git a/examples/chat/chat/component.go b/examples/chat/chat/component.go index bf7c18d..0840fe3 100644 --- a/examples/chat/chat/component.go +++ b/examples/chat/chat/component.go @@ -1,6 +1,7 @@ package chat import ( + "chat/internal/db" "fmt" "github.com/maddalax/htmgo/framework/h" "strings" @@ -10,39 +11,43 @@ import ( func MessageRow(message *Message) *h.Element { return h.Div( h.Attribute("hx-swap-oob", "beforeend"), - h.Class("flex flex-col gap-4 w-full break-words whitespace-normal"), // Ensure container breaks long words + h.Class("flex flex-col gap-4 w-full break-words whitespace-normal"), + // Ensure container breaks long words h.Id("messages"), h.Div( h.Class("flex flex-col gap-1"), h.Div( h.Class("flex gap-2 items-center"), - h.Pf(message.UserName, h.Class("font-bold")), + h.Pf( + message.UserName, + h.Class("font-bold"), + ), h.Pf(message.CreatedAt.In(time.Local).Format("01/02 03:04 PM")), ), h.Article( - h.Class("break-words whitespace-normal"), // Ensure message text wraps correctly - h.P(h.Text(message.Message)), + h.Class("break-words whitespace-normal"), + // Ensure message text wraps correctly + h.P( + h.Text(message.Message), + ), ), ), ) } -func ConnectedUsers(username string, isMe bool) *h.Element { +func ConnectedUsers(users []db.User, myId string) *h.Element { return h.Ul( - h.Attribute("hx-swap", "none"), - h.Attribute("hx-swap-oob", "beforeend"), + h.Attribute("hx-swap-oob", "outerHTML"), h.Id("connected-users"), h.Class("flex flex-col"), - // This would be populated dynamically with connected users - ConnectedUser(username, false, isMe), + h.List(users, func(user db.User, index int) *h.Element { + return connectedUser(user.Name, user.SessionID == myId) + }), ) } -func ConnectedUser(username string, remove bool, isMe bool) *h.Element { +func connectedUser(username string, isMe bool) *h.Element { id := fmt.Sprintf("connected-user-%s", strings.ReplaceAll(username, "#", "-")) - if remove { - return h.Div(h.Id(id), h.Attribute("hx-swap-oob", "delete")) - } return h.Li( h.Id(id), h.ClassX("truncate text-slate-700", h.ClassMap{ diff --git a/examples/chat/components/button.go b/examples/chat/components/button.go index bc767c8..3f9c8c1 100644 --- a/examples/chat/components/button.go +++ b/examples/chat/components/button.go @@ -28,12 +28,28 @@ func Button(props ButtonProps) h.Ren { text := h.Text(props.Text) button := h.Button( - h.If(props.Id != "", h.Id(props.Id)), - h.If(props.Children != nil, h.Children(props.Children...)), + h.If( + props.Id != "", + h.Id(props.Id), + ), + h.If( + props.Children != nil, + h.Children(props.Children...), + ), h.Class("flex gap-1 items-center justify-center border p-4 rounded cursor-hover", props.Class), - h.If(props.Get != "", h.Get(props.Get)), - h.If(props.Target != "", h.HxTarget(props.Target)), - h.IfElse(props.Type != "", h.Type(props.Type), h.Type("button")), + h.If( + props.Get != "", + h.Get(props.Get), + ), + h.If( + props.Target != "", + h.HxTarget(props.Target), + ), + h.IfElse( + props.Type != "", + h.Type(props.Type), + h.Type("button"), + ), text, ) diff --git a/examples/chat/components/error.go b/examples/chat/components/error.go index a20ba0d..4b147dd 100644 --- a/examples/chat/components/error.go +++ b/examples/chat/components/error.go @@ -6,6 +6,9 @@ func FormError(error string) *h.Element { return h.Div( h.Id("form-error"), h.Text(error), - h.If(error != "", h.Class("p-4 bg-rose-400 text-white rounded")), + h.If( + error != "", + h.Class("p-4 bg-rose-400 text-white rounded"), + ), ) } diff --git a/examples/chat/components/input.go b/examples/chat/components/input.go index fec1363..0013d4e 100644 --- a/examples/chat/components/input.go +++ b/examples/chat/components/input.go @@ -19,11 +19,14 @@ type InputProps struct { } func Input(props InputProps) *h.Element { - validation := h.If(props.ValidationPath != "", h.Children( - h.Post(props.ValidationPath, hx.BlurEvent), - h.Attribute("hx-swap", "innerHTML transition:true"), - h.Attribute("hx-target", "next div"), - )) + validation := h.If( + props.ValidationPath != "", + h.Children( + h.Post(props.ValidationPath, hx.BlurEvent), + h.Attribute("hx-swap", "innerHTML transition:true"), + h.Attribute("hx-target", "next div"), + ), + ) if props.Type == "" { props.Type = "text" @@ -32,18 +35,41 @@ func Input(props InputProps) *h.Element { input := h.Input( props.Type, h.Class("border p-2 rounded focus:outline-none focus:ring focus:ring-slate-800"), - h.If(props.Name != "", h.Name(props.Name)), - h.If(props.Children != nil, h.Children(props.Children...)), - h.If(props.Required, h.Required()), - h.If(props.Placeholder != "", h.Placeholder(props.Placeholder)), - h.If(props.DefaultValue != "", h.Attribute("value", props.DefaultValue)), + h.If( + props.Name != "", + h.Name(props.Name), + ), + h.If( + props.Children != nil, + h.Children(props.Children...), + ), + h.If( + props.Required, + h.Required(), + ), + h.If( + props.Placeholder != "", + h.Placeholder(props.Placeholder), + ), + h.If( + props.DefaultValue != "", + h.Attribute("value", props.DefaultValue), + ), validation, ) wrapped := h.Div( - h.If(props.Id != "", h.Id(props.Id)), + h.If( + props.Id != "", + h.Id(props.Id), + ), h.Class("flex flex-col gap-1"), - h.If(props.Label != "", h.Label(h.Text(props.Label))), + h.If( + props.Label != "", + h.Label( + h.Text(props.Label), + ), + ), input, h.Div( h.Id(props.Id+"-error"), diff --git a/examples/chat/go.mod b/examples/chat/go.mod index 15fbf0d..10cece9 100644 --- a/examples/chat/go.mod +++ b/examples/chat/go.mod @@ -3,10 +3,9 @@ module chat go 1.23.0 require ( - github.com/coder/websocket v1.8.12 github.com/go-chi/chi/v5 v5.1.0 github.com/google/uuid v1.6.0 - github.com/maddalax/htmgo/framework v0.0.0-20241002032603-8b816e956692 + github.com/maddalax/htmgo/framework v1.0.2-0.20241025174132-df3edccd7fb0 github.com/mattn/go-sqlite3 v1.14.23 github.com/puzpuzpuz/xsync/v3 v3.4.0 ) diff --git a/examples/chat/go.sum b/examples/chat/go.sum index c29b6fa..e84281e 100644 --- a/examples/chat/go.sum +++ b/examples/chat/go.sum @@ -1,15 +1,11 @@ -github.com/coder/websocket v1.8.12 h1:5bUXkEPPIbewrnkU8LTCLVaxi4N4J8ahufH2vlo4NAo= -github.com/coder/websocket v1.8.12/go.mod h1:LNVeNrXQZfe5qhS9ALED3uA+l5pPqvwXg3CKoDBB2gs= 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/go-chi/chi/v5 v5.1.0 h1:acVI1TYaD+hhedDJ3r54HyA6sExp3HfXq7QWEEY/xMw= github.com/go-chi/chi/v5 v5.1.0/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/maddalax/htmgo/framework v0.0.0-20241001184532-9a5b92987701 h1:0Zk282axc1kPiuspLNzK5BJV7cQ5h2kPZHe54dznhYY= -github.com/maddalax/htmgo/framework v0.0.0-20241001184532-9a5b92987701/go.mod h1:HYKI49Pb6oyY2opSJdTt145B1vWgfWIDohvlolynv80= -github.com/maddalax/htmgo/framework v0.0.0-20241002032603-8b816e956692 h1:NtLJ7GcD9hWvPYmombxC1SzVNgvnhLXWhZEQJZOstik= -github.com/maddalax/htmgo/framework v0.0.0-20241002032603-8b816e956692/go.mod h1:HYKI49Pb6oyY2opSJdTt145B1vWgfWIDohvlolynv80= +github.com/maddalax/htmgo/framework v1.0.2-0.20241025174132-df3edccd7fb0 h1:K9Q5b7BmbpCPJFjrAHS8+wPdKDcZN9NMC3Fg51n5IaQ= +github.com/maddalax/htmgo/framework v1.0.2-0.20241025174132-df3edccd7fb0/go.mod h1:NGGzWVXWksrQJ9kV9SGa/A1F1Bjsgc08cN7ZVb98RqY= github.com/mattn/go-sqlite3 v1.14.23 h1:gbShiuAP1W5j9UOksQ06aiiqPMxYecovVGwmTxWtuw0= github.com/mattn/go-sqlite3 v1.14.23/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= diff --git a/examples/chat/internal/routine/goroutine.go b/examples/chat/internal/routine/goroutine.go new file mode 100644 index 0000000..144f68a --- /dev/null +++ b/examples/chat/internal/routine/goroutine.go @@ -0,0 +1,25 @@ +package routine + +import ( + "fmt" + "time" +) + +func DebugLongRunning(name string, f func()) { + now := time.Now() + done := make(chan struct{}, 1) + go func() { + ticker := time.NewTicker(time.Second * 5) + for { + select { + case <-done: + return + case <-ticker.C: + elapsed := time.Since(now).Milliseconds() + fmt.Printf("function %s has not finished after %dms\n", name, elapsed) + } + } + }() + f() + done <- struct{}{} +} diff --git a/examples/chat/main.go b/examples/chat/main.go index 4cdff39..e5d92cb 100644 --- a/examples/chat/main.go +++ b/examples/chat/main.go @@ -4,24 +4,35 @@ import ( "chat/__htmgo" "chat/chat" "chat/internal/db" - "chat/ws" + "chat/sse" + "fmt" "github.com/maddalax/htmgo/framework/h" "github.com/maddalax/htmgo/framework/service" "io/fs" "net/http" + "runtime" + "time" ) func main() { locator := service.NewLocator() service.Set[db.Queries](locator, service.Singleton, db.Provide) - service.Set[ws.SocketManager](locator, service.Singleton, func() *ws.SocketManager { - return ws.NewSocketManager() + service.Set[sse.SocketManager](locator, service.Singleton, func() *sse.SocketManager { + return sse.NewSocketManager() }) chatManager := chat.NewManager(locator) go chatManager.StartListener() + go func() { + for { + count := runtime.NumGoroutine() + fmt.Printf("goroutines: %d\n", count) + time.Sleep(10 * time.Second) + } + }() + h.Start(h.AppOpts{ ServiceLocator: locator, LiveReload: true, @@ -35,7 +46,7 @@ func main() { http.FileServerFS(sub) app.Router.Handle("/public/*", http.StripPrefix("/public", http.FileServerFS(sub))) - app.Router.Handle("/ws/chat/{id}", ws.Handle()) + app.Router.Handle("/sse/chat/{id}", sse.Handle()) __htmgo.Register(app.Router) }, diff --git a/examples/chat/pages/chat.$id.go b/examples/chat/pages/chat.$id.go index 4191e16..deb676d 100644 --- a/examples/chat/pages/chat.$id.go +++ b/examples/chat/pages/chat.$id.go @@ -2,6 +2,7 @@ package pages import ( "chat/chat" + "chat/internal/db" "chat/partials" "fmt" "github.com/go-chi/chi/v5" @@ -15,62 +16,48 @@ func ChatRoom(ctx *h.RequestContext) *h.Page { return h.NewPage( RootPage( h.Div( - h.JoinExtensions( - h.TriggerChildren(), - h.HxExtension("ws"), - ), - - h.Attribute("sse-connect", fmt.Sprintf("/ws/chat/%s", roomId)), - + h.TriggerChildren(), + h.Attribute("sse-connect", fmt.Sprintf("/sse/chat/%s", roomId)), h.HxOnSseOpen( js.ConsoleLog("Connected to chat room"), ), - - h.HxOnSseClose( + h.HxOnSseError( js.EvalJs(fmt.Sprintf(` - const reason = e.detail.event.reason + const reason = e.detail.event.data if(['invalid room', 'no session', 'invalid user'].includes(reason)) { window.location.href = '/?roomId=%s'; } else if(e.detail.event.code === 1011) { window.location.reload() } else if (e.detail.event.code === 1008 || e.detail.event.code === 1006) { - window.location.href = '/?roomId=%s'; + window.location.href = '/?roomId=%s'; } else { console.error('Connection closed:', e.detail.event) } `, roomId, roomId)), ), - - h.Class("flex flex-row min-h-screen bg-neutral-100"), - + // Adjusted flex properties for responsive layout + h.Class("flex flex-row h-screen bg-neutral-100 overflow-x-hidden"), + // Collapse Button for mobile + CollapseButton(), // Sidebar for connected users UserSidebar(), - h.Div( - h.Class("flex flex-col flex-grow bg-white rounded p-4"), - + // Adjusted to fill height and width + h.Class("flex flex-col h-full w-full bg-white p-4 overflow-hidden"), // Room name at the top, fixed CachedRoomHeader(ctx), - - // Padding to push chat content below the fixed room name - h.Div(h.Class("pt-[50px]")), - h.HxAfterSseMessage( js.EvalJsOnSibling("#messages", `element.scrollTop = element.scrollHeight;`), ), - // Chat Messages h.Div( h.Id("messages"), - h.Class("flex flex-col gap-4 overflow-auto grow w-full mb-4 max-w-[calc(100%-215px)]"), + // Adjusted flex properties and removed max-width + h.Class("flex flex-col gap-4 mb-4 overflow-auto flex-grow w-full pt-[50px]"), ), - // Chat Input at the bottom - h.Div( - h.Class("mt-auto"), - Form(), - ), + Form(), ), ), ), @@ -93,7 +80,10 @@ func roomNameHeader(ctx *h.RequestContext) *h.Element { } return h.Div( h.Class("bg-neutral-700 text-white p-3 shadow-sm w-full fixed top-0 left-0 flex justify-center z-10"), - h.H2F(room.Name, h.Class("text-lg font-bold")), + h.H2F( + room.Name, + h.Class("text-lg font-bold"), + ), h.Div( h.Class("absolute right-5 top-3 cursor-pointer"), h.Text("Share"), @@ -108,10 +98,13 @@ func roomNameHeader(ctx *h.RequestContext) *h.Element { func UserSidebar() *h.Element { return h.Div( - h.Class("pt-[67px] min-w-48 w-48 bg-neutral-200 p-4 flex flex-col justify-between gap-3 rounded-l-lg"), + h.Class("sidebar h-full pt-[67px] min-w-48 w-48 bg-neutral-200 p-4 flex-col justify-between gap-3 rounded-l-lg hidden md:flex"), h.Div( - h.H3F("Connected Users", h.Class("text-lg font-bold")), - chat.ConnectedUsers("", false), + h.H3F( + "Connected Users", + h.Class("text-lg font-bold"), + ), + chat.ConnectedUsers(make([]db.User, 0), ""), ), h.A( h.Class("cursor-pointer"), @@ -121,8 +114,30 @@ func UserSidebar() *h.Element { ) } +func CollapseButton() *h.Element { + return h.Div( + h.Class("fixed top-0 left-4 md:hidden z-50"), + // Always visible on mobile + h.Button( + h.Class("p-2 text-2xl bg-neutral-700 text-white rounded-md"), + // Styling the button + h.OnClick( + js.EvalJs(` + const sidebar = document.querySelector('.sidebar'); + sidebar.classList.toggle('hidden'); + sidebar.classList.toggle('flex'); + `), + ), + h.UnsafeRaw("☰"), + + // The icon for collapsing the sidebar + ), + ) +} + func MessageInput() *h.Element { - return h.Input("text", + return h.Input( + "text", h.Id("message-input"), h.Required(), h.Class("p-4 rounded-md border border-slate-200 w-full focus:outline-none focus:ring focus:ring-slate-200"), diff --git a/examples/chat/pages/index.go b/examples/chat/pages/index.go index 571d5b5..229c7d9 100644 --- a/examples/chat/pages/index.go +++ b/examples/chat/pages/index.go @@ -13,12 +13,14 @@ func ChatAppFirstScreen(ctx *h.RequestContext) *h.Page { h.Class("flex flex-col items-center justify-center min-h-screen bg-neutral-100"), h.Div( h.Class("bg-white p-8 rounded-lg shadow-lg w-full max-w-md"), - h.H2F("htmgo chat", h.Class("text-3xl font-bold text-center mb-6")), + h.H2F( + "htmgo chat", + h.Class("text-3xl font-bold text-center mb-6"), + ), h.Form( h.Attribute("hx-swap", "none"), h.PostPartial(partials.CreateOrJoinRoom), h.Class("flex flex-col gap-6"), - // Username input at the top components.Input(components.InputProps{ Id: "username", @@ -30,11 +32,9 @@ func ChatAppFirstScreen(ctx *h.RequestContext) *h.Page { h.MaxLength(15), }, }), - // Single box for Create or Join a Chat Room h.Div( h.Class("p-4 border border-gray-300 rounded-md flex flex-col gap-6"), - // Create New Chat Room input components.Input(components.InputProps{ Name: "new-chat-room", @@ -45,15 +45,20 @@ func ChatAppFirstScreen(ctx *h.RequestContext) *h.Page { h.MaxLength(20), }, }), - // OR divider h.Div( h.Class("flex items-center justify-center gap-4"), - h.Div(h.Class("border-t border-gray-300 flex-grow")), - h.P(h.Text("OR"), h.Class("text-gray-500")), - h.Div(h.Class("border-t border-gray-300 flex-grow")), + h.Div( + h.Class("border-t border-gray-300 flex-grow"), + ), + h.P( + h.Text("OR"), + h.Class("text-gray-500"), + ), + h.Div( + h.Class("border-t border-gray-300 flex-grow"), + ), ), - // Join Chat Room input components.Input(components.InputProps{ Id: "join-chat-room", @@ -67,10 +72,8 @@ func ChatAppFirstScreen(ctx *h.RequestContext) *h.Page { }, }), ), - // Error message components.FormError(""), - // Submit button at the bottom components.PrimaryButton(components.ButtonProps{ Type: "submit", diff --git a/examples/chat/pages/root.go b/examples/chat/pages/root.go index 806a882..c566cf2 100644 --- a/examples/chat/pages/root.go +++ b/examples/chat/pages/root.go @@ -8,6 +8,10 @@ func RootPage(children ...h.Ren) h.Ren { extensions := h.BaseExtensions() return h.Html( h.HxExtension(extensions), + h.Meta("viewport", "width=device-width, initial-scale=1"), + h.Meta("title", "htmgo chat example"), + h.Meta("charset", "utf-8"), + h.Meta("author", "htmgo"), h.Head( h.Link("/public/main.css", "stylesheet"), h.Script("/public/htmgo.js"), diff --git a/examples/chat/partials/chat.go b/examples/chat/partials/chat.go index 59b41cc..27d43ea 100644 --- a/examples/chat/partials/chat.go +++ b/examples/chat/partials/chat.go @@ -2,14 +2,14 @@ package partials import ( "chat/components" - "chat/ws" + "chat/sse" "github.com/maddalax/htmgo/framework/h" "github.com/maddalax/htmgo/framework/service" ) func SendMessage(ctx *h.RequestContext) *h.Partial { locator := ctx.ServiceLocator() - socketManager := service.Get[ws.SocketManager](locator) + socketManager := service.Get[sse.SocketManager](locator) sessionCookie, err := ctx.Request.Cookie("session_id") diff --git a/examples/chat/sse/handler.go b/examples/chat/sse/handler.go new file mode 100644 index 0000000..b4f8000 --- /dev/null +++ b/examples/chat/sse/handler.go @@ -0,0 +1,112 @@ +package sse + +import ( + "fmt" + "github.com/go-chi/chi/v5" + "github.com/maddalax/htmgo/framework/h" + "github.com/maddalax/htmgo/framework/service" + "log/slog" + "net/http" + "sync" + "time" +) + +func Handle() http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + // Set the necessary headers + w.Header().Set("Content-Type", "text/event-stream") + w.Header().Set("Cache-Control", "no-cache") + w.Header().Set("Connection", "keep-alive") + w.Header().Set("Access-Control-Allow-Origin", "*") // Optional for CORS + + cc := r.Context().Value(h.RequestContextKey).(*h.RequestContext) + locator := cc.ServiceLocator() + manager := service.Get[SocketManager](locator) + + sessionCookie, _ := r.Cookie("session_id") + sessionId := "" + + if sessionCookie != nil { + sessionId = sessionCookie.Value + } + + ctx := r.Context() + + /* + Large buffer in case the client disconnects while we are writing + we don't want to block the writer + */ + done := make(chan bool, 1000) + writer := make(WriterChan, 1000) + + wg := sync.WaitGroup{} + wg.Add(1) + + /* + * This goroutine is responsible for writing messages to the client + */ + go func() { + defer wg.Done() + defer manager.Disconnect(sessionId) + + defer func() { + fmt.Printf("empting channels\n") + for len(writer) > 0 { + <-writer + } + for len(done) > 0 { + <-done + } + }() + + ticker := time.NewTicker(5 * time.Second) + defer ticker.Stop() + + for { + select { + case <-ctx.Done(): + return + case <-done: + fmt.Printf("closing connection: \n") + return + case <-ticker.C: + manager.Ping(sessionId) + case message := <-writer: + _, err := fmt.Fprintf(w, message) + if err != nil { + done <- true + } else { + flusher, ok := w.(http.Flusher) + if ok { + flusher.Flush() + } + } + } + } + }() + + /** + * This goroutine is responsible for adding the client to the room + */ + wg.Add(1) + go func() { + defer wg.Done() + if sessionId == "" { + manager.writeCloseRaw(writer, "no session") + return + } + + roomId := chi.URLParam(r, "id") + + if roomId == "" { + slog.Error("invalid room", slog.String("room_id", roomId)) + manager.writeCloseRaw(writer, "invalid room") + return + } + + manager.Add(roomId, sessionId, writer, done) + }() + + wg.Wait() + } +} diff --git a/examples/chat/ws/manager.go b/examples/chat/sse/manager.go similarity index 70% rename from examples/chat/ws/manager.go rename to examples/chat/sse/manager.go index fb25f7d..485afb2 100644 --- a/examples/chat/ws/manager.go +++ b/examples/chat/sse/manager.go @@ -1,12 +1,15 @@ -package ws +package sse import ( + "chat/internal/routine" "fmt" "github.com/puzpuzpuz/xsync/v3" - "net/http" + "time" ) type EventType string +type WriterChan chan string +type DoneChan chan bool const ( ConnectedEvent EventType = "connected" @@ -28,10 +31,9 @@ type CloseEvent struct { type SocketConnection struct { Id string - Writer http.ResponseWriter RoomId string - Done chan CloseEvent - Flush chan bool + Done DoneChan + Writer WriterChan } type SocketManager struct { @@ -62,13 +64,29 @@ func (manager *SocketManager) Listen(listener chan SocketEvent) { if manager.listeners == nil { manager.listeners = make([]chan SocketEvent, 0) } - manager.listeners = append(manager.listeners, listener) + if listener != nil { + manager.listeners = append(manager.listeners, listener) + } } func (manager *SocketManager) dispatch(event SocketEvent) { + fmt.Printf("dispatching event: %s\n", event.Type) + done := make(chan struct{}, 1) + go func() { + for { + select { + case <-done: + fmt.Printf("dispatched event: %s\n", event.Type) + return + case <-time.After(5 * time.Second): + fmt.Printf("havent dispatched event after 5s, chan blocked: %s\n", event.Type) + } + } + }() for _, listener := range manager.listeners { listener <- event } + done <- struct{}{} } func (manager *SocketManager) OnMessage(id string, message map[string]any) { @@ -84,7 +102,7 @@ func (manager *SocketManager) OnMessage(id string, message map[string]any) { }) } -func (manager *SocketManager) Add(roomId string, id string, writer http.ResponseWriter, done chan CloseEvent, flush chan bool) { +func (manager *SocketManager) Add(roomId string, id string, writer WriterChan, done DoneChan) { manager.idToRoom.Store(id, roomId) sockets, ok := manager.sockets.LoadOrCompute(roomId, func() *xsync.MapOf[string, SocketConnection] { @@ -96,7 +114,6 @@ func (manager *SocketManager) Add(roomId string, id string, writer http.Response Writer: writer, RoomId: roomId, Done: done, - Flush: flush, }) s, ok := sockets.Load(id) @@ -110,6 +127,8 @@ func (manager *SocketManager) Add(roomId string, id string, writer http.Response RoomId: s.RoomId, Payload: map[string]any{}, }) + + fmt.Printf("User %s connected to %s\n", id, roomId) } func (manager *SocketManager) OnClose(id string) { @@ -126,25 +145,20 @@ func (manager *SocketManager) OnClose(id string) { manager.sockets.Delete(id) } -func (manager *SocketManager) CloseWithError(id string, code int, message string) { +func (manager *SocketManager) CloseWithMessage(id string, message string) { conn := manager.Get(id) if conn != nil { - go manager.OnClose(id) - conn.Done <- CloseEvent{ - Code: code, - Reason: message, - } + defer manager.OnClose(id) + manager.writeText(*conn, "error", message) + conn.Done <- true } } func (manager *SocketManager) Disconnect(id string) { conn := manager.Get(id) if conn != nil { - go manager.OnClose(id) - conn.Done <- CloseEvent{ - Code: -1, - Reason: "", - } + manager.OnClose(id) + conn.Done <- true } } @@ -168,20 +182,32 @@ func (manager *SocketManager) Ping(id string) { } } +func (manager *SocketManager) writeCloseRaw(writer WriterChan, message string) { + manager.writeTextRaw(writer, "close", message) +} + +func (manager *SocketManager) writeTextRaw(writer WriterChan, event string, message string) { + routine.DebugLongRunning("writeTextRaw", func() { + timeout := 3 * time.Second + data := "" + if event != "" { + data = fmt.Sprintf("event: %s\ndata: %s\n\n", event, message) + } else { + data = fmt.Sprintf("data: %s\n\n", message) + } + select { + case writer <- data: + case <-time.After(timeout): + fmt.Printf("could not send %s to channel after %s\n", data, timeout) + } + }) +} + func (manager *SocketManager) writeText(socket SocketConnection, event string, message string) { if socket.Writer == nil { return } - var err error - if event != "" { - _, err = fmt.Fprintf(socket.Writer, "event: %s\ndata: %s\n\n", event, message) - } else { - _, err = fmt.Fprintf(socket.Writer, "data: %s\n\n", message) - } - if err != nil { - manager.CloseWithError(socket.Id, 1008, "failed to write message") - } - socket.Flush <- true + manager.writeTextRaw(socket.Writer, event, message) } func (manager *SocketManager) BroadcastText(roomId string, message string, predicate func(conn SocketConnection) bool) { diff --git a/examples/chat/ws/handler.go b/examples/chat/ws/handler.go deleted file mode 100644 index 0f3ce68..0000000 --- a/examples/chat/ws/handler.go +++ /dev/null @@ -1,77 +0,0 @@ -package ws - -import ( - "fmt" - "github.com/go-chi/chi/v5" - "github.com/maddalax/htmgo/framework/h" - "github.com/maddalax/htmgo/framework/service" - "log/slog" - "net/http" - "time" -) - -func Handle() http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - cc := r.Context().Value(h.RequestContextKey).(*h.RequestContext) - - sessionCookie, _ := r.Cookie("session_id") - - if sessionCookie == nil { - slog.Error("session cookie not found") - return - } - - locator := cc.ServiceLocator() - manager := service.Get[SocketManager](locator) - - sessionId := sessionCookie.Value - - roomId := chi.URLParam(r, "id") - - if roomId == "" { - slog.Error("invalid room", slog.String("room_id", roomId)) - manager.CloseWithError(sessionId, 1008, "invalid room") - return - } - - done := make(chan CloseEvent, 50) - flush := make(chan bool, 50) - - manager.Add(roomId, sessionId, w, done, flush) - - defer func() { - manager.Disconnect(sessionId) - }() - - // Set the necessary headers - w.Header().Set("Content-Type", "text/event-stream") - w.Header().Set("Cache-Control", "no-cache") - w.Header().Set("Connection", "keep-alive") - w.Header().Set("Access-Control-Allow-Origin", "*") // Optional for CORS - - // Flush the headers immediately - flusher, ok := w.(http.Flusher) - - if !ok { - http.Error(w, "Streaming unsupported", http.StatusInternalServerError) - return - } - - ticker := time.NewTicker(5 * time.Second) - defer ticker.Stop() - - for { - select { - case <-ticker.C: - manager.Ping(sessionId) - case <-flush: - if flusher != nil { - flusher.Flush() - } - case <-done: // Client closed the connection - fmt.Println("Client disconnected") - return - } - } - } -} diff --git a/examples/hackernews/.dockerignore b/examples/hackernews/.dockerignore new file mode 100644 index 0000000..fb47686 --- /dev/null +++ b/examples/hackernews/.dockerignore @@ -0,0 +1,11 @@ +# Project exclude paths +/tmp/ +node_modules/ +dist/ +js/dist +js/node_modules +go.work +go.work.sum +.idea +!framework/assets/dist +__htmgo \ No newline at end of file diff --git a/examples/hackernews/.gitignore b/examples/hackernews/.gitignore new file mode 100644 index 0000000..3d6a979 --- /dev/null +++ b/examples/hackernews/.gitignore @@ -0,0 +1,6 @@ +/assets/dist +tmp +node_modules +.idea +__htmgo +dist \ No newline at end of file diff --git a/examples/hackernews/Dockerfile b/examples/hackernews/Dockerfile new file mode 100644 index 0000000..37c299d --- /dev/null +++ b/examples/hackernews/Dockerfile @@ -0,0 +1,38 @@ +# Stage 1: Build the Go binary +FROM golang:1.23-alpine AS builder + +RUN apk update +RUN apk add git +RUN apk add curl + +# Set the working directory inside the container +WORKDIR /app + +# Copy go.mod and go.sum files +COPY go.mod go.sum ./ + +# Download and cache the Go modules +RUN go mod download + +# Copy the source code into the container +COPY . . + +# Build the Go binary for Linux +RUN GOPRIVATE=github.com/maddalax GOPROXY=direct go run github.com/maddalax/htmgo/cli/htmgo@latest build + + +# Stage 2: Create the smallest possible image +FROM gcr.io/distroless/base-debian11 + +# Set the working directory inside the container +WORKDIR /app + +# Copy the Go binary from the builder stage +COPY --from=builder /app/dist . + +# Expose the necessary port (replace with your server port) +EXPOSE 3000 + + +# Command to run the binary +CMD ["./hackernews"] diff --git a/examples/hackernews/Taskfile.yml b/examples/hackernews/Taskfile.yml new file mode 100644 index 0000000..28f1902 --- /dev/null +++ b/examples/hackernews/Taskfile.yml @@ -0,0 +1,20 @@ +version: '3' + +tasks: + run: + cmds: + - htmgo run + silent: true + + build: + cmds: + - htmgo build + + docker: + cmds: + - docker build . + + watch: + cmds: + - htmgo watch + silent: true diff --git a/examples/hackernews/assets.go b/examples/hackernews/assets.go new file mode 100644 index 0000000..8104b98 --- /dev/null +++ b/examples/hackernews/assets.go @@ -0,0 +1,13 @@ +//go:build !prod +// +build !prod + +package main + +import ( + "hackernews/internal/embedded" + "io/fs" +) + +func GetStaticAssets() fs.FS { + return embedded.NewOsFs() +} diff --git a/examples/hackernews/assets/css/input.css b/examples/hackernews/assets/css/input.css new file mode 100644 index 0000000..404b710 --- /dev/null +++ b/examples/hackernews/assets/css/input.css @@ -0,0 +1,15 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +@layer utilities { + /* Chrome, Safari and Opera */ + .no-scrollbar::-webkit-scrollbar { + display: none; + } + + .no-scrollbar { + -ms-overflow-style: none; /* IE and Edge */ + scrollbar-width: none; /* Firefox */ + } +} diff --git a/examples/hackernews/assets/public/apple-touch-icon.png b/examples/hackernews/assets/public/apple-touch-icon.png new file mode 100644 index 0000000..d10e9fe Binary files /dev/null and b/examples/hackernews/assets/public/apple-touch-icon.png differ diff --git a/examples/hackernews/assets/public/favicon.ico b/examples/hackernews/assets/public/favicon.ico new file mode 100644 index 0000000..040cccf Binary files /dev/null and b/examples/hackernews/assets/public/favicon.ico differ diff --git a/examples/hackernews/assets/public/icon-192-maskable.png b/examples/hackernews/assets/public/icon-192-maskable.png new file mode 100644 index 0000000..d4d6efb Binary files /dev/null and b/examples/hackernews/assets/public/icon-192-maskable.png differ diff --git a/examples/hackernews/assets/public/icon-192.png b/examples/hackernews/assets/public/icon-192.png new file mode 100644 index 0000000..f533435 Binary files /dev/null and b/examples/hackernews/assets/public/icon-192.png differ diff --git a/examples/hackernews/assets/public/icon-512-maskable.png b/examples/hackernews/assets/public/icon-512-maskable.png new file mode 100644 index 0000000..db61f3d Binary files /dev/null and b/examples/hackernews/assets/public/icon-512-maskable.png differ diff --git a/examples/hackernews/assets/public/icon-512.png b/examples/hackernews/assets/public/icon-512.png new file mode 100644 index 0000000..ba0665d Binary files /dev/null and b/examples/hackernews/assets/public/icon-512.png differ diff --git a/examples/hackernews/assets_prod.go b/examples/hackernews/assets_prod.go new file mode 100644 index 0000000..f0598e1 --- /dev/null +++ b/examples/hackernews/assets_prod.go @@ -0,0 +1,16 @@ +//go:build prod +// +build prod + +package main + +import ( + "embed" + "io/fs" +) + +//go:embed assets/dist/* +var staticAssets embed.FS + +func GetStaticAssets() fs.FS { + return staticAssets +} diff --git a/examples/hackernews/components/badge.go b/examples/hackernews/components/badge.go new file mode 100644 index 0000000..dd97f60 --- /dev/null +++ b/examples/hackernews/components/badge.go @@ -0,0 +1,14 @@ +package components + +import "github.com/maddalax/htmgo/framework/h" + +func Badge(text string, active bool, children ...h.Ren) *h.Element { + return h.Button( + h.Text(text), + h.ClassX("font-semibold px-3 py-1 rounded-full cursor-pointer h-[32px]", h.ClassMap{ + "bg-rose-500 text-white": active, + "bg-neutral-300": !active, + }), + h.Children(children...), + ) +} diff --git a/examples/hackernews/go.mod b/examples/hackernews/go.mod new file mode 100644 index 0000000..741c20e --- /dev/null +++ b/examples/hackernews/go.mod @@ -0,0 +1,10 @@ +module hackernews + +go 1.23.0 + +require github.com/maddalax/htmgo/framework v1.0.2-0.20241025174132-df3edccd7fb0 + +require ( + github.com/go-chi/chi/v5 v5.1.0 // indirect + github.com/google/uuid v1.6.0 // indirect +) diff --git a/examples/hackernews/go.sum b/examples/hackernews/go.sum new file mode 100644 index 0000000..f050d04 --- /dev/null +++ b/examples/hackernews/go.sum @@ -0,0 +1,16 @@ +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/go-chi/chi/v5 v5.1.0 h1:acVI1TYaD+hhedDJ3r54HyA6sExp3HfXq7QWEEY/xMw= +github.com/go-chi/chi/v5 v5.1.0/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/maddalax/htmgo/framework v1.0.2-0.20241025174132-df3edccd7fb0 h1:K9Q5b7BmbpCPJFjrAHS8+wPdKDcZN9NMC3Fg51n5IaQ= +github.com/maddalax/htmgo/framework v1.0.2-0.20241025174132-df3edccd7fb0/go.mod h1:NGGzWVXWksrQJ9kV9SGa/A1F1Bjsgc08cN7ZVb98RqY= +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/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo= +golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0= +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/examples/hackernews/internal/batch/parallel.go b/examples/hackernews/internal/batch/parallel.go new file mode 100644 index 0000000..6f03e5f --- /dev/null +++ b/examples/hackernews/internal/batch/parallel.go @@ -0,0 +1,30 @@ +package batch + +import ( + "sync" +) + +func ParallelProcess[T any, Z any](items []T, concurrency int, cb func(item T) Z) []Z { + if len(items) == 0 { + return []Z{} + } + if len(items) == 1 { + return []Z{cb(items[0])} + } + results := make([]Z, len(items)) + wg := sync.WaitGroup{} + sem := make(chan struct{}, concurrency) + for i, item := range items { + wg.Add(1) + sem <- struct{}{} + go func(item T) { + defer func() { + wg.Done() + <-sem + }() + results[i] = cb(item) + }(item) + } + wg.Wait() + return results +} diff --git a/examples/hackernews/internal/embedded/os.go b/examples/hackernews/internal/embedded/os.go new file mode 100644 index 0000000..ddfd55f --- /dev/null +++ b/examples/hackernews/internal/embedded/os.go @@ -0,0 +1,17 @@ +package embedded + +import ( + "io/fs" + "os" +) + +type OsFs struct { +} + +func (receiver OsFs) Open(name string) (fs.File, error) { + return os.Open(name) +} + +func NewOsFs() OsFs { + return OsFs{} +} diff --git a/examples/hackernews/internal/httpjson/client.go b/examples/hackernews/internal/httpjson/client.go new file mode 100644 index 0000000..4c1e5ce --- /dev/null +++ b/examples/hackernews/internal/httpjson/client.go @@ -0,0 +1,115 @@ +package httpjson + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "net/http" + "sync" + "time" +) + +var ( + client *http.Client + once sync.Once // Consider allowing configuration parameters for the singleton +) + +func getClient() *http.Client { + once.Do(func() { + client = &http.Client{ + Timeout: 10 * time.Second, + } + }) + return client +} + +func Get[T any](url string) (*T, error) { + client := getClient() + resp, err := client.Get(url) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("unexpected status code: %d", resp.StatusCode) + } + + var result T + body, err := io.ReadAll(resp.Body) + if err != nil { + return nil, err + } + + if err := json.Unmarshal(body, &result); err != nil { + return nil, err + } + + return &result, nil +} + +func Post[T any](url string, data T) (*http.Response, error) { + client := getClient() + body, err := json.Marshal(data) + if err != nil { + return nil, err + } + + resp, err := client.Post(url, "application/json", bytes.NewBuffer(body)) + if err != nil { + return nil, err + } + + if resp.StatusCode != http.StatusCreated && resp.StatusCode != http.StatusOK { + return resp, fmt.Errorf("unexpected status code: %d", resp.StatusCode) + } + + return resp, nil +} + +func Patch[T any](url string, data T) error { + client := getClient() + body, err := json.Marshal(data) + if err != nil { + return err + } + + req, err := http.NewRequest(http.MethodPatch, url, bytes.NewBuffer(body)) + if err != nil { + return err + } + req.Header.Set("Content-Type", "application/json") + + resp, err := client.Do(req) + if err != nil { + return err + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return fmt.Errorf("unexpected status code: %d", resp.StatusCode) + } + + return nil +} + +func Delete(url string) error { + client := getClient() + req, err := http.NewRequest(http.MethodDelete, url, nil) + if err != nil { + return err + } + + resp, err := client.Do(req) + if err != nil { + return err + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return fmt.Errorf("unexpected status code: %d", resp.StatusCode) + } + + return nil +} diff --git a/examples/hackernews/internal/news/news.go b/examples/hackernews/internal/news/news.go new file mode 100644 index 0000000..1b0c778 --- /dev/null +++ b/examples/hackernews/internal/news/news.go @@ -0,0 +1,146 @@ +package news + +import ( + "fmt" + "github.com/maddalax/htmgo/framework/h" + "hackernews/internal/batch" + "hackernews/internal/httpjson" + "hackernews/internal/timeformat" + "log/slog" + "strconv" + "time" +) + +const baseUrl = "https://hacker-news.firebaseio.com/v0/" + +func url(path string, qs *h.Qs) string { + return baseUrl + path + ".json?" + qs.ToString() +} + +type Category struct { + Name string + Path string +} + +var Categories = []Category{ + {"Top Stories", "topstories"}, + {"Best Stories", "beststories"}, + {"New Stories", "newstories"}, +} + +type Comment struct { + By string `json:"by"` + Text string `json:"text"` + TimeRaw int64 `json:"time"` + Time time.Time `json:"-"` + Type string `json:"type"` + Kids []int `json:"kids"` + Parent int `json:"parent"` + Id int `json:"id"` +} + +type Story struct { + Id int `json:"id"` + By string `json:"by"` + Text string `json:"text"` + Title string `json:"title"` + Type string `json:"type"` + Descendents int `json:"descendants"` + Score int `json:"score"` + Url string + TimeRaw int64 `json:"time"` + Time time.Time `json:"-"` + // comment ids + Kids []int +} + +type GetTopStoriesRequest struct { + Limit int + Page int +} + +func MustItemId(ctx *h.RequestContext) int { + raw := h.GetQueryParam(ctx, "item") + parsed, err := strconv.ParseInt(raw, 10, 64) + if err != nil { + return 0 + } + return int(parsed) +} + +func GetStories(category string, page int, limit int) []Story { + top, err := httpjson.Get[[]int](url(category, h.NewQs())) + if err != nil { + slog.Error("failed to load top stories", slog.String("err", err.Error())) + return make([]Story, 0) + } + ids := *top + start := page * limit + end := start + limit + + if start > len(ids) { + return make([]Story, 0) + } + + if end > len(ids) { + end = len(ids) + } + + return batch.ParallelProcess[int, Story]( + ids[start:end], + 50, + func(id int) Story { + story, err := GetStory(id) + if err != nil { + slog.Error("failed to load story", slog.Int("id", id), slog.String("err", err.Error())) + return Story{} + } + return *story + }, + ) +} + +func GetTopStories(page int, limit int) []Story { + return GetStories("topstories", page, limit) +} + +func GetBestStories(page int, limit int) []Story { + return GetStories("beststories", page, limit) +} + +func GetNewStories(page int, limit int) []Story { + return GetStories("newstories", page, limit) +} + +func GetComments(ids []int) []Comment { + return batch.ParallelProcess( + ids, + 50, + func(id int) Comment { + comment, err := GetComment(id) + if err != nil { + slog.Error("failed to load comment", slog.Int("id", id), slog.String("err", err.Error())) + return Comment{} + } + return *comment + }, + ) +} + +func GetComment(id int) (*Comment, error) { + c, err := httpjson.Get[Comment](url(fmt.Sprintf("item/%d", id), h.NewQs())) + if err != nil { + return nil, err + } + c.Time = timeformat.ParseUnix(c.TimeRaw) + return c, nil +} + +func GetStory(id int) (*Story, error) { + s, err := httpjson.Get[Story](url(fmt.Sprintf("item/%d", id), h.NewQs())) + if err != nil { + return nil, err + } + s.Time = timeformat.ParseUnix(s.TimeRaw) + return s, nil +} diff --git a/examples/hackernews/internal/parse/parse.go b/examples/hackernews/internal/parse/parse.go new file mode 100644 index 0000000..ffd1281 --- /dev/null +++ b/examples/hackernews/internal/parse/parse.go @@ -0,0 +1,11 @@ +package parse + +import "strconv" + +func MustParseInt(s string, fallback int) int { + v, err := strconv.ParseInt(s, 10, 64) + if err != nil { + return fallback + } + return int(v) +} diff --git a/examples/hackernews/internal/random.go b/examples/hackernews/internal/random.go new file mode 100644 index 0000000..cdd5416 --- /dev/null +++ b/examples/hackernews/internal/random.go @@ -0,0 +1,13 @@ +package internal + +import "math/rand" + +var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") + +func RandSeq(n int) string { + b := make([]rune, n) + for i := range b { + b[i] = letters[rand.Intn(len(letters))] + } + return string(b) +} diff --git a/examples/hackernews/internal/timeformat/time.go b/examples/hackernews/internal/timeformat/time.go new file mode 100644 index 0000000..62af620 --- /dev/null +++ b/examples/hackernews/internal/timeformat/time.go @@ -0,0 +1,39 @@ +package timeformat + +import ( + "fmt" + "time" +) + +func ParseUnix(t int64) time.Time { + return time.UnixMilli(t * 1000) +} + +func RelativeTime(t time.Time) string { + now := time.Now() + diff := now.Sub(t) + + var pluralize = func(s string) string { + if s[0] == '1' { + return s[:len(s)-5] + " ago" + } + return s + } + + switch { + case diff < time.Minute: + return "just now" + case diff < time.Hour: + return pluralize(fmt.Sprintf("%d minutes ago", int(diff.Minutes()))) + case diff < time.Hour*24: + return pluralize(fmt.Sprintf("%d hours ago", int(diff.Hours()))) + case diff < time.Hour*24*7: + return pluralize(fmt.Sprintf("%d days ago", int(diff.Hours()/24))) + case diff < time.Hour*24*30: + return pluralize(fmt.Sprintf("%d weeks ago", int(diff.Hours()/(24*7)))) + case diff < time.Hour*24*365: + return pluralize(fmt.Sprintf("%d months ago", int(diff.Hours()/(24*30)))) + default: + return pluralize(fmt.Sprintf("%d years ago", int(diff.Hours()/(24*365)))) + } +} diff --git a/examples/hackernews/main.go b/examples/hackernews/main.go new file mode 100644 index 0000000..1d38712 --- /dev/null +++ b/examples/hackernews/main.go @@ -0,0 +1,36 @@ +package main + +import ( + "fmt" + "github.com/maddalax/htmgo/framework/h" + "github.com/maddalax/htmgo/framework/service" + "hackernews/__htmgo" + "io/fs" + "net/http" +) + +func main() { + locator := service.NewLocator() + + h.Start(h.AppOpts{ + ServiceLocator: locator, + LiveReload: true, + Register: func(app *h.App) { + sub, err := fs.Sub(GetStaticAssets(), "assets/dist") + + if err != nil { + panic(err) + } + + http.FileServerFS(sub) + + app.Router.Handle("/item", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + id := r.URL.Query().Get("id") + w.Header().Set("Location", fmt.Sprintf("/?item=%s", id)) + w.WriteHeader(302) + })) + app.Router.Handle("/public/*", http.StripPrefix("/public", http.FileServerFS(sub))) + __htmgo.Register(app.Router) + }, + }) +} diff --git a/examples/hackernews/pages/index.go b/examples/hackernews/pages/index.go new file mode 100644 index 0000000..44cc3fd --- /dev/null +++ b/examples/hackernews/pages/index.go @@ -0,0 +1,21 @@ +package pages + +import ( + "github.com/maddalax/htmgo/framework/h" + "hackernews/partials" +) + +func IndexPage(ctx *h.RequestContext) *h.Page { + return h.NewPage( + RootPage( + h.Div( + h.Class("flex gap-2 min-h-screen"), + partials.StorySidebar(ctx), + h.Main( + h.Class("flex justify-left items-start p-6 w-full"), + partials.Story(ctx), + ), + ), + ), + ) +} diff --git a/examples/hackernews/pages/root.go b/examples/hackernews/pages/root.go new file mode 100644 index 0000000..3358b22 --- /dev/null +++ b/examples/hackernews/pages/root.go @@ -0,0 +1,41 @@ +package pages + +import ( + "github.com/maddalax/htmgo/framework/h" +) + +func RootPage(children ...h.Ren) h.Ren { + banner := h.A( + h.Class("bg-neutral-200 text-neutral-600 text-center p-2 flex items-center justify-center"), + h.Href("https://github.com/maddalax/htmgo"), + h.Attribute("target", "_blank"), + h.Text("Built with htmgo.dev"), + ) + + return h.Html( + h.HxExtensions( + h.BaseExtensions(), + ), + h.Head( + h.Meta("viewport", "width=device-width, initial-scale=1"), + h.Link("/public/favicon.ico", "icon"), + h.Link("/public/apple-touch-icon.png", "apple-touch-icon"), + h.Meta("title", "hackernews"), + h.Meta("charset", "utf-8"), + h.Meta("author", "htmgo"), + h.Meta("description", "hacker news reader, built with htmgo"), + h.Meta("og:title", "hacker news reader"), + h.Meta("og:url", "https://hn.htmgo.dev"), + h.Link("canonical", "https://hn.htmgo.dev"), + h.Meta("og:description", "hacker news reader, built with htmgo"), + h.Link("/public/main.css", "stylesheet"), + h.Script("/public/htmgo.js"), + ), + h.Body( + banner, + h.Div( + h.Fragment(children...), + ), + ), + ) +} diff --git a/examples/hackernews/partials/comments.go b/examples/hackernews/partials/comments.go new file mode 100644 index 0000000..95ce923 --- /dev/null +++ b/examples/hackernews/partials/comments.go @@ -0,0 +1,102 @@ +package partials + +import ( + "fmt" + "github.com/maddalax/htmgo/framework/h" + "hackernews/internal/batch" + "hackernews/internal/news" + "hackernews/internal/timeformat" + "strings" + "time" +) + +func StoryComments(ctx *h.RequestContext) *h.Partial { + return h.NewPartial( + h.Fragment( + h.OobSwap( + ctx, + h.Div( + h.Id("comments-loader"), + ), + ), + h.Div( + h.Class("flex flex-col gap-3 prose max-w-none"), + CachedStoryComments(news.MustItemId(ctx)), + ), + ), + ) +} + +var CachedStoryComments = h.CachedPerKeyT[string, int](time.Minute*3, func(itemId int) (string, h.GetElementFunc) { + return fmt.Sprintf("story-comments-%d", itemId), func() *h.Element { + story, err := news.GetStory(itemId) + + if err != nil { + return h.Div( + h.Text("Failed to load story"), + ) + } + + comments := news.GetComments(story.Kids) + + // parallel process because each comment needs to load its children comments + items := batch.ParallelProcess[news.Comment, *h.Element](comments, 50, func(item news.Comment) *h.Element { + return Comment(item, 0) + }) + + return h.List(items, func(item *h.Element, index int) *h.Element { + return item + }) + } +}) + +func Comment(item news.Comment, nesting int) *h.Element { + if item.Text == "" { + return h.Empty() + } + + children := news.GetComments(item.Kids) + + return h.Div( + h.ClassX("block bg-white pb-2 pt-2", h.ClassMap{ + "border-b border-gray-200": nesting == 0, + "border-l border-gray-200": nesting > 0, + }), + h.If( + nesting > 0, + h.Attribute("style", fmt.Sprintf("margin-left: %dpx", (nesting-1)*15)), + ), + h.Div( + h.If( + nesting > 0, + h.Class("pl-4"), + ), + h.Div( + h.Class("flex gap-1 items-center"), + h.Div( + h.Class("font-bold text-rose-500"), + h.UnsafeRaw(item.By), + ), + h.Div( + h.Class("text-sm text-gray-600"), + h.UnsafeRaw("•"), + h.TextF(" %s", timeformat.RelativeTime(item.Time)), + ), + ), + h.Div( + h.Class("text-sm text-gray-600"), + h.UnsafeRaw(strings.TrimSpace(item.Text)), + ), + ), + h.If( + len(children) > 0, + h.List( + children, func(child news.Comment, index int) *h.Element { + return h.Div( + Comment(child, nesting+1), + ) + }, + ), + ), + ) +} diff --git a/examples/hackernews/partials/sidebar.go b/examples/hackernews/partials/sidebar.go new file mode 100644 index 0000000..d3bbef0 --- /dev/null +++ b/examples/hackernews/partials/sidebar.go @@ -0,0 +1,164 @@ +package partials + +import ( + "fmt" + "github.com/maddalax/htmgo/framework/h" + "hackernews/components" + "hackernews/internal/news" + "hackernews/internal/parse" + "hackernews/internal/timeformat" + "time" +) + +var ScrollJs = ` + const scrollContainer = self; + let isDown = false; + let startX; + let scrollLeft; + + scrollContainer.addEventListener("mousedown", (e) => { + isDown = true; + scrollContainer.classList.add("active"); + startX = e.pageX - scrollContainer.offsetLeft; + scrollLeft = scrollContainer.scrollLeft; + }); + + scrollContainer.addEventListener("mouseleave", () => { + isDown = false; + scrollContainer.classList.remove("active"); + }); + + scrollContainer.addEventListener("mouseup", () => { + isDown = false; + scrollContainer.classList.remove("active"); + }); + + scrollContainer.addEventListener("mousemove", (e) => { + if (!isDown) return; + e.preventDefault(); + const x = e.pageX - scrollContainer.offsetLeft; + const walk = (x - startX) * 3; // Adjust scroll speed here + scrollContainer.scrollLeft = scrollLeft - walk; + }); +` + +func StorySidebar(ctx *h.RequestContext) *h.Partial { + category := h.GetQueryParam(ctx, "category") + pageRaw := h.GetQueryParam(ctx, "page") + mode := h.GetQueryParam(ctx, "mode") + + if pageRaw == "" { + pageRaw = "0" + } + + if category == "" { + category = "topstories" + } + + page := parse.MustParseInt(pageRaw, 0) + + fetchMorePath := h.GetPartialPathWithQs( + StorySidebar, + h.NewQs("mode", "infinite", "page", fmt.Sprintf("%d", page+1), "category", category), + ) + + list := CachedStoryList(category, page, 50, fetchMorePath) + + body := h.Aside( + h.Id("story-sidebar"), + h.JoinExtensions( + h.TriggerChildren(), + ), + h.Class("sticky top-0 h-screen p-1 bg-gray-100 overflow-y-auto max-w-80 min-w-80"), + h.Div( + h.Class("flex flex-col gap-1"), + SidebarTitle(category), + h.Id("story-list"), + list, + ), + ) + + if mode == "infinite" { + return h.NewPartial( + list, + ) + } + + if ctx.IsHxRequest() { + return h.SwapManyPartial(ctx, body) + } + + return h.NewPartial(body) +} + +func SidebarTitle(defaultCategory string) *h.Element { + today := time.Now().Format("Mon, 02 Jan 2006") + return h.Div( + h.Class("flex flex-col px-2 pt-4 pb-2"), + h.Div( + h.Class("text-sm text-gray-600"), + h.Text(today), + ), + h.Div( + h.Class("font-bold text-xl"), + h.Text("Hacker News"), + ), + h.Div( + h.OnLoad( + h.EvalJs(ScrollJs), + ), + h.Class("scroll-container mt-2 flex gap-1 no-scrollbar overflow-y-hidden whitespace-nowrap overflow-x-auto"), + h.List(news.Categories, func(item news.Category, index int) *h.Element { + return CategoryBadge(defaultCategory, item) + }), + ), + ) +} + +func CategoryBadge(defaultCategory string, category news.Category) *h.Element { + selected := category.Path == defaultCategory + return components.Badge( + category.Name, + selected, + h.Attribute("hx-swap", "none"), + h.If( + !selected, + h.PostPartialOnClickQs( + StorySidebar, + h.NewQs("category", category.Path), + ), + ), + ) +} + +var CachedStoryList = h.CachedPerKeyT4(time.Minute*5, func(category string, page int, limit int, fetchMorePath string) (string, h.GetElementFunc) { + return fmt.Sprintf("%s-stories-%d-%d", category, page, limit), func() *h.Element { + stories := news.GetStories(category, page, limit) + return h.List(stories, func(item news.Story, index int) *h.Element { + return h.Div( + h.Attribute("hx-swap", "none"), + h.PostPartialOnClickQs(Story, h.NewQs("item", fmt.Sprintf("%d", item.Id))), + h.A(h.Href(item.Url)), + h.Class("block p-2 bg-white rounded-md shadow cursor-pointer"), + h.Div( + h.Class("font-bold"), + h.UnsafeRaw(item.Title), + ), + h.Div( + h.Class("text-sm text-gray-600"), + h.Div(h.TextF("%s ", item.By), h.UnsafeRaw("•"), h.TextF(" %s", timeformat.RelativeTime(item.Time))), + ), + h.Div( + h.Class("text-sm text-gray-600"), + h.UnsafeRaw(fmt.Sprintf("%d upvotes • %d comments", item.Score, item.Descendents)), + ), + h.If(index == len(stories)-1, h.Div( + h.Id("load-more"), + h.Attribute("hx-swap", "beforeend"), + h.HxTarget("#story-list"), + h.Get(fetchMorePath, "intersect once"), + )), + ) + }) + } +}) diff --git a/examples/hackernews/partials/story.go b/examples/hackernews/partials/story.go new file mode 100644 index 0000000..8b72272 --- /dev/null +++ b/examples/hackernews/partials/story.go @@ -0,0 +1,92 @@ +package partials + +import ( + "fmt" + "github.com/maddalax/htmgo/framework/h" + "hackernews/internal/news" + "hackernews/internal/timeformat" + "time" +) + +func Story(ctx *h.RequestContext) *h.Partial { + storyId := news.MustItemId(ctx) + if storyId == 0 { + return h.NewPartial( + h.Div( + h.Class("flex justify-center bg-neutral-300"), + h.Id("story-body"), + ), + ) + } + + if ctx.IsHxRequest() { + return h.SwapManyPartialWithHeaders( + ctx, + h.PushUrlHeader(fmt.Sprintf("/?item=%d", storyId)), + h.Div( + h.Class("w-full"), + h.Id("story-body"), + CachedStoryBody(storyId), + ), + ) + } + + return h.NewPartial( + CachedStoryBody(storyId), + ) +} + +var CachedStoryBody = h.CachedPerKeyT[string, int](time.Minute*3, func(itemId int) (string, h.GetElementFunc) { + return fmt.Sprintf("story-%d", itemId), func() *h.Element { + story, err := news.GetStory(itemId) + if err != nil { + return h.Div( + h.Id("story-body"), + h.Text("Failed to load story"), + ) + } + return StoryBody(story) + } +}) + +func StoryBody(story *news.Story) *h.Element { + return h.Div( + h.Class("w-full"), + h.Id("story-body"), + h.Div( + h.Class("prose prose-2xl border-b border-gray-200 pb-3 max-w-none w-full"), + h.H5( + h.Class("flex gap-2 items-left font-bold"), + h.UnsafeRaw(story.Title), + ), + h.A( + h.Href(story.Url), + h.Class("text-sm text-rose-400 no-underline"), + h.Text(story.Url), + ), + h.Div( + h.Class("text-sm text-gray-600"), + h.UnsafeRaw(story.Text), + ), + h.Div( + h.Class("text-sm text-gray-600 mt-2"), + h.TextF("%d upvotes ", story.Score), + h.UnsafeRaw("•"), + h.TextF(" %s ", story.By), + h.UnsafeRaw("•"), + h.TextF(" %s", timeformat.RelativeTime(story.Time)), + ), + ), + h.Div( + h.Id("comments-loader"), + h.Class("flex justify-center items-center h-24"), + h.Div( + h.Class("animate-spin rounded-full h-8 w-8 border-t-2 border-b-2 border-rose-500"), + ), + ), + h.Div( + h.Class("mt-2 min-w-3xl max-w-3xl"), + h.GetPartial(StoryComments, "load"), + ), + ) +} diff --git a/examples/hackernews/tailwind.config.js b/examples/hackernews/tailwind.config.js new file mode 100644 index 0000000..f1b453a --- /dev/null +++ b/examples/hackernews/tailwind.config.js @@ -0,0 +1,7 @@ +/** @type {import('tailwindcss').Config} */ +module.exports = { + content: ["**/*.go"], + plugins: [ + require('@tailwindcss/typography') + ], +}; diff --git a/examples/simple-auth/.dockerignore b/examples/simple-auth/.dockerignore new file mode 100644 index 0000000..fb47686 --- /dev/null +++ b/examples/simple-auth/.dockerignore @@ -0,0 +1,11 @@ +# Project exclude paths +/tmp/ +node_modules/ +dist/ +js/dist +js/node_modules +go.work +go.work.sum +.idea +!framework/assets/dist +__htmgo \ No newline at end of file diff --git a/examples/simple-auth/.gitignore b/examples/simple-auth/.gitignore new file mode 100644 index 0000000..3d6a979 --- /dev/null +++ b/examples/simple-auth/.gitignore @@ -0,0 +1,6 @@ +/assets/dist +tmp +node_modules +.idea +__htmgo +dist \ No newline at end of file diff --git a/examples/simple-auth/Dockerfile b/examples/simple-auth/Dockerfile new file mode 100644 index 0000000..345375d --- /dev/null +++ b/examples/simple-auth/Dockerfile @@ -0,0 +1,36 @@ +# Stage 1: Build the Go binary +FROM golang:1.23 AS builder + +# Set the working directory inside the container +WORKDIR /app + +# Copy go.mod and go.sum files +COPY go.mod go.sum ./ + +# Download and cache the Go modules +RUN go mod download + +# Copy the source code into the container +COPY . . + +# Build the Go binary for Linux +RUN CGO_ENABLED=0 GOPRIVATE=github.com/maddalax LOG_LEVEL=debug go run github.com/maddalax/htmgo/cli/htmgo@latest build + +RUN CGO_ENABLED=1 GOOS=linux go build -tags prod -o ./dist -a -ldflags '-linkmode external -extldflags "-static"' . + + +# Stage 2: Create the smallest possible image +FROM gcr.io/distroless/base-debian11 + +# Set the working directory inside the container +WORKDIR /app + +# Copy the Go binary from the builder stage +COPY --from=builder /app/dist . + +# Expose the necessary port (replace with your server port) +EXPOSE 3000 + + +# Command to run the binary +CMD ["./simpleauth"] diff --git a/examples/simple-auth/Taskfile.yml b/examples/simple-auth/Taskfile.yml new file mode 100644 index 0000000..28f1902 --- /dev/null +++ b/examples/simple-auth/Taskfile.yml @@ -0,0 +1,20 @@ +version: '3' + +tasks: + run: + cmds: + - htmgo run + silent: true + + build: + cmds: + - htmgo build + + docker: + cmds: + - docker build . + + watch: + cmds: + - htmgo watch + silent: true diff --git a/examples/simple-auth/assets.go b/examples/simple-auth/assets.go new file mode 100644 index 0000000..9a76f11 --- /dev/null +++ b/examples/simple-auth/assets.go @@ -0,0 +1,13 @@ +//go:build !prod +// +build !prod + +package main + +import ( + "io/fs" + "simpleauth/internal/embedded" +) + +func GetStaticAssets() fs.FS { + return embedded.NewOsFs() +} diff --git a/examples/simple-auth/assets/css/input.css b/examples/simple-auth/assets/css/input.css new file mode 100644 index 0000000..bd6213e --- /dev/null +++ b/examples/simple-auth/assets/css/input.css @@ -0,0 +1,3 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; \ No newline at end of file diff --git a/examples/simple-auth/assets/public/apple-touch-icon.png b/examples/simple-auth/assets/public/apple-touch-icon.png new file mode 100644 index 0000000..d10e9fe Binary files /dev/null and b/examples/simple-auth/assets/public/apple-touch-icon.png differ diff --git a/examples/simple-auth/assets/public/favicon.ico b/examples/simple-auth/assets/public/favicon.ico new file mode 100644 index 0000000..040cccf Binary files /dev/null and b/examples/simple-auth/assets/public/favicon.ico differ diff --git a/examples/simple-auth/assets/public/icon-192-maskable.png b/examples/simple-auth/assets/public/icon-192-maskable.png new file mode 100644 index 0000000..d4d6efb Binary files /dev/null and b/examples/simple-auth/assets/public/icon-192-maskable.png differ diff --git a/examples/simple-auth/assets/public/icon-192.png b/examples/simple-auth/assets/public/icon-192.png new file mode 100644 index 0000000..f533435 Binary files /dev/null and b/examples/simple-auth/assets/public/icon-192.png differ diff --git a/examples/simple-auth/assets/public/icon-512-maskable.png b/examples/simple-auth/assets/public/icon-512-maskable.png new file mode 100644 index 0000000..db61f3d Binary files /dev/null and b/examples/simple-auth/assets/public/icon-512-maskable.png differ diff --git a/examples/simple-auth/assets/public/icon-512.png b/examples/simple-auth/assets/public/icon-512.png new file mode 100644 index 0000000..ba0665d Binary files /dev/null and b/examples/simple-auth/assets/public/icon-512.png differ diff --git a/examples/simple-auth/assets_prod.go b/examples/simple-auth/assets_prod.go new file mode 100644 index 0000000..f0598e1 --- /dev/null +++ b/examples/simple-auth/assets_prod.go @@ -0,0 +1,16 @@ +//go:build prod +// +build prod + +package main + +import ( + "embed" + "io/fs" +) + +//go:embed assets/dist/* +var staticAssets embed.FS + +func GetStaticAssets() fs.FS { + return staticAssets +} diff --git a/examples/simple-auth/go.mod b/examples/simple-auth/go.mod new file mode 100644 index 0000000..d41f1b9 --- /dev/null +++ b/examples/simple-auth/go.mod @@ -0,0 +1,14 @@ +module simpleauth + +go 1.23.0 + +require ( + github.com/maddalax/htmgo/framework v1.0.2-0.20241025174132-df3edccd7fb0 + github.com/mattn/go-sqlite3 v1.14.24 + golang.org/x/crypto v0.28.0 +) + +require ( + github.com/go-chi/chi/v5 v5.1.0 // indirect + github.com/google/uuid v1.6.0 // indirect +) diff --git a/examples/simple-auth/go.sum b/examples/simple-auth/go.sum new file mode 100644 index 0000000..9814172 --- /dev/null +++ b/examples/simple-auth/go.sum @@ -0,0 +1,20 @@ +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/go-chi/chi/v5 v5.1.0 h1:acVI1TYaD+hhedDJ3r54HyA6sExp3HfXq7QWEEY/xMw= +github.com/go-chi/chi/v5 v5.1.0/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/maddalax/htmgo/framework v1.0.2-0.20241025174132-df3edccd7fb0 h1:K9Q5b7BmbpCPJFjrAHS8+wPdKDcZN9NMC3Fg51n5IaQ= +github.com/maddalax/htmgo/framework v1.0.2-0.20241025174132-df3edccd7fb0/go.mod h1:NGGzWVXWksrQJ9kV9SGa/A1F1Bjsgc08cN7ZVb98RqY= +github.com/mattn/go-sqlite3 v1.14.24 h1:tpSp2G2KyMnnQu99ngJ47EIkWVmliIizyZBfPrBWDRM= +github.com/mattn/go-sqlite3 v1.14.24/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= +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/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw= +golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U= +golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo= +golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0= +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/examples/simple-auth/htmgo.yml b/examples/simple-auth/htmgo.yml new file mode 100644 index 0000000..d60d2ff --- /dev/null +++ b/examples/simple-auth/htmgo.yml @@ -0,0 +1,10 @@ +# htmgo configuration + +# if tailwindcss is enabled, htmgo will automatically compile your tailwind and output it to assets/dist +tailwind: true + +# which directories to ignore when watching for changes, supports glob patterns through https://github.com/bmatcuk/doublestar +watch_ignore: [".git", "node_modules", "dist/*"] + +# files to watch for changes, supports glob patterns through https://github.com/bmatcuk/doublestar +watch_files: ["**/*.go", "**/*.css", "**/*.md"] diff --git a/examples/simple-auth/internal/db/db.go b/examples/simple-auth/internal/db/db.go new file mode 100644 index 0000000..41b7a34 --- /dev/null +++ b/examples/simple-auth/internal/db/db.go @@ -0,0 +1,31 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.27.0 + +package db + +import ( + "context" + "database/sql" +) + +type DBTX interface { + ExecContext(context.Context, string, ...interface{}) (sql.Result, error) + PrepareContext(context.Context, string) (*sql.Stmt, error) + QueryContext(context.Context, string, ...interface{}) (*sql.Rows, error) + QueryRowContext(context.Context, string, ...interface{}) *sql.Row +} + +func New(db DBTX) *Queries { + return &Queries{db: db} +} + +type Queries struct { + db DBTX +} + +func (q *Queries) WithTx(tx *sql.Tx) *Queries { + return &Queries{ + db: tx, + } +} diff --git a/examples/simple-auth/internal/db/models.go b/examples/simple-auth/internal/db/models.go new file mode 100644 index 0000000..a63cbae --- /dev/null +++ b/examples/simple-auth/internal/db/models.go @@ -0,0 +1,26 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.27.0 + +package db + +import ( + "database/sql" +) + +type Session struct { + ID int64 + UserID int64 + SessionID string + CreatedAt sql.NullString + ExpiresAt string +} + +type User struct { + ID int64 + Email string + Password string + Metadata interface{} + CreatedAt sql.NullString + UpdatedAt sql.NullString +} diff --git a/examples/simple-auth/internal/db/provider.go b/examples/simple-auth/internal/db/provider.go new file mode 100644 index 0000000..8bc1693 --- /dev/null +++ b/examples/simple-auth/internal/db/provider.go @@ -0,0 +1,25 @@ +package db + +import ( + "context" + "database/sql" + _ "embed" + _ "github.com/mattn/go-sqlite3" +) + +//go:embed schema.sql +var ddl string + +func Provide() *Queries { + db, err := sql.Open("sqlite3", "file:htmgo-user-example.db?cache=shared&_fk=1") + + if err != nil { + panic(err) + } + + if _, err := db.ExecContext(context.Background(), ddl); err != nil { + panic(err) + } + + return New(db) +} diff --git a/examples/simple-auth/internal/db/queries.sql b/examples/simple-auth/internal/db/queries.sql new file mode 100644 index 0000000..e96497e --- /dev/null +++ b/examples/simple-auth/internal/db/queries.sql @@ -0,0 +1,31 @@ +-- Queries for User Management + +-- name: CreateUser :one +INSERT INTO user (email, password, metadata) +VALUES (?, ?, ?) +RETURNING id; + +-- name: CreateSession :exec +INSERT INTO sessions (user_id, session_id, expires_at) +VALUES (?, ?, ?); + +-- name: GetUserByToken :one +SELECT u.* +FROM user u + JOIN sessions t ON u.id = t.user_id +WHERE t.session_id = ? + AND t.expires_at > datetime('now'); + +-- name: GetUserByID :one +SELECT * +FROM user +WHERE id = ?; + + +-- name: GetUserByEmail :one +SELECT * +FROM user +WHERE email = ?; + +-- name: UpdateUserMetadata :exec +UPDATE user SET metadata = json_patch(COALESCE(metadata, '{}'), ?) WHERE id = ?; diff --git a/examples/simple-auth/internal/db/queries.sql.go b/examples/simple-auth/internal/db/queries.sql.go new file mode 100644 index 0000000..eee80ac --- /dev/null +++ b/examples/simple-auth/internal/db/queries.sql.go @@ -0,0 +1,123 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.27.0 +// source: queries.sql + +package db + +import ( + "context" +) + +const createSession = `-- name: CreateSession :exec +INSERT INTO sessions (user_id, session_id, expires_at) +VALUES (?, ?, ?) +` + +type CreateSessionParams struct { + UserID int64 + SessionID string + ExpiresAt string +} + +func (q *Queries) CreateSession(ctx context.Context, arg CreateSessionParams) error { + _, err := q.db.ExecContext(ctx, createSession, arg.UserID, arg.SessionID, arg.ExpiresAt) + return err +} + +const createUser = `-- name: CreateUser :one + +INSERT INTO user (email, password, metadata) +VALUES (?, ?, ?) +RETURNING id +` + +type CreateUserParams struct { + Email string + Password string + Metadata interface{} +} + +// Queries for User Management +func (q *Queries) CreateUser(ctx context.Context, arg CreateUserParams) (int64, error) { + row := q.db.QueryRowContext(ctx, createUser, arg.Email, arg.Password, arg.Metadata) + var id int64 + err := row.Scan(&id) + return id, err +} + +const getUserByEmail = `-- name: GetUserByEmail :one +SELECT id, email, password, metadata, created_at, updated_at +FROM user +WHERE email = ? +` + +func (q *Queries) GetUserByEmail(ctx context.Context, email string) (User, error) { + row := q.db.QueryRowContext(ctx, getUserByEmail, email) + var i User + err := row.Scan( + &i.ID, + &i.Email, + &i.Password, + &i.Metadata, + &i.CreatedAt, + &i.UpdatedAt, + ) + return i, err +} + +const getUserByID = `-- name: GetUserByID :one +SELECT id, email, password, metadata, created_at, updated_at +FROM user +WHERE id = ? +` + +func (q *Queries) GetUserByID(ctx context.Context, id int64) (User, error) { + row := q.db.QueryRowContext(ctx, getUserByID, id) + var i User + err := row.Scan( + &i.ID, + &i.Email, + &i.Password, + &i.Metadata, + &i.CreatedAt, + &i.UpdatedAt, + ) + return i, err +} + +const getUserByToken = `-- name: GetUserByToken :one +SELECT u.id, u.email, u.password, u.metadata, u.created_at, u.updated_at +FROM user u + JOIN sessions t ON u.id = t.user_id +WHERE t.session_id = ? + AND t.expires_at > datetime('now') +` + +func (q *Queries) GetUserByToken(ctx context.Context, sessionID string) (User, error) { + row := q.db.QueryRowContext(ctx, getUserByToken, sessionID) + var i User + err := row.Scan( + &i.ID, + &i.Email, + &i.Password, + &i.Metadata, + &i.CreatedAt, + &i.UpdatedAt, + ) + return i, err +} + +const updateUserMetadata = `-- name: UpdateUserMetadata :exec +UPDATE user SET metadata = json_patch(COALESCE(metadata, '{}'), ?) WHERE id = ? +` + +type UpdateUserMetadataParams struct { + JsonPatch interface{} + ID int64 +} + +func (q *Queries) UpdateUserMetadata(ctx context.Context, arg UpdateUserMetadataParams) error { + _, err := q.db.ExecContext(ctx, updateUserMetadata, arg.JsonPatch, arg.ID) + return err +} diff --git a/examples/simple-auth/internal/db/schema.sql b/examples/simple-auth/internal/db/schema.sql new file mode 100644 index 0000000..e7b53a9 --- /dev/null +++ b/examples/simple-auth/internal/db/schema.sql @@ -0,0 +1,28 @@ +-- SQLite schema for User Management + +-- User table +CREATE TABLE IF NOT EXISTS user +( + id INTEGER PRIMARY KEY AUTOINCREMENT, + email TEXT NOT NULL UNIQUE, + password TEXT NOT NULL, + metadata JSON DEFAULT '{}', + created_at TEXT DEFAULT (datetime('now')), + updated_at TEXT DEFAULT (datetime('now')) +); + +-- Auth Token table +CREATE TABLE IF NOT EXISTS sessions +( + id INTEGER PRIMARY KEY AUTOINCREMENT, + user_id INTEGER NOT NULL, + session_id TEXT NOT NULL UNIQUE, + created_at TEXT DEFAULT (datetime('now')), + expires_at TEXT NOT NULL, + FOREIGN KEY (user_id) REFERENCES user (id) ON DELETE CASCADE +); + +-- Indexes to improve query performance +CREATE INDEX IF NOT EXISTS idx_user_email ON user (email); +CREATE INDEX IF NOT EXISTS idx_session_id ON sessions (session_id); +CREATE INDEX IF NOT EXISTS idx_auth_sessions_user_id ON sessions (user_id); diff --git a/examples/simple-auth/internal/embedded/os.go b/examples/simple-auth/internal/embedded/os.go new file mode 100644 index 0000000..ddfd55f --- /dev/null +++ b/examples/simple-auth/internal/embedded/os.go @@ -0,0 +1,17 @@ +package embedded + +import ( + "io/fs" + "os" +) + +type OsFs struct { +} + +func (receiver OsFs) Open(name string) (fs.File, error) { + return os.Open(name) +} + +func NewOsFs() OsFs { + return OsFs{} +} diff --git a/examples/simple-auth/internal/user/handler.go b/examples/simple-auth/internal/user/handler.go new file mode 100644 index 0000000..0ff3a97 --- /dev/null +++ b/examples/simple-auth/internal/user/handler.go @@ -0,0 +1,115 @@ +package user + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "github.com/maddalax/htmgo/framework/h" + "github.com/maddalax/htmgo/framework/service" + "simpleauth/internal/db" +) + +type CreateUserRequest struct { + Email string + Password string +} + +type LoginUserRequest struct { + Email string + Password string +} + +type CreatedUser struct { + Id string + Email string +} + +func Create(ctx *h.RequestContext, request CreateUserRequest) (int64, error) { + if len(request.Password) < 6 { + return 0, errors.New("password must be at least 6 characters long") + } + + queries := service.Get[db.Queries](ctx.ServiceLocator()) + + hashedPassword, err := HashPassword(request.Password) + + if err != nil { + return 0, errors.New("something went wrong") + } + + id, err := queries.CreateUser(context.Background(), db.CreateUserParams{ + Email: request.Email, + Password: hashedPassword, + }) + + if err != nil { + + if err.Error() == "UNIQUE constraint failed: user.email" { + return 0, errors.New("email already exists") + } + + return 0, err + } + + return id, nil +} + +func Login(ctx *h.RequestContext, request LoginUserRequest) (int64, error) { + + queries := service.Get[db.Queries](ctx.ServiceLocator()) + + user, err := queries.GetUserByEmail(context.Background(), request.Email) + + if err != nil { + fmt.Printf("error: %s\n", err.Error()) + return 0, errors.New("email or password is incorrect") + } + + if !PasswordMatches(request.Password, user.Password) { + return 0, errors.New("email or password is incorrect") + } + + session, err := CreateSession(ctx, user.ID) + + if err != nil { + return 0, errors.New("something went wrong") + } + + WriteSessionCookie(ctx, session) + + return user.ID, nil +} + +func ParseMeta(meta any) map[string]interface{} { + if meta == nil { + return map[string]interface{}{} + } + if m, ok := meta.(string); ok { + var dest map[string]interface{} + json.Unmarshal([]byte(m), &dest) + return dest + } + return meta.(map[string]interface{}) +} + +func GetMetaKey(meta map[string]interface{}, key string) string { + if val, ok := meta[key]; ok { + return val.(string) + } + return "" +} + +func SetMeta(ctx *h.RequestContext, userId int64, meta map[string]interface{}) error { + queries := service.Get[db.Queries](ctx.ServiceLocator()) + serialized, _ := json.Marshal(meta) + fmt.Printf("serialized: %s\n", string(serialized)) + err := queries.UpdateUserMetadata(context.Background(), db.UpdateUserMetadataParams{ + JsonPatch: serialized, + ID: userId, + }) + if err != nil { + return err + } + return nil +} diff --git a/examples/simple-auth/internal/user/http.go b/examples/simple-auth/internal/user/http.go new file mode 100644 index 0000000..8e1f462 --- /dev/null +++ b/examples/simple-auth/internal/user/http.go @@ -0,0 +1,17 @@ +package user + +import ( + "github.com/maddalax/htmgo/framework/h" + "simpleauth/internal/db" +) + +func GetUserOrRedirect(ctx *h.RequestContext) (db.User, bool) { + user, err := GetUserFromSession(ctx) + + if err != nil { + ctx.Redirect("/login", 302) + return db.User{}, false + } + + return user, true +} diff --git a/examples/simple-auth/internal/user/password.go b/examples/simple-auth/internal/user/password.go new file mode 100644 index 0000000..fd6a6a7 --- /dev/null +++ b/examples/simple-auth/internal/user/password.go @@ -0,0 +1,18 @@ +package user + +import ( + "golang.org/x/crypto/bcrypt" +) + +func HashPassword(password string) (string, error) { + hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) + if err != nil { + return "", err + } + return string(hashedPassword), nil +} + +func PasswordMatches(password string, hashedPassword string) bool { + err := bcrypt.CompareHashAndPassword([]byte(hashedPassword), []byte(password)) + return err == nil +} diff --git a/examples/simple-auth/internal/user/session.go b/examples/simple-auth/internal/user/session.go new file mode 100644 index 0000000..19cd5f8 --- /dev/null +++ b/examples/simple-auth/internal/user/session.go @@ -0,0 +1,83 @@ +package user + +import ( + "context" + "crypto/rand" + "encoding/hex" + "github.com/maddalax/htmgo/framework/h" + "github.com/maddalax/htmgo/framework/service" + "net/http" + "simpleauth/internal/db" + "time" +) + +type CreatedSession struct { + Id string + Expiration time.Time + UserId int64 +} + +func CreateSession(ctx *h.RequestContext, userId int64) (CreatedSession, error) { + sessionId, err := GenerateSessionID() + + if err != nil { + return CreatedSession{}, err + } + + // create a session in the database + queries := service.Get[db.Queries](ctx.ServiceLocator()) + + created := CreatedSession{ + Id: sessionId, + Expiration: time.Now().Add(time.Hour * 24), + UserId: userId, + } + + err = queries.CreateSession(context.Background(), db.CreateSessionParams{ + UserID: created.UserId, + SessionID: created.Id, + ExpiresAt: created.Expiration.Format(time.RFC3339), + }) + + if err != nil { + return CreatedSession{}, err + } + + return created, nil +} + +func GetUserFromSession(ctx *h.RequestContext) (db.User, error) { + cookie, err := ctx.Request.Cookie("session_id") + if err != nil { + return db.User{}, err + } + queries := service.Get[db.Queries](ctx.ServiceLocator()) + user, err := queries.GetUserByToken(context.Background(), cookie.Value) + if err != nil { + return db.User{}, err + } + return user, nil +} + +func WriteSessionCookie(ctx *h.RequestContext, session CreatedSession) { + cookie := http.Cookie{ + Name: "session_id", + Value: session.Id, + HttpOnly: true, + SameSite: http.SameSiteStrictMode, + Expires: session.Expiration, + Path: "/", + } + ctx.SetCookie(&cookie) +} + +func GenerateSessionID() (string, error) { + // Create a byte slice for storing the random bytes + bytes := make([]byte, 32) // 32 bytes = 256 bits, which is a secure length + // Read random bytes from crypto/rand + if _, err := rand.Read(bytes); err != nil { + return "", err + } + // Encode to hexadecimal to get a string representation + return hex.EncodeToString(bytes), nil +} diff --git a/examples/simple-auth/main.go b/examples/simple-auth/main.go new file mode 100644 index 0000000..ca237d4 --- /dev/null +++ b/examples/simple-auth/main.go @@ -0,0 +1,35 @@ +package main + +import ( + "github.com/maddalax/htmgo/framework/h" + "github.com/maddalax/htmgo/framework/service" + "io/fs" + "net/http" + "simpleauth/__htmgo" + "simpleauth/internal/db" +) + +func main() { + locator := service.NewLocator() + + service.Set(locator, service.Singleton, func() *db.Queries { + return db.Provide() + }) + + h.Start(h.AppOpts{ + ServiceLocator: locator, + LiveReload: true, + Register: func(app *h.App) { + sub, err := fs.Sub(GetStaticAssets(), "assets/dist") + + if err != nil { + panic(err) + } + + http.FileServerFS(sub) + + app.Router.Handle("/public/*", http.StripPrefix("/public", http.FileServerFS(sub))) + __htmgo.Register(app.Router) + }, + }) +} diff --git a/examples/simple-auth/pages/index.go b/examples/simple-auth/pages/index.go new file mode 100644 index 0000000..f159adc --- /dev/null +++ b/examples/simple-auth/pages/index.go @@ -0,0 +1,72 @@ +package pages + +import ( + "github.com/maddalax/htmgo/framework/h" + "simpleauth/internal/db" + "simpleauth/internal/user" + "simpleauth/partials" + "simpleauth/ui" +) + +func IndexPage(ctx *h.RequestContext) *h.Page { + u, ok := user.GetUserOrRedirect(ctx) + if !ok { + return nil + } + return h.NewPage( + RootPage(UserProfilePage(u)), + ) +} + +func UserProfilePage(u db.User) *h.Element { + + meta := user.ParseMeta(u.Metadata) + + return h.Div( + h.Class("flex flex-col gap-6 items-center pt-10 min-h-screen bg-neutral-100"), + h.H3F( + "User Profile", + h.Class("text-2xl font-bold"), + ), + h.Pf("Welcome, %s!", u.Email), + h.Form( + h.Attribute("hx-swap", "none"), + h.PostPartial(partials.UpdateProfile), + h.TriggerChildren(), + h.Class("flex flex-col gap-4 w-full max-w-md p-6 bg-white rounded-md shadow-md"), + ui.Input(ui.InputProps{ + Id: "email", + Name: "email", + Label: "Email Address", + Type: "email", + DefaultValue: u.Email, + Children: []h.Ren{ + h.Disabled(), + }, + }), + ui.Input(ui.InputProps{ + Name: "birth-date", + Label: "Birth Date", + DefaultValue: user.GetMetaKey(meta, "birthDate"), + Type: "date", + }), + ui.Input(ui.InputProps{ + Name: "favorite-color", + Label: "Favorite Color", + DefaultValue: user.GetMetaKey(meta, "favoriteColor"), + }), + ui.Input(ui.InputProps{ + Name: "occupation", + Label: "Occupation", + DefaultValue: user.GetMetaKey(meta, "occupation"), + }), + ui.FormError(""), + ui.SubmitButton("Save Changes"), + ), + h.A( + h.Text("Log out"), + h.Href("/logout"), + h.Class("text-blue-400"), + ), + ) +} diff --git a/examples/simple-auth/pages/login.go b/examples/simple-auth/pages/login.go new file mode 100644 index 0000000..a9b148d --- /dev/null +++ b/examples/simple-auth/pages/login.go @@ -0,0 +1,49 @@ +package pages + +import ( + "github.com/maddalax/htmgo/framework/h" + "simpleauth/partials" + "simpleauth/ui" +) + +func Login(ctx *h.RequestContext) *h.Page { + return h.NewPage( + RootPage( + ui.CenteredForm(ui.CenteredFormProps{ + Title: "Sign In", + SubmitText: "Sign In", + PostUrl: h.GetPartialPath(partials.LoginUser), + Children: []h.Ren{ + ui.Input(ui.InputProps{ + Id: "username", + Name: "email", + Label: "Email Address", + Type: "email", + Required: true, + Children: []h.Ren{ + h.Attribute("autocomplete", "off"), + h.MaxLength(50), + }, + }), + + ui.Input(ui.InputProps{ + Id: "password", + Name: "password", + Label: "Password", + Type: "password", + Required: true, + Children: []h.Ren{ + h.MinLength(6), + }, + }), + + h.A( + h.Href("/register"), + h.Text("Don't have an account? Register here"), + h.Class("text-blue-500"), + ), + }, + }), + ), + ) +} diff --git a/examples/simple-auth/pages/logout.go b/examples/simple-auth/pages/logout.go new file mode 100644 index 0000000..3655a42 --- /dev/null +++ b/examples/simple-auth/pages/logout.go @@ -0,0 +1,23 @@ +package pages + +import "github.com/maddalax/htmgo/framework/h" + +func LogoutPage(ctx *h.RequestContext) *h.Page { + + // clear the session cookie + ctx.Response.Header().Set( + "Set-Cookie", + "session_id=; Path=/; Max-Age=0", + ) + + ctx.Response.Header().Set( + "Location", + "/login", + ) + + ctx.Response.WriteHeader( + 302, + ) + + return nil +} diff --git a/examples/simple-auth/pages/register.go b/examples/simple-auth/pages/register.go new file mode 100644 index 0000000..476c180 --- /dev/null +++ b/examples/simple-auth/pages/register.go @@ -0,0 +1,49 @@ +package pages + +import ( + "github.com/maddalax/htmgo/framework/h" + "simpleauth/partials" + "simpleauth/ui" +) + +func Register(ctx *h.RequestContext) *h.Page { + return h.NewPage( + RootPage( + ui.CenteredForm(ui.CenteredFormProps{ + PostUrl: h.GetPartialPath(partials.RegisterUser), + Title: "Create an Account", + SubmitText: "Register", + Children: []h.Ren{ + ui.Input(ui.InputProps{ + Id: "username", + Name: "email", + Label: "Email Address", + Type: "email", + Required: true, + Children: []h.Ren{ + h.Attribute("autocomplete", "off"), + h.MaxLength(50), + }, + }), + + ui.Input(ui.InputProps{ + Id: "password", + Name: "password", + Label: "Password", + Type: "password", + Required: true, + Children: []h.Ren{ + h.MinLength(6), + }, + }), + + h.A( + h.Href("/login"), + h.Text("Already have an account? Login here"), + h.Class("text-blue-500"), + ), + }, + }), + ), + ) +} diff --git a/examples/simple-auth/pages/root.go b/examples/simple-auth/pages/root.go new file mode 100644 index 0000000..510163e --- /dev/null +++ b/examples/simple-auth/pages/root.go @@ -0,0 +1,34 @@ +package pages + +import ( + "github.com/maddalax/htmgo/framework/h" +) + +func RootPage(children ...h.Ren) h.Ren { + return h.Html( + h.HxExtensions( + h.BaseExtensions(), + ), + h.Head( + h.Meta("viewport", "width=device-width, initial-scale=1"), + h.Link("/public/favicon.ico", "icon"), + h.Link("/public/apple-touch-icon.png", "apple-touch-icon"), + h.Meta("title", "htmgo template"), + h.Meta("charset", "utf-8"), + h.Meta("author", "htmgo"), + h.Meta("description", "this is a template"), + h.Meta("og:title", "htmgo template"), + h.Meta("og:url", "https://htmgo.dev"), + h.Link("canonical", "https://htmgo.dev"), + h.Meta("og:description", "this is a template"), + h.Link("/public/main.css", "stylesheet"), + h.Script("/public/htmgo.js"), + ), + h.Body( + h.Div( + h.Class("flex flex-col gap-2 bg-white h-full"), + h.Fragment(children...), + ), + ), + ) +} diff --git a/examples/simple-auth/partials/profile.go b/examples/simple-auth/partials/profile.go new file mode 100644 index 0000000..8f18d2f --- /dev/null +++ b/examples/simple-auth/partials/profile.go @@ -0,0 +1,36 @@ +package partials + +import ( + "github.com/maddalax/htmgo/framework/h" + "log/slog" + "simpleauth/internal/user" + "simpleauth/ui" +) + +func UpdateProfile(ctx *h.RequestContext) *h.Partial { + if !ctx.IsHttpPost() { + return nil + } + + patch := map[string]any{ + "birthDate": ctx.FormValue("birth-date"), + "favoriteColor": ctx.FormValue("favorite-color"), + "occupation": ctx.FormValue("occupation"), + } + + u, ok := user.GetUserOrRedirect(ctx) + + if !ok { + return nil + } + + err := user.SetMeta(ctx, u.ID, patch) + + if err != nil { + slog.Error("failed to update user profile", slog.String("error", err.Error())) + ctx.Response.WriteHeader(400) + return ui.SwapFormError(ctx, "something went wrong") + } + + return h.RedirectPartial("/") +} diff --git a/examples/simple-auth/partials/user.go b/examples/simple-auth/partials/user.go new file mode 100644 index 0000000..1023e6f --- /dev/null +++ b/examples/simple-auth/partials/user.go @@ -0,0 +1,62 @@ +package partials + +import ( + "github.com/maddalax/htmgo/framework/h" + "simpleauth/internal/user" + "simpleauth/ui" +) + +func RegisterUser(ctx *h.RequestContext) *h.Partial { + if !ctx.IsHttpPost() { + return nil + } + + payload := user.CreateUserRequest{ + Email: ctx.FormValue("email"), + Password: ctx.FormValue("password"), + } + + id, err := user.Create( + ctx, + payload, + ) + + if err != nil { + ctx.Response.WriteHeader(400) + return ui.SwapFormError(ctx, err.Error()) + } + + session, err := user.CreateSession(ctx, id) + + if err != nil { + ctx.Response.WriteHeader(500) + return ui.SwapFormError(ctx, "something went wrong") + } + + user.WriteSessionCookie(ctx, session) + + return h.RedirectPartial("/") +} + +func LoginUser(ctx *h.RequestContext) *h.Partial { + if !ctx.IsHttpPost() { + return nil + } + + payload := user.LoginUserRequest{ + Email: ctx.FormValue("email"), + Password: ctx.FormValue("password"), + } + + _, err := user.Login( + ctx, + payload, + ) + + if err != nil { + ctx.Response.WriteHeader(400) + return ui.SwapFormError(ctx, err.Error()) + } + + return h.RedirectPartial("/") +} diff --git a/examples/simple-auth/sqlc.yaml b/examples/simple-auth/sqlc.yaml new file mode 100644 index 0000000..30c0518 --- /dev/null +++ b/examples/simple-auth/sqlc.yaml @@ -0,0 +1,9 @@ +version: "2" +sql: + - schema: "internal/db/schema.sql" + queries: "internal/db/queries.sql" + engine: "sqlite" + gen: + go: + package: "db" + out: "internal/db" diff --git a/examples/simple-auth/tailwind.config.js b/examples/simple-auth/tailwind.config.js new file mode 100644 index 0000000..b18125c --- /dev/null +++ b/examples/simple-auth/tailwind.config.js @@ -0,0 +1,5 @@ +/** @type {import('tailwindcss').Config} */ +module.exports = { + content: ["**/*.go"], + plugins: [], +}; diff --git a/examples/simple-auth/ui/button.go b/examples/simple-auth/ui/button.go new file mode 100644 index 0000000..015a5e0 --- /dev/null +++ b/examples/simple-auth/ui/button.go @@ -0,0 +1,41 @@ +package ui + +import ( + "github.com/maddalax/htmgo/framework/h" + "github.com/maddalax/htmgo/framework/js" +) + +func SubmitButton(submitText string) *h.Element { + buttonClasses := "rounded items-center px-3 py-2 bg-slate-800 text-white w-full text-center" + + return h.Div( + h.HxBeforeRequest( + js.RemoveClassOnChildren(".loading", "hidden"), + js.SetClassOnChildren(".submit", "hidden"), + ), + h.HxAfterRequest( + js.SetClassOnChildren(".loading", "hidden"), + js.RemoveClassOnChildren(".submit", "hidden"), + ), + h.Class("flex gap-2 justify-center"), + h.Button( + h.Class("loading hidden relative text-center", buttonClasses), + spinner(), + h.Disabled(), + h.Text("Submitting..."), + ), + h.Button( + h.Type("submit"), + h.Class("submit", buttonClasses), + h.Text(submitText), + ), + ) +} + +func spinner(children ...h.Ren) *h.Element { + return h.Div( + h.Children(children...), + h.Class("absolute left-1 spinner spinner-border animate-spin inline-block w-6 h-6 border-4 rounded-full border-slate-200 border-t-transparent"), + h.Attribute("role", "status"), + ) +} diff --git a/examples/simple-auth/ui/error.go b/examples/simple-auth/ui/error.go new file mode 100644 index 0000000..47d1eac --- /dev/null +++ b/examples/simple-auth/ui/error.go @@ -0,0 +1,20 @@ +package ui + +import "github.com/maddalax/htmgo/framework/h" + +func FormError(error string) *h.Element { + return h.Div( + h.Id("form-error"), + h.Text(error), + h.If( + error != "", + h.Class("p-4 bg-rose-400 text-white rounded"), + ), + ) +} + +func SwapFormError(ctx *h.RequestContext, error string) *h.Partial { + return h.SwapPartial(ctx, + FormError(error), + ) +} diff --git a/examples/simple-auth/ui/input.go b/examples/simple-auth/ui/input.go new file mode 100644 index 0000000..6e302fb --- /dev/null +++ b/examples/simple-auth/ui/input.go @@ -0,0 +1,81 @@ +package ui + +import ( + "github.com/maddalax/htmgo/framework/h" + "github.com/maddalax/htmgo/framework/hx" +) + +type InputProps struct { + Id string + Label string + Name string + Type string + DefaultValue string + Placeholder string + Required bool + ValidationPath string + Error string + Children []h.Ren +} + +func Input(props InputProps) *h.Element { + validation := h.If( + props.ValidationPath != "", + h.Children( + h.Post(props.ValidationPath, hx.BlurEvent), + h.Attribute("hx-swap", "innerHTML transition:true"), + h.Attribute("hx-target", "next div"), + ), + ) + + if props.Type == "" { + props.Type = "text" + } + + input := h.Input( + props.Type, + h.Class("border p-2 rounded focus:outline-none focus:ring focus:ring-slate-800"), + h.If( + props.Name != "", + h.Name(props.Name), + ), + h.If( + props.Children != nil, + h.Children(props.Children...), + ), + h.If( + props.Required, + h.Required(), + ), + h.If( + props.Placeholder != "", + h.Placeholder(props.Placeholder), + ), + h.If( + props.DefaultValue != "", + h.Attribute("value", props.DefaultValue), + ), + validation, + ) + + wrapped := h.Div( + h.If( + props.Id != "", + h.Id(props.Id), + ), + h.Class("flex flex-col gap-1"), + h.If( + props.Label != "", + h.Label( + h.Text(props.Label), + ), + ), + input, + h.Div( + h.Id(props.Id+"-error"), + h.Class("text-red-500"), + ), + ) + + return wrapped +} diff --git a/examples/simple-auth/ui/login.go b/examples/simple-auth/ui/login.go new file mode 100644 index 0000000..50cf046 --- /dev/null +++ b/examples/simple-auth/ui/login.go @@ -0,0 +1,36 @@ +package ui + +import ( + "github.com/maddalax/htmgo/framework/h" +) + +type CenteredFormProps struct { + Title string + Children []h.Ren + SubmitText string + PostUrl string +} + +func CenteredForm(props CenteredFormProps) *h.Element { + return h.Div( + h.Class("flex flex-col items-center justify-center min-h-screen bg-neutral-100"), + h.Div( + h.Class("bg-white p-8 rounded-lg shadow-lg w-full max-w-md"), + h.H2F( + props.Title, + h.Class("text-3xl font-bold text-center mb-6"), + ), + h.Form( + h.TriggerChildren(), + h.Post(props.PostUrl), + h.Attribute("hx-swap", "none"), + h.Class("flex flex-col gap-4"), + h.Children(props.Children...), + // Error message + FormError(""), + // Submit button at the bottom + SubmitButton(props.SubmitText), + ), + ), + ) +} diff --git a/examples/todo-list/go.mod b/examples/todo-list/go.mod index c361433..9d1467e 100644 --- a/examples/todo-list/go.mod +++ b/examples/todo-list/go.mod @@ -5,7 +5,7 @@ go 1.23.0 require ( entgo.io/ent v0.14.1 github.com/google/uuid v1.6.0 - github.com/maddalax/htmgo/framework v0.0.0-20240930180419-e33ab7366d58 + github.com/maddalax/htmgo/framework v1.0.2-0.20241025174132-df3edccd7fb0 github.com/mattn/go-sqlite3 v1.14.23 ) diff --git a/examples/todo-list/go.sum b/examples/todo-list/go.sum index 889771d..6ebdaf9 100644 --- a/examples/todo-list/go.sum +++ b/examples/todo-list/go.sum @@ -33,8 +33,8 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= -github.com/maddalax/htmgo/framework v0.0.0-20240930180419-e33ab7366d58 h1:G1ZKaigLbmtKWy67XMhulKm4qXnAjRdrFiymCM+zX+U= -github.com/maddalax/htmgo/framework v0.0.0-20240930180419-e33ab7366d58/go.mod h1:HYKI49Pb6oyY2opSJdTt145B1vWgfWIDohvlolynv80= +github.com/maddalax/htmgo/framework v1.0.2-0.20241025174132-df3edccd7fb0 h1:K9Q5b7BmbpCPJFjrAHS8+wPdKDcZN9NMC3Fg51n5IaQ= +github.com/maddalax/htmgo/framework v1.0.2-0.20241025174132-df3edccd7fb0/go.mod h1:NGGzWVXWksrQJ9kV9SGa/A1F1Bjsgc08cN7ZVb98RqY= github.com/mattn/go-sqlite3 v1.14.23 h1:gbShiuAP1W5j9UOksQ06aiiqPMxYecovVGwmTxWtuw0= github.com/mattn/go-sqlite3 v1.14.23/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7 h1:DpOJ2HYzCv8LZP15IdmG+YdwD2luVPHITV96TkirNBM= diff --git a/examples/todo-list/pages/base/root.go b/examples/todo-list/pages/base/root.go index 7a9ca91..f612a62 100644 --- a/examples/todo-list/pages/base/root.go +++ b/examples/todo-list/pages/base/root.go @@ -6,7 +6,9 @@ import ( func RootPage(children ...h.Ren) h.Ren { return h.Html( - h.HxExtension(h.BaseExtensions()), + h.HxExtension( + h.BaseExtensions(), + ), h.Head( h.Meta("viewport", "width=device-width, initial-scale=1"), h.Meta("title", "htmgo todo mvc"), diff --git a/examples/todo-list/pages/index.go b/examples/todo-list/pages/index.go index 65b5cac..2cc736d 100644 --- a/examples/todo-list/pages/index.go +++ b/examples/todo-list/pages/index.go @@ -10,7 +10,10 @@ import ( func TaskListPage(ctx *h.RequestContext) *h.Page { title := h.Div( - h.H1(h.Class("text-7xl font-extralight text-rose-500 tracking-wide"), h.Text("todos")), + h.H1( + h.Class("text-7xl font-extralight text-rose-500 tracking-wide"), + h.Text("todos"), + ), ) return h.NewPage(base.RootPage( @@ -21,7 +24,9 @@ func TaskListPage(ctx *h.RequestContext) *h.Page { title, task.Card(ctx), h.Children( - h.Div(h.Text("Double-click to edit a todo")), + h.Div( + h.Text("Double-click to edit a todo"), + ), ), ), ), diff --git a/examples/todo-list/partials/task/task.go b/examples/todo-list/partials/task/task.go index 29498ab..f307a57 100644 --- a/examples/todo-list/partials/task/task.go +++ b/examples/todo-list/partials/task/task.go @@ -58,7 +58,9 @@ func Input(list []*ent.Task) *h.Element { h.Name("name"), h.Class("pl-12 text-xl p-4 w-full outline-none focus:outline-2 focus:outline-rose-400"), h.Placeholder("What needs to be done?"), - h.Post(h.GetPartialPath(Create)), + h.Post( + h.GetPartialPath(Create), + ), h.HxTrigger(hx.OnEvent(hx.TriggerKeyUpEnter)), ), CompleteAllIcon(list), @@ -66,23 +68,34 @@ func Input(list []*ent.Task) *h.Element { } func CompleteAllIcon(list []*ent.Task) *h.Element { - notCompletedCount := len(h.Filter(list, func(item *ent.Task) bool { - return item.CompletedAt == nil - })) + notCompletedCount := len( + h.Filter(list, func(item *ent.Task) bool { + return item.CompletedAt == nil + }), + ) return h.Div( h.ClassX("absolute top-1 left-5 p-2 rotate-90 text-3xl cursor-pointer", map[string]bool{ "text-slate-400": notCompletedCount > 0, - }), h.UnsafeRaw("›"), - h.PostPartialWithQs(CompleteAll, h.NewQs("complete", h.Ternary(notCompletedCount > 0, "true", "false"))), + }), + h.UnsafeRaw("›"), + h.PostPartialWithQs( + CompleteAll, + h.NewQs( + "complete", + h.Ternary(notCompletedCount > 0, "true", "false"), + ), + ), ) } func Footer(list []*ent.Task, activeTab Tab) *h.Element { - notCompletedCount := len(h.Filter(list, func(item *ent.Task) bool { - return item.CompletedAt == nil - })) + notCompletedCount := len( + h.Filter(list, func(item *ent.Task) bool { + return item.CompletedAt == nil + }), + ) tabs := []Tab{TabAll, TabActive, TabComplete} @@ -96,7 +109,12 @@ func Footer(list []*ent.Task, activeTab Tab) *h.Element { h.Class("flex items-center gap-4"), h.List(tabs, func(tab Tab, index int) *h.Element { return h.P( - h.PostOnClick(h.GetPartialPathWithQs(ChangeTab, h.NewQs("tab", tab))), + h.PostOnClick( + h.GetPartialPathWithQs( + ChangeTab, + h.NewQs("tab", tab), + ), + ), h.ClassX("cursor-pointer px-2 py-1 rounded", map[string]bool{ "border border-rose-600": activeTab == tab, }), @@ -139,12 +157,14 @@ func Task(task *ent.Task, editing bool) *h.Element { "border border-b-slate-100": !editing, }), CompleteIcon(task), - h.IfElse(editing, + h.IfElse( + editing, h.Div( h.Class("flex-1 h-full"), h.Form( h.Class("h-full"), - h.Input("text", + h.Input( + "text", h.Name("task"), h.Value(task.ID.String()), h.Class("hidden"), @@ -168,30 +188,43 @@ func Task(task *ent.Task, editing bool) *h.Element { ), ), h.P( - h.GetPartialWithQs(EditNameForm, h.NewQs("id", task.ID.String()), hx.TriggerDblClick), + h.GetPartialWithQs( + EditNameForm, + h.NewQs("id", task.ID.String()), + hx.TriggerDblClick, + ), h.ClassX("text-xl break-all text-wrap truncate", map[string]bool{ "line-through text-slate-400": task.CompletedAt != nil, }), h.Text(task.Name), - )), + ), + ), ) } func CompleteIcon(task *ent.Task) *h.Element { return h.Div( h.HxTrigger(hx.OnClick()), - h.Post(h.GetPartialPathWithQs(ToggleCompleted, h.NewQs("id", task.ID.String()))), + h.Post( + h.GetPartialPathWithQs( + ToggleCompleted, + h.NewQs("id", task.ID.String()), + ), + ), h.Class("flex items-center justify-center cursor-pointer"), h.Div( h.ClassX("w-10 h-10 border rounded-full flex items-center justify-center", map[string]bool{ "border-green-500": task.CompletedAt != nil, "border-slate-400": task.CompletedAt == nil, }), - h.If(task.CompletedAt != nil, h.UnsafeRaw(` + h.If( + task.CompletedAt != nil, + h.UnsafeRaw(` - `)), + `), + ), ), ) } @@ -199,46 +232,75 @@ func CompleteIcon(task *ent.Task) *h.Element { func UpdateName(ctx *h.RequestContext) *h.Partial { id, err := uuid.Parse(ctx.FormValue("task")) if err != nil { - return h.NewPartial(h.Div(h.Text("invalid id"))) + return h.NewPartial( + h.Div( + h.Text("invalid id"), + ), + ) } name := ctx.FormValue("name") if name == "" { - return h.NewPartial(h.Div(h.Text("name is required"))) + return h.NewPartial( + h.Div( + h.Text("name is required"), + ), + ) } if len(name) > 150 { - return h.NewPartial(h.Div(h.Text("task must be less than 150 characters"))) + return h.NewPartial( + h.Div( + h.Text("task must be less than 150 characters"), + ), + ) } service := tasks.NewService(ctx) task, err := service.Get(id) if task == nil { - return h.NewPartial(h.Div(h.Text("task not found"))) + return h.NewPartial( + h.Div( + h.Text("task not found"), + ), + ) } task, err = service.SetName(task.ID, name) if err != nil { - return h.NewPartial(h.Div(h.Text("failed to update"))) + return h.NewPartial( + h.Div( + h.Text("failed to update"), + ), + ) } return h.NewPartial( - h.OobSwap(ctx, Task(task, false))) + h.OobSwap(ctx, Task(task, false)), + ) } func EditNameForm(ctx *h.RequestContext) *h.Partial { id, err := uuid.Parse(ctx.QueryParam("id")) if err != nil { - return h.NewPartial(h.Div(h.Text("invalid id"))) + return h.NewPartial( + h.Div( + h.Text("invalid id"), + ), + ) } service := tasks.NewService(ctx) task, err := service.Get(id) if task == nil { - return h.NewPartial(h.Div(h.Text("task not found"))) + return h.NewPartial( + h.Div( + h.Text("task not found"), + ), + ) } return h.NewPartial( @@ -249,21 +311,36 @@ func EditNameForm(ctx *h.RequestContext) *h.Partial { func ToggleCompleted(ctx *h.RequestContext) *h.Partial { id, err := uuid.Parse(ctx.QueryParam("id")) if err != nil { - return h.NewPartial(h.Div(h.Text("invalid id"))) + return h.NewPartial( + h.Div( + h.Text("invalid id"), + ), + ) } service := tasks.NewService(ctx) task, err := service.Get(id) if task == nil { - return h.NewPartial(h.Div(h.Text("task not found"))) + return h.NewPartial( + h.Div( + h.Text("task not found"), + ), + ) } - task, err = service.SetCompleted(task.ID, h. - Ternary(task.CompletedAt == nil, true, false)) + task, err = service.SetCompleted( + task.ID, + h. + Ternary(task.CompletedAt == nil, true, false), + ) if err != nil { - return h.NewPartial(h.Div(h.Text("failed to update"))) + return h.NewPartial( + h.Div( + h.Text("failed to update"), + ), + ) } list, _ := service.List() @@ -282,7 +359,9 @@ func CompleteAll(ctx *h.RequestContext) *h.Partial { list, _ := service.List() - return h.NewPartial(h.OobSwap(ctx, CardBody(list, getActiveTab(ctx)))) + return h.NewPartial( + h.OobSwap(ctx, CardBody(list, getActiveTab(ctx))), + ) } func ClearCompleted(ctx *h.RequestContext) *h.Partial { @@ -291,7 +370,9 @@ func ClearCompleted(ctx *h.RequestContext) *h.Partial { list, _ := service.List() - return h.NewPartial(h.OobSwap(ctx, CardBody(list, getActiveTab(ctx)))) + return h.NewPartial( + h.OobSwap(ctx, CardBody(list, getActiveTab(ctx))), + ) } func Create(ctx *h.RequestContext) *h.Partial { @@ -300,7 +381,9 @@ func Create(ctx *h.RequestContext) *h.Partial { if len(name) > 150 { return h.NewPartial( h.Div( - h.HxOnLoad(js.Alert("Task must be less than 150 characters")), + h.HxOnLoad( + js.Alert("Task must be less than 150 characters"), + ), ), ) } @@ -312,7 +395,9 @@ func Create(ctx *h.RequestContext) *h.Partial { if list != nil && len(list) >= 100 { return h.NewPartial( h.Div( - h.HxOnLoad(js.Alert("There are too many tasks, please complete and clear some.")), + h.HxOnLoad( + js.Alert("There are too many tasks, please complete and clear some."), + ), ), ) } @@ -322,7 +407,11 @@ func Create(ctx *h.RequestContext) *h.Partial { }) if err != nil { - return h.NewPartial(h.Div(h.Text("failed to create"))) + return h.NewPartial( + h.Div( + h.Text("failed to create"), + ), + ) } list, err = service.List() @@ -338,8 +427,12 @@ func ChangeTab(ctx *h.RequestContext) *h.Partial { tab := ctx.QueryParam("tab") - return h.SwapManyPartialWithHeaders(ctx, - h.PushQsHeader(ctx, h.NewQs("tab", tab)), + return h.SwapManyPartialWithHeaders( + ctx, + h.PushQsHeader( + ctx, + h.NewQs("tab", tab), + ), List(list, tab), Footer(list, tab), ) diff --git a/framework-ui/go.mod b/framework-ui/go.mod index 9815b52..8c49a12 100644 --- a/framework-ui/go.mod +++ b/framework-ui/go.mod @@ -2,7 +2,7 @@ module github.com/maddalax/htmgo/framework-ui go 1.23.0 -require github.com/maddalax/htmgo/framework v0.0.0-20240930180419-e33ab7366d58 +require github.com/maddalax/htmgo/framework v1.0.2-0.20241025174132-df3edccd7fb0 require ( github.com/go-chi/chi/v5 v5.1.0 // indirect diff --git a/framework-ui/go.sum b/framework-ui/go.sum index cce2d15..f050d04 100644 --- a/framework-ui/go.sum +++ b/framework-ui/go.sum @@ -4,8 +4,8 @@ github.com/go-chi/chi/v5 v5.1.0 h1:acVI1TYaD+hhedDJ3r54HyA6sExp3HfXq7QWEEY/xMw= github.com/go-chi/chi/v5 v5.1.0/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/maddalax/htmgo/framework v0.0.0-20240930180419-e33ab7366d58 h1:G1ZKaigLbmtKWy67XMhulKm4qXnAjRdrFiymCM+zX+U= -github.com/maddalax/htmgo/framework v0.0.0-20240930180419-e33ab7366d58/go.mod h1:HYKI49Pb6oyY2opSJdTt145B1vWgfWIDohvlolynv80= +github.com/maddalax/htmgo/framework v1.0.2-0.20241025174132-df3edccd7fb0 h1:K9Q5b7BmbpCPJFjrAHS8+wPdKDcZN9NMC3Fg51n5IaQ= +github.com/maddalax/htmgo/framework v1.0.2-0.20241025174132-df3edccd7fb0/go.mod h1:NGGzWVXWksrQJ9kV9SGa/A1F1Bjsgc08cN7ZVb98RqY= 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/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= diff --git a/framework/assets/dist/htmgo.js b/framework/assets/dist/htmgo.js index d691193..df752a2 100644 --- a/framework/assets/dist/htmgo.js +++ b/framework/assets/dist/htmgo.js @@ -1,3 +1 @@ -var te=function(){let htmx={onLoad:null,process:null,on:null,off:null,trigger:null,ajax:null,find:null,findAll:null,closest:null,values:function(e,t){return getInputValues(e,t||"post").values},remove:null,addClass:null,removeClass:null,toggleClass:null,takeClass:null,swap:null,defineExtension:null,removeExtension:null,logAll:null,logNone:null,logger:null,config:{historyEnabled:!0,historyCacheSize:10,refreshOnHistoryMiss:!1,defaultSwapStyle:"innerHTML",defaultSwapDelay:0,defaultSettleDelay:20,includeIndicatorStyles:!0,indicatorClass:"htmx-indicator",requestClass:"htmx-request",addedClass:"htmx-added",settlingClass:"htmx-settling",swappingClass:"htmx-swapping",allowEval:!0,allowScriptTags:!0,inlineScriptNonce:"",inlineStyleNonce:"",attributesToSettle:["class","style","width","height"],withCredentials:!1,timeout:0,wsReconnectDelay:"full-jitter",wsBinaryType:"blob",disableSelector:"[hx-disable], [data-hx-disable]",scrollBehavior:"instant",defaultFocusScroll:!1,getCacheBusterParam:!1,globalViewTransitions:!1,methodsThatUseUrlParams:["get","delete"],selfRequestsOnly:!0,ignoreTitle:!1,scrollIntoViewOnBoost:!0,triggerSpecsCache:null,disableInheritance:!1,responseHandling:[{code:"204",swap:!1},{code:"[23]..",swap:!0},{code:"[45]..",swap:!1,error:!0}],allowNestedOobSwaps:!0},parseInterval:null,_:null,version:"2.0.2"};htmx.onLoad=onLoadHelper,htmx.process=processNode,htmx.on=addEventListenerImpl,htmx.off=removeEventListenerImpl,htmx.trigger=triggerEvent,htmx.ajax=ajaxHelper,htmx.find=find,htmx.findAll=findAll,htmx.closest=closest,htmx.remove=removeElement,htmx.addClass=addClassToElement,htmx.removeClass=removeClassFromElement,htmx.toggleClass=toggleClassOnElement,htmx.takeClass=takeClassForElement,htmx.swap=swap,htmx.defineExtension=defineExtension,htmx.removeExtension=removeExtension,htmx.logAll=logAll,htmx.logNone=logNone,htmx.parseInterval=parseInterval,htmx._=internalEval;let internalAPI={addTriggerHandler,bodyContains,canAccessLocalStorage,findThisElement,filterValues,swap,hasAttribute,getAttributeValue,getClosestAttributeValue,getClosestMatch,getExpressionVars,getHeaders,getInputValues,getInternalData,getSwapSpecification,getTriggerSpecs,getTarget,makeFragment,mergeObjects,makeSettleInfo,oobSwap,querySelectorExt,settleImmediately,shouldCancel,triggerEvent,triggerErrorEvent,withExtensions},VERBS=["get","post","put","delete","patch"],VERB_SELECTOR=VERBS.map(function(e){return "[hx-"+e+"], [data-hx-"+e+"]"}).join(", "),HEAD_TAG_REGEX=makeTagRegEx("head");function makeTagRegEx(e,t=!1){return new RegExp(`<${e}(\\s[^>]*>|>)([\\s\\S]*?)<\\/${e}>`,t?"gim":"im")}function parseInterval(e){if(e==null)return;let t=NaN;return e.slice(-2)=="ms"?t=parseFloat(e.slice(0,-2)):e.slice(-1)=="s"?t=parseFloat(e.slice(0,-1))*1e3:e.slice(-1)=="m"?t=parseFloat(e.slice(0,-1))*1e3*60:t=parseFloat(e),isNaN(t)?void 0:t}function getRawAttribute(e,t){return e instanceof Element&&e.getAttribute(t)}function hasAttribute(e,t){return !!e.hasAttribute&&(e.hasAttribute(t)||e.hasAttribute("data-"+t))}function getAttributeValue(e,t){return getRawAttribute(e,t)||getRawAttribute(e,"data-"+t)}function parentElt(e){let t=e.parentElement;return !t&&e.parentNode instanceof ShadowRoot?e.parentNode:t}function getDocument(){return document}function getRootNode(e,t){return e.getRootNode?e.getRootNode({composed:t}):getDocument()}function getClosestMatch(e,t){for(;e&&!t(e);)e=parentElt(e);return e||null}function getAttributeValueWithDisinheritance(e,t,n){let r=getAttributeValue(t,n),o=getAttributeValue(t,"hx-disinherit");var i=getAttributeValue(t,"hx-inherit");if(e!==t){if(htmx.config.disableInheritance)return i&&(i==="*"||i.split(" ").indexOf(n)>=0)?r:null;if(o&&(o==="*"||o.split(" ").indexOf(n)>=0))return "unset"}return r}function getClosestAttributeValue(e,t){let n=null;if(getClosestMatch(e,function(r){return !!(n=getAttributeValueWithDisinheritance(e,asElement(r),t))}),n!=="unset")return n}function matches(e,t){let n=e instanceof Element&&(e.matches||e.matchesSelector||e.msMatchesSelector||e.mozMatchesSelector||e.webkitMatchesSelector||e.oMatchesSelector);return !!n&&n.call(e,t)}function getStartTag(e){let n=/<([a-z][^\/\0>\x20\t\r\n\f]*)/i.exec(e);return n?n[1].toLowerCase():""}function parseHTML(e){return new DOMParser().parseFromString(e,"text/html")}function takeChildrenFor(e,t){for(;t.childNodes.length>0;)e.append(t.childNodes[0]);}function duplicateScript(e){let t=getDocument().createElement("script");return forEach(e.attributes,function(n){t.setAttribute(n.name,n.value);}),t.textContent=e.textContent,t.async=!1,htmx.config.inlineScriptNonce&&(t.nonce=htmx.config.inlineScriptNonce),t}function isJavaScriptScriptNode(e){return e.matches("script")&&(e.type==="text/javascript"||e.type==="module"||e.type==="")}function normalizeScriptTags(e){Array.from(e.querySelectorAll("script")).forEach(t=>{if(isJavaScriptScriptNode(t)){let n=duplicateScript(t),r=t.parentNode;try{r.insertBefore(n,t);}catch(o){logError(o);}finally{t.remove();}}});}function makeFragment(e){let t=e.replace(HEAD_TAG_REGEX,""),n=getStartTag(t),r;if(n==="html"){r=new DocumentFragment;let i=parseHTML(e);takeChildrenFor(r,i.body),r.title=i.title;}else if(n==="body"){r=new DocumentFragment;let i=parseHTML(t);takeChildrenFor(r,i.body),r.title=i.title;}else {let i=parseHTML('");r=i.querySelector("template").content,r.title=i.title;var o=r.querySelector("title");o&&o.parentNode===r&&(o.remove(),r.title=o.innerText);}return r&&(htmx.config.allowScriptTags?normalizeScriptTags(r):r.querySelectorAll("script").forEach(i=>i.remove())),r}function maybeCall(e){e&&e();}function isType(e,t){return Object.prototype.toString.call(e)==="[object "+t+"]"}function isFunction(e){return typeof e=="function"}function isRawObject(e){return isType(e,"Object")}function getInternalData(e){let t="htmx-internal-data",n=e[t];return n||(n=e[t]={}),n}function toArray(e){let t=[];if(e)for(let n=0;n=0}function bodyContains(e){let t=e.getRootNode&&e.getRootNode();return t&&t instanceof window.ShadowRoot?getDocument().body.contains(t.host):getDocument().body.contains(e)}function splitOnWhitespace(e){return e.trim().split(/\s+/)}function mergeObjects(e,t){for(let n in t)t.hasOwnProperty(n)&&(e[n]=t[n]);return e}function parseJSON(e){try{return JSON.parse(e)}catch(t){return logError(t),null}}function canAccessLocalStorage(){let e="htmx:localStorageTest";try{return localStorage.setItem(e,e),localStorage.removeItem(e),!0}catch{return !1}}function normalizePath(e){try{let t=new URL(e);return t&&(e=t.pathname+t.search),/^\/$/.test(e)||(e=e.replace(/\/+$/,"")),e}catch{return e}}function internalEval(str){return maybeEval(getDocument().body,function(){return eval(str)})}function onLoadHelper(e){return htmx.on("htmx:load",function(n){e(n.detail.elt);})}function logAll(){htmx.logger=function(e,t,n){console&&console.log(t,e,n);};}function logNone(){htmx.logger=null;}function find(e,t){return typeof e!="string"?e.querySelector(t):find(getDocument(),e)}function findAll(e,t){return typeof e!="string"?e.querySelectorAll(t):findAll(getDocument(),e)}function getWindow(){return window}function removeElement(e,t){e=resolveTarget(e),t?getWindow().setTimeout(function(){removeElement(e),e=null;},t):parentElt(e).removeChild(e);}function asElement(e){return e instanceof Element?e:null}function asHtmlElement(e){return e instanceof HTMLElement?e:null}function asString(e){return typeof e=="string"?e:null}function asParentNode(e){return e instanceof Element||e instanceof Document||e instanceof DocumentFragment?e:null}function addClassToElement(e,t,n){e=asElement(resolveTarget(e)),e&&(n?getWindow().setTimeout(function(){addClassToElement(e,t),e=null;},n):e.classList&&e.classList.add(t));}function removeClassFromElement(e,t,n){let r=asElement(resolveTarget(e));r&&(n?getWindow().setTimeout(function(){removeClassFromElement(r,t),r=null;},n):r.classList&&(r.classList.remove(t),r.classList.length===0&&r.removeAttribute("class")));}function toggleClassOnElement(e,t){e=resolveTarget(e),e.classList.toggle(t);}function takeClassForElement(e,t){e=resolveTarget(e),forEach(e.parentElement.children,function(n){removeClassFromElement(n,t);}),addClassToElement(asElement(e),t);}function closest(e,t){if(e=asElement(resolveTarget(e)),e&&e.closest)return e.closest(t);do if(e==null||matches(e,t))return e;while(e=e&&asElement(parentElt(e)));return null}function startsWith(e,t){return e.substring(0,t.length)===t}function endsWith(e,t){return e.substring(e.length-t.length)===t}function normalizeSelector(e){let t=e.trim();return startsWith(t,"<")&&endsWith(t,"/>")?t.substring(1,t.length-2):t}function querySelectorAllExt(e,t,n){return e=resolveTarget(e),t.indexOf("closest ")===0?[closest(asElement(e),normalizeSelector(t.substr(8)))]:t.indexOf("find ")===0?[find(asParentNode(e),normalizeSelector(t.substr(5)))]:t==="next"?[asElement(e).nextElementSibling]:t.indexOf("next ")===0?[scanForwardQuery(e,normalizeSelector(t.substr(5)),!!n)]:t==="previous"?[asElement(e).previousElementSibling]:t.indexOf("previous ")===0?[scanBackwardsQuery(e,normalizeSelector(t.substr(9)),!!n)]:t==="document"?[document]:t==="window"?[window]:t==="body"?[document.body]:t==="root"?[getRootNode(e,!!n)]:t.indexOf("global ")===0?querySelectorAllExt(e,t.slice(7),!0):toArray(asParentNode(getRootNode(e,!!n)).querySelectorAll(normalizeSelector(t)))}var scanForwardQuery=function(e,t,n){let r=asParentNode(getRootNode(e,n)).querySelectorAll(t);for(let o=0;o=0;o--){let i=r[o];if(i.compareDocumentPosition(e)===Node.DOCUMENT_POSITION_FOLLOWING)return i}};function querySelectorExt(e,t){return typeof e!="string"?querySelectorAllExt(e,t)[0]:querySelectorAllExt(getDocument().body,e)[0]}function resolveTarget(e,t){return typeof e=="string"?find(asParentNode(t)||document,e):e}function processEventArgs(e,t,n){return isFunction(t)?{target:getDocument().body,event:asString(e),listener:t}:{target:resolveTarget(e),event:asString(t),listener:n}}function addEventListenerImpl(e,t,n){return ready(function(){let o=processEventArgs(e,t,n);o.target.addEventListener(o.event,o.listener);}),isFunction(t)?t:n}function removeEventListenerImpl(e,t,n){return ready(function(){let r=processEventArgs(e,t,n);r.target.removeEventListener(r.event,r.listener);}),isFunction(t)?t:n}let DUMMY_ELT=getDocument().createElement("output");function findAttributeTargets(e,t){let n=getClosestAttributeValue(e,t);if(n){if(n==="this")return [findThisElement(e,t)];{let r=querySelectorAllExt(e,n);return r.length===0?(logError('The selector "'+n+'" on '+t+" returned no matches!"),[DUMMY_ELT]):r}}}function findThisElement(e,t){return asElement(getClosestMatch(e,function(n){return getAttributeValue(asElement(n),t)!=null}))}function getTarget(e){let t=getClosestAttributeValue(e,"hx-target");return t?t==="this"?findThisElement(e,"hx-target"):querySelectorExt(e,t):getInternalData(e).boosted?getDocument().body:e}function shouldSettleAttribute(e){let t=htmx.config.attributesToSettle;for(let n=0;n0?(o=e.substr(0,e.indexOf(":")),r=e.substr(e.indexOf(":")+1,e.length)):o=e);let i=getDocument().querySelectorAll(r);return i?(forEach(i,function(s){let l,a=t.cloneNode(!0);l=getDocument().createDocumentFragment(),l.appendChild(a),isInlineSwap(o,s)||(l=asParentNode(a));let u={shouldSwap:!0,target:s,fragment:l};triggerEvent(s,"htmx:oobBeforeSwap",u)&&(s=u.target,u.shouldSwap&&swapWithStyle(o,s,s,l,n),forEach(n.elts,function(f){triggerEvent(f,"htmx:oobAfterSwap",u);}));}),t.parentNode.removeChild(t)):(t.parentNode.removeChild(t),triggerErrorEvent(getDocument().body,"htmx:oobErrorNoTarget",{content:t})),e}function handlePreservedElements(e){forEach(findAll(e,"[hx-preserve], [data-hx-preserve]"),function(t){let n=getAttributeValue(t,"id"),r=getDocument().getElementById(n);r!=null&&t.parentNode.replaceChild(r,t);});}function handleAttributes(e,t,n){forEach(t.querySelectorAll("[id]"),function(r){let o=getRawAttribute(r,"id");if(o&&o.length>0){let i=o.replace("'","\\'"),s=r.tagName.replace(":","\\:"),l=asParentNode(e),a=l&&l.querySelector(s+"[id='"+i+"']");if(a&&a!==l){let u=r.cloneNode();cloneAttributes(r,a),n.tasks.push(function(){cloneAttributes(r,u);});}}});}function makeAjaxLoadTask(e){return function(){removeClassFromElement(e,htmx.config.addedClass),processNode(asElement(e)),processFocus(asParentNode(e)),triggerEvent(e,"htmx:load");}}function processFocus(e){let t="[autofocus]",n=asHtmlElement(matches(e,t)?e:e.querySelector(t));n?.focus();}function insertNodesBefore(e,t,n,r){for(handleAttributes(e,n,r);n.childNodes.length>0;){let o=n.firstChild;addClassToElement(asElement(o),htmx.config.addedClass),e.insertBefore(o,t),o.nodeType!==Node.TEXT_NODE&&o.nodeType!==Node.COMMENT_NODE&&r.tasks.push(makeAjaxLoadTask(o));}}function stringHash(e,t){let n=0;for(;n0}function swap(e,t,n,r){r||(r={}),e=resolveTarget(e);let o=document.activeElement,i={};try{i={elt:o,start:o?o.selectionStart:null,end:o?o.selectionEnd:null};}catch{}let s=makeSettleInfo(e);if(n.swapStyle==="textContent")e.textContent=t;else {let a=makeFragment(t);if(s.title=a.title,r.selectOOB){let u=r.selectOOB.split(",");for(let f=0;f0?getWindow().setTimeout(l,n.settleDelay):l();}function handleTriggerHeader(e,t,n){let r=e.getResponseHeader(t);if(r.indexOf("{")===0){let o=parseJSON(r);for(let i in o)if(o.hasOwnProperty(i)){let s=o[i];isRawObject(s)?n=s.target!==void 0?s.target:n:s={value:s},triggerEvent(n,i,s);}}else {let o=r.split(",");for(let i=0;i0;){let s=t[0];if(s==="]"){if(r--,r===0){i===null&&(o=o+"true"),t.shift(),o+=")})";try{let l=maybeEval(e,function(){return Function(o)()},function(){return !0});return l.source=o,l}catch(l){return triggerErrorEvent(getDocument().body,"htmx:syntax:error",{error:l,source:o}),null}}}else s==="["&&r++;isPossibleRelativeReference(s,i,n)?o+="(("+n+"."+s+") ? ("+n+"."+s+") : (window."+s+"))":o=o+s,i=t.shift();}}}function consumeUntil(e,t){let n="";for(;e.length>0&&!t.test(e[0]);)n+=e.shift();return n}function consumeCSSSelector(e){let t;return e.length>0&&COMBINED_SELECTOR_START.test(e[0])?(e.shift(),t=consumeUntil(e,COMBINED_SELECTOR_END).trim(),e.shift()):t=consumeUntil(e,WHITESPACE_OR_COMMA),t}let INPUT_SELECTOR="input, textarea, select";function parseAndCacheTrigger(e,t,n){let r=[],o=tokenizeString(t);do{consumeUntil(o,NOT_WHITESPACE);let l=o.length,a=consumeUntil(o,/[,\[\s]/);if(a!=="")if(a==="every"){let u={trigger:"every"};consumeUntil(o,NOT_WHITESPACE),u.pollInterval=parseInterval(consumeUntil(o,/[,\[\s]/)),consumeUntil(o,NOT_WHITESPACE);var i=maybeGenerateConditional(e,o,"event");i&&(u.eventFilter=i),r.push(u);}else {let u={trigger:a};var i=maybeGenerateConditional(e,o,"event");for(i&&(u.eventFilter=i);o.length>0&&o[0]!==",";){consumeUntil(o,NOT_WHITESPACE);let c=o.shift();if(c==="changed")u.changed=!0;else if(c==="once")u.once=!0;else if(c==="consume")u.consume=!0;else if(c==="delay"&&o[0]===":")o.shift(),u.delay=parseInterval(consumeUntil(o,WHITESPACE_OR_COMMA));else if(c==="from"&&o[0]===":"){if(o.shift(),COMBINED_SELECTOR_START.test(o[0]))var s=consumeCSSSelector(o);else {var s=consumeUntil(o,WHITESPACE_OR_COMMA);if(s==="closest"||s==="find"||s==="next"||s==="previous"){o.shift();let b=consumeCSSSelector(o);b.length>0&&(s+=" "+b);}}u.from=s;}else c==="target"&&o[0]===":"?(o.shift(),u.target=consumeCSSSelector(o)):c==="throttle"&&o[0]===":"?(o.shift(),u.throttle=parseInterval(consumeUntil(o,WHITESPACE_OR_COMMA))):c==="queue"&&o[0]===":"?(o.shift(),u.queue=consumeUntil(o,WHITESPACE_OR_COMMA)):c==="root"&&o[0]===":"?(o.shift(),u[c]=consumeCSSSelector(o)):c==="threshold"&&o[0]===":"?(o.shift(),u[c]=consumeUntil(o,WHITESPACE_OR_COMMA)):triggerErrorEvent(e,"htmx:syntax:error",{token:o.shift()});}r.push(u);}o.length===l&&triggerErrorEvent(e,"htmx:syntax:error",{token:o.shift()}),consumeUntil(o,NOT_WHITESPACE);}while(o[0]===","&&o.shift());return n&&(n[t]=r),r}function getTriggerSpecs(e){let t=getAttributeValue(e,"hx-trigger"),n=[];if(t){let r=htmx.config.triggerSpecsCache;n=r&&r[t]||parseAndCacheTrigger(e,t,r);}return n.length>0?n:matches(e,"form")?[{trigger:"submit"}]:matches(e,'input[type="button"], input[type="submit"]')?[{trigger:"click"}]:matches(e,INPUT_SELECTOR)?[{trigger:"change"}]:[{trigger:"click"}]}function cancelPolling(e){getInternalData(e).cancelled=!0;}function processPolling(e,t,n){let r=getInternalData(e);r.timeout=getWindow().setTimeout(function(){bodyContains(e)&&r.cancelled!==!0&&(maybeFilterEvent(n,e,makeEvent("hx:poll:trigger",{triggerSpec:n,target:e}))||t(e),processPolling(e,t,n));},n.pollInterval);}function isLocalLink(e){return location.hostname===e.hostname&&getRawAttribute(e,"href")&&getRawAttribute(e,"href").indexOf("#")!==0}function eltIsDisabled(e){return closest(e,htmx.config.disableSelector)}function boostElement(e,t,n){if(e instanceof HTMLAnchorElement&&isLocalLink(e)&&(e.target===""||e.target==="_self")||e.tagName==="FORM"&&String(getRawAttribute(e,"method")).toLowerCase()!=="dialog"){t.boosted=!0;let r,o;if(e.tagName==="A")r="get",o=getRawAttribute(e,"href");else {let i=getRawAttribute(e,"method");r=i?i.toLowerCase():"get",o=getRawAttribute(e,"action");}n.forEach(function(i){addEventListener(e,function(s,l){let a=asElement(s);if(eltIsDisabled(a)){cleanUpElement(a);return}issueAjaxRequest(r,o,a,l);},t,i,!0);});}}function shouldCancel(e,t){let n=asElement(t);return n?!!((e.type==="submit"||e.type==="click")&&(n.tagName==="FORM"||matches(n,'input[type="submit"], button')&&closest(n,"form")!==null||n instanceof HTMLAnchorElement&&n.href&&(n.getAttribute("href")==="#"||n.getAttribute("href").indexOf("#")!==0))):!1}function ignoreBoostedAnchorCtrlClick(e,t){return getInternalData(e).boosted&&e instanceof HTMLAnchorElement&&t.type==="click"&&(t.ctrlKey||t.metaKey)}function maybeFilterEvent(e,t,n){let r=e.eventFilter;if(r)try{return r.call(t,n)!==!0}catch(o){let i=r.source;return triggerErrorEvent(getDocument().body,"htmx:eventFilter:error",{error:o,source:i}),!0}return !1}function addEventListener(e,t,n,r,o){let i=getInternalData(e),s;r.from?s=querySelectorAllExt(e,r.from):s=[e],r.changed&&s.forEach(function(l){let a=getInternalData(l);a.lastValue=l.value;}),forEach(s,function(l){let a=function(u){if(!bodyContains(e)){l.removeEventListener(r.trigger,a);return}if(ignoreBoostedAnchorCtrlClick(e,u)||((o||shouldCancel(u,e))&&u.preventDefault(),maybeFilterEvent(r,e,u)))return;let f=getInternalData(u);if(f.triggerSpec=r,f.handledFor==null&&(f.handledFor=[]),f.handledFor.indexOf(e)<0){if(f.handledFor.push(e),r.consume&&u.stopPropagation(),r.target&&u.target&&!matches(asElement(u.target),r.target))return;if(r.once){if(i.triggeredOnce)return;i.triggeredOnce=!0;}if(r.changed){let c=getInternalData(l),d=l.value;if(c.lastValue===d)return;c.lastValue=d;}if(i.delayed&&clearTimeout(i.delayed),i.throttle)return;r.throttle>0?i.throttle||(triggerEvent(e,"htmx:trigger"),t(e,u),i.throttle=getWindow().setTimeout(function(){i.throttle=null;},r.throttle)):r.delay>0?i.delayed=getWindow().setTimeout(function(){triggerEvent(e,"htmx:trigger"),t(e,u);},r.delay):(triggerEvent(e,"htmx:trigger"),t(e,u));}};n.listenerInfos==null&&(n.listenerInfos=[]),n.listenerInfos.push({trigger:r.trigger,listener:a,on:l}),l.addEventListener(r.trigger,a);});}let windowIsScrolling=!1,scrollHandler=null;function initScrollHandler(){scrollHandler||(scrollHandler=function(){windowIsScrolling=!0;},window.addEventListener("scroll",scrollHandler),setInterval(function(){windowIsScrolling&&(windowIsScrolling=!1,forEach(getDocument().querySelectorAll("[hx-trigger*='revealed'],[data-hx-trigger*='revealed']"),function(e){maybeReveal(e);}));},200));}function maybeReveal(e){!hasAttribute(e,"data-hx-revealed")&&isScrolledIntoView(e)&&(e.setAttribute("data-hx-revealed","true"),getInternalData(e).initHash?triggerEvent(e,"revealed"):e.addEventListener("htmx:afterProcessNode",function(){triggerEvent(e,"revealed");},{once:!0}));}function loadImmediately(e,t,n,r){let o=function(){n.loaded||(n.loaded=!0,t(e));};r>0?getWindow().setTimeout(o,r):o();}function processVerbs(e,t,n){let r=!1;return forEach(VERBS,function(o){if(hasAttribute(e,"hx-"+o)){let i=getAttributeValue(e,"hx-"+o);r=!0,t.path=i,t.verb=o,n.forEach(function(s){addTriggerHandler(e,s,t,function(l,a){let u=asElement(l);if(closest(u,htmx.config.disableSelector)){cleanUpElement(u);return}issueAjaxRequest(o,i,u,a);});});}}),r}function addTriggerHandler(e,t,n,r){if(t.trigger==="revealed")initScrollHandler(),addEventListener(e,r,n,t),maybeReveal(asElement(e));else if(t.trigger==="intersect"){let o={};t.root&&(o.root=querySelectorExt(e,t.root)),t.threshold&&(o.threshold=parseFloat(t.threshold)),new IntersectionObserver(function(s){for(let l=0;l0?(n.polling=!0,processPolling(asElement(e),r,t)):addEventListener(e,r,n,t);}function shouldProcessHxOn(e){let t=asElement(e);if(!t)return !1;let n=t.attributes;for(let r=0;r", "+i).join(""))}else return []}function maybeSetLastButtonClicked(e){let t=closest(asElement(e.target),"button, input[type='submit']"),n=getRelatedFormData(e);n&&(n.lastButtonClicked=t);}function maybeUnsetLastButtonClicked(e){let t=getRelatedFormData(e);t&&(t.lastButtonClicked=null);}function getRelatedFormData(e){let t=closest(asElement(e.target),"button, input[type='submit']");if(!t)return;let n=resolveTarget("#"+getRawAttribute(t,"form"),t.getRootNode())||closest(t,"form");if(n)return getInternalData(n)}function initButtonTracking(e){e.addEventListener("click",maybeSetLastButtonClicked),e.addEventListener("focusin",maybeSetLastButtonClicked),e.addEventListener("focusout",maybeUnsetLastButtonClicked);}function addHxOnEventHandler(e,t,n){let r=getInternalData(e);Array.isArray(r.onHandlers)||(r.onHandlers=[]);let o,i=function(s){maybeEval(e,function(){eltIsDisabled(e)||(o||(o=new Function("event",n)),o.call(e,s));});};e.addEventListener(t,i),r.onHandlers.push({event:t,listener:i});}function processHxOnWildcard(e){deInitOnHandlers(e);for(let t=0;thtmx.config.historyCacheSize;)i.shift();for(;i.length>0;)try{localStorage.setItem("htmx-history-cache",JSON.stringify(i));break}catch(l){triggerErrorEvent(getDocument().body,"htmx:historyCacheError",{cause:l,cache:i}),i.shift();}}function getCachedHistory(e){if(!canAccessLocalStorage())return null;e=normalizePath(e);let t=parseJSON(localStorage.getItem("htmx-history-cache"))||[];for(let n=0;n=200&&this.status<400){triggerEvent(getDocument().body,"htmx:historyCacheMissLoad",n);let r=makeFragment(this.response),o=r.querySelector("[hx-history-elt],[data-hx-history-elt]")||r,i=getHistoryElement(),s=makeSettleInfo(i);handleTitle(r.title),swapInnerHTML(i,o,s),settleImmediately(s.tasks),currentPathForHistory=e,triggerEvent(getDocument().body,"htmx:historyRestore",{path:e,cacheMiss:!0,serverResponse:this.response});}else triggerErrorEvent(getDocument().body,"htmx:historyCacheMissLoadError",n);},t.send();}function restoreHistory(e){saveCurrentPageToHistory(),e=e||location.pathname+location.search;let t=getCachedHistory(e);if(t){let n=makeFragment(t.content),r=getHistoryElement(),o=makeSettleInfo(r);handleTitle(n.title),swapInnerHTML(r,n,o),settleImmediately(o.tasks),getWindow().setTimeout(function(){window.scrollTo(0,t.scroll);},0),currentPathForHistory=e,triggerEvent(getDocument().body,"htmx:historyRestore",{path:e,item:t});}else htmx.config.refreshOnHistoryMiss?window.location.reload(!0):loadHistoryFromServer(e);}function addRequestIndicatorClasses(e){let t=findAttributeTargets(e,"hx-indicator");return t==null&&(t=[e]),forEach(t,function(n){let r=getInternalData(n);r.requestCount=(r.requestCount||0)+1,n.classList.add.call(n.classList,htmx.config.requestClass);}),t}function disableElements(e){let t=findAttributeTargets(e,"hx-disabled-elt");return t==null&&(t=[]),forEach(t,function(n){let r=getInternalData(n);r.requestCount=(r.requestCount||0)+1,n.setAttribute("disabled",""),n.setAttribute("data-disabled-by-htmx","");}),t}function removeRequestIndicators(e,t){forEach(e,function(n){let r=getInternalData(n);r.requestCount=(r.requestCount||0)-1,r.requestCount===0&&n.classList.remove.call(n.classList,htmx.config.requestClass);}),forEach(t,function(n){let r=getInternalData(n);r.requestCount=(r.requestCount||0)-1,r.requestCount===0&&(n.removeAttribute("disabled"),n.removeAttribute("data-disabled-by-htmx"));});}function haveSeenNode(e,t){for(let n=0;nt.indexOf(o)<0):r=r.filter(o=>o!==t),n.delete(e),forEach(r,o=>n.append(e,o));}}function processInputValue(e,t,n,r,o){if(!(r==null||haveSeenNode(e,r))){if(e.push(r),shouldInclude(r)){let i=getRawAttribute(r,"name"),s=r.value;r instanceof HTMLSelectElement&&r.multiple&&(s=toArray(r.querySelectorAll("option:checked")).map(function(l){return l.value})),r instanceof HTMLInputElement&&r.files&&(s=toArray(r.files)),addValueToFormData(i,s,t),o&&validateElement(r,n);}r instanceof HTMLFormElement&&(forEach(r.elements,function(i){e.indexOf(i)>=0?removeValueFromFormData(i.name,i.value,t):e.push(i),o&&validateElement(i,n);}),new FormData(r).forEach(function(i,s){i instanceof File&&i.name===""||addValueToFormData(s,i,t);}));}}function validateElement(e,t){let n=e;n.willValidate&&(triggerEvent(n,"htmx:validation:validate"),n.checkValidity()||(t.push({elt:n,message:n.validationMessage,validity:n.validity}),triggerEvent(n,"htmx:validation:failed",{message:n.validationMessage,validity:n.validity})));}function overrideFormData(e,t){for(let n of t.keys())e.delete(n);return t.forEach(function(n,r){e.append(r,n);}),e}function getInputValues(e,t){let n=[],r=new FormData,o=new FormData,i=[],s=getInternalData(e);s.lastButtonClicked&&!bodyContains(s.lastButtonClicked)&&(s.lastButtonClicked=null);let l=e instanceof HTMLFormElement&&e.noValidate!==!0||getAttributeValue(e,"hx-validate")==="true";if(s.lastButtonClicked&&(l=l&&s.lastButtonClicked.formNoValidate!==!0),t!=="get"&&processInputValue(n,o,i,closest(e,"form"),l),processInputValue(n,r,i,e,l),s.lastButtonClicked||e.tagName==="BUTTON"||e.tagName==="INPUT"&&getRawAttribute(e,"type")==="submit"){let u=s.lastButtonClicked||e,f=getRawAttribute(u,"name");addValueToFormData(f,u.value,o);}let a=findAttributeTargets(e,"hx-include");return forEach(a,function(u){processInputValue(n,r,i,asElement(u),l),matches(u,"form")||forEach(asParentNode(u).querySelectorAll(INPUT_SELECTOR),function(f){processInputValue(n,r,i,f,l);});}),overrideFormData(r,o),{errors:i,formData:r,values:formDataProxy(r)}}function appendParam(e,t,n){e!==""&&(e+="&"),String(n)==="[object Object]"&&(n=JSON.stringify(n));let r=encodeURIComponent(n);return e+=encodeURIComponent(t)+"="+r,e}function urlEncode(e){e=formDataFromObject(e);let t="";return e.forEach(function(n,r){t=appendParam(t,r,n);}),t}function getHeaders(e,t,n){let r={"HX-Request":"true","HX-Trigger":getRawAttribute(e,"id"),"HX-Trigger-Name":getRawAttribute(e,"name"),"HX-Target":getAttributeValue(t,"id"),"HX-Current-URL":getDocument().location.href};return getValuesForElement(e,"hx-headers",!1,r),n!==void 0&&(r["HX-Prompt"]=n),getInternalData(e).boosted&&(r["HX-Boosted"]="true"),r}function filterValues(e,t){let n=getClosestAttributeValue(t,"hx-params");if(n){if(n==="none")return new FormData;if(n==="*")return e;if(n.indexOf("not ")===0)return forEach(n.substr(4).split(","),function(r){r=r.trim(),e.delete(r);}),e;{let r=new FormData;return forEach(n.split(","),function(o){o=o.trim(),e.has(o)&&e.getAll(o).forEach(function(i){r.append(o,i);});}),r}}else return e}function isAnchorLink(e){return !!getRawAttribute(e,"href")&&getRawAttribute(e,"href").indexOf("#")>=0}function getSwapSpecification(e,t){let n=t||getClosestAttributeValue(e,"hx-swap"),r={swapStyle:getInternalData(e).boosted?"innerHTML":htmx.config.defaultSwapStyle,swapDelay:htmx.config.defaultSwapDelay,settleDelay:htmx.config.defaultSettleDelay};if(htmx.config.scrollIntoViewOnBoost&&getInternalData(e).boosted&&!isAnchorLink(e)&&(r.show="top"),n){let s=splitOnWhitespace(n);if(s.length>0)for(let l=0;l0?o.join(":"):null;r.scroll=f,r.scrollTarget=i;}else if(a.indexOf("show:")===0){var o=a.substr(5).split(":");let c=o.pop();var i=o.length>0?o.join(":"):null;r.show=c,r.showTarget=i;}else if(a.indexOf("focus-scroll:")===0){let u=a.substr(13);r.focusScroll=u=="true";}else l==0?r.swapStyle=a:logError("Unknown modifier in hx-swap: "+a);}}return r}function usesFormData(e){return getClosestAttributeValue(e,"hx-encoding")==="multipart/form-data"||matches(e,"form")&&getRawAttribute(e,"enctype")==="multipart/form-data"}function encodeParamsForBody(e,t,n){let r=null;return withExtensions(t,function(o){r==null&&(r=o.encodeParameters(e,n,t));}),r??(usesFormData(t)?overrideFormData(new FormData,formDataFromObject(n)):urlEncode(n))}function makeSettleInfo(e){return {tasks:[],elts:[e]}}function updateScrollState(e,t){let n=e[0],r=e[e.length-1];if(t.scroll){var o=null;t.scrollTarget&&(o=asElement(querySelectorExt(n,t.scrollTarget))),t.scroll==="top"&&(n||o)&&(o=o||n,o.scrollTop=0),t.scroll==="bottom"&&(r||o)&&(o=o||r,o.scrollTop=o.scrollHeight);}if(t.show){var o=null;if(t.showTarget){let s=t.showTarget;t.showTarget==="window"&&(s="body"),o=asElement(querySelectorExt(n,s));}t.show==="top"&&(n||o)&&(o=o||n,o.scrollIntoView({block:"start",behavior:htmx.config.scrollBehavior})),t.show==="bottom"&&(r||o)&&(o=o||r,o.scrollIntoView({block:"end",behavior:htmx.config.scrollBehavior}));}}function getValuesForElement(e,t,n,r){if(r==null&&(r={}),e==null)return r;let o=getAttributeValue(e,t);if(o){let i=o.trim(),s=n;if(i==="unset")return null;i.indexOf("javascript:")===0?(i=i.substr(11),s=!0):i.indexOf("js:")===0&&(i=i.substr(3),s=!0),i.indexOf("{")!==0&&(i="{"+i+"}");let l;s?l=maybeEval(e,function(){return Function("return ("+i+")")()},{}):l=parseJSON(i);for(let a in l)l.hasOwnProperty(a)&&r[a]==null&&(r[a]=l[a]);}return getValuesForElement(asElement(parentElt(e)),t,n,r)}function maybeEval(e,t,n){return htmx.config.allowEval?t():(triggerErrorEvent(e,"htmx:evalDisallowedError"),n)}function getHXVarsForElement(e,t){return getValuesForElement(e,"hx-vars",!0,t)}function getHXValsForElement(e,t){return getValuesForElement(e,"hx-vals",!1,t)}function getExpressionVars(e){return mergeObjects(getHXVarsForElement(e),getHXValsForElement(e))}function safelySetHeaderValue(e,t,n){if(n!==null)try{e.setRequestHeader(t,n);}catch{e.setRequestHeader(t,encodeURIComponent(n)),e.setRequestHeader(t+"-URI-AutoEncoded","true");}}function getPathFromResponse(e){if(e.responseURL&&typeof URL<"u")try{let t=new URL(e.responseURL);return t.pathname+t.search}catch{triggerErrorEvent(getDocument().body,"htmx:badResponseUrl",{url:e.responseURL});}}function hasHeader(e,t){return t.test(e.getAllResponseHeaders())}function ajaxHelper(e,t,n){return e=e.toLowerCase(),n?n instanceof Element||typeof n=="string"?issueAjaxRequest(e,t,null,null,{targetOverride:resolveTarget(n),returnPromise:!0}):issueAjaxRequest(e,t,resolveTarget(n.source),n.event,{handler:n.handler,headers:n.headers,values:n.values,targetOverride:resolveTarget(n.target),swapOverride:n.swap,select:n.select,returnPromise:!0}):issueAjaxRequest(e,t,null,null,{returnPromise:!0})}function hierarchyForElt(e){let t=[];for(;e;)t.push(e),e=e.parentElement;return t}function verifyPath(e,t,n){let r,o;return typeof URL=="function"?(o=new URL(t,document.location.href),r=document.location.origin===o.origin):(o=t,r=startsWith(t,document.location.origin)),htmx.config.selfRequestsOnly&&!r?!1:triggerEvent(e,"htmx:validateUrl",mergeObjects({url:o,sameHost:r},n))}function formDataFromObject(e){if(e instanceof FormData)return e;let t=new FormData;for(let n in e)e.hasOwnProperty(n)&&(typeof e[n].forEach=="function"?e[n].forEach(function(r){t.append(n,r);}):typeof e[n]=="object"&&!(e[n]instanceof Blob)?t.append(n,JSON.stringify(e[n])):t.append(n,e[n]));return t}function formDataArrayProxy(e,t,n){return new Proxy(n,{get:function(r,o){return typeof o=="number"?r[o]:o==="length"?r.length:o==="push"?function(i){r.push(i),e.append(t,i);}:typeof r[o]=="function"?function(){r[o].apply(r,arguments),e.delete(t),r.forEach(function(i){e.append(t,i);});}:r[o]&&r[o].length===1?r[o][0]:r[o]},set:function(r,o,i){return r[o]=i,e.delete(t),r.forEach(function(s){e.append(t,s);}),!0}})}function formDataProxy(e){return new Proxy(e,{get:function(t,n){if(typeof n=="symbol")return Reflect.get(t,n);if(n==="toJSON")return ()=>Object.fromEntries(e);if(n in t)return typeof t[n]=="function"?function(){return e[n].apply(e,arguments)}:t[n];let r=e.getAll(n);if(r.length!==0)return r.length===1?r[0]:formDataArrayProxy(t,n,r)},set:function(t,n,r){return typeof n!="string"?!1:(t.delete(n),typeof r.forEach=="function"?r.forEach(function(o){t.append(n,o);}):typeof r=="object"&&!(r instanceof Blob)?t.append(n,JSON.stringify(r)):t.append(n,r),!0)},deleteProperty:function(t,n){return typeof n=="string"&&t.delete(n),!0},ownKeys:function(t){return Reflect.ownKeys(Object.fromEntries(t))},getOwnPropertyDescriptor:function(t,n){return Reflect.getOwnPropertyDescriptor(Object.fromEntries(t),n)}})}function issueAjaxRequest(e,t,n,r,o,i){let s=null,l=null;if(o=o??{},o.returnPromise&&typeof Promise<"u")var a=new Promise(function(g,E){s=g,l=E;});n==null&&(n=getDocument().body);let u=o.handler||handleAjaxResponse,f=o.select||null;if(!bodyContains(n))return maybeCall(s),a;let c=o.targetOverride||asElement(getTarget(n));if(c==null||c==DUMMY_ELT)return triggerErrorEvent(n,"htmx:targetError",{target:getAttributeValue(n,"hx-target")}),maybeCall(l),a;let d=getInternalData(n),b=d.lastButtonClicked;if(b){let g=getRawAttribute(b,"formaction");g!=null&&(t=g);let E=getRawAttribute(b,"formmethod");E!=null&&E.toLowerCase()!=="dialog"&&(e=E);}let S=getClosestAttributeValue(n,"hx-confirm");if(i===void 0&&triggerEvent(n,"htmx:confirm",{target:c,elt:n,path:t,verb:e,triggeringEvent:r,etc:o,issueRequest:function(O){return issueAjaxRequest(e,t,n,r,o,!!O)},question:S})===!1)return maybeCall(s),a;let A=n,p=getClosestAttributeValue(n,"hx-sync"),x=null,H=!1;if(p){let g=p.split(":"),E=g[0].trim();if(E==="this"?A=findThisElement(n,"hx-sync"):A=asElement(querySelectorExt(n,E)),p=(g[1]||"drop").trim(),d=getInternalData(A),p==="drop"&&d.xhr&&d.abortable!==!0)return maybeCall(s),a;if(p==="abort"){if(d.xhr)return maybeCall(s),a;H=!0;}else p==="replace"?triggerEvent(A,"htmx:abort"):p.indexOf("queue")===0&&(x=(p.split(" ")[1]||"last").trim());}if(d.xhr)if(d.abortable)triggerEvent(A,"htmx:abort");else {if(x==null){if(r){let g=getInternalData(r);g&&g.triggerSpec&&g.triggerSpec.queue&&(x=g.triggerSpec.queue);}x==null&&(x="last");}return d.queuedRequests==null&&(d.queuedRequests=[]),x==="first"&&d.queuedRequests.length===0?d.queuedRequests.push(function(){issueAjaxRequest(e,t,n,r,o);}):x==="all"?d.queuedRequests.push(function(){issueAjaxRequest(e,t,n,r,o);}):x==="last"&&(d.queuedRequests=[],d.queuedRequests.push(function(){issueAjaxRequest(e,t,n,r,o);})),maybeCall(s),a}let m=new XMLHttpRequest;d.xhr=m,d.abortable=H;let T=function(){d.xhr=null,d.abortable=!1,d.queuedRequests!=null&&d.queuedRequests.length>0&&d.queuedRequests.shift()();},P=getClosestAttributeValue(n,"hx-prompt");if(P){var I=prompt(P);if(I===null||!triggerEvent(n,"htmx:prompt",{prompt:I,target:c}))return maybeCall(s),T(),a}if(S&&!i&&!confirm(S))return maybeCall(s),T(),a;let R=getHeaders(n,c,I);e!=="get"&&!usesFormData(n)&&(R["Content-Type"]="application/x-www-form-urlencoded"),o.headers&&(R=mergeObjects(R,o.headers));let v=getInputValues(n,e),q=v.errors,N=v.formData;o.values&&overrideFormData(N,formDataFromObject(o.values));let _=formDataFromObject(getExpressionVars(n)),W=overrideFormData(N,_),L=filterValues(W,n);htmx.config.getCacheBusterParam&&e==="get"&&L.set("org.htmx.cache-buster",getRawAttribute(c,"id")||"true"),(t==null||t==="")&&(t=getDocument().location.href);let X=getValuesForElement(n,"hx-request"),G=getInternalData(n).boosted,F=htmx.config.methodsThatUseUrlParams.indexOf(e)>=0,w={boosted:G,useUrlParams:F,formData:L,parameters:formDataProxy(L),unfilteredFormData:W,unfilteredParameters:formDataProxy(W),headers:R,target:c,verb:e,errors:q,withCredentials:o.credentials||X.credentials||htmx.config.withCredentials,timeout:o.timeout||X.timeout||htmx.config.timeout,path:t,triggeringEvent:r};if(!triggerEvent(n,"htmx:configRequest",w))return maybeCall(s),T(),a;if(t=w.path,e=w.verb,R=w.headers,L=formDataFromObject(w.parameters),q=w.errors,F=w.useUrlParams,q&&q.length>0)return triggerEvent(n,"htmx:validation:halted",w),maybeCall(s),T(),a;let Y=t.split("#"),Z=Y[0],j=Y[1],D=t;if(F&&(D=Z,!L.keys().next().done&&(D.indexOf("?")<0?D+="?":D+="&",D+=urlEncode(L),j&&(D+="#"+j))),!verifyPath(n,D,w))return triggerErrorEvent(n,"htmx:invalidPath",w),maybeCall(l),a;if(m.open(e.toUpperCase(),D,!0),m.overrideMimeType("text/html"),m.withCredentials=w.withCredentials,m.timeout=w.timeout,!X.noHeaders){for(let g in R)if(R.hasOwnProperty(g)){let E=R[g];safelySetHeaderValue(m,g,E);}}let y={xhr:m,target:c,requestConfig:w,etc:o,boosted:G,select:f,pathInfo:{requestPath:t,finalRequestPath:D,responsePath:null,anchor:j}};if(m.onload=function(){try{let g=hierarchyForElt(n);if(y.pathInfo.responsePath=getPathFromResponse(m),u(n,y),y.keepIndicators!==!0&&removeRequestIndicators(M,V),triggerEvent(n,"htmx:afterRequest",y),triggerEvent(n,"htmx:afterOnLoad",y),!bodyContains(n)){let E=null;for(;g.length>0&&E==null;){let O=g.shift();bodyContains(O)&&(E=O);}E&&(triggerEvent(E,"htmx:afterRequest",y),triggerEvent(E,"htmx:afterOnLoad",y));}maybeCall(s),T();}catch(g){throw triggerErrorEvent(n,"htmx:onLoadError",mergeObjects({error:g},y)),g}},m.onerror=function(){removeRequestIndicators(M,V),triggerErrorEvent(n,"htmx:afterRequest",y),triggerErrorEvent(n,"htmx:sendError",y),maybeCall(l),T();},m.onabort=function(){removeRequestIndicators(M,V),triggerErrorEvent(n,"htmx:afterRequest",y),triggerErrorEvent(n,"htmx:sendAbort",y),maybeCall(l),T();},m.ontimeout=function(){removeRequestIndicators(M,V),triggerErrorEvent(n,"htmx:afterRequest",y),triggerErrorEvent(n,"htmx:timeout",y),maybeCall(l),T();},!triggerEvent(n,"htmx:beforeRequest",y))return maybeCall(s),T(),a;var M=addRequestIndicatorClasses(n),V=disableElements(n);forEach(["loadstart","loadend","progress","abort"],function(g){forEach([m,m.upload],function(E){E.addEventListener(g,function(O){triggerEvent(n,"htmx:xhr:"+g,{lengthComputable:O.lengthComputable,loaded:O.loaded,total:O.total});});});}),triggerEvent(n,"htmx:beforeSend",y);let ee=F?null:encodeParamsForBody(m,n,L);return m.send(ee),a}function determineHistoryUpdates(e,t){let n=t.xhr,r=null,o=null;if(hasHeader(n,/HX-Push:/i)?(r=n.getResponseHeader("HX-Push"),o="push"):hasHeader(n,/HX-Push-Url:/i)?(r=n.getResponseHeader("HX-Push-Url"),o="push"):hasHeader(n,/HX-Replace-Url:/i)&&(r=n.getResponseHeader("HX-Replace-Url"),o="replace"),r)return r==="false"?{}:{type:o,path:r};let i=t.pathInfo.finalRequestPath,s=t.pathInfo.responsePath,l=getClosestAttributeValue(e,"hx-push-url"),a=getClosestAttributeValue(e,"hx-replace-url"),u=getInternalData(e).boosted,f=null,c=null;return l?(f="push",c=l):a?(f="replace",c=a):u&&(f="push",c=s||i),c?c==="false"?{}:(c==="true"&&(c=s||i),t.pathInfo.anchor&&c.indexOf("#")===-1&&(c=c+"#"+t.pathInfo.anchor),{type:f,path:c}):{}}function codeMatches(e,t){var n=new RegExp(e.code);return n.test(t.toString(10))}function resolveResponseHandling(e){for(var t=0;t0?getWindow().setTimeout(I,x.swapDelay):I();}c&&triggerErrorEvent(e,"htmx:responseError",mergeObjects({error:"Response Status Error Code "+n.status+" from "+t.pathInfo.requestPath},t));}}let extensions={};function extensionBase(){return {init:function(e){return null},getSelectors:function(){return null},onEvent:function(e,t){return !0},transformResponse:function(e,t,n){return e},isInlineSwap:function(e){return !1},handleSwap:function(e,t,n,r){return !1},encodeParameters:function(e,t,n){return null}}}function defineExtension(e,t){t.init&&t.init(internalAPI),extensions[e]=mergeObjects(extensionBase(),t);}function removeExtension(e){delete extensions[e];}function getExtensions(e,t,n){if(t==null&&(t=[]),e==null)return t;n==null&&(n=[]);let r=getAttributeValue(e,"hx-ext");return r&&forEach(r.split(","),function(o){if(o=o.replace(/ /g,""),o.slice(0,7)=="ignore:"){n.push(o.slice(7));return}if(n.indexOf(o)<0){let i=extensions[o];i&&t.indexOf(i)<0&&t.push(i);}}),getExtensions(asElement(parentElt(e)),t,n)}var isReady=!1;getDocument().addEventListener("DOMContentLoaded",function(){isReady=!0;});function ready(e){isReady||getDocument().readyState==="complete"?e():getDocument().addEventListener("DOMContentLoaded",e);}function insertIndicatorStyles(){if(htmx.config.includeIndicatorStyles!==!1){let e=htmx.config.inlineStyleNonce?` nonce="${htmx.config.inlineStyleNonce}"`:"";getDocument().head.insertAdjacentHTML("beforeend"," ."+htmx.config.indicatorClass+"{opacity:0} ."+htmx.config.requestClass+" ."+htmx.config.indicatorClass+"{opacity:1; transition: opacity 200ms ease-in;} ."+htmx.config.requestClass+"."+htmx.config.indicatorClass+"{opacity:1; transition: opacity 200ms ease-in;} ");}}function getMetaConfig(){let e=getDocument().querySelector('meta[name="htmx-config"]');return e?parseJSON(e.content):null}function mergeMetaConfig(){let e=getMetaConfig();e&&(htmx.config=mergeObjects(htmx.config,e));}return ready(function(){mergeMetaConfig(),insertIndicatorStyles();let e=getDocument().body;processNode(e);let t=getDocument().querySelectorAll("[hx-trigger='restored'],[data-hx-trigger='restored']");e.addEventListener("htmx:abort",function(r){let o=r.target,i=getInternalData(o);i&&i.xhr&&i.xhr.abort();});let n=window.onpopstate?window.onpopstate.bind(window):null;window.onpopstate=function(r){r.state&&r.state.htmx?(restoreHistory(),forEach(t,function(o){triggerEvent(o,"htmx:restored",{document:getDocument(),triggerEvent});})):n&&n(r);},getWindow().setTimeout(function(){triggerEvent(e,"htmx:load",{}),e=null;},0);}),htmx}(),h=te;function ne(e,t){if(e==="ignore")return !1;let n=e.split("/"),r=t.split("/");for(let o=0;o{let s=oe(t).replace("htmx:","hx-on::");if(!r.has(o)){if(o.hasAttribute(s)){let l=se(s.replace("hx-on::","htmx:"),n.detail);l.detail.meta="trigger-children",o.dispatchEvent(l),r.add(o);}o.children&&$(o,t,n,r);}});}h.defineExtension("trigger-children",{onEvent:(e,t)=>{if(!(t instanceof CustomEvent)||t.detail.meta==="trigger-children")return !1;let n=new Set,r=t.target||t.detail.target;return $(r,e,t,n),!0},init:function(e){},transformResponse:function(e,t,n){return e},isInlineSwap:function(e){return !1},handleSwap:function(e,t,n,r){return !1},encodeParameters:function(e,t,n){},getSelectors:function(){return null}});h.defineExtension("debug",{onEvent:function(e,t){console.debug?console.debug(e,t):console&&console.log("DEBUG:",e,t);}});var C=h.config,k,ae="hx-target-";function Q(e,t){return e.substring(0,t.length)===t}function le(e,t){if(!e||!t)return null;let n=t.toString(),r=[n,n.substr(0,2)+"*",n.substr(0,2)+"x",n.substr(0,1)+"*",n.substr(0,1)+"x",n.substr(0,1)+"**",n.substr(0,1)+"xx","*","x","***","xxx"];(Q(n,"4")||Q(n,"5"))&&r.push("error");for(let o=0;o{k=e,C.responseTargetUnsetsError===void 0&&(C.responseTargetUnsetsError=!0),C.responseTargetSetsError===void 0&&(C.responseTargetSetsError=!1),C.responseTargetPrefersExisting===void 0&&(C.responseTargetPrefersExisting=!1),C.responseTargetPrefersRetargetHeader===void 0&&(C.responseTargetPrefersRetargetHeader=!0);},onEvent:(e,t)=>{if(!(t instanceof CustomEvent))return !1;if(e==="htmx:beforeSwap"&&t.detail.xhr&&t.detail.xhr.status!==200){if(t.detail.target&&(C.responseTargetPrefersExisting||C.responseTargetPrefersRetargetHeader&&t.detail.xhr.getAllResponseHeaders().match(/HX-Retarget:/i)))return t.detail.shouldSwap=!0,z(t),!0;if(!t.detail.requestConfig)return !0;let n=le(t.detail.requestConfig.elt,t.detail.xhr.status);return n&&(z(t),t.detail.shouldSwap=!0,t.detail.target=n),!0}}});h.defineExtension("mutation-error",{onEvent:(e,t)=>{if(!(t instanceof CustomEvent))return !1;if(e==="htmx:afterRequest"){if(!t.detail||!t.detail.xhr)return;let n=t.detail.xhr.status;n>=400&&h.findAll("[hx-on\\:\\:mutation-error]").forEach(r=>{h.trigger(r,"htmx:mutation-error",{status:n});});}}});var U="";h.defineExtension("livereload",{init:function(){let e=!1;for(let n of Array.from(h.findAll("[hx-ext]")))if(n.getAttribute("hx-ext")?.split(" ").includes("livereload")){e=!0;break}if(!e)return;console.log("livereload extension initialized.");let t=new EventSource("/dev/livereload");t.onmessage=function(n){let r=n.data;U===""&&(U=r),U!==r&&(U=r,ue());},t.onerror=function(n){console.error("EventSource error:",n);};},onEvent:function(e,t){}});function ue(){window.location.reload();}var ce=/__eval_[A-Za-z0-9]+\([a-z]+\)/gm;h.defineExtension("htmgo",{onEvent:function(e,t){e==="htmx:beforeCleanupElement"&&t.target&&J(t.target);}});function J(e){let t=Array.from(e.attributes);for(let n of t){let r=n.value.match(ce)||[];for(let o of r){let i=o.replace("()","").replace("(this)","").replace(";",""),s=document.getElementById(i);s&&s.tagName==="SCRIPT"&&(console.debug("removing associated script with id",i),s.remove());}}}var B=null,K=new Set;h.defineExtension("sse",{init:function(e){B=e;},onEvent:function(e,t){let n=t.target;if(n instanceof HTMLElement&&(e==="htmx:beforeCleanupElement"&&J(n),e==="htmx:beforeProcessNode")){let r=document.querySelectorAll("[sse-connect]");for(let o of Array.from(r)){let i=o.getAttribute("sse-connect");i&&!K.has(i)&&(fe(o,i),K.add(i));}}}});function fe(e,t){if(!t)return;console.info("Connecting to EventSource",t);let n=new EventSource(t);n.onopen=function(r){console.log("EventSource open:",r),h.trigger(e,"htmx:sseOpen",{event:r});},n.onerror=function(r){h.trigger(e,"htmx:sseError",{event:r}),n.readyState==EventSource.CLOSED&&h.trigger(e,"htmx:sseClose",{event:r});},n.onmessage=function(r){console.log("EventSource message:",r.data),h.trigger(e,"htmx:sseBeforeMessage",{event:r});let o=r.data,i=B.makeFragment(o),s=Array.from(i.children);for(let l of s)B.oobSwap(B.getAttributeValue(l,"hx-swap-oob")||"true",l,{tasks:[]}),l.tagName==="SCRIPT"&&l.id.startsWith("__eval")&&document.body.appendChild(l);h.trigger(e,"htmx:sseAfterMessage",{event:r});};}function de(e){let t=window.location.href;setInterval(()=>{window.location.href!==t&&(e(t,window.location.href),t=window.location.href);},100);}de((e,t)=>{he(t);});function he(e){let t=new URL(e);document.querySelectorAll("[hx-trigger]").forEach(function(n){let r=n.getAttribute("hx-trigger");if(!r)return;if(r.split(", ").find(i=>i==="url"))h.swap(n,"url",{swapStyle:"outerHTML",swapDelay:0,settleDelay:0});else for(let[i,s]of t.searchParams){let l="qs:"+i;if(r.includes(l)){console.log("triggering",l),h.trigger(n,l,null);break}}}),document.querySelectorAll("[hx-match-qp]").forEach(n=>{let r=!1;for(let o of n.getAttributeNames())if(o.startsWith("hx-match-qp-mapping:")){let i=o.replace("hx-match-qp-mapping:","");if(t.searchParams.get(i)){h.swap(n,n.getAttribute(o)??"",{swapStyle:"innerHTML",swapDelay:0,settleDelay:0}),r=!0;break}}if(!r){let o=n.getAttribute("hx-match-qp-default");o&&h.swap(n,n.getAttribute("hx-match-qp-mapping:"+o)??"",{swapStyle:"innerHTML",swapDelay:0,settleDelay:0});}});} -//# sourceMappingURL=htmgo.js.map -//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uL2pzL25vZGVfbW9kdWxlcy9odG14Lm9yZy9kaXN0L2h0bXguZXNtLmpzIiwiLi4vanMvaHRteGV4dGVuc2lvbnMvcGF0aGRlcHMudHMiLCIuLi9qcy9odG14ZXh0ZW5zaW9ucy90cmlnZ2VyLWNoaWxkcmVuLnRzIiwiLi4vanMvaHRteGV4dGVuc2lvbnMvZGVidWcudHMiLCIuLi9qcy9odG14ZXh0ZW5zaW9ucy9yZXNwb25zZS10YXJnZXRzLnRzIiwiLi4vanMvaHRteGV4dGVuc2lvbnMvbXV0YXRpb24tZXJyb3IudHMiLCIuLi9qcy9odG14ZXh0ZW5zaW9ucy9saXZlcmVsb2FkLnRzIiwiLi4vanMvaHRteGV4dGVuc2lvbnMvaHRtZ28udHMiLCIuLi9qcy9odG14ZXh0ZW5zaW9ucy9zc2UudHMiLCIuLi9qcy9odG1nby50cyJdLCJuYW1lcyI6WyJodG14IiwiZWx0IiwidHlwZSIsInZlcmIiLCJ0YWciLCJnbG9iYWwiLCJzdHIiLCJpbnRlcnZhbCIsIm5hbWUiLCJxdWFsaWZpZWROYW1lIiwicGFyZW50IiwiY29uZGl0aW9uIiwiaW5pdGlhbEVsZW1lbnQiLCJhbmNlc3RvciIsImF0dHJpYnV0ZU5hbWUiLCJhdHRyaWJ1dGVWYWx1ZSIsImRpc2luaGVyaXQiLCJpbmhlcml0IiwiY2xvc2VzdEF0dHIiLCJlIiwic2VsZWN0b3IiLCJtYXRjaGVzRnVuY3Rpb24iLCJtYXRjaCIsInJlc3AiLCJmcmFnbWVudCIsInNjcmlwdCIsIm5ld1NjcmlwdCIsImF0dHIiLCJyZXNwb25zZSIsInJlc3BvbnNlV2l0aE5vSGVhZCIsInN0YXJ0VGFnIiwiZG9jIiwidGl0bGVFbGVtZW50IiwiZnVuYyIsIm8iLCJkYXRhUHJvcCIsImRhdGEiLCJhcnIiLCJyZXR1cm5BcnIiLCJpIiwiZWwiLCJyZWN0IiwiZWxlbVRvcCIsImVsZW1Cb3R0b20iLCJyb290Tm9kZSIsInRyaWdnZXIiLCJvYmoxIiwib2JqMiIsImtleSIsImpTdHJpbmciLCJlcnJvciIsInRlc3QiLCJwYXRoIiwidXJsIiwiY2FsbGJhY2siLCJldnQiLCJldmVudCIsImVsdE9yU2VsZWN0b3IiLCJkZWxheSIsInZhbHVlIiwiY2xhenoiLCJub2RlIiwiY2hpbGQiLCJwcmVmaXgiLCJzdWZmaXgiLCJ0cmltbWVkU2VsZWN0b3IiLCJzdGFydCIsInJlc3VsdHMiLCJjb250ZXh0IiwiYXJnMSIsImFyZzIiLCJhcmczIiwiZXZlbnRBcmdzIiwiYXR0ck5hbWUiLCJhdHRyVGFyZ2V0IiwicmVzdWx0IiwiYXR0cmlidXRlIiwidGFyZ2V0U3RyIiwiYXR0cmlidXRlc1RvU2V0dGxlIiwibWVyZ2VUbyIsIm1lcmdlRnJvbSIsInN3YXBTdHlsZSIsInRhcmdldCIsImV4dGVuc2lvbnMiLCJleHRlbnNpb24iLCJvb2JWYWx1ZSIsIm9vYkVsZW1lbnQiLCJzZXR0bGVJbmZvIiwidGFyZ2V0cyIsIm9vYkVsZW1lbnRDbG9uZSIsImJlZm9yZVN3YXBEZXRhaWxzIiwicHJlc2VydmVkRWx0IiwiaWQiLCJvbGRFbHQiLCJwYXJlbnROb2RlIiwibmV3Tm9kZSIsIm5vcm1hbGl6ZWRJZCIsIm5vcm1hbGl6ZWRUYWciLCJwYXJlbnRFbHQiLCJvbGROb2RlIiwibmV3QXR0cmlidXRlcyIsImF1dG9mb2N1cyIsImF1dG9Gb2N1c2VkRWx0IiwiaW5zZXJ0QmVmb3JlIiwic3RyaW5nIiwiaGFzaCIsImNoYXIiLCJpbnRlcm5hbERhdGEiLCJoYW5kbGVySW5mbyIsImVsZW1lbnQiLCJpbmZvIiwibmV3RWx0IiwiZWx0QmVmb3JlTmV3Q29udGVudCIsImZpcnN0Q2hpbGQiLCJleHQiLCJuZXdFbGVtZW50cyIsImoiLCJvb2JFbHRzIiwiY29udGVudCIsInN3YXBTcGVjIiwic3dhcE9wdGlvbnMiLCJhY3RpdmVFbHQiLCJzZWxlY3Rpb25JbmZvIiwib29iU2VsZWN0VmFsdWVzIiwib29iU2VsZWN0VmFsdWUiLCJ0ZW1wbGF0ZSIsIm5ld0ZyYWdtZW50IiwibmV3QWN0aXZlRWx0IiwiZm9jdXNPcHRpb25zIiwiZG9TZXR0bGUiLCJ0YXNrIiwiYW5jaG9yVGFyZ2V0IiwieGhyIiwiaGVhZGVyIiwidHJpZ2dlckJvZHkiLCJ0cmlnZ2VycyIsImV2ZW50TmFtZSIsImRldGFpbCIsImV2ZW50TmFtZXMiLCJ0b2tlbnMiLCJwb3NpdGlvbiIsInN0YXJ0UG9zaXRpb24iLCJzdGFydENoYXIiLCJzeW1ib2wiLCJ0b2tlbiIsImxhc3QiLCJwYXJhbU5hbWUiLCJicmFja2V0Q291bnQiLCJjb25kaXRpb25hbFNvdXJjZSIsImNvbmRpdGlvbkZ1bmN0aW9uIiwiZXhwbGljaXRUcmlnZ2VyIiwiY2FjaGUiLCJ0cmlnZ2VyU3BlY3MiLCJpbml0aWFsTGVuZ3RoIiwiZXZlcnkiLCJldmVudEZpbHRlciIsInRyaWdnZXJTcGVjIiwiZnJvbV9hcmciLCJoYW5kbGVyIiwic3BlYyIsIm5vZGVEYXRhIiwicmF3QXR0cmlidXRlIiwic291cmNlIiwiZXhwbGljaXRDYW5jZWwiLCJlbGVtZW50RGF0YSIsImVsdHNUb0xpc3Rlbk9uIiwiZWx0VG9MaXN0ZW5PbiIsImVsdFRvTGlzdGVuT25EYXRhIiwiZXZlbnRMaXN0ZW5lciIsImV2ZW50RGF0YSIsImxvYWQiLCJleHBsaWNpdEFjdGlvbiIsIm9ic2VydmVyT3B0aW9ucyIsImVudHJpZXMiLCJhdHRyaWJ1dGVzIiwiZWxlbWVudHMiLCJpdGVyIiwiYm9vc3RlZFNlbGVjdG9yIiwiZXh0ZW5zaW9uU2VsZWN0b3JzIiwic2VsZWN0b3JzIiwicyIsImZvcm0iLCJjb2RlIiwibGlzdGVuZXIiLCJhZnRlck9uUG9zaXRpb24iLCJuZXh0Q2hhciIsInRvRG8iLCJtc2ciLCJldmVudFJlc3VsdCIsImtlYmFiTmFtZSIsImtlYmFiZWRFdmVudCIsInJvb3RFbHQiLCJpbm5lckhUTUwiLCJ0aXRsZSIsInNjcm9sbCIsImhpc3RvcnlDYWNoZSIsIm5ld0hpc3RvcnlJdGVtIiwiY2xhc3NOYW1lIiwiY2xvbmUiLCJkaXNhYmxlSGlzdG9yeUNhY2hlIiwidGFza3MiLCJyZXF1ZXN0IiwiZGV0YWlscyIsImhpc3RvcnlFbGVtZW50IiwiY2FjaGVkIiwiaW5kaWNhdG9ycyIsImljIiwiZGlzYWJsZWRFbHRzIiwiZGlzYWJsZWRFbGVtZW50IiwiZGlzYWJsZWQiLCJwcm9jZXNzZWQiLCJmb3JtRGF0YSIsInYiLCJ2YWx1ZXMiLCJlcnJvcnMiLCJ2YWxpZGF0ZSIsImlucHV0IiwicmVjZWl2ZXIiLCJkb25vciIsInByaW9yaXR5Rm9ybURhdGEiLCJidXR0b24iLCJpbmNsdWRlcyIsImRlc2NlbmRhbnQiLCJyZXR1cm5TdHIiLCJyZWFsVmFsdWUiLCJwcm9tcHQiLCJoZWFkZXJzIiwiaW5wdXRWYWx1ZXMiLCJwYXJhbXNWYWx1ZSIsIm5ld1ZhbHVlcyIsInN3YXBJbmZvT3ZlcnJpZGUiLCJzd2FwSW5mbyIsInNwbGl0Iiwic3BsaXRTcGVjIiwic2Nyb2xsVmFsIiwic2VsZWN0b3JWYWwiLCJzaG93VmFsIiwiZm9jdXNTY3JvbGxWYWwiLCJmaWx0ZXJlZFBhcmFtZXRlcnMiLCJlbmNvZGVkUGFyYW1ldGVycyIsImZpcnN0IiwiZXZhbEFzRGVmYXVsdCIsImV2YWx1YXRlVmFsdWUiLCJ2YXJzVmFsdWVzIiwidG9FdmFsIiwiZGVmYXVsdFZhbCIsImV4cHJlc3Npb25WYXJzIiwiaGVhZGVyVmFsdWUiLCJyZWdleHAiLCJyZXF1ZXN0Q29uZmlnIiwic2FtZUhvc3QiLCJvYmoiLCJhcnJheSIsImluZGV4IiwicHJvcCIsImV0YyIsImNvbmZpcm1lZCIsInJlc29sdmUiLCJyZWplY3QiLCJwcm9taXNlIiwiX3Jlc29sdmUiLCJfcmVqZWN0IiwicmVzcG9uc2VIYW5kbGVyIiwic2VsZWN0IiwiZWx0RGF0YSIsInN1Ym1pdHRlciIsImJ1dHRvblBhdGgiLCJidXR0b25WZXJiIiwiY29uZmlybVF1ZXN0aW9uIiwic2tpcENvbmZpcm1hdGlvbiIsInN5bmNFbHQiLCJzeW5jU3RyYXRlZ3kiLCJxdWV1ZVN0cmF0ZWd5IiwiYWJvcnRhYmxlIiwic3luY1N0cmluZ3MiLCJlbmRSZXF1ZXN0TG9jayIsInByb21wdFF1ZXN0aW9uIiwicHJvbXB0UmVzcG9uc2UiLCJyYXdGb3JtRGF0YSIsImFsbEZvcm1EYXRhIiwiZmlsdGVyZWRGb3JtRGF0YSIsInJlcXVlc3RBdHRyVmFsdWVzIiwiZWx0SXNCb29zdGVkIiwidXNlVXJsUGFyYW1zIiwic3BsaXRQYXRoIiwicGF0aE5vQW5jaG9yIiwiYW5jaG9yIiwiZmluYWxQYXRoIiwicmVzcG9uc2VJbmZvIiwiaGllcmFyY2h5IiwiZGlzYWJsZUVsdHMiLCJzZWNvbmRhcnlUcmlnZ2VyRWx0IiwicGFyZW50RWx0SW5IaWVyYXJjaHkiLCJwYXJhbXMiLCJwYXRoRnJvbUhlYWRlcnMiLCJ0eXBlRnJvbUhlYWRlcnMiLCJyZXF1ZXN0UGF0aCIsInJlc3BvbnNlUGF0aCIsInB1c2hVcmwiLCJyZXBsYWNlVXJsIiwiZWxlbWVudElzQm9vc3RlZCIsInNhdmVUeXBlIiwicmVzcG9uc2VIYW5kbGluZ0NvbmZpZyIsInN0YXR1cyIsInJlZ0V4cCIsInJlc3BvbnNlSGFuZGxpbmdFbGVtZW50IiwidGl0bGVFbHQiLCJyZXNwb25zZUluZm9TZWxlY3QiLCJyZWRpcmVjdFBhdGgiLCJyZWRpcmVjdFN3YXBTcGVjIiwic2hvdWxkUmVmcmVzaCIsImhpc3RvcnlVcGRhdGUiLCJyZXNwb25zZUhhbmRsaW5nIiwic2hvdWxkU3dhcCIsImlzRXJyb3IiLCJpZ25vcmVUaXRsZSIsInNlbGVjdE92ZXJyaWRlIiwic3dhcE92ZXJyaWRlIiwic2VydmVyUmVzcG9uc2UiLCJzZXR0bGVSZXNvbHZlIiwic2V0dGxlUmVqZWN0Iiwic2VsZWN0T09CIiwiZG9Td2FwIiwiZmluYWxFbHQiLCJzaG91bGRUcmFuc2l0aW9uIiwic2V0dGxlUHJvbWlzZSIsImlubmVyRG9Td2FwIiwiYXBpIiwidGV4dCIsInBhcmFtZXRlcnMiLCJleHRlbnNpb25zVG9SZXR1cm4iLCJleHRlbnNpb25zVG9JZ25vcmUiLCJleHRlbnNpb25zRm9yRWxlbWVudCIsImV4dGVuc2lvbk5hbWUiLCJmbiIsIm5vbmNlQXR0cmlidXRlIiwibWV0YUNvbmZpZyIsImJvZHkiLCJyZXN0b3JlZEVsdHMiLCJvcmlnaW5hbFBvcHN0YXRlIiwiaHRteF9lc21fZGVmYXVsdCIsImRlcGVuZHNPbiIsInBhdGhTcGVjIiwiZGVwZW5kZW5jeVBhdGgiLCJ1cmxQYXRoIiwiZGVwZW5kZW5jeUVsZW1lbnQiLCJwYXRoRWxlbWVudCIsInJlZnJlc2hQYXRoIiwiZWx0c1dpdGhEZXBzIiwiY29uZmlnIiwia2ViYWJFdmVudE5hbWUiLCJpZ25vcmVkRXZlbnRzIiwibWFrZUV2ZW50IiwidHJpZ2dlckNoaWxkcmVuIiwidHJpZ2dlcmVkIiwibmV3RXZlbnQiLCJhdHRyUHJlZml4Iiwic3RhcnRzV2l0aCIsImdldFJlc3BDb2RlVGFyZ2V0IiwicmVzcENvZGVOdW1iZXIiLCJyZXNwQ29kZSIsImF0dHJQb3NzaWJpbGl0aWVzIiwiYXR0clZhbHVlIiwiaGFuZGxlRXJyb3JGbGFnIiwiYXBpUmVmIiwibGFzdFZlcnNpb24iLCJlbmFibGVkIiwiZXZlbnRTb3VyY2UiLCJtZXNzYWdlIiwicmVsb2FkIiwiZXZhbEZ1bmNSZWdleCIsInJlbW92ZUFzc29jaWF0ZWRTY3JpcHRzIiwibWF0Y2hlcyIsImVsZSIsImNvbm5lY3RFdmVudFNvdXJjZSIsImNoaWxkcmVuIiwid2F0Y2hVcmwiLCJsYXN0VXJsIiwiXyIsIm5ld1VybCIsIm9uVXJsQ2hhbmdlIiwiaGFzTWF0Y2giLCJkZWZhdWx0S2V5Il0sIm1hcHBpbmdzIjoiQUFBQSxJQUFJQSxFQUFBQSxDQUFRLFVBQVcsQ0FJckIsSUFBTSxJQUFPLENBQUEsQ0FJWCxPQUFRLElBRVIsQ0FBQSxPQUFBLENBQVMsS0FFVCxFQUFJLENBQUEsSUFBQSxDQUVKLElBQUssSUFFTCxDQUFBLE9BQUEsQ0FBUyxLQUVULElBQU0sQ0FBQSxJQUFBLENBR04sS0FBTSxJQUVOLENBQUEsT0FBQSxDQUFTLEtBRVQsT0FBUyxDQUFBLElBQUEsQ0FVVCxPQUFRLFNBQVNDLENBQUFBLENBQUtDLEVBQU0sQ0FFMUIsT0FEb0IsZUFBZUQsQ0FBS0MsQ0FBQUEsQ0FBQUEsRUFBUSxNQUFNLENBQ25D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Q0FBQUEsQ0FBUzJCLEVBQVcsSUFBSSxDQUFBLENBQ3JDLEtBQ0YsQ0FDRixDQUVKLENBQUMsQ0FFRCxDQUFBLFFBQUEsQ0FBUyxpQkFBaUIsZUFBZSxDQUFBLENBQUUsUUFBU2hHLENBQU8sRUFBQSxDQUN6RCxJQUFJcVYsQ0FBVyxDQUFBLENBQUEsQ0FBQSxDQUNmLFFBQVNyWCxDQUFRZ0MsSUFBQUEsQ0FBQUEsQ0FBRyxtQkFDbEIsQ0FBQSxHQUFJaEMsRUFBSyxVQUFXLENBQUEsc0JBQXNCLEVBQUcsQ0FDM0MsSUFBSWMsRUFBUWQsQ0FBSyxDQUFBLE9BQUEsQ0FBUSx1QkFBd0IsRUFBRSxDQUFBLENBRW5ELEdBRFk2QyxDQUFJLENBQUEsWUFBQSxDQUFhLElBQUkvQixDQUFLLENBQUEsQ0FDM0IsQ0FDVDhULENBQUssQ0FBQSxJQUFBLENBQUs1UyxDQUFJQSxDQUFBQSxDQUFBQSxDQUFHLFlBQWFoQyxDQUFBQSxDQUFJLEdBQUssRUFBSSxDQUFBLENBQ3pDLFVBQVcsV0FDWCxDQUFBLFNBQUEsQ0FBVyxFQUNYLFdBQWEsQ0FBQSxDQUNmLENBQUMsQ0FDRHFYLENBQUFBLENBQUFBLENBQVcsR0FDWCxLQUNGLENBQ0YsQ0FFRixHQUFJLENBQUNBLEVBQVUsQ0FDYixJQUFJQyxFQUFhdFYsQ0FBRyxDQUFBLFlBQUEsQ0FBYSxxQkFBcUIsQ0FDbERzVixDQUFBQSxDQUFBQSxFQUNGMUMsRUFBSyxJQUNINVMsQ0FBQUEsQ0FBQUEsQ0FDQUEsRUFBRyxZQUFhLENBQUEsc0JBQUEsQ0FBeUJzVixDQUFVLENBQUssRUFBQSxFQUFBLENBQ3hELENBQUUsU0FBVyxDQUFBLFdBQUEsQ0FBYSxVQUFXLENBQUcsQ0FBQSxXQUFBLENBQWEsQ0FBRSxDQUN6RCxFQUVKLENBQ0YsQ0FBQyxFQUNIIiwiZmlsZSI6Imh0bWdvLmpzIiwic291cmNlc0NvbnRlbnQiOlsidmFyIGh0bXggPSAoZnVuY3Rpb24oKSB7XG4gICd1c2Ugc3RyaWN0J1xuXG4gIC8vIFB1YmxpYyBBUElcbiAgY29uc3QgaHRteCA9IHtcbiAgICAvLyBUc2MgbWFkbmVzcyBoZXJlLCBhc3NpZ25pbmcgdGhlIGZ1bmN0aW9ucyBkaXJlY3RseSByZXN1bHRzIGluIGFuIGludmFsaWQgVHlwZVNjcmlwdCBvdXRwdXQsIGJ1dCByZWFzc2lnbmluZyBpcyBmaW5lXG4gICAgLyogRXZlbnQgcHJvY2Vzc2luZyAqL1xuICAgIC8qKiBAdHlwZSB7dHlwZW9mIG9uTG9hZEhlbHBlcn0gKi9cbiAgICBvbkxvYWQ6IG51bGwsXG4gICAgLyoqIEB0eXBlIHt0eXBlb2YgcHJvY2Vzc05vZGV9ICovXG4gICAgcHJvY2VzczogbnVsbCxcbiAgICAvKiogQHR5cGUge3R5cGVvZiBhZGRFdmVudExpc3RlbmVySW1wbH0gKi9cbiAgICBvbjogbnVsbCxcbiAgICAvKiogQHR5cGUge3R5cGVvZiByZW1vdmVFdmVudExpc3RlbmVySW1wbH0gKi9cbiAgICBvZmY6IG51bGwsXG4gICAgLyoqIEB0eXBlIHt0eXBlb2YgdHJpZ2dlckV2ZW50fSAqL1xuICAgIHRyaWdnZXI6IG51bGwsXG4gICAgLyoqIEB0eXBlIHt0eXBlb2YgYWpheEhlbHBlcn0gKi9cbiAgICBhamF4OiBudWxsLFxuICAgIC8qIERPTSBxdWVyeWluZyBoZWxwZXJzICovXG4gICAgLyoqIEB0eXBlIHt0eXBlb2YgZmluZH0gKi9cbiAgICBmaW5kOiBudWxsLFxuICAgIC8qKiBAdHlwZSB7dHlwZW9mIGZpbmRBbGx9ICovXG4gICAgZmluZEFsbDogbnVsbCxcbiAgICAvKiogQHR5cGUge3R5cGVvZiBjbG9zZXN0fSAqL1xuICAgIGNsb3Nlc3Q6IG51bGwsXG4gICAgLyoqXG4gICAgICogUmV0dXJucyB0aGUgaW5wdXQgdmFsdWVzIHRoYXQgd291bGQgcmVzb2x2ZSBmb3IgYSBnaXZlbiBlbGVtZW50IHZpYSB0aGUgaHRteCB2YWx1ZSByZXNvbHV0aW9uIG1lY2hhbmlzbVxuICAgICAqXG4gICAgICogQHNlZSBodHRwczovL2h0bXgub3JnL2FwaS8jdmFsdWVzXG4gICAgICpcbiAgICAgKiBAcGFyYW0ge0VsZW1lbnR9IGVsdCB0aGUgZWxlbWVudCB0byByZXNvbHZlIHZhbHVlcyBvblxuICAgICAqIEBwYXJhbSB7SHR0cFZlcmJ9IHR5cGUgdGhlIHJlcXVlc3QgdHlwZSAoZS5nLiAqKmdldCoqIG9yICoqcG9zdCoqKSBub24tR0VUJ3Mgd2lsbCBpbmNsdWRlIHRoZSBlbmNsb3NpbmcgZm9ybSBvZiB0aGUgZWxlbWVudC4gRGVmYXVsdHMgdG8gKipwb3N0KipcbiAgICAgKiBAcmV0dXJucyB7T2JqZWN0fVxuICAgICAqL1xuICAgIHZhbHVlczogZnVuY3Rpb24oZWx0LCB0eXBlKSB7XG4gICAgICBjb25zdCBpbnB1dFZhbHVlcyA9IGdldElucHV0VmFsdWVzKGVsdCwgdHlwZSB8fCAncG9zdCcpXG4gICAgICByZXR1cm4gaW5wdXRWYWx1ZXMudmFsdWVzXG4gICAgfSxcbiAgICAvKiBET00gbWFuaXB1bGF0aW9uIGhlbHBlcnMgKi9cbiAgICAvKiogQHR5cGUge3R5cGVvZiByZW1vdmVFbGVtZW50fSAqL1xuICAgIHJlbW92ZTogbnVsbCxcbiAgICAvKiogQHR5cGUge3R5cGVvZiBhZGRDbGFzc1RvRWxlbWVudH0gKi9cbiAgICBhZGRDbGFzczogbnVsbCxcbiAgICAvKiogQHR5cGUge3R5cGVvZiByZW1vdmVDbGFzc0Zyb21FbGVtZW50fSAqL1xuICAgIHJlbW92ZUNsYXNzOiBudWxsLFxuICAgIC8qKiBAdHlwZSB7dHlwZW9mIHRvZ2dsZUNsYXNzT25FbGVtZW50fSAqL1xuICAgIHRvZ2dsZUNsYXNzOiBudWxsLFxuICAgIC8qKiBAdHlwZSB7dHlwZW9mIHRha2VDbGFzc0ZvckVsZW1lbnR9ICovXG4gICAgdGFrZUNsYXNzOiBudWxsLFxuICAgIC8qKiBAdHlwZSB7dHlwZW9mIHN3YXB9ICovXG4gICAgc3dhcDogbnVsbCxcbiAgICAvKiBFeHRlbnNpb24gZW50cnlwb2ludHMgKi9cbiAgICAvKiogQHR5cGUge3R5cGVvZiBkZWZpbmVFeHRlbnNpb259ICovXG4gICAgZGVmaW5lRXh0ZW5zaW9uOiBudWxsLFxuICAgIC8qKiBAdHlwZSB7dHlwZW9mIHJlbW92ZUV4dGVuc2lvbn0gKi9cbiAgICByZW1vdmVFeHRlbnNpb246IG51bGwsXG4gICAgLyogRGVidWdnaW5nICovXG4gICAgLyoqIEB0eXBlIHt0eXBlb2YgbG9nQWxsfSAqL1xuICAgIGxvZ0FsbDogbnVsbCxcbiAgICAvKiogQHR5cGUge3R5cGVvZiBsb2dOb25lfSAqL1xuICAgIGxvZ05vbmU6IG51bGwsXG4gICAgLyogRGVidWdnaW5nICovXG4gICAgLyoqXG4gICAgICogVGhlIGxvZ2dlciBodG14IHVzZXMgdG8gbG9nIHdpdGhcbiAgICAgKlxuICAgICAqIEBzZWUgaHR0cHM6Ly9odG14Lm9yZy9hcGkvI2xvZ2dlclxuICAgICAqL1xuICAgIGxvZ2dlcjogbnVsbCxcbiAgICAvKipcbiAgICAgKiBBIHByb3BlcnR5IGhvbGRpbmcgdGhlIGNvbmZpZ3VyYXRpb24gaHRteCB1c2VzIGF0IHJ1bnRpbWUuXG4gICAgICpcbiAgICAgKiBOb3RlIHRoYXQgdXNpbmcgYSBbbWV0YSB0YWddKGh0dHBzOi8vaHRteC5vcmcvZG9jcy8jY29uZmlnKSBpcyB0aGUgcHJlZmVycmVkIG1lY2hhbmlzbSBmb3Igc2V0dGluZyB0aGVzZSBwcm9wZXJ0aWVzLlxuICAgICAqXG4gICAgICogQHNlZSBodHRwczovL2h0bXgub3JnL2FwaS8jY29uZmlnXG4gICAgICovXG4gICAgY29uZmlnOiB7XG4gICAgICAvKipcbiAgICAgICAqIFdoZXRoZXIgdG8gdXNlIGhpc3RvcnkuXG4gICAgICAgKiBAdHlwZSBib29sZWFuXG4gICAgICAgKiBAZGVmYXVsdCB0cnVlXG4gICAgICAgKi9cbiAgICAgIGhpc3RvcnlFbmFibGVkOiB0cnVlLFxuICAgICAgLyoqXG4gICAgICAgKiBUaGUgbnVtYmVyIG9mIHBhZ2VzIHRvIGtlZXAgaW4gKipsb2NhbFN0b3JhZ2UqKiBmb3IgaGlzdG9yeSBzdXBwb3J0LlxuICAgICAgICogQHR5cGUgbnVtYmVyXG4gICAgICAgKiBAZGVmYXVsdCAxMFxuICAgICAgICovXG4gICAgICBoaXN0b3J5Q2FjaGVTaXplOiAxMCxcbiAgICAgIC8qKlxuICAgICAgICogQHR5cGUgYm9vbGVhblxuICAgICAgICogQGRlZmF1bHQgZmFsc2VcbiAgICAgICAqL1xuICAgICAgcmVmcmVzaE9uSGlzdG9yeU1pc3M6IGZhbHNlLFxuICAgICAgLyoqXG4gICAgICAgKiBUaGUgZGVmYXVsdCBzd2FwIHN0eWxlIHRvIHVzZSBpZiAqKltoeC1zd2FwXShodHRwczovL2h0bXgub3JnL2F0dHJpYnV0ZXMvaHgtc3dhcCkqKiBpcyBvbWl0dGVkLlxuICAgICAgICogQHR5cGUgSHRteFN3YXBTdHlsZVxuICAgICAgICogQGRlZmF1bHQgJ2lubmVySFRNTCdcbiAgICAgICAqL1xuICAgICAgZGVmYXVsdFN3YXBTdHlsZTogJ2lubmVySFRNTCcsXG4gICAgICAvKipcbiAgICAgICAqIFRoZSBkZWZhdWx0IGRlbGF5IGJldHdlZW4gcmVjZWl2aW5nIGEgcmVzcG9uc2UgZnJvbSB0aGUgc2VydmVyIGFuZCBkb2luZyB0aGUgc3dhcC5cbiAgICAgICAqIEB0eXBlIG51bWJlclxuICAgICAgICogQGRlZmF1bHQgMFxuICAgICAgICovXG4gICAgICBkZWZhdWx0U3dhcERlbGF5OiAwLFxuICAgICAgLyoqXG4gICAgICAgKiBUaGUgZGVmYXVsdCBkZWxheSBiZXR3ZWVuIGNvbXBsZXRpbmcgdGhlIGNvbnRlbnQgc3dhcCBhbmQgc2V0dGxpbmcgYXR0cmlidXRlcy5cbiAgICAgICAqIEB0eXBlIG51bWJlclxuICAgICAgICogQGRlZmF1bHQgMjBcbiAgICAgICAqL1xuICAgICAgZGVmYXVsdFNldHRsZURlbGF5OiAyMCxcbiAgICAgIC8qKlxuICAgICAgICogSWYgdHJ1ZSwgaHRteCB3aWxsIGluamVjdCBhIHNtYWxsIGFtb3VudCBvZiBDU1MgaW50byB0aGUgcGFnZSB0byBtYWtlIGluZGljYXRvcnMgaW52aXNpYmxlIHVubGVzcyB0aGUgKipodG14LWluZGljYXRvcioqIGNsYXNzIGlzIHByZXNlbnQuXG4gICAgICAgKiBAdHlwZSBib29sZWFuXG4gICAgICAgKiBAZGVmYXVsdCB0cnVlXG4gICAgICAgKi9cbiAgICAgIGluY2x1ZGVJbmRpY2F0b3JTdHlsZXM6IHRydWUsXG4gICAgICAvKipcbiAgICAgICAqIFRoZSBjbGFzcyB0byBwbGFjZSBvbiBpbmRpY2F0b3JzIHdoZW4gYSByZXF1ZXN0IGlzIGluIGZsaWdodC5cbiAgICAgICAqIEB0eXBlIHN0cmluZ1xuICAgICAgICogQGRlZmF1bHQgJ2h0bXgtaW5kaWNhdG9yJ1xuICAgICAgICovXG4gICAgICBpbmRpY2F0b3JDbGFzczogJ2h0bXgtaW5kaWNhdG9yJyxcbiAgICAgIC8qKlxuICAgICAgICogVGhlIGNsYXNzIHRvIHBsYWNlIG9uIHRyaWdnZXJpbmcgZWxlbWVudHMgd2hlbiBhIHJlcXVlc3QgaXMgaW4gZmxpZ2h0LlxuICAgICAgICogQHR5cGUgc3RyaW5nXG4gICAgICAgKiBAZGVmYXVsdCAnaHRteC1yZXF1ZXN0J1xuICAgICAgICovXG4gICAgICByZXF1ZXN0Q2xhc3M6ICdodG14LXJlcXVlc3QnLFxuICAgICAgLyoqXG4gICAgICAgKiBUaGUgY2xhc3MgdG8gdGVtcG9yYXJpbHkgcGxhY2Ugb24gZWxlbWVudHMgdGhhdCBodG14IGhhcyBhZGRlZCB0byB0aGUgRE9NLlxuICAgICAgICogQHR5cGUgc3RyaW5nXG4gICAgICAgKiBAZGVmYXVsdCAnaHRteC1hZGRlZCdcbiAgICAgICAqL1xuICAgICAgYWRkZWRDbGFzczogJ2h0bXgtYWRkZWQnLFxuICAgICAgLyoqXG4gICAgICAgKiBUaGUgY2xhc3MgdG8gcGxhY2Ugb24gdGFyZ2V0IGVsZW1lbnRzIHdoZW4gaHRteCBpcyBpbiB0aGUgc2V0dGxpbmcgcGhhc2UuXG4gICAgICAgKiBAdHlwZSBzdHJpbmdcbiAgICAgICAqIEBkZWZhdWx0ICdodG14LXNldHRsaW5nJ1xuICAgICAgICovXG4gICAgICBzZXR0bGluZ0NsYXNzOiAnaHRteC1zZXR0bGluZycsXG4gICAgICAvKipcbiAgICAgICAqIFRoZSBjbGFzcyB0byBwbGFjZSBvbiB0YXJnZXQgZWxlbWVudHMgd2hlbiBodG14IGlzIGluIHRoZSBzd2FwcGluZyBwaGFzZS5cbiAgICAgICAqIEB0eXBlIHN0cmluZ1xuICAgICAgICogQGRlZmF1bHQgJ2h0bXgtc3dhcHBpbmcnXG4gICAgICAgKi9cbiAgICAgIHN3YXBwaW5nQ2xhc3M6ICdodG14LXN3YXBwaW5nJyxcbiAgICAgIC8qKlxuICAgICAgICogQWxsb3dzIHRoZSB1c2Ugb2YgZXZhbC1saWtlIGZ1bmN0aW9uYWxpdHkgaW4gaHRteCwgdG8gZW5hYmxlICoqaHgtdmFycyoqLCB0cmlnZ2VyIGNvbmRpdGlvbnMgJiBzY3JpcHQgdGFnIGV2YWx1YXRpb24uIENhbiBiZSBzZXQgdG8gKipmYWxzZSoqIGZvciBDU1AgY29tcGF0aWJpbGl0eS5cbiAgICAgICAqIEB0eXBlIGJvb2xlYW5cbiAgICAgICAqIEBkZWZhdWx0IHRydWVcbiAgICAgICAqL1xuICAgICAgYWxsb3dFdmFsOiB0cnVlLFxuICAgICAgLyoqXG4gICAgICAgKiBJZiBzZXQgdG8gZmFsc2UsIGRpc2FibGVzIHRoZSBpbnRlcnByZXRhdGlvbiBvZiBzY3JpcHQgdGFncy5cbiAgICAgICAqIEB0eXBlIGJvb2xlYW5cbiAgICAgICAqIEBkZWZhdWx0IHRydWVcbiAgICAgICAqL1xuICAgICAgYWxsb3dTY3JpcHRUYWdzOiB0cnVlLFxuICAgICAgLyoqXG4gICAgICAgKiBJZiBzZXQsIHRoZSBub25jZSB3aWxsIGJlIGFkZGVkIHRvIGlubGluZSBzY3JpcHRzLlxuICAgICAgICogQHR5cGUgc3RyaW5nXG4gICAgICAgKiBAZGVmYXVsdCAnJ1xuICAgICAgICovXG4gICAgICBpbmxpbmVTY3JpcHROb25jZTogJycsXG4gICAgICAvKipcbiAgICAgICAqIElmIHNldCwgdGhlIG5vbmNlIHdpbGwgYmUgYWRkZWQgdG8gaW5saW5lIHN0eWxlcy5cbiAgICAgICAqIEB0eXBlIHN0cmluZ1xuICAgICAgICogQGRlZmF1bHQgJydcbiAgICAgICAqL1xuICAgICAgaW5saW5lU3R5bGVOb25jZTogJycsXG4gICAgICAvKipcbiAgICAgICAqIFRoZSBhdHRyaWJ1dGVzIHRvIHNldHRsZSBkdXJpbmcgdGhlIHNldHRsaW5nIHBoYXNlLlxuICAgICAgICogQHR5cGUgc3RyaW5nW11cbiAgICAgICAqIEBkZWZhdWx0IFsnY2xhc3MnLCAnc3R5bGUnLCAnd2lkdGgnLCAnaGVpZ2h0J11cbiAgICAgICAqL1xuICAgICAgYXR0cmlidXRlc1RvU2V0dGxlOiBbJ2NsYXNzJywgJ3N0eWxlJywgJ3dpZHRoJywgJ2hlaWdodCddLFxuICAgICAgLyoqXG4gICAgICAgKiBBbGxvdyBjcm9zcy1zaXRlIEFjY2Vzcy1Db250cm9sIHJlcXVlc3RzIHVzaW5nIGNyZWRlbnRpYWxzIHN1Y2ggYXMgY29va2llcywgYXV0aG9yaXphdGlvbiBoZWFkZXJzIG9yIFRMUyBjbGllbnQgY2VydGlmaWNhdGVzLlxuICAgICAgICogQHR5cGUgYm9vbGVhblxuICAgICAgICogQGRlZmF1bHQgZmFsc2VcbiAgICAgICAqL1xuICAgICAgd2l0aENyZWRlbnRpYWxzOiBmYWxzZSxcbiAgICAgIC8qKlxuICAgICAgICogQHR5cGUgbnVtYmVyXG4gICAgICAgKiBAZGVmYXVsdCAwXG4gICAgICAgKi9cbiAgICAgIHRpbWVvdXQ6IDAsXG4gICAgICAvKipcbiAgICAgICAqIFRoZSBkZWZhdWx0IGltcGxlbWVudGF0aW9uIG9mICoqZ2V0V2ViU29ja2V0UmVjb25uZWN0RGVsYXkqKiBmb3IgcmVjb25uZWN0aW5nIGFmdGVyIHVuZXhwZWN0ZWQgY29ubmVjdGlvbiBsb3NzIGJ5IHRoZSBldmVudCBjb2RlICoqQWJub3JtYWwgQ2xvc3VyZSoqLCAqKlNlcnZpY2UgUmVzdGFydCoqIG9yICoqVHJ5IEFnYWluIExhdGVyKiouXG4gICAgICAgKiBAdHlwZSB7J2Z1bGwtaml0dGVyJyB8ICgocmV0cnlDb3VudDpudW1iZXIpID0+IG51bWJlcil9XG4gICAgICAgKiBAZGVmYXVsdCBcImZ1bGwtaml0dGVyXCJcbiAgICAgICAqL1xuICAgICAgd3NSZWNvbm5lY3REZWxheTogJ2Z1bGwtaml0dGVyJyxcbiAgICAgIC8qKlxuICAgICAgICogVGhlIHR5cGUgb2YgYmluYXJ5IGRhdGEgYmVpbmcgcmVjZWl2ZWQgb3ZlciB0aGUgV2ViU29ja2V0IGNvbm5lY3Rpb25cbiAgICAgICAqIEB0eXBlIEJpbmFyeVR5cGVcbiAgICAgICAqIEBkZWZhdWx0ICdibG9iJ1xuICAgICAgICovXG4gICAgICB3c0JpbmFyeVR5cGU6ICdibG9iJyxcbiAgICAgIC8qKlxuICAgICAgICogQHR5cGUgc3RyaW5nXG4gICAgICAgKiBAZGVmYXVsdCAnW2h4LWRpc2FibGVdLCBbZGF0YS1oeC1kaXNhYmxlXSdcbiAgICAgICAqL1xuICAgICAgZGlzYWJsZVNlbGVjdG9yOiAnW2h4LWRpc2FibGVdLCBbZGF0YS1oeC1kaXNhYmxlXScsXG4gICAgICAvKipcbiAgICAgICAqIEB0eXBlIHsnYXV0bycgfCAnaW5zdGFudCcgfCAnc21vb3RoJ31cbiAgICAgICAqIEBkZWZhdWx0ICdpbnN0YW50J1xuICAgICAgICovXG4gICAgICBzY3JvbGxCZWhhdmlvcjogJ2luc3RhbnQnLFxuICAgICAgLyoqXG4gICAgICAgKiBJZiB0aGUgZm9jdXNlZCBlbGVtZW50IHNob3VsZCBiZSBzY3JvbGxlZCBpbnRvIHZpZXcuXG4gICAgICAgKiBAdHlwZSBib29sZWFuXG4gICAgICAgKiBAZGVmYXVsdCBmYWxzZVxuICAgICAgICovXG4gICAgICBkZWZhdWx0Rm9jdXNTY3JvbGw6IGZhbHNlLFxuICAgICAgLyoqXG4gICAgICAgKiBJZiBzZXQgdG8gdHJ1ZSBodG14IHdpbGwgaW5jbHVkZSBhIGNhY2hlLWJ1c3RpbmcgcGFyYW1ldGVyIGluIEdFVCByZXF1ZXN0cyB0byBhdm9pZCBjYWNoaW5nIHBhcnRpYWwgcmVzcG9uc2VzIGJ5IHRoZSBicm93c2VyXG4gICAgICAgKiBAdHlwZSBib29sZWFuXG4gICAgICAgKiBAZGVmYXVsdCBmYWxzZVxuICAgICAgICovXG4gICAgICBnZXRDYWNoZUJ1c3RlclBhcmFtOiBmYWxzZSxcbiAgICAgIC8qKlxuICAgICAgICogSWYgc2V0IHRvIHRydWUsIGh0bXggd2lsbCB1c2UgdGhlIFZpZXcgVHJhbnNpdGlvbiBBUEkgd2hlbiBzd2FwcGluZyBpbiBuZXcgY29udGVudC5cbiAgICAgICAqIEB0eXBlIGJvb2xlYW5cbiAgICAgICAqIEBkZWZhdWx0IGZhbHNlXG4gICAgICAgKi9cbiAgICAgIGdsb2JhbFZpZXdUcmFuc2l0aW9uczogZmFsc2UsXG4gICAgICAvKipcbiAgICAgICAqIGh0bXggd2lsbCBmb3JtYXQgcmVxdWVzdHMgd2l0aCB0aGVzZSBtZXRob2RzIGJ5IGVuY29kaW5nIHRoZWlyIHBhcmFtZXRlcnMgaW4gdGhlIFVSTCwgbm90IHRoZSByZXF1ZXN0IGJvZHlcbiAgICAgICAqIEB0eXBlIHsoSHR0cFZlcmIpW119XG4gICAgICAgKiBAZGVmYXVsdCBbJ2dldCcsICdkZWxldGUnXVxuICAgICAgICovXG4gICAgICBtZXRob2RzVGhhdFVzZVVybFBhcmFtczogWydnZXQnLCAnZGVsZXRlJ10sXG4gICAgICAvKipcbiAgICAgICAqIElmIHNldCB0byB0cnVlLCBkaXNhYmxlcyBodG14LWJhc2VkIHJlcXVlc3RzIHRvIG5vbi1vcmlnaW4gaG9zdHMuXG4gICAgICAgKiBAdHlwZSBib29sZWFuXG4gICAgICAgKiBAZGVmYXVsdCBmYWxzZVxuICAgICAgICovXG4gICAgICBzZWxmUmVxdWVzdHNPbmx5OiB0cnVlLFxuICAgICAgLyoqXG4gICAgICAgKiBJZiBzZXQgdG8gdHJ1ZSBodG14IHdpbGwgbm90IHVwZGF0ZSB0aGUgdGl0bGUgb2YgdGhlIGRvY3VtZW50IHdoZW4gYSB0aXRsZSB0YWcgaXMgZm91bmQgaW4gbmV3IGNvbnRlbnRcbiAgICAgICAqIEB0eXBlIGJvb2xlYW5cbiAgICAgICAqIEBkZWZhdWx0IGZhbHNlXG4gICAgICAgKi9cbiAgICAgIGlnbm9yZVRpdGxlOiBmYWxzZSxcbiAgICAgIC8qKlxuICAgICAgICogV2hldGhlciB0aGUgdGFyZ2V0IG9mIGEgYm9vc3RlZCBlbGVtZW50IGlzIHNjcm9sbGVkIGludG8gdGhlIHZpZXdwb3J0LlxuICAgICAgICogQHR5cGUgYm9vbGVhblxuICAgICAgICogQGRlZmF1bHQgdHJ1ZVxuICAgICAgICovXG4gICAgICBzY3JvbGxJbnRvVmlld09uQm9vc3Q6IHRydWUsXG4gICAgICAvKipcbiAgICAgICAqIFRoZSBjYWNoZSB0byBzdG9yZSBldmFsdWF0ZWQgdHJpZ2dlciBzcGVjaWZpY2F0aW9ucyBpbnRvLlxuICAgICAgICogWW91IG1heSBkZWZpbmUgYSBzaW1wbGUgb2JqZWN0IHRvIHVzZSBhIG5ldmVyLWNsZWFyaW5nIGNhY2hlLCBvciBpbXBsZW1lbnQgeW91ciBvd24gc3lzdGVtIHVzaW5nIGEgW3Byb3h5IG9iamVjdF0oaHR0cHM6Ly9kZXZlbG9wZXIubW96aWxsYS5vcmcvZG9jcy9XZWIvSmF2YVNjcmlwdC9SZWZlcmVuY2UvR2xvYmFsX09iamVjdHMvUHJveHkpXG4gICAgICAgKiBAdHlwZSB7T2JqZWN0fG51bGx9XG4gICAgICAgKiBAZGVmYXVsdCBudWxsXG4gICAgICAgKi9cbiAgICAgIHRyaWdnZXJTcGVjc0NhY2hlOiBudWxsLFxuICAgICAgLyoqIEB0eXBlIGJvb2xlYW4gKi9cbiAgICAgIGRpc2FibGVJbmhlcml0YW5jZTogZmFsc2UsXG4gICAgICAvKiogQHR5cGUgSHRteFJlc3BvbnNlSGFuZGxpbmdDb25maWdbXSAqL1xuICAgICAgcmVzcG9uc2VIYW5kbGluZzogW1xuICAgICAgICB7IGNvZGU6ICcyMDQnLCBzd2FwOiBmYWxzZSB9LFxuICAgICAgICB7IGNvZGU6ICdbMjNdLi4nLCBzd2FwOiB0cnVlIH0sXG4gICAgICAgIHsgY29kZTogJ1s0NV0uLicsIHN3YXA6IGZhbHNlLCBlcnJvcjogdHJ1ZSB9XG4gICAgICBdLFxuICAgICAgLyoqXG4gICAgICAgKiBXaGV0aGVyIHRvIHByb2Nlc3MgT09CIHN3YXBzIG9uIGVsZW1lbnRzIHRoYXQgYXJlIG5lc3RlZCB3aXRoaW4gdGhlIG1haW4gcmVzcG9uc2UgZWxlbWVudC5cbiAgICAgICAqIEB0eXBlIGJvb2xlYW5cbiAgICAgICAqIEBkZWZhdWx0IHRydWVcbiAgICAgICAqL1xuICAgICAgYWxsb3dOZXN0ZWRPb2JTd2FwczogdHJ1ZVxuICAgIH0sXG4gICAgLyoqIEB0eXBlIHt0eXBlb2YgcGFyc2VJbnRlcnZhbH0gKi9cbiAgICBwYXJzZUludGVydmFsOiBudWxsLFxuICAgIC8qKiBAdHlwZSB7dHlwZW9mIGludGVybmFsRXZhbH0gKi9cbiAgICBfOiBudWxsLFxuICAgIHZlcnNpb246ICcyLjAuMidcbiAgfVxuICAvLyBUc2MgbWFkbmVzcyBwYXJ0IDJcbiAgaHRteC5vbkxvYWQgPSBvbkxvYWRIZWxwZXJcbiAgaHRteC5wcm9jZXNzID0gcHJvY2Vzc05vZGVcbiAgaHRteC5vbiA9IGFkZEV2ZW50TGlzdGVuZXJJbXBsXG4gIGh0bXgub2ZmID0gcmVtb3ZlRXZlbnRMaXN0ZW5lckltcGxcbiAgaHRteC50cmlnZ2VyID0gdHJpZ2dlckV2ZW50XG4gIGh0bXguYWpheCA9IGFqYXhIZWxwZXJcbiAgaHRteC5maW5kID0gZmluZFxuICBodG14LmZpbmRBbGwgPSBmaW5kQWxsXG4gIGh0bXguY2xvc2VzdCA9IGNsb3Nlc3RcbiAgaHRteC5yZW1vdmUgPSByZW1vdmVFbGVtZW50XG4gIGh0bXguYWRkQ2xhc3MgPSBhZGRDbGFzc1RvRWxlbWVudFxuICBodG14LnJlbW92ZUNsYXNzID0gcmVtb3ZlQ2xhc3NGcm9tRWxlbWVudFxuICBodG14LnRvZ2dsZUNsYXNzID0gdG9nZ2xlQ2xhc3NPbkVsZW1lbnRcbiAgaHRteC50YWtlQ2xhc3MgPSB0YWtlQ2xhc3NGb3JFbGVtZW50XG4gIGh0bXguc3dhcCA9IHN3YXBcbiAgaHRteC5kZWZpbmVFeHRlbnNpb24gPSBkZWZpbmVFeHRlbnNpb25cbiAgaHRteC5yZW1vdmVFeHRlbnNpb24gPSByZW1vdmVFeHRlbnNpb25cbiAgaHRteC5sb2dBbGwgPSBsb2dBbGxcbiAgaHRteC5sb2dOb25lID0gbG9nTm9uZVxuICBodG14LnBhcnNlSW50ZXJ2YWwgPSBwYXJzZUludGVydmFsXG4gIGh0bXguXyA9IGludGVybmFsRXZhbFxuXG4gIGNvbnN0IGludGVybmFsQVBJID0ge1xuICAgIGFkZFRyaWdnZXJIYW5kbGVyLFxuICAgIGJvZHlDb250YWlucyxcbiAgICBjYW5BY2Nlc3NMb2NhbFN0b3JhZ2UsXG4gICAgZmluZFRoaXNFbGVtZW50LFxuICAgIGZpbHRlclZhbHVlcyxcbiAgICBzd2FwLFxuICAgIGhhc0F0dHJpYnV0ZSxcbiAgICBnZXRBdHRyaWJ1dGVWYWx1ZSxcbiAgICBnZXRDbG9zZXN0QXR0cmlidXRlVmFsdWUsXG4gICAgZ2V0Q2xvc2VzdE1hdGNoLFxuICAgIGdldEV4cHJlc3Npb25WYXJzLFxuICAgIGdldEhlYWRlcnMsXG4gICAgZ2V0SW5wdXRWYWx1ZXMsXG4gICAgZ2V0SW50ZXJuYWxEYXRhLFxuICAgIGdldFN3YXBTcGVjaWZpY2F0aW9uLFxuICAgIGdldFRyaWdnZXJTcGVjcyxcbiAgICBnZXRUYXJnZXQsXG4gICAgbWFrZUZyYWdtZW50LFxuICAgIG1lcmdlT2JqZWN0cyxcbiAgICBtYWtlU2V0dGxlSW5mbyxcbiAgICBvb2JTd2FwLFxuICAgIHF1ZXJ5U2VsZWN0b3JFeHQsXG4gICAgc2V0dGxlSW1tZWRpYXRlbHksXG4gICAgc2hvdWxkQ2FuY2VsLFxuICAgIHRyaWdnZXJFdmVudCxcbiAgICB0cmlnZ2VyRXJyb3JFdmVudCxcbiAgICB3aXRoRXh0ZW5zaW9uc1xuICB9XG5cbiAgY29uc3QgVkVSQlMgPSBbJ2dldCcsICdwb3N0JywgJ3B1dCcsICdkZWxldGUnLCAncGF0Y2gnXVxuICBjb25zdCBWRVJCX1NFTEVDVE9SID0gVkVSQlMubWFwKGZ1bmN0aW9uKHZlcmIpIHtcbiAgICByZXR1cm4gJ1toeC0nICsgdmVyYiArICddLCBbZGF0YS1oeC0nICsgdmVyYiArICddJ1xuICB9KS5qb2luKCcsICcpXG5cbiAgY29uc3QgSEVBRF9UQUdfUkVHRVggPSBtYWtlVGFnUmVnRXgoJ2hlYWQnKVxuXG4gIC8vPSA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4gIC8vIFV0aWxpdGllc1xuICAvLz0gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuXG4gIC8qKlxuICAgKiBAcGFyYW0ge3N0cmluZ30gdGFnXG4gICAqIEBwYXJhbSB7Ym9vbGVhbn0gZ2xvYmFsXG4gICAqIEByZXR1cm5zIHtSZWdFeHB9XG4gICAqL1xuICBmdW5jdGlvbiBtYWtlVGFnUmVnRXgodGFnLCBnbG9iYWwgPSBmYWxzZSkge1xuICAgIHJldHVybiBuZXcgUmVnRXhwKGA8JHt0YWd9KFxcXFxzW14+XSo+fD4pKFtcXFxcc1xcXFxTXSo/KTxcXFxcLyR7dGFnfT5gLFxuICAgICAgZ2xvYmFsID8gJ2dpbScgOiAnaW0nKVxuICB9XG5cbiAgLyoqXG4gICAqIFBhcnNlcyBhbiBpbnRlcnZhbCBzdHJpbmcgY29uc2lzdGVudCB3aXRoIHRoZSB3YXkgaHRteCBkb2VzLiBVc2VmdWwgZm9yIHBsdWdpbnMgdGhhdCBoYXZlIHRpbWluZy1yZWxhdGVkIGF0dHJpYnV0ZXMuXG4gICAqXG4gICAqIENhdXRpb246IEFjY2VwdHMgYW4gaW50IGZvbGxvd2VkIGJ5IGVpdGhlciAqKnMqKiBvciAqKm1zKiouIEFsbCBvdGhlciB2YWx1ZXMgdXNlICoqcGFyc2VGbG9hdCoqXG4gICAqXG4gICAqIEBzZWUgaHR0cHM6Ly9odG14Lm9yZy9hcGkvI3BhcnNlSW50ZXJ2YWxcbiAgICpcbiAgICogQHBhcmFtIHtzdHJpbmd9IHN0ciB0aW1pbmcgc3RyaW5nXG4gICAqIEByZXR1cm5zIHtudW1iZXJ8dW5kZWZpbmVkfVxuICAgKi9cbiAgZnVuY3Rpb24gcGFyc2VJbnRlcnZhbChzdHIpIHtcbiAgICBpZiAoc3RyID09IHVuZGVmaW5lZCkge1xuICAgICAgcmV0dXJuIHVuZGVmaW5lZFxuICAgIH1cblxuICAgIGxldCBpbnRlcnZhbCA9IE5hTlxuICAgIGlmIChzdHIuc2xpY2UoLTIpID09ICdtcycpIHtcbiAgICAgIGludGVydmFsID0gcGFyc2VGbG9hdChzdHIuc2xpY2UoMCwgLTIpKVxuICAgIH0gZWxzZSBpZiAoc3RyLnNsaWNlKC0xKSA9PSAncycpIHtcbiAgICAgIGludGVydmFsID0gcGFyc2VGbG9hdChzdHIuc2xpY2UoMCwgLTEpKSAqIDEwMDBcbiAgICB9IGVsc2UgaWYgKHN0ci5zbGljZSgtMSkgPT0gJ20nKSB7XG4gICAgICBpbnRlcnZhbCA9IHBhcnNlRmxvYXQoc3RyLnNsaWNlKDAsIC0xKSkgKiAxMDAwICogNjBcbiAgICB9IGVsc2Uge1xuICAgICAgaW50ZXJ2YWwgPSBwYXJzZUZsb2F0KHN0cilcbiAgICB9XG4gICAgcmV0dXJuIGlzTmFOKGludGVydmFsKSA/IHVuZGVmaW5lZCA6IGludGVydmFsXG4gIH1cblxuICAvKipcbiAgICogQHBhcmFtIHtOb2RlfSBlbHRcbiAgICogQHBhcmFtIHtzdHJpbmd9IG5hbWVcbiAgICogQHJldHVybnMgeyhzdHJpbmcgfCBudWxsKX1cbiAgICovXG4gIGZ1bmN0aW9uIGdldFJhd0F0dHJpYnV0ZShlbHQsIG5hbWUpIHtcbiAgICByZXR1cm4gZWx0IGluc3RhbmNlb2YgRWxlbWVudCAmJiBlbHQuZ2V0QXR0cmlidXRlKG5hbWUpXG4gIH1cblxuICAvKipcbiAgICogQHBhcmFtIHtFbGVtZW50fSBlbHRcbiAgICogQHBhcmFtIHtzdHJpbmd9IHF1YWxpZmllZE5hbWVcbiAgICogQHJldHVybnMge2Jvb2xlYW59XG4gICAqL1xuICAvLyByZXNvbHZlIHdpdGggYm90aCBoeCBhbmQgZGF0YS1oeCBwcmVmaXhlc1xuICBmdW5jdGlvbiBoYXNBdHRyaWJ1dGUoZWx0LCBxdWFsaWZpZWROYW1lKSB7XG4gICAgcmV0dXJuICEhZWx0Lmhhc0F0dHJpYnV0ZSAmJiAoZWx0Lmhhc0F0dHJpYnV0ZShxdWFsaWZpZWROYW1lKSB8fFxuICAgICAgZWx0Lmhhc0F0dHJpYnV0ZSgnZGF0YS0nICsgcXVhbGlmaWVkTmFtZSkpXG4gIH1cblxuICAvKipcbiAgICpcbiAgICogQHBhcmFtIHtOb2RlfSBlbHRcbiAgICogQHBhcmFtIHtzdHJpbmd9IHF1YWxpZmllZE5hbWVcbiAgICogQHJldHVybnMgeyhzdHJpbmcgfCBudWxsKX1cbiAgICovXG4gIGZ1bmN0aW9uIGdldEF0dHJpYnV0ZVZhbHVlKGVsdCwgcXVhbGlmaWVkTmFtZSkge1xuICAgIHJldHVybiBnZXRSYXdBdHRyaWJ1dGUoZWx0LCBxdWFsaWZpZWROYW1lKSB8fCBnZXRSYXdBdHRyaWJ1dGUoZWx0LCAnZGF0YS0nICsgcXVhbGlmaWVkTmFtZSlcbiAgfVxuXG4gIC8qKlxuICAgKiBAcGFyYW0ge05vZGV9IGVsdFxuICAgKiBAcmV0dXJucyB7Tm9kZSB8IG51bGx9XG4gICAqL1xuICBmdW5jdGlvbiBwYXJlbnRFbHQoZWx0KSB7XG4gICAgY29uc3QgcGFyZW50ID0gZWx0LnBhcmVudEVsZW1lbnRcbiAgICBpZiAoIXBhcmVudCAmJiBlbHQucGFyZW50Tm9kZSBpbnN0YW5jZW9mIFNoYWRvd1Jvb3QpIHJldHVybiBlbHQucGFyZW50Tm9kZVxuICAgIHJldHVybiBwYXJlbnRcbiAgfVxuXG4gIC8qKlxuICAgKiBAcmV0dXJucyB7RG9jdW1lbnR9XG4gICAqL1xuICBmdW5jdGlvbiBnZXREb2N1bWVudCgpIHtcbiAgICByZXR1cm4gZG9jdW1lbnRcbiAgfVxuXG4gIC8qKlxuICAgKiBAcGFyYW0ge05vZGV9IGVsdFxuICAgKiBAcGFyYW0ge2Jvb2xlYW59IGdsb2JhbFxuICAgKiBAcmV0dXJucyB7Tm9kZXxEb2N1bWVudH1cbiAgICovXG4gIGZ1bmN0aW9uIGdldFJvb3ROb2RlKGVsdCwgZ2xvYmFsKSB7XG4gICAgcmV0dXJuIGVsdC5nZXRSb290Tm9kZSA/IGVsdC5nZXRSb290Tm9kZSh7IGNvbXBvc2VkOiBnbG9iYWwgfSkgOiBnZXREb2N1bWVudCgpXG4gIH1cblxuICAvKipcbiAgICogQHBhcmFtIHtOb2RlfSBlbHRcbiAgICogQHBhcmFtIHsoZTpOb2RlKSA9PiBib29sZWFufSBjb25kaXRpb25cbiAgICogQHJldHVybnMge05vZGUgfCBudWxsfVxuICAgKi9cbiAgZnVuY3Rpb24gZ2V0Q2xvc2VzdE1hdGNoKGVsdCwgY29uZGl0aW9uKSB7XG4gICAgd2hpbGUgKGVsdCAmJiAhY29uZGl0aW9uKGVsdCkpIHtcbiAgICAgIGVsdCA9IHBhcmVudEVsdChlbHQpXG4gICAgfVxuXG4gICAgcmV0dXJuIGVsdCB8fCBudWxsXG4gIH1cblxuICAvKipcbiAgICogQHBhcmFtIHtFbGVtZW50fSBpbml0aWFsRWxlbWVudFxuICAgKiBAcGFyYW0ge0VsZW1lbnR9IGFuY2VzdG9yXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBhdHRyaWJ1dGVOYW1lXG4gICAqIEByZXR1cm5zIHtzdHJpbmd8bnVsbH1cbiAgICovXG4gIGZ1bmN0aW9uIGdldEF0dHJpYnV0ZVZhbHVlV2l0aERpc2luaGVyaXRhbmNlKGluaXRpYWxFbGVtZW50LCBhbmNlc3RvciwgYXR0cmlidXRlTmFtZSkge1xuICAgIGNvbnN0IGF0dHJpYnV0ZVZhbHVlID0gZ2V0QXR0cmlidXRlVmFsdWUoYW5jZXN0b3IsIGF0dHJpYnV0ZU5hbWUpXG4gICAgY29uc3QgZGlzaW5oZXJpdCA9IGdldEF0dHJpYnV0ZVZhbHVlKGFuY2VzdG9yLCAnaHgtZGlzaW5oZXJpdCcpXG4gICAgdmFyIGluaGVyaXQgPSBnZXRBdHRyaWJ1dGVWYWx1ZShhbmNlc3RvciwgJ2h4LWluaGVyaXQnKVxuICAgIGlmIChpbml0aWFsRWxlbWVudCAhPT0gYW5jZXN0b3IpIHtcbiAgICAgIGlmIChodG14LmNvbmZpZy5kaXNhYmxlSW5oZXJpdGFuY2UpIHtcbiAgICAgICAgaWYgKGluaGVyaXQgJiYgKGluaGVyaXQgPT09ICcqJyB8fCBpbmhlcml0LnNwbGl0KCcgJykuaW5kZXhPZihhdHRyaWJ1dGVOYW1lKSA+PSAwKSkge1xuICAgICAgICAgIHJldHVybiBhdHRyaWJ1dGVWYWx1ZVxuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIHJldHVybiBudWxsXG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIGlmIChkaXNpbmhlcml0ICYmIChkaXNpbmhlcml0ID09PSAnKicgfHwgZGlzaW5oZXJpdC5zcGxpdCgnICcpLmluZGV4T2YoYXR0cmlidXRlTmFtZSkgPj0gMCkpIHtcbiAgICAgICAgcmV0dXJuICd1bnNldCdcbiAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIGF0dHJpYnV0ZVZhbHVlXG4gIH1cblxuICAvKipcbiAgICogQHBhcmFtIHtFbGVtZW50fSBlbHRcbiAgICogQHBhcmFtIHtzdHJpbmd9IGF0dHJpYnV0ZU5hbWVcbiAgICogQHJldHVybnMge3N0cmluZyB8IG51bGx9XG4gICAqL1xuICBmdW5jdGlvbiBnZXRDbG9zZXN0QXR0cmlidXRlVmFsdWUoZWx0LCBhdHRyaWJ1dGVOYW1lKSB7XG4gICAgbGV0IGNsb3Nlc3RBdHRyID0gbnVsbFxuICAgIGdldENsb3Nlc3RNYXRjaChlbHQsIGZ1bmN0aW9uKGUpIHtcbiAgICAgIHJldHVybiAhIShjbG9zZXN0QXR0ciA9IGdldEF0dHJpYnV0ZVZhbHVlV2l0aERpc2luaGVyaXRhbmNlKGVsdCwgYXNFbGVtZW50KGUpLCBhdHRyaWJ1dGVOYW1lKSlcbiAgICB9KVxuICAgIGlmIChjbG9zZXN0QXR0ciAhPT0gJ3Vuc2V0Jykge1xuICAgICAgcmV0dXJuIGNsb3Nlc3RBdHRyXG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIEBwYXJhbSB7Tm9kZX0gZWx0XG4gICAqIEBwYXJhbSB7c3RyaW5nfSBzZWxlY3RvclxuICAgKiBAcmV0dXJucyB7Ym9vbGVhbn1cbiAgICovXG4gIGZ1bmN0aW9uIG1hdGNoZXMoZWx0LCBzZWxlY3Rvcikge1xuICAgIC8vIEB0cy1pZ25vcmU6IG5vbi1zdGFuZGFyZCBwcm9wZXJ0aWVzIGZvciBicm93c2VyIGNvbXBhdGliaWxpdHlcbiAgICAvLyBub2luc3BlY3Rpb24gSlNVbnJlc29sdmVkVmFyaWFibGVcbiAgICBjb25zdCBtYXRjaGVzRnVuY3Rpb24gPSBlbHQgaW5zdGFuY2VvZiBFbGVtZW50ICYmIChlbHQubWF0Y2hlcyB8fCBlbHQubWF0Y2hlc1NlbGVjdG9yIHx8IGVsdC5tc01hdGNoZXNTZWxlY3RvciB8fCBlbHQubW96TWF0Y2hlc1NlbGVjdG9yIHx8IGVsdC53ZWJraXRNYXRjaGVzU2VsZWN0b3IgfHwgZWx0Lm9NYXRjaGVzU2VsZWN0b3IpXG4gICAgcmV0dXJuICEhbWF0Y2hlc0Z1bmN0aW9uICYmIG1hdGNoZXNGdW5jdGlvbi5jYWxsKGVsdCwgc2VsZWN0b3IpXG4gIH1cblxuICAvKipcbiAgICogQHBhcmFtIHtzdHJpbmd9IHN0clxuICAgKiBAcmV0dXJucyB7c3RyaW5nfVxuICAgKi9cbiAgZnVuY3Rpb24gZ2V0U3RhcnRUYWcoc3RyKSB7XG4gICAgY29uc3QgdGFnTWF0Y2hlciA9IC88KFthLXpdW15cXC9cXDA+XFx4MjBcXHRcXHJcXG5cXGZdKikvaVxuICAgIGNvbnN0IG1hdGNoID0gdGFnTWF0Y2hlci5leGVjKHN0cilcbiAgICBpZiAobWF0Y2gpIHtcbiAgICAgIHJldHVybiBtYXRjaFsxXS50b0xvd2VyQ2FzZSgpXG4gICAgfSBlbHNlIHtcbiAgICAgIHJldHVybiAnJ1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBAcGFyYW0ge3N0cmluZ30gcmVzcFxuICAgKiBAcmV0dXJucyB7RG9jdW1lbnR9XG4gICAqL1xuICBmdW5jdGlvbiBwYXJzZUhUTUwocmVzcCkge1xuICAgIGNvbnN0IHBhcnNlciA9IG5ldyBET01QYXJzZXIoKVxuICAgIHJldHVybiBwYXJzZXIucGFyc2VGcm9tU3RyaW5nKHJlc3AsICd0ZXh0L2h0bWwnKVxuICB9XG5cbiAgLyoqXG4gICAqIEBwYXJhbSB7RG9jdW1lbnRGcmFnbWVudH0gZnJhZ21lbnRcbiAgICogQHBhcmFtIHtOb2RlfSBlbHRcbiAgICovXG4gIGZ1bmN0aW9uIHRha2VDaGlsZHJlbkZvcihmcmFnbWVudCwgZWx0KSB7XG4gICAgd2hpbGUgKGVsdC5jaGlsZE5vZGVzLmxlbmd0aCA+IDApIHtcbiAgICAgIGZyYWdtZW50LmFwcGVuZChlbHQuY2hpbGROb2Rlc1swXSlcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogQHBhcmFtIHtIVE1MU2NyaXB0RWxlbWVudH0gc2NyaXB0XG4gICAqIEByZXR1cm5zIHtIVE1MU2NyaXB0RWxlbWVudH1cbiAgICovXG4gIGZ1bmN0aW9uIGR1cGxpY2F0ZVNjcmlwdChzY3JpcHQpIHtcbiAgICBjb25zdCBuZXdTY3JpcHQgPSBnZXREb2N1bWVudCgpLmNyZWF0ZUVsZW1lbnQoJ3NjcmlwdCcpXG4gICAgZm9yRWFjaChzY3JpcHQuYXR0cmlidXRlcywgZnVuY3Rpb24oYXR0cikge1xuICAgICAgbmV3U2NyaXB0LnNldEF0dHJpYnV0ZShhdHRyLm5hbWUsIGF0dHIudmFsdWUpXG4gICAgfSlcbiAgICBuZXdTY3JpcHQudGV4dENvbnRlbnQgPSBzY3JpcHQudGV4dENvbnRlbnRcbiAgICBuZXdTY3JpcHQuYXN5bmMgPSBmYWxzZVxuICAgIGlmIChodG14LmNvbmZpZy5pbmxpbmVTY3JpcHROb25jZSkge1xuICAgICAgbmV3U2NyaXB0Lm5vbmNlID0gaHRteC5jb25maWcuaW5saW5lU2NyaXB0Tm9uY2VcbiAgICB9XG4gICAgcmV0dXJuIG5ld1NjcmlwdFxuICB9XG5cbiAgLyoqXG4gICAqIEBwYXJhbSB7SFRNTFNjcmlwdEVsZW1lbnR9IHNjcmlwdFxuICAgKiBAcmV0dXJucyB7Ym9vbGVhbn1cbiAgICovXG4gIGZ1bmN0aW9uIGlzSmF2YVNjcmlwdFNjcmlwdE5vZGUoc2NyaXB0KSB7XG4gICAgcmV0dXJuIHNjcmlwdC5tYXRjaGVzKCdzY3JpcHQnKSAmJiAoc2NyaXB0LnR5cGUgPT09ICd0ZXh0L2phdmFzY3JpcHQnIHx8IHNjcmlwdC50eXBlID09PSAnbW9kdWxlJyB8fCBzY3JpcHQudHlwZSA9PT0gJycpXG4gIH1cblxuICAvKipcbiAgICogd2UgaGF2ZSB0byBtYWtlIG5ldyBjb3BpZXMgb2Ygc2NyaXB0IHRhZ3MgdGhhdCB3ZSBhcmUgZ29pbmcgdG8gaW5zZXJ0IGJlY2F1c2VcbiAgICogU09NRSBicm93c2VycyAobm90IHNheWluZyB3aG8sIGJ1dCBpdCBpbnZvbHZlcyBhbiBlbGVtZW50IGFuZCBhbiBhbmltYWwpIGRvbid0XG4gICAqIGV4ZWN1dGUgc2NyaXB0cyBjcmVhdGVkIGluIDx0ZW1wbGF0ZT4gdGFncyB3aGVuIHRoZXkgYXJlIGluc2VydGVkIGludG8gdGhlIERPTVxuICAgKiBhbmQgYWxsIHRoZSBvdGhlcnMgZG8gbG1hb1xuICAgKiBAcGFyYW0ge0RvY3VtZW50RnJhZ21lbnR9IGZyYWdtZW50XG4gICAqL1xuICBmdW5jdGlvbiBub3JtYWxpemVTY3JpcHRUYWdzKGZyYWdtZW50KSB7XG4gICAgQXJyYXkuZnJvbShmcmFnbWVudC5xdWVyeVNlbGVjdG9yQWxsKCdzY3JpcHQnKSkuZm9yRWFjaCgvKiogQHBhcmFtIHtIVE1MU2NyaXB0RWxlbWVudH0gc2NyaXB0ICovIChzY3JpcHQpID0+IHtcbiAgICAgIGlmIChpc0phdmFTY3JpcHRTY3JpcHROb2RlKHNjcmlwdCkpIHtcbiAgICAgICAgY29uc3QgbmV3U2NyaXB0ID0gZHVwbGljYXRlU2NyaXB0KHNjcmlwdClcbiAgICAgICAgY29uc3QgcGFyZW50ID0gc2NyaXB0LnBhcmVudE5vZGVcbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICBwYXJlbnQuaW5zZXJ0QmVmb3JlKG5ld1NjcmlwdCwgc2NyaXB0KVxuICAgICAgICB9IGNhdGNoIChlKSB7XG4gICAgICAgICAgbG9nRXJyb3IoZSlcbiAgICAgICAgfSBmaW5hbGx5IHtcbiAgICAgICAgICBzY3JpcHQucmVtb3ZlKClcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH0pXG4gIH1cblxuICAvKipcbiAgICogQHR5cGVkZWYge0RvY3VtZW50RnJhZ21lbnQgJiB7dGl0bGU/OiBzdHJpbmd9fSBEb2N1bWVudEZyYWdtZW50V2l0aFRpdGxlXG4gICAqIEBkZXNjcmlwdGlvbiAgYSBkb2N1bWVudCBmcmFnbWVudCByZXByZXNlbnRpbmcgdGhlIHJlc3BvbnNlIEhUTUwsIGluY2x1ZGluZ1xuICAgKiBhIGB0aXRsZWAgcHJvcGVydHkgZm9yIGFueSB0aXRsZSBpbmZvcm1hdGlvbiBmb3VuZFxuICAgKi9cblxuICAvKipcbiAgICogQHBhcmFtIHtzdHJpbmd9IHJlc3BvbnNlIEhUTUxcbiAgICogQHJldHVybnMge0RvY3VtZW50RnJhZ21lbnRXaXRoVGl0bGV9XG4gICAqL1xuICBmdW5jdGlvbiBtYWtlRnJhZ21lbnQocmVzcG9uc2UpIHtcbiAgICAvLyBzdHJpcCBoZWFkIHRhZyB0byBkZXRlcm1pbmUgc2hhcGUgb2YgcmVzcG9uc2Ugd2UgYXJlIGRlYWxpbmcgd2l0aFxuICAgIGNvbnN0IHJlc3BvbnNlV2l0aE5vSGVhZCA9IHJlc3BvbnNlLnJlcGxhY2UoSEVBRF9UQUdfUkVHRVgsICcnKVxuICAgIGNvbnN0IHN0YXJ0VGFnID0gZ2V0U3RhcnRUYWcocmVzcG9uc2VXaXRoTm9IZWFkKVxuICAgIC8qKiBAdHlwZSBEb2N1bWVudEZyYWdtZW50V2l0aFRpdGxlICovXG4gICAgbGV0IGZyYWdtZW50XG4gICAgaWYgKHN0YXJ0VGFnID09PSAnaHRtbCcpIHtcbiAgICAgIC8vIGlmIGl0IGlzIGEgZnVsbCBkb2N1bWVudCwgcGFyc2UgaXQgYW5kIHJldHVybiB0aGUgYm9keVxuICAgICAgZnJhZ21lbnQgPSAvKiogQHR5cGUgRG9jdW1lbnRGcmFnbWVudFdpdGhUaXRsZSAqLyAobmV3IERvY3VtZW50RnJhZ21lbnQoKSlcbiAgICAgIGNvbnN0IGRvYyA9IHBhcnNlSFRNTChyZXNwb25zZSlcbiAgICAgIHRha2VDaGlsZHJlbkZvcihmcmFnbWVudCwgZG9jLmJvZHkpXG4gICAgICBmcmFnbWVudC50aXRsZSA9IGRvYy50aXRsZVxuICAgIH0gZWxzZSBpZiAoc3RhcnRUYWcgPT09ICdib2R5Jykge1xuICAgICAgLy8gcGFyc2UgYm9keSB3L28gd3JhcHBpbmcgaW4gdGVtcGxhdGVcbiAgICAgIGZyYWdtZW50ID0gLyoqIEB0eXBlIERvY3VtZW50RnJhZ21lbnRXaXRoVGl0bGUgKi8gKG5ldyBEb2N1bWVudEZyYWdtZW50KCkpXG4gICAgICBjb25zdCBkb2MgPSBwYXJzZUhUTUwocmVzcG9uc2VXaXRoTm9IZWFkKVxuICAgICAgdGFrZUNoaWxkcmVuRm9yKGZyYWdtZW50LCBkb2MuYm9keSlcbiAgICAgIGZyYWdtZW50LnRpdGxlID0gZG9jLnRpdGxlXG4gICAgfSBlbHNlIHtcbiAgICAgIC8vIG90aGVyd2lzZSB3ZSBoYXZlIG5vbi1ib2R5IHBhcnRpYWwgSFRNTCBjb250ZW50LCBzbyB3cmFwIGl0IGluIGEgdGVtcGxhdGUgdG8gbWF4aW1pemUgcGFyc2luZyBmbGV4aWJpbGl0eVxuICAgICAgY29uc3QgZG9jID0gcGFyc2VIVE1MKCc8Ym9keT48dGVtcGxhdGUgY2xhc3M9XCJpbnRlcm5hbC1odG14LXdyYXBwZXJcIj4nICsgcmVzcG9uc2VXaXRoTm9IZWFkICsgJzwvdGVtcGxhdGU+PC9ib2R5PicpXG4gICAgICBmcmFnbWVudCA9IC8qKiBAdHlwZSBEb2N1bWVudEZyYWdtZW50V2l0aFRpdGxlICovIChkb2MucXVlcnlTZWxlY3RvcigndGVtcGxhdGUnKS5jb250ZW50KVxuICAgICAgLy8gZXh0cmFjdCB0aXRsZSBpbnRvIGZyYWdtZW50IGZvciBsYXRlciBwcm9jZXNzaW5nXG4gICAgICBmcmFnbWVudC50aXRsZSA9IGRvYy50aXRsZVxuXG4gICAgICAvLyBmb3IgbGVnYWN5IHJlYXNvbnMgd2Ugc3VwcG9ydCBhIHRpdGxlIHRhZyBhdCB0aGUgcm9vdCBsZXZlbCBvZiBub24tYm9keSByZXNwb25zZXMsIHNvIHdlIG5lZWQgdG8gaGFuZGxlIGl0XG4gICAgICB2YXIgdGl0bGVFbGVtZW50ID0gZnJhZ21lbnQucXVlcnlTZWxlY3RvcigndGl0bGUnKVxuICAgICAgaWYgKHRpdGxlRWxlbWVudCAmJiB0aXRsZUVsZW1lbnQucGFyZW50Tm9kZSA9PT0gZnJhZ21lbnQpIHtcbiAgICAgICAgdGl0bGVFbGVtZW50LnJlbW92ZSgpXG4gICAgICAgIGZyYWdtZW50LnRpdGxlID0gdGl0bGVFbGVtZW50LmlubmVyVGV4dFxuICAgICAgfVxuICAgIH1cbiAgICBpZiAoZnJhZ21lbnQpIHtcbiAgICAgIGlmIChodG14LmNvbmZpZy5hbGxvd1NjcmlwdFRhZ3MpIHtcbiAgICAgICAgbm9ybWFsaXplU2NyaXB0VGFncyhmcmFnbWVudClcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIC8vIHJlbW92ZSBhbGwgc2NyaXB0IHRhZ3MgaWYgc2NyaXB0cyBhcmUgZGlzYWJsZWRcbiAgICAgICAgZnJhZ21lbnQucXVlcnlTZWxlY3RvckFsbCgnc2NyaXB0JykuZm9yRWFjaCgoc2NyaXB0KSA9PiBzY3JpcHQucmVtb3ZlKCkpXG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiBmcmFnbWVudFxuICB9XG5cbiAgLyoqXG4gICAqIEBwYXJhbSB7RnVuY3Rpb259IGZ1bmNcbiAgICovXG4gIGZ1bmN0aW9uIG1heWJlQ2FsbChmdW5jKSB7XG4gICAgaWYgKGZ1bmMpIHtcbiAgICAgIGZ1bmMoKVxuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBAcGFyYW0ge2FueX0gb1xuICAgKiBAcGFyYW0ge3N0cmluZ30gdHlwZVxuICAgKiBAcmV0dXJuc1xuICAgKi9cbiAgZnVuY3Rpb24gaXNUeXBlKG8sIHR5cGUpIHtcbiAgICByZXR1cm4gT2JqZWN0LnByb3RvdHlwZS50b1N0cmluZy5jYWxsKG8pID09PSAnW29iamVjdCAnICsgdHlwZSArICddJ1xuICB9XG5cbiAgLyoqXG4gICAqIEBwYXJhbSB7Kn0gb1xuICAgKiBAcmV0dXJucyB7byBpcyBGdW5jdGlvbn1cbiAgICovXG4gIGZ1bmN0aW9uIGlzRnVuY3Rpb24obykge1xuICAgIHJldHVybiB0eXBlb2YgbyA9PT0gJ2Z1bmN0aW9uJ1xuICB9XG5cbiAgLyoqXG4gICAqIEBwYXJhbSB7Kn0gb1xuICAgKiBAcmV0dXJucyB7byBpcyBPYmplY3R9XG4gICAqL1xuICBmdW5jdGlvbiBpc1Jhd09iamVjdChvKSB7XG4gICAgcmV0dXJuIGlzVHlwZShvLCAnT2JqZWN0JylcbiAgfVxuXG4gIC8qKlxuICAgKiBAdHlwZWRlZiB7T2JqZWN0fSBPbkhhbmRsZXJcbiAgICogQHByb3BlcnR5IHsoa2V5b2YgSFRNTEVsZW1lbnRFdmVudE1hcCl8c3RyaW5nfSBldmVudFxuICAgKiBAcHJvcGVydHkge0V2ZW50TGlzdGVuZXJ9IGxpc3RlbmVyXG4gICAqL1xuXG4gIC8qKlxuICAgKiBAdHlwZWRlZiB7T2JqZWN0fSBMaXN0ZW5lckluZm9cbiAgICogQHByb3BlcnR5IHtzdHJpbmd9IHRyaWdnZXJcbiAgICogQHByb3BlcnR5IHtFdmVudExpc3RlbmVyfSBsaXN0ZW5lclxuICAgKiBAcHJvcGVydHkge0V2ZW50VGFyZ2V0fSBvblxuICAgKi9cblxuICAvKipcbiAgICogQHR5cGVkZWYge09iamVjdH0gSHRteE5vZGVJbnRlcm5hbERhdGFcbiAgICogRWxlbWVudCBkYXRhXG4gICAqIEBwcm9wZXJ0eSB7bnVtYmVyfSBbaW5pdEhhc2hdXG4gICAqIEBwcm9wZXJ0eSB7Ym9vbGVhbn0gW2Jvb3N0ZWRdXG4gICAqIEBwcm9wZXJ0eSB7T25IYW5kbGVyW119IFtvbkhhbmRsZXJzXVxuICAgKiBAcHJvcGVydHkge251bWJlcn0gW3RpbWVvdXRdXG4gICAqIEBwcm9wZXJ0eSB7TGlzdGVuZXJJbmZvW119IFtsaXN0ZW5lckluZm9zXVxuICAgKiBAcHJvcGVydHkge2Jvb2xlYW59IFtjYW5jZWxsZWRdXG4gICAqIEBwcm9wZXJ0eSB7Ym9vbGVhbn0gW3RyaWdnZXJlZE9uY2VdXG4gICAqIEBwcm9wZXJ0eSB7bnVtYmVyfSBbZGVsYXllZF1cbiAgICogQHByb3BlcnR5IHtudW1iZXJ8bnVsbH0gW3Rocm90dGxlXVxuICAgKiBAcHJvcGVydHkge3N0cmluZ30gW2xhc3RWYWx1ZV1cbiAgICogQHByb3BlcnR5IHtib29sZWFufSBbbG9hZGVkXVxuICAgKiBAcHJvcGVydHkge3N0cmluZ30gW3BhdGhdXG4gICAqIEBwcm9wZXJ0eSB7c3RyaW5nfSBbdmVyYl1cbiAgICogQHByb3BlcnR5IHtib29sZWFufSBbcG9sbGluZ11cbiAgICogQHByb3BlcnR5IHtIVE1MQnV0dG9uRWxlbWVudHxIVE1MSW5wdXRFbGVtZW50fG51bGx9IFtsYXN0QnV0dG9uQ2xpY2tlZF1cbiAgICogQHByb3BlcnR5IHtudW1iZXJ9IFtyZXF1ZXN0Q291bnRdXG4gICAqIEBwcm9wZXJ0eSB7WE1MSHR0cFJlcXVlc3R9IFt4aHJdXG4gICAqIEBwcm9wZXJ0eSB7KCgpID0+IHZvaWQpW119IFtxdWV1ZWRSZXF1ZXN0c11cbiAgICogQHByb3BlcnR5IHtib29sZWFufSBbYWJvcnRhYmxlXVxuICAgKlxuICAgKiBFdmVudCBkYXRhXG4gICAqIEBwcm9wZXJ0eSB7SHRteFRyaWdnZXJTcGVjaWZpY2F0aW9ufSBbdHJpZ2dlclNwZWNdXG4gICAqIEBwcm9wZXJ0eSB7RXZlbnRUYXJnZXRbXX0gW2hhbmRsZWRGb3JdXG4gICAqL1xuXG4gIC8qKlxuICAgKiBnZXRJbnRlcm5hbERhdGEgcmV0cmlldmVzIFwicHJpdmF0ZVwiIGRhdGEgc3RvcmVkIGJ5IGh0bXggd2l0aGluIGFuIGVsZW1lbnRcbiAgICogQHBhcmFtIHtFdmVudFRhcmdldHxFdmVudH0gZWx0XG4gICAqIEByZXR1cm5zIHtIdG14Tm9kZUludGVybmFsRGF0YX1cbiAgICovXG4gIGZ1bmN0aW9uIGdldEludGVybmFsRGF0YShlbHQpIHtcbiAgICBjb25zdCBkYXRhUHJvcCA9ICdodG14LWludGVybmFsLWRhdGEnXG4gICAgbGV0IGRhdGEgPSBlbHRbZGF0YVByb3BdXG4gICAgaWYgKCFkYXRhKSB7XG4gICAgICBkYXRhID0gZWx0W2RhdGFQcm9wXSA9IHt9XG4gICAgfVxuICAgIHJldHVybiBkYXRhXG4gIH1cblxuICAvKipcbiAgICogdG9BcnJheSBjb252ZXJ0cyBhbiBBcnJheUxpa2Ugb2JqZWN0IGludG8gYSByZWFsIGFycmF5LlxuICAgKiBAdGVtcGxhdGUgVFxuICAgKiBAcGFyYW0ge0FycmF5TGlrZTxUPn0gYXJyXG4gICAqIEByZXR1cm5zIHtUW119XG4gICAqL1xuICBmdW5jdGlvbiB0b0FycmF5KGFycikge1xuICAgIGNvbnN0IHJldHVybkFyciA9IFtdXG4gICAgaWYgKGFycikge1xuICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCBhcnIubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgcmV0dXJuQXJyLnB1c2goYXJyW2ldKVxuICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4gcmV0dXJuQXJyXG4gIH1cblxuICAvKipcbiAgICogQHRlbXBsYXRlIFRcbiAgICogQHBhcmFtIHtUW118TmFtZWROb2RlTWFwfEhUTUxDb2xsZWN0aW9ufEhUTUxGb3JtQ29udHJvbHNDb2xsZWN0aW9ufEFycmF5TGlrZTxUPn0gYXJyXG4gICAqIEBwYXJhbSB7KFQpID0+IHZvaWR9IGZ1bmNcbiAgICovXG4gIGZ1bmN0aW9uIGZvckVhY2goYXJyLCBmdW5jKSB7XG4gICAgaWYgKGFycikge1xuICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCBhcnIubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgZnVuYyhhcnJbaV0pXG4gICAgICB9XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIEBwYXJhbSB7RWxlbWVudH0gZWxcbiAgICogQHJldHVybnMge2Jvb2xlYW59XG4gICAqL1xuICBmdW5jdGlvbiBpc1Njcm9sbGVkSW50b1ZpZXcoZWwpIHtcbiAgICBjb25zdCByZWN0ID0gZWwuZ2V0Qm91bmRpbmdDbGllbnRSZWN0KClcbiAgICBjb25zdCBlbGVtVG9wID0gcmVjdC50b3BcbiAgICBjb25zdCBlbGVtQm90dG9tID0gcmVjdC5ib3R0b21cbiAgICByZXR1cm4gZWxlbVRvcCA8IHdpbmRvdy5pbm5lckhlaWdodCAmJiBlbGVtQm90dG9tID49IDBcbiAgfVxuXG4gIC8qKlxuICAgKiBAcGFyYW0ge05vZGV9IGVsdFxuICAgKiBAcmV0dXJucyB7Ym9vbGVhbn1cbiAgICovXG4gIGZ1bmN0aW9uIGJvZHlDb250YWlucyhlbHQpIHtcbiAgICAvLyBJRSBGaXhcbiAgICBjb25zdCByb290Tm9kZSA9IGVsdC5nZXRSb290Tm9kZSAmJiBlbHQuZ2V0Um9vdE5vZGUoKVxuICAgIGlmIChyb290Tm9kZSAmJiByb290Tm9kZSBpbnN0YW5jZW9mIHdpbmRvdy5TaGFkb3dSb290KSB7XG4gICAgICByZXR1cm4gZ2V0RG9jdW1lbnQoKS5ib2R5LmNvbnRhaW5zKHJvb3ROb2RlLmhvc3QpXG4gICAgfSBlbHNlIHtcbiAgICAgIHJldHVybiBnZXREb2N1bWVudCgpLmJvZHkuY29udGFpbnMoZWx0KVxuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBAcGFyYW0ge3N0cmluZ30gdHJpZ2dlclxuICAgKiBAcmV0dXJucyB7c3RyaW5nW119XG4gICAqL1xuICBmdW5jdGlvbiBzcGxpdE9uV2hpdGVzcGFjZSh0cmlnZ2VyKSB7XG4gICAgcmV0dXJuIHRyaWdnZXIudHJpbSgpLnNwbGl0KC9cXHMrLylcbiAgfVxuXG4gIC8qKlxuICAgKiBtZXJnZU9iamVjdHMgdGFrZXMgYWxsIHRoZSBrZXlzIGZyb21cbiAgICogb2JqMiBhbmQgZHVwbGljYXRlcyB0aGVtIGludG8gb2JqMVxuICAgKiBAdGVtcGxhdGUgVDFcbiAgICogQHRlbXBsYXRlIFQyXG4gICAqIEBwYXJhbSB7VDF9IG9iajFcbiAgICogQHBhcmFtIHtUMn0gb2JqMlxuICAgKiBAcmV0dXJucyB7VDEgJiBUMn1cbiAgICovXG4gIGZ1bmN0aW9uIG1lcmdlT2JqZWN0cyhvYmoxLCBvYmoyKSB7XG4gICAgZm9yIChjb25zdCBrZXkgaW4gb2JqMikge1xuICAgICAgaWYgKG9iajIuaGFzT3duUHJvcGVydHkoa2V5KSkge1xuICAgICAgICAvLyBAdHMtaWdub3JlIHRzYyBkb2Vzbid0IHNlZW0gdG8gcHJvcGVybHkgaGFuZGxlIHR5cGVzIG1lcmdpbmdcbiAgICAgICAgb2JqMVtrZXldID0gb2JqMltrZXldXG4gICAgICB9XG4gICAgfVxuICAgIC8vIEB0cy1pZ25vcmUgdHNjIGRvZXNuJ3Qgc2VlbSB0byBwcm9wZXJseSBoYW5kbGUgdHlwZXMgbWVyZ2luZ1xuICAgIHJldHVybiBvYmoxXG4gIH1cblxuICAvKipcbiAgICogQHBhcmFtIHtzdHJpbmd9IGpTdHJpbmdcbiAgICogQHJldHVybnMge2FueXxudWxsfVxuICAgKi9cbiAgZnVuY3Rpb24gcGFyc2VKU09OKGpTdHJpbmcpIHtcbiAgICB0cnkge1xuICAgICAgcmV0dXJuIEpTT04ucGFyc2UoalN0cmluZylcbiAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgbG9nRXJyb3IoZXJyb3IpXG4gICAgICByZXR1cm4gbnVsbFxuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBAcmV0dXJucyB7Ym9vbGVhbn1cbiAgICovXG4gIGZ1bmN0aW9uIGNhbkFjY2Vzc0xvY2FsU3RvcmFnZSgpIHtcbiAgICBjb25zdCB0ZXN0ID0gJ2h0bXg6bG9jYWxTdG9yYWdlVGVzdCdcbiAgICB0cnkge1xuICAgICAgbG9jYWxTdG9yYWdlLnNldEl0ZW0odGVzdCwgdGVzdClcbiAgICAgIGxvY2FsU3RvcmFnZS5yZW1vdmVJdGVtKHRlc3QpXG4gICAgICByZXR1cm4gdHJ1ZVxuICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgIHJldHVybiBmYWxzZVxuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBAcGFyYW0ge3N0cmluZ30gcGF0aFxuICAgKiBAcmV0dXJucyB7c3RyaW5nfVxuICAgKi9cbiAgZnVuY3Rpb24gbm9ybWFsaXplUGF0aChwYXRoKSB7XG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IHVybCA9IG5ldyBVUkwocGF0aClcbiAgICAgIGlmICh1cmwpIHtcbiAgICAgICAgcGF0aCA9IHVybC5wYXRobmFtZSArIHVybC5zZWFyY2hcbiAgICAgIH1cbiAgICAgIC8vIHJlbW92ZSB0cmFpbGluZyBzbGFzaCwgdW5sZXNzIGluZGV4IHBhZ2VcbiAgICAgIGlmICghKC9eXFwvJC8udGVzdChwYXRoKSkpIHtcbiAgICAgICAgcGF0aCA9IHBhdGgucmVwbGFjZSgvXFwvKyQvLCAnJylcbiAgICAgIH1cbiAgICAgIHJldHVybiBwYXRoXG4gICAgfSBjYXRjaCAoZSkge1xuICAgICAgLy8gYmUga2luZCB0byBJRTExLCB3aGljaCBkb2Vzbid0IHN1cHBvcnQgVVJMKClcbiAgICAgIHJldHVybiBwYXRoXG4gICAgfVxuICB9XG5cbiAgLy89ID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4gIC8vIHB1YmxpYyBBUElcbiAgLy89ID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG5cbiAgLyoqXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBzdHJcbiAgICogQHJldHVybnMge2FueX1cbiAgICovXG4gIGZ1bmN0aW9uIGludGVybmFsRXZhbChzdHIpIHtcbiAgICByZXR1cm4gbWF5YmVFdmFsKGdldERvY3VtZW50KCkuYm9keSwgZnVuY3Rpb24oKSB7XG4gICAgICByZXR1cm4gZXZhbChzdHIpXG4gICAgfSlcbiAgfVxuXG4gIC8qKlxuICAgKiBBZGRzIGEgY2FsbGJhY2sgZm9yIHRoZSAqKmh0bXg6bG9hZCoqIGV2ZW50LiBUaGlzIGNhbiBiZSB1c2VkIHRvIHByb2Nlc3MgbmV3IGNvbnRlbnQsIGZvciBleGFtcGxlIGluaXRpYWxpemluZyB0aGUgY29udGVudCB3aXRoIGEgamF2YXNjcmlwdCBsaWJyYXJ5XG4gICAqXG4gICAqIEBzZWUgaHR0cHM6Ly9odG14Lm9yZy9hcGkvI29uTG9hZFxuICAgKlxuICAgKiBAcGFyYW0geyhlbHQ6IE5vZGUpID0+IHZvaWR9IGNhbGxiYWNrIHRoZSBjYWxsYmFjayB0byBjYWxsIG9uIG5ld2x5IGxvYWRlZCBjb250ZW50XG4gICAqIEByZXR1cm5zIHtFdmVudExpc3RlbmVyfVxuICAgKi9cbiAgZnVuY3Rpb24gb25Mb2FkSGVscGVyKGNhbGxiYWNrKSB7XG4gICAgY29uc3QgdmFsdWUgPSBodG14Lm9uKCdodG14OmxvYWQnLCAvKiogQHBhcmFtIHtDdXN0b21FdmVudH0gZXZ0ICovIGZ1bmN0aW9uKGV2dCkge1xuICAgICAgY2FsbGJhY2soZXZ0LmRldGFpbC5lbHQpXG4gICAgfSlcbiAgICByZXR1cm4gdmFsdWVcbiAgfVxuXG4gIC8qKlxuICAgKiBMb2cgYWxsIGh0bXggZXZlbnRzLCB1c2VmdWwgZm9yIGRlYnVnZ2luZy5cbiAgICpcbiAgICogQHNlZSBodHRwczovL2h0bXgub3JnL2FwaS8jbG9nQWxsXG4gICAqL1xuICBmdW5jdGlvbiBsb2dBbGwoKSB7XG4gICAgaHRteC5sb2dnZXIgPSBmdW5jdGlvbihlbHQsIGV2ZW50LCBkYXRhKSB7XG4gICAgICBpZiAoY29uc29sZSkge1xuICAgICAgICBjb25zb2xlLmxvZyhldmVudCwgZWx0LCBkYXRhKVxuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIGZ1bmN0aW9uIGxvZ05vbmUoKSB7XG4gICAgaHRteC5sb2dnZXIgPSBudWxsXG4gIH1cblxuICAvKipcbiAgICogRmluZHMgYW4gZWxlbWVudCBtYXRjaGluZyB0aGUgc2VsZWN0b3JcbiAgICpcbiAgICogQHNlZSBodHRwczovL2h0bXgub3JnL2FwaS8jZmluZFxuICAgKlxuICAgKiBAcGFyYW0ge1BhcmVudE5vZGV8c3RyaW5nfSBlbHRPclNlbGVjdG9yICB0aGUgcm9vdCBlbGVtZW50IHRvIGZpbmQgdGhlIG1hdGNoaW5nIGVsZW1lbnQgaW4sIGluY2x1c2l2ZSB8IHRoZSBzZWxlY3RvciB0byBtYXRjaFxuICAgKiBAcGFyYW0ge3N0cmluZ30gW3NlbGVjdG9yXSB0aGUgc2VsZWN0b3IgdG8gbWF0Y2hcbiAgICogQHJldHVybnMge0VsZW1lbnR8bnVsbH1cbiAgICovXG4gIGZ1bmN0aW9uIGZpbmQoZWx0T3JTZWxlY3Rvciwgc2VsZWN0b3IpIHtcbiAgICBpZiAodHlwZW9mIGVsdE9yU2VsZWN0b3IgIT09ICdzdHJpbmcnKSB7XG4gICAgICByZXR1cm4gZWx0T3JTZWxlY3Rvci5xdWVyeVNlbGVjdG9yKHNlbGVjdG9yKVxuICAgIH0gZWxzZSB7XG4gICAgICByZXR1cm4gZmluZChnZXREb2N1bWVudCgpLCBlbHRPclNlbGVjdG9yKVxuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBGaW5kcyBhbGwgZWxlbWVudHMgbWF0Y2hpbmcgdGhlIHNlbGVjdG9yXG4gICAqXG4gICAqIEBzZWUgaHR0cHM6Ly9odG14Lm9yZy9hcGkvI2ZpbmRBbGxcbiAgICpcbiAgICogQHBhcmFtIHtQYXJlbnROb2RlfHN0cmluZ30gZWx0T3JTZWxlY3RvciB0aGUgcm9vdCBlbGVtZW50IHRvIGZpbmQgdGhlIG1hdGNoaW5nIGVsZW1lbnRzIGluLCBpbmNsdXNpdmUgfCB0aGUgc2VsZWN0b3IgdG8gbWF0Y2hcbiAgICogQHBhcmFtIHtzdHJpbmd9IFtzZWxlY3Rvcl0gdGhlIHNlbGVjdG9yIHRvIG1hdGNoXG4gICAqIEByZXR1cm5zIHtOb2RlTGlzdE9mPEVsZW1lbnQ+fVxuICAgKi9cbiAgZnVuY3Rpb24gZmluZEFsbChlbHRPclNlbGVjdG9yLCBzZWxlY3Rvcikge1xuICAgIGlmICh0eXBlb2YgZWx0T3JTZWxlY3RvciAhPT0gJ3N0cmluZycpIHtcbiAgICAgIHJldHVybiBlbHRPclNlbGVjdG9yLnF1ZXJ5U2VsZWN0b3JBbGwoc2VsZWN0b3IpXG4gICAgfSBlbHNlIHtcbiAgICAgIHJldHVybiBmaW5kQWxsKGdldERvY3VtZW50KCksIGVsdE9yU2VsZWN0b3IpXG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIEByZXR1cm5zIFdpbmRvd1xuICAgKi9cbiAgZnVuY3Rpb24gZ2V0V2luZG93KCkge1xuICAgIHJldHVybiB3aW5kb3dcbiAgfVxuXG4gIC8qKlxuICAgKiBSZW1vdmVzIGFuIGVsZW1lbnQgZnJvbSB0aGUgRE9NXG4gICAqXG4gICAqIEBzZWUgaHR0cHM6Ly9odG14Lm9yZy9hcGkvI3JlbW92ZVxuICAgKlxuICAgKiBAcGFyYW0ge05vZGV9IGVsdFxuICAgKiBAcGFyYW0ge251bWJlcn0gW2RlbGF5XVxuICAgKi9cbiAgZnVuY3Rpb24gcmVtb3ZlRWxlbWVudChlbHQsIGRlbGF5KSB7XG4gICAgZWx0ID0gcmVzb2x2ZVRhcmdldChlbHQpXG4gICAgaWYgKGRlbGF5KSB7XG4gICAgICBnZXRXaW5kb3coKS5zZXRUaW1lb3V0KGZ1bmN0aW9uKCkge1xuICAgICAgICByZW1vdmVFbGVtZW50KGVsdClcbiAgICAgICAgZWx0ID0gbnVsbFxuICAgICAgfSwgZGVsYXkpXG4gICAgfSBlbHNlIHtcbiAgICAgIHBhcmVudEVsdChlbHQpLnJlbW92ZUNoaWxkKGVsdClcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogQHBhcmFtIHthbnl9IGVsdFxuICAgKiBAcmV0dXJuIHtFbGVtZW50fG51bGx9XG4gICAqL1xuICBmdW5jdGlvbiBhc0VsZW1lbnQoZWx0KSB7XG4gICAgcmV0dXJuIGVsdCBpbnN0YW5jZW9mIEVsZW1lbnQgPyBlbHQgOiBudWxsXG4gIH1cblxuICAvKipcbiAgICogQHBhcmFtIHthbnl9IGVsdFxuICAgKiBAcmV0dXJuIHtIVE1MRWxlbWVudHxudWxsfVxuICAgKi9cbiAgZnVuY3Rpb24gYXNIdG1sRWxlbWVudChlbHQpIHtcbiAgICByZXR1cm4gZWx0IGluc3RhbmNlb2YgSFRNTEVsZW1lbnQgPyBlbHQgOiBudWxsXG4gIH1cblxuICAvKipcbiAgICogQHBhcmFtIHthbnl9IHZhbHVlXG4gICAqIEByZXR1cm4ge3N0cmluZ3xudWxsfVxuICAgKi9cbiAgZnVuY3Rpb24gYXNTdHJpbmcodmFsdWUpIHtcbiAgICByZXR1cm4gdHlwZW9mIHZhbHVlID09PSAnc3RyaW5nJyA/IHZhbHVlIDogbnVsbFxuICB9XG5cbiAgLyoqXG4gICAqIEBwYXJhbSB7RXZlbnRUYXJnZXR9IGVsdFxuICAgKiBAcmV0dXJuIHtQYXJlbnROb2RlfG51bGx9XG4gICAqL1xuICBmdW5jdGlvbiBhc1BhcmVudE5vZGUoZWx0KSB7XG4gICAgcmV0dXJuIGVsdCBpbnN0YW5jZW9mIEVsZW1lbnQgfHwgZWx0IGluc3RhbmNlb2YgRG9jdW1lbnQgfHwgZWx0IGluc3RhbmNlb2YgRG9jdW1lbnRGcmFnbWVudCA/IGVsdCA6IG51bGxcbiAgfVxuXG4gIC8qKlxuICAgKiBUaGlzIG1ldGhvZCBhZGRzIGEgY2xhc3MgdG8gdGhlIGdpdmVuIGVsZW1lbnQuXG4gICAqXG4gICAqIEBzZWUgaHR0cHM6Ly9odG14Lm9yZy9hcGkvI2FkZENsYXNzXG4gICAqXG4gICAqIEBwYXJhbSB7RWxlbWVudHxzdHJpbmd9IGVsdCB0aGUgZWxlbWVudCB0byBhZGQgdGhlIGNsYXNzIHRvXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBjbGF6eiB0aGUgY2xhc3MgdG8gYWRkXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBbZGVsYXldIHRoZSBkZWxheSAoaW4gbWlsbGlzZWNvbmRzKSBiZWZvcmUgY2xhc3MgaXMgYWRkZWRcbiAgICovXG4gIGZ1bmN0aW9uIGFkZENsYXNzVG9FbGVtZW50KGVsdCwgY2xhenosIGRlbGF5KSB7XG4gICAgZWx0ID0gYXNFbGVtZW50KHJlc29sdmVUYXJnZXQoZWx0KSlcbiAgICBpZiAoIWVsdCkge1xuICAgICAgcmV0dXJuXG4gICAgfVxuICAgIGlmIChkZWxheSkge1xuICAgICAgZ2V0V2luZG93KCkuc2V0VGltZW91dChmdW5jdGlvbigpIHtcbiAgICAgICAgYWRkQ2xhc3NUb0VsZW1lbnQoZWx0LCBjbGF6eilcbiAgICAgICAgZWx0ID0gbnVsbFxuICAgICAgfSwgZGVsYXkpXG4gICAgfSBlbHNlIHtcbiAgICAgIGVsdC5jbGFzc0xpc3QgJiYgZWx0LmNsYXNzTGlzdC5hZGQoY2xhenopXG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIFJlbW92ZXMgYSBjbGFzcyBmcm9tIHRoZSBnaXZlbiBlbGVtZW50XG4gICAqXG4gICAqIEBzZWUgaHR0cHM6Ly9odG14Lm9yZy9hcGkvI3JlbW92ZUNsYXNzXG4gICAqXG4gICAqIEBwYXJhbSB7Tm9kZXxzdHJpbmd9IG5vZGUgZWxlbWVudCB0byByZW1vdmUgdGhlIGNsYXNzIGZyb21cbiAgICogQHBhcmFtIHtzdHJpbmd9IGNsYXp6IHRoZSBjbGFzcyB0byByZW1vdmVcbiAgICogQHBhcmFtIHtudW1iZXJ9IFtkZWxheV0gdGhlIGRlbGF5IChpbiBtaWxsaXNlY29uZHMgYmVmb3JlIGNsYXNzIGlzIHJlbW92ZWQpXG4gICAqL1xuICBmdW5jdGlvbiByZW1vdmVDbGFzc0Zyb21FbGVtZW50KG5vZGUsIGNsYXp6LCBkZWxheSkge1xuICAgIGxldCBlbHQgPSBhc0VsZW1lbnQocmVzb2x2ZVRhcmdldChub2RlKSlcbiAgICBpZiAoIWVsdCkge1xuICAgICAgcmV0dXJuXG4gICAgfVxuICAgIGlmIChkZWxheSkge1xuICAgICAgZ2V0V2luZG93KCkuc2V0VGltZW91dChmdW5jdGlvbigpIHtcbiAgICAgICAgcmVtb3ZlQ2xhc3NGcm9tRWxlbWVudChlbHQsIGNsYXp6KVxuICAgICAgICBlbHQgPSBudWxsXG4gICAgICB9LCBkZWxheSlcbiAgICB9IGVsc2Uge1xuICAgICAgaWYgKGVsdC5jbGFzc0xpc3QpIHtcbiAgICAgICAgZWx0LmNsYXNzTGlzdC5yZW1vdmUoY2xhenopXG4gICAgICAgIC8vIGlmIHRoZXJlIGFyZSBubyBjbGFzc2VzIGxlZnQsIHJlbW92ZSB0aGUgY2xhc3MgYXR0cmlidXRlXG4gICAgICAgIGlmIChlbHQuY2xhc3NMaXN0Lmxlbmd0aCA9PT0gMCkge1xuICAgICAgICAgIGVsdC5yZW1vdmVBdHRyaWJ1dGUoJ2NsYXNzJylcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBUb2dnbGVzIHRoZSBnaXZlbiBjbGFzcyBvbiBhbiBlbGVtZW50XG4gICAqXG4gICAqIEBzZWUgaHR0cHM6Ly9odG14Lm9yZy9hcGkvI3RvZ2dsZUNsYXNzXG4gICAqXG4gICAqIEBwYXJhbSB7RWxlbWVudHxzdHJpbmd9IGVsdCB0aGUgZWxlbWVudCB0byB0b2dnbGUgdGhlIGNsYXNzIG9uXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBjbGF6eiB0aGUgY2xhc3MgdG8gdG9nZ2xlXG4gICAqL1xuICBmdW5jdGlvbiB0b2dnbGVDbGFzc09uRWxlbWVudChlbHQsIGNsYXp6KSB7XG4gICAgZWx0ID0gcmVzb2x2ZVRhcmdldChlbHQpXG4gICAgZWx0LmNsYXNzTGlzdC50b2dnbGUoY2xhenopXG4gIH1cblxuICAvKipcbiAgICogVGFrZXMgdGhlIGdpdmVuIGNsYXNzIGZyb20gaXRzIHNpYmxpbmdzLCBzbyB0aGF0IGFtb25nIGl0cyBzaWJsaW5ncywgb25seSB0aGUgZ2l2ZW4gZWxlbWVudCB3aWxsIGhhdmUgdGhlIGNsYXNzLlxuICAgKlxuICAgKiBAc2VlIGh0dHBzOi8vaHRteC5vcmcvYXBpLyN0YWtlQ2xhc3NcbiAgICpcbiAgICogQHBhcmFtIHtOb2RlfHN0cmluZ30gZWx0IHRoZSBlbGVtZW50IHRoYXQgd2lsbCB0YWtlIHRoZSBjbGFzc1xuICAgKiBAcGFyYW0ge3N0cmluZ30gY2xhenogdGhlIGNsYXNzIHRvIHRha2VcbiAgICovXG4gIGZ1bmN0aW9uIHRha2VDbGFzc0ZvckVsZW1lbnQoZWx0LCBjbGF6eikge1xuICAgIGVsdCA9IHJlc29sdmVUYXJnZXQoZWx0KVxuICAgIGZvckVhY2goZWx0LnBhcmVudEVsZW1lbnQuY2hpbGRyZW4sIGZ1bmN0aW9uKGNoaWxkKSB7XG4gICAgICByZW1vdmVDbGFzc0Zyb21FbGVtZW50KGNoaWxkLCBjbGF6eilcbiAgICB9KVxuICAgIGFkZENsYXNzVG9FbGVtZW50KGFzRWxlbWVudChlbHQpLCBjbGF6eilcbiAgfVxuXG4gIC8qKlxuICAgKiBGaW5kcyB0aGUgY2xvc2VzdCBtYXRjaGluZyBlbGVtZW50IGluIHRoZSBnaXZlbiBlbGVtZW50cyBwYXJlbnRhZ2UsIGluY2x1c2l2ZSBvZiB0aGUgZWxlbWVudFxuICAgKlxuICAgKiBAc2VlIGh0dHBzOi8vaHRteC5vcmcvYXBpLyNjbG9zZXN0XG4gICAqXG4gICAqIEBwYXJhbSB7RWxlbWVudHxzdHJpbmd9IGVsdCB0aGUgZWxlbWVudCB0byBmaW5kIHRoZSBzZWxlY3RvciBmcm9tXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBzZWxlY3RvciB0aGUgc2VsZWN0b3IgdG8gZmluZFxuICAgKiBAcmV0dXJucyB7RWxlbWVudHxudWxsfVxuICAgKi9cbiAgZnVuY3Rpb24gY2xvc2VzdChlbHQsIHNlbGVjdG9yKSB7XG4gICAgZWx0ID0gYXNFbGVtZW50KHJlc29sdmVUYXJnZXQoZWx0KSlcbiAgICBpZiAoZWx0ICYmIGVsdC5jbG9zZXN0KSB7XG4gICAgICByZXR1cm4gZWx0LmNsb3Nlc3Qoc2VsZWN0b3IpXG4gICAgfSBlbHNlIHtcbiAgICAgIC8vIFRPRE8gcmVtb3ZlIHdoZW4gSUUgZ29lcyBhd2F5XG4gICAgICBkbyB7XG4gICAgICAgIGlmIChlbHQgPT0gbnVsbCB8fCBtYXRjaGVzKGVsdCwgc2VsZWN0b3IpKSB7XG4gICAgICAgICAgcmV0dXJuIGVsdFxuICAgICAgICB9XG4gICAgICB9XG4gICAgICB3aGlsZSAoZWx0ID0gZWx0ICYmIGFzRWxlbWVudChwYXJlbnRFbHQoZWx0KSkpXG4gICAgICByZXR1cm4gbnVsbFxuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBAcGFyYW0ge3N0cmluZ30gc3RyXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBwcmVmaXhcbiAgICogQHJldHVybnMge2Jvb2xlYW59XG4gICAqL1xuICBmdW5jdGlvbiBzdGFydHNXaXRoKHN0ciwgcHJlZml4KSB7XG4gICAgcmV0dXJuIHN0ci5zdWJzdHJpbmcoMCwgcHJlZml4Lmxlbmd0aCkgPT09IHByZWZpeFxuICB9XG5cbiAgLyoqXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBzdHJcbiAgICogQHBhcmFtIHtzdHJpbmd9IHN1ZmZpeFxuICAgKiBAcmV0dXJucyB7Ym9vbGVhbn1cbiAgICovXG4gIGZ1bmN0aW9uIGVuZHNXaXRoKHN0ciwgc3VmZml4KSB7XG4gICAgcmV0dXJuIHN0ci5zdWJzdHJpbmcoc3RyLmxlbmd0aCAtIHN1ZmZpeC5sZW5ndGgpID09PSBzdWZmaXhcbiAgfVxuXG4gIC8qKlxuICAgKiBAcGFyYW0ge3N0cmluZ30gc2VsZWN0b3JcbiAgICogQHJldHVybnMge3N0cmluZ31cbiAgICovXG4gIGZ1bmN0aW9uIG5vcm1hbGl6ZVNlbGVjdG9yKHNlbGVjdG9yKSB7XG4gICAgY29uc3QgdHJpbW1lZFNlbGVjdG9yID0gc2VsZWN0b3IudHJpbSgpXG4gICAgaWYgKHN0YXJ0c1dpdGgodHJpbW1lZFNlbGVjdG9yLCAnPCcpICYmIGVuZHNXaXRoKHRyaW1tZWRTZWxlY3RvciwgJy8+JykpIHtcbiAgICAgIHJldHVybiB0cmltbWVkU2VsZWN0b3Iuc3Vic3RyaW5nKDEsIHRyaW1tZWRTZWxlY3Rvci5sZW5ndGggLSAyKVxuICAgIH0gZWxzZSB7XG4gICAgICByZXR1cm4gdHJpbW1lZFNlbGVjdG9yXG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIEBwYXJhbSB7Tm9kZXxFbGVtZW50fERvY3VtZW50fHN0cmluZ30gZWx0XG4gICAqIEBwYXJhbSB7c3RyaW5nfSBzZWxlY3RvclxuICAgKiBAcGFyYW0ge2Jvb2xlYW49fSBnbG9iYWxcbiAgICogQHJldHVybnMgeyhOb2RlfFdpbmRvdylbXX1cbiAgICovXG4gIGZ1bmN0aW9uIHF1ZXJ5U2VsZWN0b3JBbGxFeHQoZWx0LCBzZWxlY3RvciwgZ2xvYmFsKSB7XG4gICAgZWx0ID0gcmVzb2x2ZVRhcmdldChlbHQpXG4gICAgaWYgKHNlbGVjdG9yLmluZGV4T2YoJ2Nsb3Nlc3QgJykgPT09IDApIHtcbiAgICAgIHJldHVybiBbY2xvc2VzdChhc0VsZW1lbnQoZWx0KSwgbm9ybWFsaXplU2VsZWN0b3Ioc2VsZWN0b3Iuc3Vic3RyKDgpKSldXG4gICAgfSBlbHNlIGlmIChzZWxlY3Rvci5pbmRleE9mKCdmaW5kICcpID09PSAwKSB7XG4gICAgICByZXR1cm4gW2ZpbmQoYXNQYXJlbnROb2RlKGVsdCksIG5vcm1hbGl6ZVNlbGVjdG9yKHNlbGVjdG9yLnN1YnN0cig1KSkpXVxuICAgIH0gZWxzZSBpZiAoc2VsZWN0b3IgPT09ICduZXh0Jykge1xuICAgICAgcmV0dXJuIFthc0VsZW1lbnQoZWx0KS5uZXh0RWxlbWVudFNpYmxpbmddXG4gICAgfSBlbHNlIGlmIChzZWxlY3Rvci5pbmRleE9mKCduZXh0ICcpID09PSAwKSB7XG4gICAgICByZXR1cm4gW3NjYW5Gb3J3YXJkUXVlcnkoZWx0LCBub3JtYWxpemVTZWxlY3RvcihzZWxlY3Rvci5zdWJzdHIoNSkpLCAhIWdsb2JhbCldXG4gICAgfSBlbHNlIGlmIChzZWxlY3RvciA9PT0gJ3ByZXZpb3VzJykge1xuICAgICAgcmV0dXJuIFthc0VsZW1lbnQoZWx0KS5wcmV2aW91c0VsZW1lbnRTaWJsaW5nXVxuICAgIH0gZWxzZSBpZiAoc2VsZWN0b3IuaW5kZXhPZigncHJldmlvdXMgJykgPT09IDApIHtcbiAgICAgIHJldHVybiBbc2NhbkJhY2t3YXJkc1F1ZXJ5KGVsdCwgbm9ybWFsaXplU2VsZWN0b3Ioc2VsZWN0b3Iuc3Vic3RyKDkpKSwgISFnbG9iYWwpXVxuICAgIH0gZWxzZSBpZiAoc2VsZWN0b3IgPT09ICdkb2N1bWVudCcpIHtcbiAgICAgIHJldHVybiBbZG9jdW1lbnRdXG4gICAgfSBlbHNlIGlmIChzZWxlY3RvciA9PT0gJ3dpbmRvdycpIHtcbiAgICAgIHJldHVybiBbd2luZG93XVxuICAgIH0gZWxzZSBpZiAoc2VsZWN0b3IgPT09ICdib2R5Jykge1xuICAgICAgcmV0dXJuIFtkb2N1bWVudC5ib2R5XVxuICAgIH0gZWxzZSBpZiAoc2VsZWN0b3IgPT09ICdyb290Jykge1xuICAgICAgcmV0dXJuIFtnZXRSb290Tm9kZShlbHQsICEhZ2xvYmFsKV1cbiAgICB9IGVsc2UgaWYgKHNlbGVjdG9yLmluZGV4T2YoJ2dsb2JhbCAnKSA9PT0gMCkge1xuICAgICAgcmV0dXJuIHF1ZXJ5U2VsZWN0b3JBbGxFeHQoZWx0LCBzZWxlY3Rvci5zbGljZSg3KSwgdHJ1ZSlcbiAgICB9IGVsc2Uge1xuICAgICAgcmV0dXJuIHRvQXJyYXkoYXNQYXJlbnROb2RlKGdldFJvb3ROb2RlKGVsdCwgISFnbG9iYWwpKS5xdWVyeVNlbGVjdG9yQWxsKG5vcm1hbGl6ZVNlbGVjdG9yKHNlbGVjdG9yKSkpXG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIEBwYXJhbSB7Tm9kZX0gc3RhcnRcbiAgICogQHBhcmFtIHtzdHJpbmd9IG1hdGNoXG4gICAqIEBwYXJhbSB7Ym9vbGVhbn0gZ2xvYmFsXG4gICAqIEByZXR1cm5zIHtFbGVtZW50fVxuICAgKi9cbiAgdmFyIHNjYW5Gb3J3YXJkUXVlcnkgPSBmdW5jdGlvbihzdGFydCwgbWF0Y2gsIGdsb2JhbCkge1xuICAgIGNvbnN0IHJlc3VsdHMgPSBhc1BhcmVudE5vZGUoZ2V0Um9vdE5vZGUoc3RhcnQsIGdsb2JhbCkpLnF1ZXJ5U2VsZWN0b3JBbGwobWF0Y2gpXG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCByZXN1bHRzLmxlbmd0aDsgaSsrKSB7XG4gICAgICBjb25zdCBlbHQgPSByZXN1bHRzW2ldXG4gICAgICBpZiAoZWx0LmNvbXBhcmVEb2N1bWVudFBvc2l0aW9uKHN0YXJ0KSA9PT0gTm9kZS5ET0NVTUVOVF9QT1NJVElPTl9QUkVDRURJTkcpIHtcbiAgICAgICAgcmV0dXJuIGVsdFxuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBAcGFyYW0ge05vZGV9IHN0YXJ0XG4gICAqIEBwYXJhbSB7c3RyaW5nfSBtYXRjaFxuICAgKiBAcGFyYW0ge2Jvb2xlYW59IGdsb2JhbFxuICAgKiBAcmV0dXJucyB7RWxlbWVudH1cbiAgICovXG4gIHZhciBzY2FuQmFja3dhcmRzUXVlcnkgPSBmdW5jdGlvbihzdGFydCwgbWF0Y2gsIGdsb2JhbCkge1xuICAgIGNvbnN0IHJlc3VsdHMgPSBhc1BhcmVudE5vZGUoZ2V0Um9vdE5vZGUoc3RhcnQsIGdsb2JhbCkpLnF1ZXJ5U2VsZWN0b3JBbGwobWF0Y2gpXG4gICAgZm9yIChsZXQgaSA9IHJlc3VsdHMubGVuZ3RoIC0gMTsgaSA+PSAwOyBpLS0pIHtcbiAgICAgIGNvbnN0IGVsdCA9IHJlc3VsdHNbaV1cbiAgICAgIGlmIChlbHQuY29tcGFyZURvY3VtZW50UG9zaXRpb24oc3RhcnQpID09PSBOb2RlLkRPQ1VNRU5UX1BPU0lUSU9OX0ZPTExPV0lORykge1xuICAgICAgICByZXR1cm4gZWx0XG4gICAgICB9XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIEBwYXJhbSB7Tm9kZXxzdHJpbmd9IGVsdE9yU2VsZWN0b3JcbiAgICogQHBhcmFtIHtzdHJpbmc9fSBzZWxlY3RvclxuICAgKiBAcmV0dXJucyB7Tm9kZXxXaW5kb3d9XG4gICAqL1xuICBmdW5jdGlvbiBxdWVyeVNlbGVjdG9yRXh0KGVsdE9yU2VsZWN0b3IsIHNlbGVjdG9yKSB7XG4gICAgaWYgKHR5cGVvZiBlbHRPclNlbGVjdG9yICE9PSAnc3RyaW5nJykge1xuICAgICAgcmV0dXJuIHF1ZXJ5U2VsZWN0b3JBbGxFeHQoZWx0T3JTZWxlY3Rvciwgc2VsZWN0b3IpWzBdXG4gICAgfSBlbHNlIHtcbiAgICAgIHJldHVybiBxdWVyeVNlbGVjdG9yQWxsRXh0KGdldERvY3VtZW50KCkuYm9keSwgZWx0T3JTZWxlY3RvcilbMF1cbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogQHRlbXBsYXRlIHtFdmVudFRhcmdldH0gVFxuICAgKiBAcGFyYW0ge1R8c3RyaW5nfSBlbHRPclNlbGVjdG9yXG4gICAqIEBwYXJhbSB7VH0gW2NvbnRleHRdXG4gICAqIEByZXR1cm5zIHtFbGVtZW50fFR8bnVsbH1cbiAgICovXG4gIGZ1bmN0aW9uIHJlc29sdmVUYXJnZXQoZWx0T3JTZWxlY3RvciwgY29udGV4dCkge1xuICAgIGlmICh0eXBlb2YgZWx0T3JTZWxlY3RvciA9PT0gJ3N0cmluZycpIHtcbiAgICAgIHJldHVybiBmaW5kKGFzUGFyZW50Tm9kZShjb250ZXh0KSB8fCBkb2N1bWVudCwgZWx0T3JTZWxlY3RvcilcbiAgICB9IGVsc2Uge1xuICAgICAgcmV0dXJuIGVsdE9yU2VsZWN0b3JcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogQHR5cGVkZWYge2tleW9mIEhUTUxFbGVtZW50RXZlbnRNYXB8c3RyaW5nfSBBbnlFdmVudE5hbWVcbiAgICovXG5cbiAgLyoqXG4gICAqIEB0eXBlZGVmIHtPYmplY3R9IEV2ZW50QXJnc1xuICAgKiBAcHJvcGVydHkge0V2ZW50VGFyZ2V0fSB0YXJnZXRcbiAgICogQHByb3BlcnR5IHtBbnlFdmVudE5hbWV9IGV2ZW50XG4gICAqIEBwcm9wZXJ0eSB7RXZlbnRMaXN0ZW5lcn0gbGlzdGVuZXJcbiAgICovXG5cbiAgLyoqXG4gICAqIEBwYXJhbSB7RXZlbnRUYXJnZXR8QW55RXZlbnROYW1lfSBhcmcxXG4gICAqIEBwYXJhbSB7QW55RXZlbnROYW1lfEV2ZW50TGlzdGVuZXJ9IGFyZzJcbiAgICogQHBhcmFtIHtFdmVudExpc3RlbmVyfSBbYXJnM11cbiAgICogQHJldHVybnMge0V2ZW50QXJnc31cbiAgICovXG4gIGZ1bmN0aW9uIHByb2Nlc3NFdmVudEFyZ3MoYXJnMSwgYXJnMiwgYXJnMykge1xuICAgIGlmIChpc0Z1bmN0aW9uKGFyZzIpKSB7XG4gICAgICByZXR1cm4ge1xuICAgICAgICB0YXJnZXQ6IGdldERvY3VtZW50KCkuYm9keSxcbiAgICAgICAgZXZlbnQ6IGFzU3RyaW5nKGFyZzEpLFxuICAgICAgICBsaXN0ZW5lcjogYXJnMlxuICAgICAgfVxuICAgIH0gZWxzZSB7XG4gICAgICByZXR1cm4ge1xuICAgICAgICB0YXJnZXQ6IHJlc29sdmVUYXJnZXQoYXJnMSksXG4gICAgICAgIGV2ZW50OiBhc1N0cmluZyhhcmcyKSxcbiAgICAgICAgbGlzdGVuZXI6IGFyZzNcbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogQWRkcyBhbiBldmVudCBsaXN0ZW5lciB0byBhbiBlbGVtZW50XG4gICAqXG4gICAqIEBzZWUgaHR0cHM6Ly9odG14Lm9yZy9hcGkvI29uXG4gICAqXG4gICAqIEBwYXJhbSB7RXZlbnRUYXJnZXR8c3RyaW5nfSBhcmcxIHRoZSBlbGVtZW50IHRvIGFkZCB0aGUgbGlzdGVuZXIgdG8gfCB0aGUgZXZlbnQgbmFtZSB0byBhZGQgdGhlIGxpc3RlbmVyIGZvclxuICAgKiBAcGFyYW0ge3N0cmluZ3xFdmVudExpc3RlbmVyfSBhcmcyIHRoZSBldmVudCBuYW1lIHRvIGFkZCB0aGUgbGlzdGVuZXIgZm9yIHwgdGhlIGxpc3RlbmVyIHRvIGFkZFxuICAgKiBAcGFyYW0ge0V2ZW50TGlzdGVuZXJ9IFthcmczXSB0aGUgbGlzdGVuZXIgdG8gYWRkXG4gICAqIEByZXR1cm5zIHtFdmVudExpc3RlbmVyfVxuICAgKi9cbiAgZnVuY3Rpb24gYWRkRXZlbnRMaXN0ZW5lckltcGwoYXJnMSwgYXJnMiwgYXJnMykge1xuICAgIHJlYWR5KGZ1bmN0aW9uKCkge1xuICAgICAgY29uc3QgZXZlbnRBcmdzID0gcHJvY2Vzc0V2ZW50QXJncyhhcmcxLCBhcmcyLCBhcmczKVxuICAgICAgZXZlbnRBcmdzLnRhcmdldC5hZGRFdmVudExpc3RlbmVyKGV2ZW50QXJncy5ldmVudCwgZXZlbnRBcmdzLmxpc3RlbmVyKVxuICAgIH0pXG4gICAgY29uc3QgYiA9IGlzRnVuY3Rpb24oYXJnMilcbiAgICByZXR1cm4gYiA/IGFyZzIgOiBhcmczXG4gIH1cblxuICAvKipcbiAgICogUmVtb3ZlcyBhbiBldmVudCBsaXN0ZW5lciBmcm9tIGFuIGVsZW1lbnRcbiAgICpcbiAgICogQHNlZSBodHRwczovL2h0bXgub3JnL2FwaS8jb2ZmXG4gICAqXG4gICAqIEBwYXJhbSB7RXZlbnRUYXJnZXR8c3RyaW5nfSBhcmcxIHRoZSBlbGVtZW50IHRvIHJlbW92ZSB0aGUgbGlzdGVuZXIgZnJvbSB8IHRoZSBldmVudCBuYW1lIHRvIHJlbW92ZSB0aGUgbGlzdGVuZXIgZnJvbVxuICAgKiBAcGFyYW0ge3N0cmluZ3xFdmVudExpc3RlbmVyfSBhcmcyIHRoZSBldmVudCBuYW1lIHRvIHJlbW92ZSB0aGUgbGlzdGVuZXIgZnJvbSB8IHRoZSBsaXN0ZW5lciB0byByZW1vdmVcbiAgICogQHBhcmFtIHtFdmVudExpc3RlbmVyfSBbYXJnM10gdGhlIGxpc3RlbmVyIHRvIHJlbW92ZVxuICAgKiBAcmV0dXJucyB7RXZlbnRMaXN0ZW5lcn1cbiAgICovXG4gIGZ1bmN0aW9uIHJlbW92ZUV2ZW50TGlzdGVuZXJJbXBsKGFyZzEsIGFyZzIsIGFyZzMpIHtcbiAgICByZWFkeShmdW5jdGlvbigpIHtcbiAgICAgIGNvbnN0IGV2ZW50QXJncyA9IHByb2Nlc3NFdmVudEFyZ3MoYXJnMSwgYXJnMiwgYXJnMylcbiAgICAgIGV2ZW50QXJncy50YXJnZXQucmVtb3ZlRXZlbnRMaXN0ZW5lcihldmVudEFyZ3MuZXZlbnQsIGV2ZW50QXJncy5saXN0ZW5lcilcbiAgICB9KVxuICAgIHJldHVybiBpc0Z1bmN0aW9uKGFyZzIpID8gYXJnMiA6IGFyZzNcbiAgfVxuXG4gIC8vPSA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4gIC8vIE5vZGUgcHJvY2Vzc2luZ1xuICAvLz0gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuXG4gIGNvbnN0IERVTU1ZX0VMVCA9IGdldERvY3VtZW50KCkuY3JlYXRlRWxlbWVudCgnb3V0cHV0JykgLy8gZHVtbXkgZWxlbWVudCBmb3IgYmFkIHNlbGVjdG9yc1xuICAvKipcbiAgICogQHBhcmFtIHtFbGVtZW50fSBlbHRcbiAgICogQHBhcmFtIHtzdHJpbmd9IGF0dHJOYW1lXG4gICAqIEByZXR1cm5zIHsoTm9kZXxXaW5kb3cpW119XG4gICAqL1xuICBmdW5jdGlvbiBmaW5kQXR0cmlidXRlVGFyZ2V0cyhlbHQsIGF0dHJOYW1lKSB7XG4gICAgY29uc3QgYXR0clRhcmdldCA9IGdldENsb3Nlc3RBdHRyaWJ1dGVWYWx1ZShlbHQsIGF0dHJOYW1lKVxuICAgIGlmIChhdHRyVGFyZ2V0KSB7XG4gICAgICBpZiAoYXR0clRhcmdldCA9PT0gJ3RoaXMnKSB7XG4gICAgICAgIHJldHVybiBbZmluZFRoaXNFbGVtZW50KGVsdCwgYXR0ck5hbWUpXVxuICAgICAgfSBlbHNlIHtcbiAgICAgICAgY29uc3QgcmVzdWx0ID0gcXVlcnlTZWxlY3RvckFsbEV4dChlbHQsIGF0dHJUYXJnZXQpXG4gICAgICAgIGlmIChyZXN1bHQubGVuZ3RoID09PSAwKSB7XG4gICAgICAgICAgbG9nRXJyb3IoJ1RoZSBzZWxlY3RvciBcIicgKyBhdHRyVGFyZ2V0ICsgJ1wiIG9uICcgKyBhdHRyTmFtZSArICcgcmV0dXJuZWQgbm8gbWF0Y2hlcyEnKVxuICAgICAgICAgIHJldHVybiBbRFVNTVlfRUxUXVxuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIHJldHVybiByZXN1bHRcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBAcGFyYW0ge0VsZW1lbnR9IGVsdFxuICAgKiBAcGFyYW0ge3N0cmluZ30gYXR0cmlidXRlXG4gICAqIEByZXR1cm5zIHtFbGVtZW50fG51bGx9XG4gICAqL1xuICBmdW5jdGlvbiBmaW5kVGhpc0VsZW1lbnQoZWx0LCBhdHRyaWJ1dGUpIHtcbiAgICByZXR1cm4gYXNFbGVtZW50KGdldENsb3Nlc3RNYXRjaChlbHQsIGZ1bmN0aW9uKGVsdCkge1xuICAgICAgcmV0dXJuIGdldEF0dHJpYnV0ZVZhbHVlKGFzRWxlbWVudChlbHQpLCBhdHRyaWJ1dGUpICE9IG51bGxcbiAgICB9KSlcbiAgfVxuXG4gIC8qKlxuICAgKiBAcGFyYW0ge0VsZW1lbnR9IGVsdFxuICAgKiBAcmV0dXJucyB7Tm9kZXxXaW5kb3d8bnVsbH1cbiAgICovXG4gIGZ1bmN0aW9uIGdldFRhcmdldChlbHQpIHtcbiAgICBjb25zdCB0YXJnZXRTdHIgPSBnZXRDbG9zZXN0QXR0cmlidXRlVmFsdWUoZWx0LCAnaHgtdGFyZ2V0JylcbiAgICBpZiAodGFyZ2V0U3RyKSB7XG4gICAgICBpZiAodGFyZ2V0U3RyID09PSAndGhpcycpIHtcbiAgICAgICAgcmV0dXJuIGZpbmRUaGlzRWxlbWVudChlbHQsICdoeC10YXJnZXQnKVxuICAgICAgfSBlbHNlIHtcbiAgICAgICAgcmV0dXJuIHF1ZXJ5U2VsZWN0b3JFeHQoZWx0LCB0YXJnZXRTdHIpXG4gICAgICB9XG4gICAgfSBlbHNlIHtcbiAgICAgIGNvbnN0IGRhdGEgPSBnZXRJbnRlcm5hbERhdGEoZWx0KVxuICAgICAgaWYgKGRhdGEuYm9vc3RlZCkge1xuICAgICAgICByZXR1cm4gZ2V0RG9jdW1lbnQoKS5ib2R5XG4gICAgICB9IGVsc2Uge1xuICAgICAgICByZXR1cm4gZWx0XG4gICAgICB9XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBuYW1lXG4gICAqIEByZXR1cm5zIHtib29sZWFufVxuICAgKi9cbiAgZnVuY3Rpb24gc2hvdWxkU2V0dGxlQXR0cmlidXRlKG5hbWUpIHtcbiAgICBjb25zdCBhdHRyaWJ1dGVzVG9TZXR0bGUgPSBodG14LmNvbmZpZy5hdHRyaWJ1dGVzVG9TZXR0bGVcbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IGF0dHJpYnV0ZXNUb1NldHRsZS5sZW5ndGg7IGkrKykge1xuICAgICAgaWYgKG5hbWUgPT09IGF0dHJpYnV0ZXNUb1NldHRsZVtpXSkge1xuICAgICAgICByZXR1cm4gdHJ1ZVxuICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4gZmFsc2VcbiAgfVxuXG4gIC8qKlxuICAgKiBAcGFyYW0ge0VsZW1lbnR9IG1lcmdlVG9cbiAgICogQHBhcmFtIHtFbGVtZW50fSBtZXJnZUZyb21cbiAgICovXG4gIGZ1bmN0aW9uIGNsb25lQXR0cmlidXRlcyhtZXJnZVRvLCBtZXJnZUZyb20pIHtcbiAgICBmb3JFYWNoKG1lcmdlVG8uYXR0cmlidXRlcywgZnVuY3Rpb24oYXR0cikge1xuICAgICAgaWYgKCFtZXJnZUZyb20uaGFzQXR0cmlidXRlKGF0dHIubmFtZSkgJiYgc2hvdWxkU2V0dGxlQXR0cmlidXRlKGF0dHIubmFtZSkpIHtcbiAgICAgICAgbWVyZ2VUby5yZW1vdmVBdHRyaWJ1dGUoYXR0ci5uYW1lKVxuICAgICAgfVxuICAgIH0pXG4gICAgZm9yRWFjaChtZXJnZUZyb20uYXR0cmlidXRlcywgZnVuY3Rpb24oYXR0cikge1xuICAgICAgaWYgKHNob3VsZFNldHRsZUF0dHJpYnV0ZShhdHRyLm5hbWUpKSB7XG4gICAgICAgIG1lcmdlVG8uc2V0QXR0cmlidXRlKGF0dHIubmFtZSwgYXR0ci52YWx1ZSlcbiAgICAgIH1cbiAgICB9KVxuICB9XG5cbiAgLyoqXG4gICAqIEBwYXJhbSB7SHRteFN3YXBTdHlsZX0gc3dhcFN0eWxlXG4gICAqIEBwYXJhbSB7RWxlbWVudH0gdGFyZ2V0XG4gICAqIEByZXR1cm5zIHtib29sZWFufVxuICAgKi9cbiAgZnVuY3Rpb24gaXNJbmxpbmVTd2FwKHN3YXBTdHlsZSwgdGFyZ2V0KSB7XG4gICAgY29uc3QgZXh0ZW5zaW9ucyA9IGdldEV4dGVuc2lvbnModGFyZ2V0KVxuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgZXh0ZW5zaW9ucy5sZW5ndGg7IGkrKykge1xuICAgICAgY29uc3QgZXh0ZW5zaW9uID0gZXh0ZW5zaW9uc1tpXVxuICAgICAgdHJ5IHtcbiAgICAgICAgaWYgKGV4dGVuc2lvbi5pc0lubGluZVN3YXAoc3dhcFN0eWxlKSkge1xuICAgICAgICAgIHJldHVybiB0cnVlXG4gICAgICAgIH1cbiAgICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgICAgbG9nRXJyb3IoZSlcbiAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIHN3YXBTdHlsZSA9PT0gJ291dGVySFRNTCdcbiAgfVxuXG4gIC8qKlxuICAgKiBAcGFyYW0ge3N0cmluZ30gb29iVmFsdWVcbiAgICogQHBhcmFtIHtFbGVtZW50fSBvb2JFbGVtZW50XG4gICAqIEBwYXJhbSB7SHRteFNldHRsZUluZm99IHNldHRsZUluZm9cbiAgICogQHJldHVybnNcbiAgICovXG4gIGZ1bmN0aW9uIG9vYlN3YXAob29iVmFsdWUsIG9vYkVsZW1lbnQsIHNldHRsZUluZm8pIHtcbiAgICBsZXQgc2VsZWN0b3IgPSAnIycgKyBnZXRSYXdBdHRyaWJ1dGUob29iRWxlbWVudCwgJ2lkJylcbiAgICAvKiogQHR5cGUgSHRteFN3YXBTdHlsZSAqL1xuICAgIGxldCBzd2FwU3R5bGUgPSAnb3V0ZXJIVE1MJ1xuICAgIGlmIChvb2JWYWx1ZSA9PT0gJ3RydWUnKSB7XG4gICAgICAvLyBkbyBub3RoaW5nXG4gICAgfSBlbHNlIGlmIChvb2JWYWx1ZS5pbmRleE9mKCc6JykgPiAwKSB7XG4gICAgICBzd2FwU3R5bGUgPSBvb2JWYWx1ZS5zdWJzdHIoMCwgb29iVmFsdWUuaW5kZXhPZignOicpKVxuICAgICAgc2VsZWN0b3IgPSBvb2JWYWx1ZS5zdWJzdHIob29iVmFsdWUuaW5kZXhPZignOicpICsgMSwgb29iVmFsdWUubGVuZ3RoKVxuICAgIH0gZWxzZSB7XG4gICAgICBzd2FwU3R5bGUgPSBvb2JWYWx1ZVxuICAgIH1cblxuICAgIGNvbnN0IHRhcmdldHMgPSBnZXREb2N1bWVudCgpLnF1ZXJ5U2VsZWN0b3JBbGwoc2VsZWN0b3IpXG4gICAgaWYgKHRhcmdldHMpIHtcbiAgICAgIGZvckVhY2goXG4gICAgICAgIHRhcmdldHMsXG4gICAgICAgIGZ1bmN0aW9uKHRhcmdldCkge1xuICAgICAgICAgIGxldCBmcmFnbWVudFxuICAgICAgICAgIGNvbnN0IG9vYkVsZW1lbnRDbG9uZSA9IG9vYkVsZW1lbnQuY2xvbmVOb2RlKHRydWUpXG4gICAgICAgICAgZnJhZ21lbnQgPSBnZXREb2N1bWVudCgpLmNyZWF0ZURvY3VtZW50RnJhZ21lbnQoKVxuICAgICAgICAgIGZyYWdtZW50LmFwcGVuZENoaWxkKG9vYkVsZW1lbnRDbG9uZSlcbiAgICAgICAgICBpZiAoIWlzSW5saW5lU3dhcChzd2FwU3R5bGUsIHRhcmdldCkpIHtcbiAgICAgICAgICAgIGZyYWdtZW50ID0gYXNQYXJlbnROb2RlKG9vYkVsZW1lbnRDbG9uZSkgLy8gaWYgdGhpcyBpcyBub3QgYW4gaW5saW5lIHN3YXAsIHdlIHVzZSB0aGUgY29udGVudCBvZiB0aGUgbm9kZSwgbm90IHRoZSBub2RlIGl0c2VsZlxuICAgICAgICAgIH1cblxuICAgICAgICAgIGNvbnN0IGJlZm9yZVN3YXBEZXRhaWxzID0geyBzaG91bGRTd2FwOiB0cnVlLCB0YXJnZXQsIGZyYWdtZW50IH1cbiAgICAgICAgICBpZiAoIXRyaWdnZXJFdmVudCh0YXJnZXQsICdodG14Om9vYkJlZm9yZVN3YXAnLCBiZWZvcmVTd2FwRGV0YWlscykpIHJldHVyblxuXG4gICAgICAgICAgdGFyZ2V0ID0gYmVmb3JlU3dhcERldGFpbHMudGFyZ2V0IC8vIGFsbG93IHJlLXRhcmdldGluZ1xuICAgICAgICAgIGlmIChiZWZvcmVTd2FwRGV0YWlscy5zaG91bGRTd2FwKSB7XG4gICAgICAgICAgICBzd2FwV2l0aFN0eWxlKHN3YXBTdHlsZSwgdGFyZ2V0LCB0YXJnZXQsIGZyYWdtZW50LCBzZXR0bGVJbmZvKVxuICAgICAgICAgIH1cbiAgICAgICAgICBmb3JFYWNoKHNldHRsZUluZm8uZWx0cywgZnVuY3Rpb24oZWx0KSB7XG4gICAgICAgICAgICB0cmlnZ2VyRXZlbnQoZWx0LCAnaHRteDpvb2JBZnRlclN3YXAnLCBiZWZvcmVTd2FwRGV0YWlscylcbiAgICAgICAgICB9KVxuICAgICAgICB9XG4gICAgICApXG4gICAgICBvb2JFbGVtZW50LnBhcmVudE5vZGUucmVtb3ZlQ2hpbGQob29iRWxlbWVudClcbiAgICB9IGVsc2Uge1xuICAgICAgb29iRWxlbWVudC5wYXJlbnROb2RlLnJlbW92ZUNoaWxkKG9vYkVsZW1lbnQpXG4gICAgICB0cmlnZ2VyRXJyb3JFdmVudChnZXREb2N1bWVudCgpLmJvZHksICdodG14Om9vYkVycm9yTm9UYXJnZXQnLCB7IGNvbnRlbnQ6IG9vYkVsZW1lbnQgfSlcbiAgICB9XG4gICAgcmV0dXJuIG9vYlZhbHVlXG4gIH1cblxuICAvKipcbiAgICogQHBhcmFtIHtEb2N1bWVudEZyYWdtZW50fSBmcmFnbWVudFxuICAgKi9cbiAgZnVuY3Rpb24gaGFuZGxlUHJlc2VydmVkRWxlbWVudHMoZnJhZ21lbnQpIHtcbiAgICBmb3JFYWNoKGZpbmRBbGwoZnJhZ21lbnQsICdbaHgtcHJlc2VydmVdLCBbZGF0YS1oeC1wcmVzZXJ2ZV0nKSwgZnVuY3Rpb24ocHJlc2VydmVkRWx0KSB7XG4gICAgICBjb25zdCBpZCA9IGdldEF0dHJpYnV0ZVZhbHVlKHByZXNlcnZlZEVsdCwgJ2lkJylcbiAgICAgIGNvbnN0IG9sZEVsdCA9IGdldERvY3VtZW50KCkuZ2V0RWxlbWVudEJ5SWQoaWQpXG4gICAgICBpZiAob2xkRWx0ICE9IG51bGwpIHtcbiAgICAgICAgcHJlc2VydmVkRWx0LnBhcmVudE5vZGUucmVwbGFjZUNoaWxkKG9sZEVsdCwgcHJlc2VydmVkRWx0KVxuICAgICAgfVxuICAgIH0pXG4gIH1cblxuICAvKipcbiAgICogQHBhcmFtIHtOb2RlfSBwYXJlbnROb2RlXG4gICAqIEBwYXJhbSB7UGFyZW50Tm9kZX0gZnJhZ21lbnRcbiAgICogQHBhcmFtIHtIdG14U2V0dGxlSW5mb30gc2V0dGxlSW5mb1xuICAgKi9cbiAgZnVuY3Rpb24gaGFuZGxlQXR0cmlidXRlcyhwYXJlbnROb2RlLCBmcmFnbWVudCwgc2V0dGxlSW5mbykge1xuICAgIGZvckVhY2goZnJhZ21lbnQucXVlcnlTZWxlY3RvckFsbCgnW2lkXScpLCBmdW5jdGlvbihuZXdOb2RlKSB7XG4gICAgICBjb25zdCBpZCA9IGdldFJhd0F0dHJpYnV0ZShuZXdOb2RlLCAnaWQnKVxuICAgICAgaWYgKGlkICYmIGlkLmxlbmd0aCA+IDApIHtcbiAgICAgICAgY29uc3Qgbm9ybWFsaXplZElkID0gaWQucmVwbGFjZShcIidcIiwgXCJcXFxcJ1wiKVxuICAgICAgICBjb25zdCBub3JtYWxpemVkVGFnID0gbmV3Tm9kZS50YWdOYW1lLnJlcGxhY2UoJzonLCAnXFxcXDonKVxuICAgICAgICBjb25zdCBwYXJlbnRFbHQgPSBhc1BhcmVudE5vZGUocGFyZW50Tm9kZSlcbiAgICAgICAgY29uc3Qgb2xkTm9kZSA9IHBhcmVudEVsdCAmJiBwYXJlbnRFbHQucXVlcnlTZWxlY3Rvcihub3JtYWxpemVkVGFnICsgXCJbaWQ9J1wiICsgbm9ybWFsaXplZElkICsgXCInXVwiKVxuICAgICAgICBpZiAob2xkTm9kZSAmJiBvbGROb2RlICE9PSBwYXJlbnRFbHQpIHtcbiAgICAgICAgICBjb25zdCBuZXdBdHRyaWJ1dGVzID0gbmV3Tm9kZS5jbG9uZU5vZGUoKVxuICAgICAgICAgIGNsb25lQXR0cmlidXRlcyhuZXdOb2RlLCBvbGROb2RlKVxuICAgICAgICAgIHNldHRsZUluZm8udGFza3MucHVzaChmdW5jdGlvbigpIHtcbiAgICAgICAgICAgIGNsb25lQXR0cmlidXRlcyhuZXdOb2RlLCBuZXdBdHRyaWJ1dGVzKVxuICAgICAgICAgIH0pXG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9KVxuICB9XG5cbiAgLyoqXG4gICAqIEBwYXJhbSB7Tm9kZX0gY2hpbGRcbiAgICogQHJldHVybnMge0h0bXhTZXR0bGVUYXNrfVxuICAgKi9cbiAgZnVuY3Rpb24gbWFrZUFqYXhMb2FkVGFzayhjaGlsZCkge1xuICAgIHJldHVybiBmdW5jdGlvbigpIHtcbiAgICAgIHJlbW92ZUNsYXNzRnJvbUVsZW1lbnQoY2hpbGQsIGh0bXguY29uZmlnLmFkZGVkQ2xhc3MpXG4gICAgICBwcm9jZXNzTm9kZShhc0VsZW1lbnQoY2hpbGQpKVxuICAgICAgcHJvY2Vzc0ZvY3VzKGFzUGFyZW50Tm9kZShjaGlsZCkpXG4gICAgICB0cmlnZ2VyRXZlbnQoY2hpbGQsICdodG14OmxvYWQnKVxuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBAcGFyYW0ge1BhcmVudE5vZGV9IGNoaWxkXG4gICAqL1xuICBmdW5jdGlvbiBwcm9jZXNzRm9jdXMoY2hpbGQpIHtcbiAgICBjb25zdCBhdXRvZm9jdXMgPSAnW2F1dG9mb2N1c10nXG4gICAgY29uc3QgYXV0b0ZvY3VzZWRFbHQgPSBhc0h0bWxFbGVtZW50KG1hdGNoZXMoY2hpbGQsIGF1dG9mb2N1cykgPyBjaGlsZCA6IGNoaWxkLnF1ZXJ5U2VsZWN0b3IoYXV0b2ZvY3VzKSlcbiAgICBpZiAoYXV0b0ZvY3VzZWRFbHQgIT0gbnVsbCkge1xuICAgICAgYXV0b0ZvY3VzZWRFbHQuZm9jdXMoKVxuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBAcGFyYW0ge05vZGV9IHBhcmVudE5vZGVcbiAgICogQHBhcmFtIHtOb2RlfSBpbnNlcnRCZWZvcmVcbiAgICogQHBhcmFtIHtQYXJlbnROb2RlfSBmcmFnbWVudFxuICAgKiBAcGFyYW0ge0h0bXhTZXR0bGVJbmZvfSBzZXR0bGVJbmZvXG4gICAqL1xuICBmdW5jdGlvbiBpbnNlcnROb2Rlc0JlZm9yZShwYXJlbnROb2RlLCBpbnNlcnRCZWZvcmUsIGZyYWdtZW50LCBzZXR0bGVJbmZvKSB7XG4gICAgaGFuZGxlQXR0cmlidXRlcyhwYXJlbnROb2RlLCBmcmFnbWVudCwgc2V0dGxlSW5mbylcbiAgICB3aGlsZSAoZnJhZ21lbnQuY2hpbGROb2Rlcy5sZW5ndGggPiAwKSB7XG4gICAgICBjb25zdCBjaGlsZCA9IGZyYWdtZW50LmZpcnN0Q2hpbGRcbiAgICAgIGFkZENsYXNzVG9FbGVtZW50KGFzRWxlbWVudChjaGlsZCksIGh0bXguY29uZmlnLmFkZGVkQ2xhc3MpXG4gICAgICBwYXJlbnROb2RlLmluc2VydEJlZm9yZShjaGlsZCwgaW5zZXJ0QmVmb3JlKVxuICAgICAgaWYgKGNoaWxkLm5vZGVUeXBlICE9PSBOb2RlLlRFWFRfTk9ERSAmJiBjaGlsZC5ub2RlVHlwZSAhPT0gTm9kZS5DT01NRU5UX05PREUpIHtcbiAgICAgICAgc2V0dGxlSW5mby50YXNrcy5wdXNoKG1ha2VBamF4TG9hZFRhc2soY2hpbGQpKVxuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBiYXNlZCBvbiBodHRwczovL2dpc3QuZ2l0aHViLmNvbS9oeWFtYW1vdG8vZmQ0MzU1MDVkMjllYmZhM2Q5NzE2ZmQyYmU4ZDQyZjAsXG4gICAqIGRlcml2ZWQgZnJvbSBKYXZhJ3Mgc3RyaW5nIGhhc2hjb2RlIGltcGxlbWVudGF0aW9uXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBzdHJpbmdcbiAgICogQHBhcmFtIHtudW1iZXJ9IGhhc2hcbiAgICogQHJldHVybnMge251bWJlcn1cbiAgICovXG4gIGZ1bmN0aW9uIHN0cmluZ0hhc2goc3RyaW5nLCBoYXNoKSB7XG4gICAgbGV0IGNoYXIgPSAwXG4gICAgd2hpbGUgKGNoYXIgPCBzdHJpbmcubGVuZ3RoKSB7XG4gICAgICBoYXNoID0gKGhhc2ggPDwgNSkgLSBoYXNoICsgc3RyaW5nLmNoYXJDb2RlQXQoY2hhcisrKSB8IDAgLy8gYml0d2lzZSBvciBlbnN1cmVzIHdlIGhhdmUgYSAzMi1iaXQgaW50XG4gICAgfVxuICAgIHJldHVybiBoYXNoXG4gIH1cblxuICAvKipcbiAgICogQHBhcmFtIHtFbGVtZW50fSBlbHRcbiAgICogQHJldHVybnMge251bWJlcn1cbiAgICovXG4gIGZ1bmN0aW9uIGF0dHJpYnV0ZUhhc2goZWx0KSB7XG4gICAgbGV0IGhhc2ggPSAwXG4gICAgLy8gSUUgZml4XG4gICAgaWYgKGVsdC5hdHRyaWJ1dGVzKSB7XG4gICAgICBmb3IgKGxldCBpID0gMDsgaSA8IGVsdC5hdHRyaWJ1dGVzLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgIGNvbnN0IGF0dHJpYnV0ZSA9IGVsdC5hdHRyaWJ1dGVzW2ldXG4gICAgICAgIGlmIChhdHRyaWJ1dGUudmFsdWUpIHsgLy8gb25seSBpbmNsdWRlIGF0dHJpYnV0ZXMgdy8gYWN0dWFsIHZhbHVlcyAoZW1wdHkgaXMgc2FtZSBhcyBub24tZXhpc3RlbnQpXG4gICAgICAgICAgaGFzaCA9IHN0cmluZ0hhc2goYXR0cmlidXRlLm5hbWUsIGhhc2gpXG4gICAgICAgICAgaGFzaCA9IHN0cmluZ0hhc2goYXR0cmlidXRlLnZhbHVlLCBoYXNoKVxuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiBoYXNoXG4gIH1cblxuICAvKipcbiAgICogQHBhcmFtIHtFdmVudFRhcmdldH0gZWx0XG4gICAqL1xuICBmdW5jdGlvbiBkZUluaXRPbkhhbmRsZXJzKGVsdCkge1xuICAgIGNvbnN0IGludGVybmFsRGF0YSA9IGdldEludGVybmFsRGF0YShlbHQpXG4gICAgaWYgKGludGVybmFsRGF0YS5vbkhhbmRsZXJzKSB7XG4gICAgICBmb3IgKGxldCBpID0gMDsgaSA8IGludGVybmFsRGF0YS5vbkhhbmRsZXJzLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgIGNvbnN0IGhhbmRsZXJJbmZvID0gaW50ZXJuYWxEYXRhLm9uSGFuZGxlcnNbaV1cbiAgICAgICAgcmVtb3ZlRXZlbnRMaXN0ZW5lckltcGwoZWx0LCBoYW5kbGVySW5mby5ldmVudCwgaGFuZGxlckluZm8ubGlzdGVuZXIpXG4gICAgICB9XG4gICAgICBkZWxldGUgaW50ZXJuYWxEYXRhLm9uSGFuZGxlcnNcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogQHBhcmFtIHtOb2RlfSBlbGVtZW50XG4gICAqL1xuICBmdW5jdGlvbiBkZUluaXROb2RlKGVsZW1lbnQpIHtcbiAgICBjb25zdCBpbnRlcm5hbERhdGEgPSBnZXRJbnRlcm5hbERhdGEoZWxlbWVudClcbiAgICBpZiAoaW50ZXJuYWxEYXRhLnRpbWVvdXQpIHtcbiAgICAgIGNsZWFyVGltZW91dChpbnRlcm5hbERhdGEudGltZW91dClcbiAgICB9XG4gICAgaWYgKGludGVybmFsRGF0YS5saXN0ZW5lckluZm9zKSB7XG4gICAgICBmb3JFYWNoKGludGVybmFsRGF0YS5saXN0ZW5lckluZm9zLCBmdW5jdGlvbihpbmZvKSB7XG4gICAgICAgIGlmIChpbmZvLm9uKSB7XG4gICAgICAgICAgcmVtb3ZlRXZlbnRMaXN0ZW5lckltcGwoaW5mby5vbiwgaW5mby50cmlnZ2VyLCBpbmZvLmxpc3RlbmVyKVxuICAgICAgICB9XG4gICAgICB9KVxuICAgIH1cbiAgICBkZUluaXRPbkhhbmRsZXJzKGVsZW1lbnQpXG4gICAgZm9yRWFjaChPYmplY3Qua2V5cyhpbnRlcm5hbERhdGEpLCBmdW5jdGlvbihrZXkpIHsgZGVsZXRlIGludGVybmFsRGF0YVtrZXldIH0pXG4gIH1cblxuICAvKipcbiAgICogQHBhcmFtIHtOb2RlfSBlbGVtZW50XG4gICAqL1xuICBmdW5jdGlvbiBjbGVhblVwRWxlbWVudChlbGVtZW50KSB7XG4gICAgdHJpZ2dlckV2ZW50KGVsZW1lbnQsICdodG14OmJlZm9yZUNsZWFudXBFbGVtZW50JylcbiAgICBkZUluaXROb2RlKGVsZW1lbnQpXG4gICAgLy8gQHRzLWlnbm9yZSBJRTExIGNvZGVcbiAgICAvLyBub2luc3BlY3Rpb24gSlNVbnJlc29sdmVkUmVmZXJlbmNlXG4gICAgaWYgKGVsZW1lbnQuY2hpbGRyZW4pIHsgLy8gSUVcbiAgICAgIC8vIEB0cy1pZ25vcmVcbiAgICAgIGZvckVhY2goZWxlbWVudC5jaGlsZHJlbiwgZnVuY3Rpb24oY2hpbGQpIHsgY2xlYW5VcEVsZW1lbnQoY2hpbGQpIH0pXG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIEBwYXJhbSB7Tm9kZX0gdGFyZ2V0XG4gICAqIEBwYXJhbSB7UGFyZW50Tm9kZX0gZnJhZ21lbnRcbiAgICogQHBhcmFtIHtIdG14U2V0dGxlSW5mb30gc2V0dGxlSW5mb1xuICAgKi9cbiAgZnVuY3Rpb24gc3dhcE91dGVySFRNTCh0YXJnZXQsIGZyYWdtZW50LCBzZXR0bGVJbmZvKSB7XG4gICAgaWYgKHRhcmdldCBpbnN0YW5jZW9mIEVsZW1lbnQgJiYgdGFyZ2V0LnRhZ05hbWUgPT09ICdCT0RZJykgeyAvLyBzcGVjaWFsIGNhc2UgdGhlIGJvZHkgdG8gaW5uZXJIVE1MIGJlY2F1c2UgRG9jdW1lbnRGcmFnbWVudHMgY2FuJ3QgY29udGFpbiBhIGJvZHkgZWx0IHVuZm9ydHVuYXRlbHlcbiAgICAgIHJldHVybiBzd2FwSW5uZXJIVE1MKHRhcmdldCwgZnJhZ21lbnQsIHNldHRsZUluZm8pXG4gICAgfVxuICAgIC8qKiBAdHlwZSB7Tm9kZX0gKi9cbiAgICBsZXQgbmV3RWx0XG4gICAgY29uc3QgZWx0QmVmb3JlTmV3Q29udGVudCA9IHRhcmdldC5wcmV2aW91c1NpYmxpbmdcbiAgICBpbnNlcnROb2Rlc0JlZm9yZShwYXJlbnRFbHQodGFyZ2V0KSwgdGFyZ2V0LCBmcmFnbWVudCwgc2V0dGxlSW5mbylcbiAgICBpZiAoZWx0QmVmb3JlTmV3Q29udGVudCA9PSBudWxsKSB7XG4gICAgICBuZXdFbHQgPSBwYXJlbnRFbHQodGFyZ2V0KS5maXJzdENoaWxkXG4gICAgfSBlbHNlIHtcbiAgICAgIG5ld0VsdCA9IGVsdEJlZm9yZU5ld0NvbnRlbnQubmV4dFNpYmxpbmdcbiAgICB9XG4gICAgc2V0dGxlSW5mby5lbHRzID0gc2V0dGxlSW5mby5lbHRzLmZpbHRlcihmdW5jdGlvbihlKSB7IHJldHVybiBlICE9PSB0YXJnZXQgfSlcbiAgICAvLyBzY2FuIHRocm91Z2ggYWxsIG5ld2x5IGFkZGVkIGNvbnRlbnQgYW5kIGFkZCBhbGwgZWxlbWVudHMgdG8gdGhlIHNldHRsZSBpbmZvIHNvIHdlIHRyaWdnZXJcbiAgICAvLyBldmVudHMgcHJvcGVybHkgb24gdGhlbVxuICAgIHdoaWxlIChuZXdFbHQgJiYgbmV3RWx0ICE9PSB0YXJnZXQpIHtcbiAgICAgIGlmIChuZXdFbHQgaW5zdGFuY2VvZiBFbGVtZW50KSB7XG4gICAgICAgIHNldHRsZUluZm8uZWx0cy5wdXNoKG5ld0VsdClcbiAgICAgIH1cbiAgICAgIG5ld0VsdCA9IG5ld0VsdC5uZXh0U2libGluZ1xuICAgIH1cbiAgICBjbGVhblVwRWxlbWVudCh0YXJnZXQpXG4gICAgaWYgKHRhcmdldCBpbnN0YW5jZW9mIEVsZW1lbnQpIHtcbiAgICAgIHRhcmdldC5yZW1vdmUoKVxuICAgIH0gZWxzZSB7XG4gICAgICB0YXJnZXQucGFyZW50Tm9kZS5yZW1vdmVDaGlsZCh0YXJnZXQpXG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIEBwYXJhbSB7Tm9kZX0gdGFyZ2V0XG4gICAqIEBwYXJhbSB7UGFyZW50Tm9kZX0gZnJhZ21lbnRcbiAgICogQHBhcmFtIHtIdG14U2V0dGxlSW5mb30gc2V0dGxlSW5mb1xuICAgKi9cbiAgZnVuY3Rpb24gc3dhcEFmdGVyQmVnaW4odGFyZ2V0LCBmcmFnbWVudCwgc2V0dGxlSW5mbykge1xuICAgIHJldHVybiBpbnNlcnROb2Rlc0JlZm9yZSh0YXJnZXQsIHRhcmdldC5maXJzdENoaWxkLCBmcmFnbWVudCwgc2V0dGxlSW5mbylcbiAgfVxuXG4gIC8qKlxuICAgKiBAcGFyYW0ge05vZGV9IHRhcmdldFxuICAgKiBAcGFyYW0ge1BhcmVudE5vZGV9IGZyYWdtZW50XG4gICAqIEBwYXJhbSB7SHRteFNldHRsZUluZm99IHNldHRsZUluZm9cbiAgICovXG4gIGZ1bmN0aW9uIHN3YXBCZWZvcmVCZWdpbih0YXJnZXQsIGZyYWdtZW50LCBzZXR0bGVJbmZvKSB7XG4gICAgcmV0dXJuIGluc2VydE5vZGVzQmVmb3JlKHBhcmVudEVsdCh0YXJnZXQpLCB0YXJnZXQsIGZyYWdtZW50LCBzZXR0bGVJbmZvKVxuICB9XG5cbiAgLyoqXG4gICAqIEBwYXJhbSB7Tm9kZX0gdGFyZ2V0XG4gICAqIEBwYXJhbSB7UGFyZW50Tm9kZX0gZnJhZ21lbnRcbiAgICogQHBhcmFtIHtIdG14U2V0dGxlSW5mb30gc2V0dGxlSW5mb1xuICAgKi9cbiAgZnVuY3Rpb24gc3dhcEJlZm9yZUVuZCh0YXJnZXQsIGZyYWdtZW50LCBzZXR0bGVJbmZvKSB7XG4gICAgcmV0dXJuIGluc2VydE5vZGVzQmVmb3JlKHRhcmdldCwgbnVsbCwgZnJhZ21lbnQsIHNldHRsZUluZm8pXG4gIH1cblxuICAvKipcbiAgICogQHBhcmFtIHtOb2RlfSB0YXJnZXRcbiAgICogQHBhcmFtIHtQYXJlbnROb2RlfSBmcmFnbWVudFxuICAgKiBAcGFyYW0ge0h0bXhTZXR0bGVJbmZvfSBzZXR0bGVJbmZvXG4gICAqL1xuICBmdW5jdGlvbiBzd2FwQWZ0ZXJFbmQodGFyZ2V0LCBmcmFnbWVudCwgc2V0dGxlSW5mbykge1xuICAgIHJldHVybiBpbnNlcnROb2Rlc0JlZm9yZShwYXJlbnRFbHQodGFyZ2V0KSwgdGFyZ2V0Lm5leHRTaWJsaW5nLCBmcmFnbWVudCwgc2V0dGxlSW5mbylcbiAgfVxuXG4gIC8qKlxuICAgKiBAcGFyYW0ge05vZGV9IHRhcmdldFxuICAgKi9cbiAgZnVuY3Rpb24gc3dhcERlbGV0ZSh0YXJnZXQpIHtcbiAgICBjbGVhblVwRWxlbWVudCh0YXJnZXQpXG4gICAgcmV0dXJuIHBhcmVudEVsdCh0YXJnZXQpLnJlbW92ZUNoaWxkKHRhcmdldClcbiAgfVxuXG4gIC8qKlxuICAgKiBAcGFyYW0ge05vZGV9IHRhcmdldFxuICAgKiBAcGFyYW0ge1BhcmVudE5vZGV9IGZyYWdtZW50XG4gICAqIEBwYXJhbSB7SHRteFNldHRsZUluZm99IHNldHRsZUluZm9cbiAgICovXG4gIGZ1bmN0aW9uIHN3YXBJbm5lckhUTUwodGFyZ2V0LCBmcmFnbWVudCwgc2V0dGxlSW5mbykge1xuICAgIGNvbnN0IGZpcnN0Q2hpbGQgPSB0YXJnZXQuZmlyc3RDaGlsZFxuICAgIGluc2VydE5vZGVzQmVmb3JlKHRhcmdldCwgZmlyc3RDaGlsZCwgZnJhZ21lbnQsIHNldHRsZUluZm8pXG4gICAgaWYgKGZpcnN0Q2hpbGQpIHtcbiAgICAgIHdoaWxlIChmaXJzdENoaWxkLm5leHRTaWJsaW5nKSB7XG4gICAgICAgIGNsZWFuVXBFbGVtZW50KGZpcnN0Q2hpbGQubmV4dFNpYmxpbmcpXG4gICAgICAgIHRhcmdldC5yZW1vdmVDaGlsZChmaXJzdENoaWxkLm5leHRTaWJsaW5nKVxuICAgICAgfVxuICAgICAgY2xlYW5VcEVsZW1lbnQoZmlyc3RDaGlsZClcbiAgICAgIHRhcmdldC5yZW1vdmVDaGlsZChmaXJzdENoaWxkKVxuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBAcGFyYW0ge0h0bXhTd2FwU3R5bGV9IHN3YXBTdHlsZVxuICAgKiBAcGFyYW0ge0VsZW1lbnR9IGVsdFxuICAgKiBAcGFyYW0ge05vZGV9IHRhcmdldFxuICAgKiBAcGFyYW0ge1BhcmVudE5vZGV9IGZyYWdtZW50XG4gICAqIEBwYXJhbSB7SHRteFNldHRsZUluZm99IHNldHRsZUluZm9cbiAgICovXG4gIGZ1bmN0aW9uIHN3YXBXaXRoU3R5bGUoc3dhcFN0eWxlLCBlbHQsIHRhcmdldCwgZnJhZ21lbnQsIHNldHRsZUluZm8pIHtcbiAgICBzd2l0Y2ggKHN3YXBTdHlsZSkge1xuICAgICAgY2FzZSAnbm9uZSc6XG4gICAgICAgIHJldHVyblxuICAgICAgY2FzZSAnb3V0ZXJIVE1MJzpcbiAgICAgICAgc3dhcE91dGVySFRNTCh0YXJnZXQsIGZyYWdtZW50LCBzZXR0bGVJbmZvKVxuICAgICAgICByZXR1cm5cbiAgICAgIGNhc2UgJ2FmdGVyYmVnaW4nOlxuICAgICAgICBzd2FwQWZ0ZXJCZWdpbih0YXJnZXQsIGZyYWdtZW50LCBzZXR0bGVJbmZvKVxuICAgICAgICByZXR1cm5cbiAgICAgIGNhc2UgJ2JlZm9yZWJlZ2luJzpcbiAgICAgICAgc3dhcEJlZm9yZUJlZ2luKHRhcmdldCwgZnJhZ21lbnQsIHNldHRsZUluZm8pXG4gICAgICAgIHJldHVyblxuICAgICAgY2FzZSAnYmVmb3JlZW5kJzpcbiAgICAgICAgc3dhcEJlZm9yZUVuZCh0YXJnZXQsIGZyYWdtZW50LCBzZXR0bGVJbmZvKVxuICAgICAgICByZXR1cm5cbiAgICAgIGNhc2UgJ2FmdGVyZW5kJzpcbiAgICAgICAgc3dhcEFmdGVyRW5kKHRhcmdldCwgZnJhZ21lbnQsIHNldHRsZUluZm8pXG4gICAgICAgIHJldHVyblxuICAgICAgY2FzZSAnZGVsZXRlJzpcbiAgICAgICAgc3dhcERlbGV0ZSh0YXJnZXQpXG4gICAgICAgIHJldHVyblxuICAgICAgZGVmYXVsdDpcbiAgICAgICAgdmFyIGV4dGVuc2lvbnMgPSBnZXRFeHRlbnNpb25zKGVsdClcbiAgICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCBleHRlbnNpb25zLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgICAgY29uc3QgZXh0ID0gZXh0ZW5zaW9uc1tpXVxuICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICBjb25zdCBuZXdFbGVtZW50cyA9IGV4dC5oYW5kbGVTd2FwKHN3YXBTdHlsZSwgdGFyZ2V0LCBmcmFnbWVudCwgc2V0dGxlSW5mbylcbiAgICAgICAgICAgIGlmIChuZXdFbGVtZW50cykge1xuICAgICAgICAgICAgICBpZiAoQXJyYXkuaXNBcnJheShuZXdFbGVtZW50cykpIHtcbiAgICAgICAgICAgICAgICAvLyBpZiBoYW5kbGVTd2FwIHJldHVybnMgYW4gYXJyYXkgKGxpa2UpIG9mIGVsZW1lbnRzLCB3ZSBoYW5kbGUgdGhlbVxuICAgICAgICAgICAgICAgIGZvciAobGV0IGogPSAwOyBqIDwgbmV3RWxlbWVudHMubGVuZ3RoOyBqKyspIHtcbiAgICAgICAgICAgICAgICAgIGNvbnN0IGNoaWxkID0gbmV3RWxlbWVudHNbal1cbiAgICAgICAgICAgICAgICAgIGlmIChjaGlsZC5ub2RlVHlwZSAhPT0gTm9kZS5URVhUX05PREUgJiYgY2hpbGQubm9kZVR5cGUgIT09IE5vZGUuQ09NTUVOVF9OT0RFKSB7XG4gICAgICAgICAgICAgICAgICAgIHNldHRsZUluZm8udGFza3MucHVzaChtYWtlQWpheExvYWRUYXNrKGNoaWxkKSlcbiAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgcmV0dXJuXG4gICAgICAgICAgICB9XG4gICAgICAgICAgfSBjYXRjaCAoZSkge1xuICAgICAgICAgICAgbG9nRXJyb3IoZSlcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgaWYgKHN3YXBTdHlsZSA9PT0gJ2lubmVySFRNTCcpIHtcbiAgICAgICAgICBzd2FwSW5uZXJIVE1MKHRhcmdldCwgZnJhZ21lbnQsIHNldHRsZUluZm8pXG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgc3dhcFdpdGhTdHlsZShodG14LmNvbmZpZy5kZWZhdWx0U3dhcFN0eWxlLCBlbHQsIHRhcmdldCwgZnJhZ21lbnQsIHNldHRsZUluZm8pXG4gICAgICAgIH1cbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogQHBhcmFtIHtEb2N1bWVudEZyYWdtZW50fSBmcmFnbWVudFxuICAgKiBAcGFyYW0ge0h0bXhTZXR0bGVJbmZvfSBzZXR0bGVJbmZvXG4gICAqL1xuICBmdW5jdGlvbiBmaW5kQW5kU3dhcE9vYkVsZW1lbnRzKGZyYWdtZW50LCBzZXR0bGVJbmZvKSB7XG4gICAgdmFyIG9vYkVsdHMgPSBmaW5kQWxsKGZyYWdtZW50LCAnW2h4LXN3YXAtb29iXSwgW2RhdGEtaHgtc3dhcC1vb2JdJylcbiAgICBmb3JFYWNoKG9vYkVsdHMsIGZ1bmN0aW9uKG9vYkVsZW1lbnQpIHtcbiAgICAgIGlmIChodG14LmNvbmZpZy5hbGxvd05lc3RlZE9vYlN3YXBzIHx8IG9vYkVsZW1lbnQucGFyZW50RWxlbWVudCA9PT0gbnVsbCkge1xuICAgICAgICBjb25zdCBvb2JWYWx1ZSA9IGdldEF0dHJpYnV0ZVZhbHVlKG9vYkVsZW1lbnQsICdoeC1zd2FwLW9vYicpXG4gICAgICAgIGlmIChvb2JWYWx1ZSAhPSBudWxsKSB7XG4gICAgICAgICAgb29iU3dhcChvb2JWYWx1ZSwgb29iRWxlbWVudCwgc2V0dGxlSW5mbylcbiAgICAgICAgfVxuICAgICAgfSBlbHNlIHtcbiAgICAgICAgb29iRWxlbWVudC5yZW1vdmVBdHRyaWJ1dGUoJ2h4LXN3YXAtb29iJylcbiAgICAgICAgb29iRWxlbWVudC5yZW1vdmVBdHRyaWJ1dGUoJ2RhdGEtaHgtc3dhcC1vb2InKVxuICAgICAgfVxuICAgIH0pXG4gICAgcmV0dXJuIG9vYkVsdHMubGVuZ3RoID4gMFxuICB9XG5cbiAgLyoqXG4gICAqIEltcGxlbWVudHMgY29tcGxldGUgc3dhcHBpbmcgcGlwZWxpbmUsIGluY2x1ZGluZzogZm9jdXMgYW5kIHNlbGVjdGlvbiBwcmVzZXJ2YXRpb24sXG4gICAqIHRpdGxlIHVwZGF0ZXMsIHNjcm9sbCwgT09CIHN3YXBwaW5nLCBub3JtYWwgc3dhcHBpbmcgYW5kIHNldHRsaW5nXG4gICAqIEBwYXJhbSB7c3RyaW5nfEVsZW1lbnR9IHRhcmdldFxuICAgKiBAcGFyYW0ge3N0cmluZ30gY29udGVudFxuICAgKiBAcGFyYW0ge0h0bXhTd2FwU3BlY2lmaWNhdGlvbn0gc3dhcFNwZWNcbiAgICogQHBhcmFtIHtTd2FwT3B0aW9uc30gW3N3YXBPcHRpb25zXVxuICAgKi9cbiAgZnVuY3Rpb24gc3dhcCh0YXJnZXQsIGNvbnRlbnQsIHN3YXBTcGVjLCBzd2FwT3B0aW9ucykge1xuICAgIGlmICghc3dhcE9wdGlvbnMpIHtcbiAgICAgIHN3YXBPcHRpb25zID0ge31cbiAgICB9XG5cbiAgICB0YXJnZXQgPSByZXNvbHZlVGFyZ2V0KHRhcmdldClcblxuICAgIC8vIHByZXNlcnZlIGZvY3VzIGFuZCBzZWxlY3Rpb25cbiAgICBjb25zdCBhY3RpdmVFbHQgPSBkb2N1bWVudC5hY3RpdmVFbGVtZW50XG4gICAgbGV0IHNlbGVjdGlvbkluZm8gPSB7fVxuICAgIHRyeSB7XG4gICAgICBzZWxlY3Rpb25JbmZvID0ge1xuICAgICAgICBlbHQ6IGFjdGl2ZUVsdCxcbiAgICAgICAgLy8gQHRzLWlnbm9yZVxuICAgICAgICBzdGFydDogYWN0aXZlRWx0ID8gYWN0aXZlRWx0LnNlbGVjdGlvblN0YXJ0IDogbnVsbCxcbiAgICAgICAgLy8gQHRzLWlnbm9yZVxuICAgICAgICBlbmQ6IGFjdGl2ZUVsdCA/IGFjdGl2ZUVsdC5zZWxlY3Rpb25FbmQgOiBudWxsXG4gICAgICB9XG4gICAgfSBjYXRjaCAoZSkge1xuICAgICAgLy8gc2FmYXJpIGlzc3VlIC0gc2VlIGh0dHBzOi8vZ2l0aHViLmNvbS9taWNyb3NvZnQvcGxheXdyaWdodC9pc3N1ZXMvNTg5NFxuICAgIH1cbiAgICBjb25zdCBzZXR0bGVJbmZvID0gbWFrZVNldHRsZUluZm8odGFyZ2V0KVxuXG4gICAgLy8gRm9yIHRleHQgY29udGVudCBzd2FwcywgZG9uJ3QgcGFyc2UgdGhlIHJlc3BvbnNlIGFzIEhUTUwsIGp1c3QgaW5zZXJ0IGl0XG4gICAgaWYgKHN3YXBTcGVjLnN3YXBTdHlsZSA9PT0gJ3RleHRDb250ZW50Jykge1xuICAgICAgdGFyZ2V0LnRleHRDb250ZW50ID0gY29udGVudFxuICAgIC8vIE90aGVyd2lzZSwgbWFrZSB0aGUgZnJhZ21lbnQgYW5kIHByb2Nlc3MgaXRcbiAgICB9IGVsc2Uge1xuICAgICAgbGV0IGZyYWdtZW50ID0gbWFrZUZyYWdtZW50KGNvbnRlbnQpXG5cbiAgICAgIHNldHRsZUluZm8udGl0bGUgPSBmcmFnbWVudC50aXRsZVxuXG4gICAgICAvLyBzZWxlY3Qtb29iIHN3YXBzXG4gICAgICBpZiAoc3dhcE9wdGlvbnMuc2VsZWN0T09CKSB7XG4gICAgICAgIGNvbnN0IG9vYlNlbGVjdFZhbHVlcyA9IHN3YXBPcHRpb25zLnNlbGVjdE9PQi5zcGxpdCgnLCcpXG4gICAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgb29iU2VsZWN0VmFsdWVzLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgICAgY29uc3Qgb29iU2VsZWN0VmFsdWUgPSBvb2JTZWxlY3RWYWx1ZXNbaV0uc3BsaXQoJzonLCAyKVxuICAgICAgICAgIGxldCBpZCA9IG9vYlNlbGVjdFZhbHVlWzBdLnRyaW0oKVxuICAgICAgICAgIGlmIChpZC5pbmRleE9mKCcjJykgPT09IDApIHtcbiAgICAgICAgICAgIGlkID0gaWQuc3Vic3RyaW5nKDEpXG4gICAgICAgICAgfVxuICAgICAgICAgIGNvbnN0IG9vYlZhbHVlID0gb29iU2VsZWN0VmFsdWVbMV0gfHwgJ3RydWUnXG4gICAgICAgICAgY29uc3Qgb29iRWxlbWVudCA9IGZyYWdtZW50LnF1ZXJ5U2VsZWN0b3IoJyMnICsgaWQpXG4gICAgICAgICAgaWYgKG9vYkVsZW1lbnQpIHtcbiAgICAgICAgICAgIG9vYlN3YXAob29iVmFsdWUsIG9vYkVsZW1lbnQsIHNldHRsZUluZm8pXG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9XG4gICAgICAvLyBvb2Igc3dhcHNcbiAgICAgIGZpbmRBbmRTd2FwT29iRWxlbWVudHMoZnJhZ21lbnQsIHNldHRsZUluZm8pXG4gICAgICBmb3JFYWNoKGZpbmRBbGwoZnJhZ21lbnQsICd0ZW1wbGF0ZScpLCAvKiogQHBhcmFtIHtIVE1MVGVtcGxhdGVFbGVtZW50fSB0ZW1wbGF0ZSAqL2Z1bmN0aW9uKHRlbXBsYXRlKSB7XG4gICAgICAgIGlmIChmaW5kQW5kU3dhcE9vYkVsZW1lbnRzKHRlbXBsYXRlLmNvbnRlbnQsIHNldHRsZUluZm8pKSB7XG4gICAgICAgICAgLy8gQXZvaWQgcG9sbHV0aW5nIHRoZSBET00gd2l0aCBlbXB0eSB0ZW1wbGF0ZXMgdGhhdCB3ZXJlIG9ubHkgdXNlZCB0byBlbmNhcHN1bGF0ZSBvb2Igc3dhcFxuICAgICAgICAgIHRlbXBsYXRlLnJlbW92ZSgpXG4gICAgICAgIH1cbiAgICAgIH0pXG5cbiAgICAgIC8vIG5vcm1hbCBzd2FwXG4gICAgICBpZiAoc3dhcE9wdGlvbnMuc2VsZWN0KSB7XG4gICAgICAgIGNvbnN0IG5ld0ZyYWdtZW50ID0gZ2V0RG9jdW1lbnQoKS5jcmVhdGVEb2N1bWVudEZyYWdtZW50KClcbiAgICAgICAgZm9yRWFjaChmcmFnbWVudC5xdWVyeVNlbGVjdG9yQWxsKHN3YXBPcHRpb25zLnNlbGVjdCksIGZ1bmN0aW9uKG5vZGUpIHtcbiAgICAgICAgICBuZXdGcmFnbWVudC5hcHBlbmRDaGlsZChub2RlKVxuICAgICAgICB9KVxuICAgICAgICBmcmFnbWVudCA9IG5ld0ZyYWdtZW50XG4gICAgICB9XG4gICAgICBoYW5kbGVQcmVzZXJ2ZWRFbGVtZW50cyhmcmFnbWVudClcbiAgICAgIHN3YXBXaXRoU3R5bGUoc3dhcFNwZWMuc3dhcFN0eWxlLCBzd2FwT3B0aW9ucy5jb250ZXh0RWxlbWVudCwgdGFyZ2V0LCBmcmFnbWVudCwgc2V0dGxlSW5mbylcbiAgICB9XG5cbiAgICAvLyBhcHBseSBzYXZlZCBmb2N1cyBhbmQgc2VsZWN0aW9uIGluZm9ybWF0aW9uIHRvIHN3YXBwZWQgY29udGVudFxuICAgIGlmIChzZWxlY3Rpb25JbmZvLmVsdCAmJlxuICAgICAgIWJvZHlDb250YWlucyhzZWxlY3Rpb25JbmZvLmVsdCkgJiZcbiAgICAgIGdldFJhd0F0dHJpYnV0ZShzZWxlY3Rpb25JbmZvLmVsdCwgJ2lkJykpIHtcbiAgICAgIGNvbnN0IG5ld0FjdGl2ZUVsdCA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKGdldFJhd0F0dHJpYnV0ZShzZWxlY3Rpb25JbmZvLmVsdCwgJ2lkJykpXG4gICAgICBjb25zdCBmb2N1c09wdGlvbnMgPSB7IHByZXZlbnRTY3JvbGw6IHN3YXBTcGVjLmZvY3VzU2Nyb2xsICE9PSB1bmRlZmluZWQgPyAhc3dhcFNwZWMuZm9jdXNTY3JvbGwgOiAhaHRteC5jb25maWcuZGVmYXVsdEZvY3VzU2Nyb2xsIH1cbiAgICAgIGlmIChuZXdBY3RpdmVFbHQpIHtcbiAgICAgICAgLy8gQHRzLWlnbm9yZVxuICAgICAgICBpZiAoc2VsZWN0aW9uSW5mby5zdGFydCAmJiBuZXdBY3RpdmVFbHQuc2V0U2VsZWN0aW9uUmFuZ2UpIHtcbiAgICAgICAgICB0cnkge1xuICAgICAgICAgICAgLy8gQHRzLWlnbm9yZVxuICAgICAgICAgICAgbmV3QWN0aXZlRWx0LnNldFNlbGVjdGlvblJhbmdlKHNlbGVjdGlvbkluZm8uc3RhcnQsIHNlbGVjdGlvbkluZm8uZW5kKVxuICAgICAgICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgICAgICAgIC8vIHRoZSBzZXRTZWxlY3Rpb25SYW5nZSBtZXRob2QgaXMgcHJlc2VudCBvbiBmaWVsZHMgdGhhdCBkb24ndCBzdXBwb3J0IGl0LCBzbyBqdXN0IGxldCB0aGlzIGZhaWxcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgbmV3QWN0aXZlRWx0LmZvY3VzKGZvY3VzT3B0aW9ucylcbiAgICAgIH1cbiAgICB9XG5cbiAgICB0YXJnZXQuY2xhc3NMaXN0LnJlbW92ZShodG14LmNvbmZpZy5zd2FwcGluZ0NsYXNzKVxuICAgIGZvckVhY2goc2V0dGxlSW5mby5lbHRzLCBmdW5jdGlvbihlbHQpIHtcbiAgICAgIGlmIChlbHQuY2xhc3NMaXN0KSB7XG4gICAgICAgIGVsdC5jbGFzc0xpc3QuYWRkKGh0bXguY29uZmlnLnNldHRsaW5nQ2xhc3MpXG4gICAgICB9XG4gICAgICB0cmlnZ2VyRXZlbnQoZWx0LCAnaHRteDphZnRlclN3YXAnLCBzd2FwT3B0aW9ucy5ldmVudEluZm8pXG4gICAgfSlcbiAgICBpZiAoc3dhcE9wdGlvbnMuYWZ0ZXJTd2FwQ2FsbGJhY2spIHtcbiAgICAgIHN3YXBPcHRpb25zLmFmdGVyU3dhcENhbGxiYWNrKClcbiAgICB9XG5cbiAgICAvLyBtZXJnZSBpbiBuZXcgdGl0bGUgYWZ0ZXIgc3dhcCBidXQgYmVmb3JlIHNldHRsZVxuICAgIGlmICghc3dhcFNwZWMuaWdub3JlVGl0bGUpIHtcbiAgICAgIGhhbmRsZVRpdGxlKHNldHRsZUluZm8udGl0bGUpXG4gICAgfVxuXG4gICAgLy8gc2V0dGxlXG4gICAgY29uc3QgZG9TZXR0bGUgPSBmdW5jdGlvbigpIHtcbiAgICAgIGZvckVhY2goc2V0dGxlSW5mby50YXNrcywgZnVuY3Rpb24odGFzaykge1xuICAgICAgICB0YXNrLmNhbGwoKVxuICAgICAgfSlcbiAgICAgIGZvckVhY2goc2V0dGxlSW5mby5lbHRzLCBmdW5jdGlvbihlbHQpIHtcbiAgICAgICAgaWYgKGVsdC5jbGFzc0xpc3QpIHtcbiAgICAgICAgICBlbHQuY2xhc3NMaXN0LnJlbW92ZShodG14LmNvbmZpZy5zZXR0bGluZ0NsYXNzKVxuICAgICAgICB9XG4gICAgICAgIHRyaWdnZXJFdmVudChlbHQsICdodG14OmFmdGVyU2V0dGxlJywgc3dhcE9wdGlvbnMuZXZlbnRJbmZvKVxuICAgICAgfSlcblxuICAgICAgaWYgKHN3YXBPcHRpb25zLmFuY2hvcikge1xuICAgICAgICBjb25zdCBhbmNob3JUYXJnZXQgPSBhc0VsZW1lbnQocmVzb2x2ZVRhcmdldCgnIycgKyBzd2FwT3B0aW9ucy5hbmNob3IpKVxuICAgICAgICBpZiAoYW5jaG9yVGFyZ2V0KSB7XG4gICAgICAgICAgYW5jaG9yVGFyZ2V0LnNjcm9sbEludG9WaWV3KHsgYmxvY2s6ICdzdGFydCcsIGJlaGF2aW9yOiAnYXV0bycgfSlcbiAgICAgICAgfVxuICAgICAgfVxuXG4gICAgICB1cGRhdGVTY3JvbGxTdGF0ZShzZXR0bGVJbmZvLmVsdHMsIHN3YXBTcGVjKVxuICAgICAgaWYgKHN3YXBPcHRpb25zLmFmdGVyU2V0dGxlQ2FsbGJhY2spIHtcbiAgICAgICAgc3dhcE9wdGlvbnMuYWZ0ZXJTZXR0bGVDYWxsYmFjaygpXG4gICAgICB9XG4gICAgfVxuXG4gICAgaWYgKHN3YXBTcGVjLnNldHRsZURlbGF5ID4gMCkge1xuICAgICAgZ2V0V2luZG93KCkuc2V0VGltZW91dChkb1NldHRsZSwgc3dhcFNwZWMuc2V0dGxlRGVsYXkpXG4gICAgfSBlbHNlIHtcbiAgICAgIGRvU2V0dGxlKClcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogQHBhcmFtIHtYTUxIdHRwUmVxdWVzdH0geGhyXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBoZWFkZXJcbiAgICogQHBhcmFtIHtFdmVudFRhcmdldH0gZWx0XG4gICAqL1xuICBmdW5jdGlvbiBoYW5kbGVUcmlnZ2VySGVhZGVyKHhociwgaGVhZGVyLCBlbHQpIHtcbiAgICBjb25zdCB0cmlnZ2VyQm9keSA9IHhoci5nZXRSZXNwb25zZUhlYWRlcihoZWFkZXIpXG4gICAgaWYgKHRyaWdnZXJCb2R5LmluZGV4T2YoJ3snKSA9PT0gMCkge1xuICAgICAgY29uc3QgdHJpZ2dlcnMgPSBwYXJzZUpTT04odHJpZ2dlckJvZHkpXG4gICAgICBmb3IgKGNvbnN0IGV2ZW50TmFtZSBpbiB0cmlnZ2Vycykge1xuICAgICAgICBpZiAodHJpZ2dlcnMuaGFzT3duUHJvcGVydHkoZXZlbnROYW1lKSkge1xuICAgICAgICAgIGxldCBkZXRhaWwgPSB0cmlnZ2Vyc1tldmVudE5hbWVdXG4gICAgICAgICAgaWYgKGlzUmF3T2JqZWN0KGRldGFpbCkpIHtcbiAgICAgICAgICAgIC8vIEB0cy1pZ25vcmVcbiAgICAgICAgICAgIGVsdCA9IGRldGFpbC50YXJnZXQgIT09IHVuZGVmaW5lZCA/IGRldGFpbC50YXJnZXQgOiBlbHRcbiAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgZGV0YWlsID0geyB2YWx1ZTogZGV0YWlsIH1cbiAgICAgICAgICB9XG4gICAgICAgICAgdHJpZ2dlckV2ZW50KGVsdCwgZXZlbnROYW1lLCBkZXRhaWwpXG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9IGVsc2Uge1xuICAgICAgY29uc3QgZXZlbnROYW1lcyA9IHRyaWdnZXJCb2R5LnNwbGl0KCcsJylcbiAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgZXZlbnROYW1lcy5sZW5ndGg7IGkrKykge1xuICAgICAgICB0cmlnZ2VyRXZlbnQoZWx0LCBldmVudE5hbWVzW2ldLnRyaW0oKSwgW10pXG4gICAgICB9XG4gICAgfVxuICB9XG5cbiAgY29uc3QgV0hJVEVTUEFDRSA9IC9cXHMvXG4gIGNvbnN0IFdISVRFU1BBQ0VfT1JfQ09NTUEgPSAvW1xccyxdL1xuICBjb25zdCBTWU1CT0xfU1RBUlQgPSAvW18kYS16QS1aXS9cbiAgY29uc3QgU1lNQk9MX0NPTlQgPSAvW18kYS16QS1aMC05XS9cbiAgY29uc3QgU1RSSU5HSVNIX1NUQVJUID0gWydcIicsIFwiJ1wiLCAnLyddXG4gIGNvbnN0IE5PVF9XSElURVNQQUNFID0gL1teXFxzXS9cbiAgY29uc3QgQ09NQklORURfU0VMRUNUT1JfU1RBUlQgPSAvW3soXS9cbiAgY29uc3QgQ09NQklORURfU0VMRUNUT1JfRU5EID0gL1t9KV0vXG5cbiAgLyoqXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBzdHJcbiAgICogQHJldHVybnMge3N0cmluZ1tdfVxuICAgKi9cbiAgZnVuY3Rpb24gdG9rZW5pemVTdHJpbmcoc3RyKSB7XG4gICAgLyoqIEB0eXBlIHN0cmluZ1tdICovXG4gICAgY29uc3QgdG9rZW5zID0gW11cbiAgICBsZXQgcG9zaXRpb24gPSAwXG4gICAgd2hpbGUgKHBvc2l0aW9uIDwgc3RyLmxlbmd0aCkge1xuICAgICAgaWYgKFNZTUJPTF9TVEFSVC5leGVjKHN0ci5jaGFyQXQocG9zaXRpb24pKSkge1xuICAgICAgICB2YXIgc3RhcnRQb3NpdGlvbiA9IHBvc2l0aW9uXG4gICAgICAgIHdoaWxlIChTWU1CT0xfQ09OVC5leGVjKHN0ci5jaGFyQXQocG9zaXRpb24gKyAxKSkpIHtcbiAgICAgICAgICBwb3NpdGlvbisrXG4gICAgICAgIH1cbiAgICAgICAgdG9rZW5zLnB1c2goc3RyLnN1YnN0cihzdGFydFBvc2l0aW9uLCBwb3NpdGlvbiAtIHN0YXJ0UG9zaXRpb24gKyAxKSlcbiAgICAgIH0gZWxzZSBpZiAoU1RSSU5HSVNIX1NUQVJULmluZGV4T2Yoc3RyLmNoYXJBdChwb3NpdGlvbikpICE9PSAtMSkge1xuICAgICAgICBjb25zdCBzdGFydENoYXIgPSBzdHIuY2hhckF0KHBvc2l0aW9uKVxuICAgICAgICB2YXIgc3RhcnRQb3NpdGlvbiA9IHBvc2l0aW9uXG4gICAgICAgIHBvc2l0aW9uKytcbiAgICAgICAgd2hpbGUgKHBvc2l0aW9uIDwgc3RyLmxlbmd0aCAmJiBzdHIuY2hhckF0KHBvc2l0aW9uKSAhPT0gc3RhcnRDaGFyKSB7XG4gICAgICAgICAgaWYgKHN0ci5jaGFyQXQocG9zaXRpb24pID09PSAnXFxcXCcpIHtcbiAgICAgICAgICAgIHBvc2l0aW9uKytcbiAgICAgICAgICB9XG4gICAgICAgICAgcG9zaXRpb24rK1xuICAgICAgICB9XG4gICAgICAgIHRva2Vucy5wdXNoKHN0ci5zdWJzdHIoc3RhcnRQb3NpdGlvbiwgcG9zaXRpb24gLSBzdGFydFBvc2l0aW9uICsgMSkpXG4gICAgICB9IGVsc2Uge1xuICAgICAgICBjb25zdCBzeW1ib2wgPSBzdHIuY2hhckF0KHBvc2l0aW9uKVxuICAgICAgICB0b2tlbnMucHVzaChzeW1ib2wpXG4gICAgICB9XG4gICAgICBwb3NpdGlvbisrXG4gICAgfVxuICAgIHJldHVybiB0b2tlbnNcbiAgfVxuXG4gIC8qKlxuICAgKiBAcGFyYW0ge3N0cmluZ30gdG9rZW5cbiAgICogQHBhcmFtIHtzdHJpbmd8bnVsbH0gbGFzdFxuICAgKiBAcGFyYW0ge3N0cmluZ30gcGFyYW1OYW1lXG4gICAqIEByZXR1cm5zIHtib29sZWFufVxuICAgKi9cbiAgZnVuY3Rpb24gaXNQb3NzaWJsZVJlbGF0aXZlUmVmZXJlbmNlKHRva2VuLCBsYXN0LCBwYXJhbU5hbWUpIHtcbiAgICByZXR1cm4gU1lNQk9MX1NUQVJULmV4ZWModG9rZW4uY2hhckF0KDApKSAmJlxuICAgICAgdG9rZW4gIT09ICd0cnVlJyAmJlxuICAgICAgdG9rZW4gIT09ICdmYWxzZScgJiZcbiAgICAgIHRva2VuICE9PSAndGhpcycgJiZcbiAgICAgIHRva2VuICE9PSBwYXJhbU5hbWUgJiZcbiAgICAgIGxhc3QgIT09ICcuJ1xuICB9XG5cbiAgLyoqXG4gICAqIEBwYXJhbSB7RXZlbnRUYXJnZXR8c3RyaW5nfSBlbHRcbiAgICogQHBhcmFtIHtzdHJpbmdbXX0gdG9rZW5zXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBwYXJhbU5hbWVcbiAgICogQHJldHVybnMge0NvbmRpdGlvbmFsRnVuY3Rpb258bnVsbH1cbiAgICovXG4gIGZ1bmN0aW9uIG1heWJlR2VuZXJhdGVDb25kaXRpb25hbChlbHQsIHRva2VucywgcGFyYW1OYW1lKSB7XG4gICAgaWYgKHRva2Vuc1swXSA9PT0gJ1snKSB7XG4gICAgICB0b2tlbnMuc2hpZnQoKVxuICAgICAgbGV0IGJyYWNrZXRDb3VudCA9IDFcbiAgICAgIGxldCBjb25kaXRpb25hbFNvdXJjZSA9ICcgcmV0dXJuIChmdW5jdGlvbignICsgcGFyYW1OYW1lICsgJyl7IHJldHVybiAoJ1xuICAgICAgbGV0IGxhc3QgPSBudWxsXG4gICAgICB3aGlsZSAodG9rZW5zLmxlbmd0aCA+IDApIHtcbiAgICAgICAgY29uc3QgdG9rZW4gPSB0b2tlbnNbMF1cbiAgICAgICAgLy8gQHRzLWlnbm9yZSBGb3Igc29tZSByZWFzb24gdHNjIGRvZXNuJ3QgdW5kZXJzdGFuZCB0aGUgc2hpZnQgY2FsbCwgYW5kIHRoaW5rcyB3ZSdyZSBjb21wYXJpbmcgdGhlIHNhbWUgdmFsdWUgaGVyZSwgaS5lLiAnWycgdnMgJ10nXG4gICAgICAgIGlmICh0b2tlbiA9PT0gJ10nKSB7XG4gICAgICAgICAgYnJhY2tldENvdW50LS1cbiAgICAgICAgICBpZiAoYnJhY2tldENvdW50ID09PSAwKSB7XG4gICAgICAgICAgICBpZiAobGFzdCA9PT0gbnVsbCkge1xuICAgICAgICAgICAgICBjb25kaXRpb25hbFNvdXJjZSA9IGNvbmRpdGlvbmFsU291cmNlICsgJ3RydWUnXG4gICAgICAgICAgICB9XG4gICAgICAgICAgICB0b2tlbnMuc2hpZnQoKVxuICAgICAgICAgICAgY29uZGl0aW9uYWxTb3VyY2UgKz0gJyl9KSdcbiAgICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICAgIGNvbnN0IGNvbmRpdGlvbkZ1bmN0aW9uID0gbWF5YmVFdmFsKGVsdCwgZnVuY3Rpb24oKSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuIEZ1bmN0aW9uKGNvbmRpdGlvbmFsU291cmNlKSgpXG4gICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgIGZ1bmN0aW9uKCkgeyByZXR1cm4gdHJ1ZSB9KVxuICAgICAgICAgICAgICBjb25kaXRpb25GdW5jdGlvbi5zb3VyY2UgPSBjb25kaXRpb25hbFNvdXJjZVxuICAgICAgICAgICAgICByZXR1cm4gY29uZGl0aW9uRnVuY3Rpb25cbiAgICAgICAgICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgICAgICAgICAgdHJpZ2dlckVycm9yRXZlbnQoZ2V0RG9jdW1lbnQoKS5ib2R5LCAnaHRteDpzeW50YXg6ZXJyb3InLCB7IGVycm9yOiBlLCBzb3VyY2U6IGNvbmRpdGlvbmFsU291cmNlIH0pXG4gICAgICAgICAgICAgIHJldHVybiBudWxsXG4gICAgICAgICAgICB9XG4gICAgICAgICAgfVxuICAgICAgICB9IGVsc2UgaWYgKHRva2VuID09PSAnWycpIHtcbiAgICAgICAgICBicmFja2V0Q291bnQrK1xuICAgICAgICB9XG4gICAgICAgIGlmIChpc1Bvc3NpYmxlUmVsYXRpdmVSZWZlcmVuY2UodG9rZW4sIGxhc3QsIHBhcmFtTmFtZSkpIHtcbiAgICAgICAgICBjb25kaXRpb25hbFNvdXJjZSArPSAnKCgnICsgcGFyYW1OYW1lICsgJy4nICsgdG9rZW4gKyAnKSA/ICgnICsgcGFyYW1OYW1lICsgJy4nICsgdG9rZW4gKyAnKSA6ICh3aW5kb3cuJyArIHRva2VuICsgJykpJ1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIGNvbmRpdGlvbmFsU291cmNlID0gY29uZGl0aW9uYWxTb3VyY2UgKyB0b2tlblxuICAgICAgICB9XG4gICAgICAgIGxhc3QgPSB0b2tlbnMuc2hpZnQoKVxuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBAcGFyYW0ge3N0cmluZ1tdfSB0b2tlbnNcbiAgICogQHBhcmFtIHtSZWdFeHB9IG1hdGNoXG4gICAqIEByZXR1cm5zIHtzdHJpbmd9XG4gICAqL1xuICBmdW5jdGlvbiBjb25zdW1lVW50aWwodG9rZW5zLCBtYXRjaCkge1xuICAgIGxldCByZXN1bHQgPSAnJ1xuICAgIHdoaWxlICh0b2tlbnMubGVuZ3RoID4gMCAmJiAhbWF0Y2gudGVzdCh0b2tlbnNbMF0pKSB7XG4gICAgICByZXN1bHQgKz0gdG9rZW5zLnNoaWZ0KClcbiAgICB9XG4gICAgcmV0dXJuIHJlc3VsdFxuICB9XG5cbiAgLyoqXG4gICAqIEBwYXJhbSB7c3RyaW5nW119IHRva2Vuc1xuICAgKiBAcmV0dXJucyB7c3RyaW5nfVxuICAgKi9cbiAgZnVuY3Rpb24gY29uc3VtZUNTU1NlbGVjdG9yKHRva2Vucykge1xuICAgIGxldCByZXN1bHRcbiAgICBpZiAodG9rZW5zLmxlbmd0aCA+IDAgJiYgQ09NQklORURfU0VMRUNUT1JfU1RBUlQudGVzdCh0b2tlbnNbMF0pKSB7XG4gICAgICB0b2tlbnMuc2hpZnQoKVxuICAgICAgcmVzdWx0ID0gY29uc3VtZVVudGlsKHRva2VucywgQ09NQklORURfU0VMRUNUT1JfRU5EKS50cmltKClcbiAgICAgIHRva2Vucy5zaGlmdCgpXG4gICAgfSBlbHNlIHtcbiAgICAgIHJlc3VsdCA9IGNvbnN1bWVVbnRpbCh0b2tlbnMsIFdISVRFU1BBQ0VfT1JfQ09NTUEpXG4gICAgfVxuICAgIHJldHVybiByZXN1bHRcbiAgfVxuXG4gIGNvbnN0IElOUFVUX1NFTEVDVE9SID0gJ2lucHV0LCB0ZXh0YXJlYSwgc2VsZWN0J1xuXG4gIC8qKlxuICAgKiBAcGFyYW0ge0VsZW1lbnR9IGVsdFxuICAgKiBAcGFyYW0ge3N0cmluZ30gZXhwbGljaXRUcmlnZ2VyXG4gICAqIEBwYXJhbSB7T2JqZWN0fSBjYWNoZSBmb3IgdHJpZ2dlciBzcGVjc1xuICAgKiBAcmV0dXJucyB7SHRteFRyaWdnZXJTcGVjaWZpY2F0aW9uW119XG4gICAqL1xuICBmdW5jdGlvbiBwYXJzZUFuZENhY2hlVHJpZ2dlcihlbHQsIGV4cGxpY2l0VHJpZ2dlciwgY2FjaGUpIHtcbiAgICAvKiogQHR5cGUgSHRteFRyaWdnZXJTcGVjaWZpY2F0aW9uW10gKi9cbiAgICBjb25zdCB0cmlnZ2VyU3BlY3MgPSBbXVxuICAgIGNvbnN0IHRva2VucyA9IHRva2VuaXplU3RyaW5nKGV4cGxpY2l0VHJpZ2dlcilcbiAgICBkbyB7XG4gICAgICBjb25zdW1lVW50aWwodG9rZW5zLCBOT1RfV0hJVEVTUEFDRSlcbiAgICAgIGNvbnN0IGluaXRpYWxMZW5ndGggPSB0b2tlbnMubGVuZ3RoXG4gICAgICBjb25zdCB0cmlnZ2VyID0gY29uc3VtZVVudGlsKHRva2VucywgL1ssXFxbXFxzXS8pXG4gICAgICBpZiAodHJpZ2dlciAhPT0gJycpIHtcbiAgICAgICAgaWYgKHRyaWdnZXIgPT09ICdldmVyeScpIHtcbiAgICAgICAgICAvKiogQHR5cGUgSHRteFRyaWdnZXJTcGVjaWZpY2F0aW9uICovXG4gICAgICAgICAgY29uc3QgZXZlcnkgPSB7IHRyaWdnZXI6ICdldmVyeScgfVxuICAgICAgICAgIGNvbnN1bWVVbnRpbCh0b2tlbnMsIE5PVF9XSElURVNQQUNFKVxuICAgICAgICAgIGV2ZXJ5LnBvbGxJbnRlcnZhbCA9IHBhcnNlSW50ZXJ2YWwoY29uc3VtZVVudGlsKHRva2VucywgL1ssXFxbXFxzXS8pKVxuICAgICAgICAgIGNvbnN1bWVVbnRpbCh0b2tlbnMsIE5PVF9XSElURVNQQUNFKVxuICAgICAgICAgIHZhciBldmVudEZpbHRlciA9IG1heWJlR2VuZXJhdGVDb25kaXRpb25hbChlbHQsIHRva2VucywgJ2V2ZW50JylcbiAgICAgICAgICBpZiAoZXZlbnRGaWx0ZXIpIHtcbiAgICAgICAgICAgIGV2ZXJ5LmV2ZW50RmlsdGVyID0gZXZlbnRGaWx0ZXJcbiAgICAgICAgICB9XG4gICAgICAgICAgdHJpZ2dlclNwZWNzLnB1c2goZXZlcnkpXG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgLyoqIEB0eXBlIEh0bXhUcmlnZ2VyU3BlY2lmaWNhdGlvbiAqL1xuICAgICAgICAgIGNvbnN0IHRyaWdnZXJTcGVjID0geyB0cmlnZ2VyIH1cbiAgICAgICAgICB2YXIgZXZlbnRGaWx0ZXIgPSBtYXliZUdlbmVyYXRlQ29uZGl0aW9uYWwoZWx0LCB0b2tlbnMsICdldmVudCcpXG4gICAgICAgICAgaWYgKGV2ZW50RmlsdGVyKSB7XG4gICAgICAgICAgICB0cmlnZ2VyU3BlYy5ldmVudEZpbHRlciA9IGV2ZW50RmlsdGVyXG4gICAgICAgICAgfVxuICAgICAgICAgIHdoaWxlICh0b2tlbnMubGVuZ3RoID4gMCAmJiB0b2tlbnNbMF0gIT09ICcsJykge1xuICAgICAgICAgICAgY29uc3VtZVVudGlsKHRva2VucywgTk9UX1dISVRFU1BBQ0UpXG4gICAgICAgICAgICBjb25zdCB0b2tlbiA9IHRva2Vucy5zaGlmdCgpXG4gICAgICAgICAgICBpZiAodG9rZW4gPT09ICdjaGFuZ2VkJykge1xuICAgICAgICAgICAgICB0cmlnZ2VyU3BlYy5jaGFuZ2VkID0gdHJ1ZVxuICAgICAgICAgICAgfSBlbHNlIGlmICh0b2tlbiA9PT0gJ29uY2UnKSB7XG4gICAgICAgICAgICAgIHRyaWdnZXJTcGVjLm9uY2UgPSB0cnVlXG4gICAgICAgICAgICB9IGVsc2UgaWYgKHRva2VuID09PSAnY29uc3VtZScpIHtcbiAgICAgICAgICAgICAgdHJpZ2dlclNwZWMuY29uc3VtZSA9IHRydWVcbiAgICAgICAgICAgIH0gZWxzZSBpZiAodG9rZW4gPT09ICdkZWxheScgJiYgdG9rZW5zWzBdID09PSAnOicpIHtcbiAgICAgICAgICAgICAgdG9rZW5zLnNoaWZ0KClcbiAgICAgICAgICAgICAgdHJpZ2dlclNwZWMuZGVsYXkgPSBwYXJzZUludGVydmFsKGNvbnN1bWVVbnRpbCh0b2tlbnMsIFdISVRFU1BBQ0VfT1JfQ09NTUEpKVxuICAgICAgICAgICAgfSBlbHNlIGlmICh0b2tlbiA9PT0gJ2Zyb20nICYmIHRva2Vuc1swXSA9PT0gJzonKSB7XG4gICAgICAgICAgICAgIHRva2Vucy5zaGlmdCgpXG4gICAgICAgICAgICAgIGlmIChDT01CSU5FRF9TRUxFQ1RPUl9TVEFSVC50ZXN0KHRva2Vuc1swXSkpIHtcbiAgICAgICAgICAgICAgICB2YXIgZnJvbV9hcmcgPSBjb25zdW1lQ1NTU2VsZWN0b3IodG9rZW5zKVxuICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIHZhciBmcm9tX2FyZyA9IGNvbnN1bWVVbnRpbCh0b2tlbnMsIFdISVRFU1BBQ0VfT1JfQ09NTUEpXG4gICAgICAgICAgICAgICAgaWYgKGZyb21fYXJnID09PSAnY2xvc2VzdCcgfHwgZnJvbV9hcmcgPT09ICdmaW5kJyB8fCBmcm9tX2FyZyA9PT0gJ25leHQnIHx8IGZyb21fYXJnID09PSAncHJldmlvdXMnKSB7XG4gICAgICAgICAgICAgICAgICB0b2tlbnMuc2hpZnQoKVxuICAgICAgICAgICAgICAgICAgY29uc3Qgc2VsZWN0b3IgPSBjb25zdW1lQ1NTU2VsZWN0b3IodG9rZW5zKVxuICAgICAgICAgICAgICAgICAgLy8gYG5leHRgIGFuZCBgcHJldmlvdXNgIGFsbG93IGEgc2VsZWN0b3ItbGVzcyBzeW50YXhcbiAgICAgICAgICAgICAgICAgIGlmIChzZWxlY3Rvci5sZW5ndGggPiAwKSB7XG4gICAgICAgICAgICAgICAgICAgIGZyb21fYXJnICs9ICcgJyArIHNlbGVjdG9yXG4gICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIHRyaWdnZXJTcGVjLmZyb20gPSBmcm9tX2FyZ1xuICAgICAgICAgICAgfSBlbHNlIGlmICh0b2tlbiA9PT0gJ3RhcmdldCcgJiYgdG9rZW5zWzBdID09PSAnOicpIHtcbiAgICAgICAgICAgICAgdG9rZW5zLnNoaWZ0KClcbiAgICAgICAgICAgICAgdHJpZ2dlclNwZWMudGFyZ2V0ID0gY29uc3VtZUNTU1NlbGVjdG9yKHRva2VucylcbiAgICAgICAgICAgIH0gZWxzZSBpZiAodG9rZW4gPT09ICd0aHJvdHRsZScgJiYgdG9rZW5zWzBdID09PSAnOicpIHtcbiAgICAgICAgICAgICAgdG9rZW5zLnNoaWZ0KClcbiAgICAgICAgICAgICAgdHJpZ2dlclNwZWMudGhyb3R0bGUgPSBwYXJzZUludGVydmFsKGNvbnN1bWVVbnRpbCh0b2tlbnMsIFdISVRFU1BBQ0VfT1JfQ09NTUEpKVxuICAgICAgICAgICAgfSBlbHNlIGlmICh0b2tlbiA9PT0gJ3F1ZXVlJyAmJiB0b2tlbnNbMF0gPT09ICc6Jykge1xuICAgICAgICAgICAgICB0b2tlbnMuc2hpZnQoKVxuICAgICAgICAgICAgICB0cmlnZ2VyU3BlYy5xdWV1ZSA9IGNvbnN1bWVVbnRpbCh0b2tlbnMsIFdISVRFU1BBQ0VfT1JfQ09NTUEpXG4gICAgICAgICAgICB9IGVsc2UgaWYgKHRva2VuID09PSAncm9vdCcgJiYgdG9rZW5zWzBdID09PSAnOicpIHtcbiAgICAgICAgICAgICAgdG9rZW5zLnNoaWZ0KClcbiAgICAgICAgICAgICAgdHJpZ2dlclNwZWNbdG9rZW5dID0gY29uc3VtZUNTU1NlbGVjdG9yKHRva2VucylcbiAgICAgICAgICAgIH0gZWxzZSBpZiAodG9rZW4gPT09ICd0aHJlc2hvbGQnICYmIHRva2Vuc1swXSA9PT0gJzonKSB7XG4gICAgICAgICAgICAgIHRva2Vucy5zaGlmdCgpXG4gICAgICAgICAgICAgIHRyaWdnZXJTcGVjW3Rva2VuXSA9IGNvbnN1bWVVbnRpbCh0b2tlbnMsIFdISVRFU1BBQ0VfT1JfQ09NTUEpXG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICB0cmlnZ2VyRXJyb3JFdmVudChlbHQsICdodG14OnN5bnRheDplcnJvcicsIHsgdG9rZW46IHRva2Vucy5zaGlmdCgpIH0pXG4gICAgICAgICAgICB9XG4gICAgICAgICAgfVxuICAgICAgICAgIHRyaWdnZXJTcGVjcy5wdXNoKHRyaWdnZXJTcGVjKVxuICAgICAgICB9XG4gICAgICB9XG4gICAgICBpZiAodG9rZW5zLmxlbmd0aCA9PT0gaW5pdGlhbExlbmd0aCkge1xuICAgICAgICB0cmlnZ2VyRXJyb3JFdmVudChlbHQsICdodG14OnN5bnRheDplcnJvcicsIHsgdG9rZW46IHRva2Vucy5zaGlmdCgpIH0pXG4gICAgICB9XG4gICAgICBjb25zdW1lVW50aWwodG9rZW5zLCBOT1RfV0hJVEVTUEFDRSlcbiAgICB9IHdoaWxlICh0b2tlbnNbMF0gPT09ICcsJyAmJiB0b2tlbnMuc2hpZnQoKSlcbiAgICBpZiAoY2FjaGUpIHtcbiAgICAgIGNhY2hlW2V4cGxpY2l0VHJpZ2dlcl0gPSB0cmlnZ2VyU3BlY3NcbiAgICB9XG4gICAgcmV0dXJuIHRyaWdnZXJTcGVjc1xuICB9XG5cbiAgLyoqXG4gICAqIEBwYXJhbSB7RWxlbWVudH0gZWx0XG4gICAqIEByZXR1cm5zIHtIdG14VHJpZ2dlclNwZWNpZmljYXRpb25bXX1cbiAgICovXG4gIGZ1bmN0aW9uIGdldFRyaWdnZXJTcGVjcyhlbHQpIHtcbiAgICBjb25zdCBleHBsaWNpdFRyaWdnZXIgPSBnZXRBdHRyaWJ1dGVWYWx1ZShlbHQsICdoeC10cmlnZ2VyJylcbiAgICBsZXQgdHJpZ2dlclNwZWNzID0gW11cbiAgICBpZiAoZXhwbGljaXRUcmlnZ2VyKSB7XG4gICAgICBjb25zdCBjYWNoZSA9IGh0bXguY29uZmlnLnRyaWdnZXJTcGVjc0NhY2hlXG4gICAgICB0cmlnZ2VyU3BlY3MgPSAoY2FjaGUgJiYgY2FjaGVbZXhwbGljaXRUcmlnZ2VyXSkgfHwgcGFyc2VBbmRDYWNoZVRyaWdnZXIoZWx0LCBleHBsaWNpdFRyaWdnZXIsIGNhY2hlKVxuICAgIH1cblxuICAgIGlmICh0cmlnZ2VyU3BlY3MubGVuZ3RoID4gMCkge1xuICAgICAgcmV0dXJuIHRyaWdnZXJTcGVjc1xuICAgIH0gZWxzZSBpZiAobWF0Y2hlcyhlbHQsICdmb3JtJykpIHtcbiAgICAgIHJldHVybiBbeyB0cmlnZ2VyOiAnc3VibWl0JyB9XVxuICAgIH0gZWxzZSBpZiAobWF0Y2hlcyhlbHQsICdpbnB1dFt0eXBlPVwiYnV0dG9uXCJdLCBpbnB1dFt0eXBlPVwic3VibWl0XCJdJykpIHtcbiAgICAgIHJldHVybiBbeyB0cmlnZ2VyOiAnY2xpY2snIH1dXG4gICAgfSBlbHNlIGlmIChtYXRjaGVzKGVsdCwgSU5QVVRfU0VMRUNUT1IpKSB7XG4gICAgICByZXR1cm4gW3sgdHJpZ2dlcjogJ2NoYW5nZScgfV1cbiAgICB9IGVsc2Uge1xuICAgICAgcmV0dXJuIFt7IHRyaWdnZXI6ICdjbGljaycgfV1cbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogQHBhcmFtIHtFbGVtZW50fSBlbHRcbiAgICovXG4gIGZ1bmN0aW9uIGNhbmNlbFBvbGxpbmcoZWx0KSB7XG4gICAgZ2V0SW50ZXJuYWxEYXRhKGVsdCkuY2FuY2VsbGVkID0gdHJ1ZVxuICB9XG5cbiAgLyoqXG4gICAqIEBwYXJhbSB7RWxlbWVudH0gZWx0XG4gICAqIEBwYXJhbSB7VHJpZ2dlckhhbmRsZXJ9IGhhbmRsZXJcbiAgICogQHBhcmFtIHtIdG14VHJpZ2dlclNwZWNpZmljYXRpb259IHNwZWNcbiAgICovXG4gIGZ1bmN0aW9uIHByb2Nlc3NQb2xsaW5nKGVsdCwgaGFuZGxlciwgc3BlYykge1xuICAgIGNvbnN0IG5vZGVEYXRhID0gZ2V0SW50ZXJuYWxEYXRhKGVsdClcbiAgICBub2RlRGF0YS50aW1lb3V0ID0gZ2V0V2luZG93KCkuc2V0VGltZW91dChmdW5jdGlvbigpIHtcbiAgICAgIGlmIChib2R5Q29udGFpbnMoZWx0KSAmJiBub2RlRGF0YS5jYW5jZWxsZWQgIT09IHRydWUpIHtcbiAgICAgICAgaWYgKCFtYXliZUZpbHRlckV2ZW50KHNwZWMsIGVsdCwgbWFrZUV2ZW50KCdoeDpwb2xsOnRyaWdnZXInLCB7XG4gICAgICAgICAgdHJpZ2dlclNwZWM6IHNwZWMsXG4gICAgICAgICAgdGFyZ2V0OiBlbHRcbiAgICAgICAgfSkpKSB7XG4gICAgICAgICAgaGFuZGxlcihlbHQpXG4gICAgICAgIH1cbiAgICAgICAgcHJvY2Vzc1BvbGxpbmcoZWx0LCBoYW5kbGVyLCBzcGVjKVxuICAgICAgfVxuICAgIH0sIHNwZWMucG9sbEludGVydmFsKVxuICB9XG5cbiAgLyoqXG4gICAqIEBwYXJhbSB7SFRNTEFuY2hvckVsZW1lbnR9IGVsdFxuICAgKiBAcmV0dXJucyB7Ym9vbGVhbn1cbiAgICovXG4gIGZ1bmN0aW9uIGlzTG9jYWxMaW5rKGVsdCkge1xuICAgIHJldHVybiBsb2NhdGlvbi5ob3N0bmFtZSA9PT0gZWx0Lmhvc3RuYW1lICYmXG4gICAgICBnZXRSYXdBdHRyaWJ1dGUoZWx0LCAnaHJlZicpICYmXG4gICAgICBnZXRSYXdBdHRyaWJ1dGUoZWx0LCAnaHJlZicpLmluZGV4T2YoJyMnKSAhPT0gMFxuICB9XG5cbiAgLyoqXG4gICAqIEBwYXJhbSB7RWxlbWVudH0gZWx0XG4gICAqL1xuICBmdW5jdGlvbiBlbHRJc0Rpc2FibGVkKGVsdCkge1xuICAgIHJldHVybiBjbG9zZXN0KGVsdCwgaHRteC5jb25maWcuZGlzYWJsZVNlbGVjdG9yKVxuICB9XG5cbiAgLyoqXG4gICAqIEBwYXJhbSB7RWxlbWVudH0gZWx0XG4gICAqIEBwYXJhbSB7SHRteE5vZGVJbnRlcm5hbERhdGF9IG5vZGVEYXRhXG4gICAqIEBwYXJhbSB7SHRteFRyaWdnZXJTcGVjaWZpY2F0aW9uW119IHRyaWdnZXJTcGVjc1xuICAgKi9cbiAgZnVuY3Rpb24gYm9vc3RFbGVtZW50KGVsdCwgbm9kZURhdGEsIHRyaWdnZXJTcGVjcykge1xuICAgIGlmICgoZWx0IGluc3RhbmNlb2YgSFRNTEFuY2hvckVsZW1lbnQgJiYgaXNMb2NhbExpbmsoZWx0KSAmJiAoZWx0LnRhcmdldCA9PT0gJycgfHwgZWx0LnRhcmdldCA9PT0gJ19zZWxmJykpIHx8IChlbHQudGFnTmFtZSA9PT0gJ0ZPUk0nICYmIFN0cmluZyhnZXRSYXdBdHRyaWJ1dGUoZWx0LCAnbWV0aG9kJykpLnRvTG93ZXJDYXNlKCkgIT09ICdkaWFsb2cnKSkge1xuICAgICAgbm9kZURhdGEuYm9vc3RlZCA9IHRydWVcbiAgICAgIGxldCB2ZXJiLCBwYXRoXG4gICAgICBpZiAoZWx0LnRhZ05hbWUgPT09ICdBJykge1xuICAgICAgICB2ZXJiID0gJ2dldCdcbiAgICAgICAgcGF0aCA9IGdldFJhd0F0dHJpYnV0ZShlbHQsICdocmVmJylcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIGNvbnN0IHJhd0F0dHJpYnV0ZSA9IGdldFJhd0F0dHJpYnV0ZShlbHQsICdtZXRob2QnKVxuICAgICAgICB2ZXJiID0gcmF3QXR0cmlidXRlID8gcmF3QXR0cmlidXRlLnRvTG93ZXJDYXNlKCkgOiAnZ2V0J1xuICAgICAgICBpZiAodmVyYiA9PT0gJ2dldCcpIHtcbiAgICAgICAgfVxuICAgICAgICBwYXRoID0gZ2V0UmF3QXR0cmlidXRlKGVsdCwgJ2FjdGlvbicpXG4gICAgICB9XG4gICAgICB0cmlnZ2VyU3BlY3MuZm9yRWFjaChmdW5jdGlvbih0cmlnZ2VyU3BlYykge1xuICAgICAgICBhZGRFdmVudExpc3RlbmVyKGVsdCwgZnVuY3Rpb24obm9kZSwgZXZ0KSB7XG4gICAgICAgICAgY29uc3QgZWx0ID0gYXNFbGVtZW50KG5vZGUpXG4gICAgICAgICAgaWYgKGVsdElzRGlzYWJsZWQoZWx0KSkge1xuICAgICAgICAgICAgY2xlYW5VcEVsZW1lbnQoZWx0KVxuICAgICAgICAgICAgcmV0dXJuXG4gICAgICAgICAgfVxuICAgICAgICAgIGlzc3VlQWpheFJlcXVlc3QodmVyYiwgcGF0aCwgZWx0LCBldnQpXG4gICAgICAgIH0sIG5vZGVEYXRhLCB0cmlnZ2VyU3BlYywgdHJ1ZSlcbiAgICAgIH0pXG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIEBwYXJhbSB7RXZlbnR9IGV2dFxuICAgKiBAcGFyYW0ge05vZGV9IG5vZGVcbiAgICogQHJldHVybnMge2Jvb2xlYW59XG4gICAqL1xuICBmdW5jdGlvbiBzaG91bGRDYW5jZWwoZXZ0LCBub2RlKSB7XG4gICAgY29uc3QgZWx0ID0gYXNFbGVtZW50KG5vZGUpXG4gICAgaWYgKCFlbHQpIHtcbiAgICAgIHJldHVybiBmYWxzZVxuICAgIH1cbiAgICBpZiAoZXZ0LnR5cGUgPT09ICdzdWJtaXQnIHx8IGV2dC50eXBlID09PSAnY2xpY2snKSB7XG4gICAgICBpZiAoZWx0LnRhZ05hbWUgPT09ICdGT1JNJykge1xuICAgICAgICByZXR1cm4gdHJ1ZVxuICAgICAgfVxuICAgICAgaWYgKG1hdGNoZXMoZWx0LCAnaW5wdXRbdHlwZT1cInN1Ym1pdFwiXSwgYnV0dG9uJykgJiYgY2xvc2VzdChlbHQsICdmb3JtJykgIT09IG51bGwpIHtcbiAgICAgICAgcmV0dXJuIHRydWVcbiAgICAgIH1cbiAgICAgIGlmIChlbHQgaW5zdGFuY2VvZiBIVE1MQW5jaG9yRWxlbWVudCAmJiBlbHQuaHJlZiAmJlxuICAgICAgICAoZWx0LmdldEF0dHJpYnV0ZSgnaHJlZicpID09PSAnIycgfHwgZWx0LmdldEF0dHJpYnV0ZSgnaHJlZicpLmluZGV4T2YoJyMnKSAhPT0gMCkpIHtcbiAgICAgICAgcmV0dXJuIHRydWVcbiAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIGZhbHNlXG4gIH1cblxuICAvKipcbiAgICogQHBhcmFtIHtOb2RlfSBlbHRcbiAgICogQHBhcmFtIHtFdmVudHxNb3VzZUV2ZW50fEtleWJvYXJkRXZlbnR8VG91Y2hFdmVudH0gZXZ0XG4gICAqIEByZXR1cm5zIHtib29sZWFufVxuICAgKi9cbiAgZnVuY3Rpb24gaWdub3JlQm9vc3RlZEFuY2hvckN0cmxDbGljayhlbHQsIGV2dCkge1xuICAgIHJldHVybiBnZXRJbnRlcm5hbERhdGEoZWx0KS5ib29zdGVkICYmIGVsdCBpbnN0YW5jZW9mIEhUTUxBbmNob3JFbGVtZW50ICYmIGV2dC50eXBlID09PSAnY2xpY2snICYmXG4gICAgICAvLyBAdHMtaWdub3JlIHRoaXMgd2lsbCByZXNvbHZlIHRvIHVuZGVmaW5lZCBmb3IgZXZlbnRzIHRoYXQgZG9uJ3QgZGVmaW5lIHRob3NlIHByb3BlcnRpZXMsIHdoaWNoIGlzIGZpbmVcbiAgICAgIChldnQuY3RybEtleSB8fCBldnQubWV0YUtleSlcbiAgfVxuXG4gIC8qKlxuICAgKiBAcGFyYW0ge0h0bXhUcmlnZ2VyU3BlY2lmaWNhdGlvbn0gdHJpZ2dlclNwZWNcbiAgICogQHBhcmFtIHtOb2RlfSBlbHRcbiAgICogQHBhcmFtIHtFdmVudH0gZXZ0XG4gICAqIEByZXR1cm5zIHtib29sZWFufVxuICAgKi9cbiAgZnVuY3Rpb24gbWF5YmVGaWx0ZXJFdmVudCh0cmlnZ2VyU3BlYywgZWx0LCBldnQpIHtcbiAgICBjb25zdCBldmVudEZpbHRlciA9IHRyaWdnZXJTcGVjLmV2ZW50RmlsdGVyXG4gICAgaWYgKGV2ZW50RmlsdGVyKSB7XG4gICAgICB0cnkge1xuICAgICAgICByZXR1cm4gZXZlbnRGaWx0ZXIuY2FsbChlbHQsIGV2dCkgIT09IHRydWVcbiAgICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgICAgY29uc3Qgc291cmNlID0gZXZlbnRGaWx0ZXIuc291cmNlXG4gICAgICAgIHRyaWdnZXJFcnJvckV2ZW50KGdldERvY3VtZW50KCkuYm9keSwgJ2h0bXg6ZXZlbnRGaWx0ZXI6ZXJyb3InLCB7IGVycm9yOiBlLCBzb3VyY2UgfSlcbiAgICAgICAgcmV0dXJuIHRydWVcbiAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIGZhbHNlXG4gIH1cblxuICAvKipcbiAgICogQHBhcmFtIHtOb2RlfSBlbHRcbiAgICogQHBhcmFtIHtUcmlnZ2VySGFuZGxlcn0gaGFuZGxlclxuICAgKiBAcGFyYW0ge0h0bXhOb2RlSW50ZXJuYWxEYXRhfSBub2RlRGF0YVxuICAgKiBAcGFyYW0ge0h0bXhUcmlnZ2VyU3BlY2lmaWNhdGlvbn0gdHJpZ2dlclNwZWNcbiAgICogQHBhcmFtIHtib29sZWFufSBbZXhwbGljaXRDYW5jZWxdXG4gICAqL1xuICBmdW5jdGlvbiBhZGRFdmVudExpc3RlbmVyKGVsdCwgaGFuZGxlciwgbm9kZURhdGEsIHRyaWdnZXJTcGVjLCBleHBsaWNpdENhbmNlbCkge1xuICAgIGNvbnN0IGVsZW1lbnREYXRhID0gZ2V0SW50ZXJuYWxEYXRhKGVsdClcbiAgICAvKiogQHR5cGUgeyhOb2RlfFdpbmRvdylbXX0gKi9cbiAgICBsZXQgZWx0c1RvTGlzdGVuT25cbiAgICBpZiAodHJpZ2dlclNwZWMuZnJvbSkge1xuICAgICAgZWx0c1RvTGlzdGVuT24gPSBxdWVyeVNlbGVjdG9yQWxsRXh0KGVsdCwgdHJpZ2dlclNwZWMuZnJvbSlcbiAgICB9IGVsc2Uge1xuICAgICAgZWx0c1RvTGlzdGVuT24gPSBbZWx0XVxuICAgIH1cbiAgICAvLyBzdG9yZSB0aGUgaW5pdGlhbCB2YWx1ZXMgb2YgdGhlIGVsZW1lbnRzLCBzbyB3ZSBjYW4gdGVsbCBpZiB0aGV5IGNoYW5nZVxuICAgIGlmICh0cmlnZ2VyU3BlYy5jaGFuZ2VkKSB7XG4gICAgICBlbHRzVG9MaXN0ZW5Pbi5mb3JFYWNoKGZ1bmN0aW9uKGVsdFRvTGlzdGVuT24pIHtcbiAgICAgICAgY29uc3QgZWx0VG9MaXN0ZW5PbkRhdGEgPSBnZXRJbnRlcm5hbERhdGEoZWx0VG9MaXN0ZW5PbilcbiAgICAgICAgLy8gQHRzLWlnbm9yZSB2YWx1ZSB3aWxsIGJlIHVuZGVmaW5lZCBmb3Igbm9uLWlucHV0IGVsZW1lbnRzLCB3aGljaCBpcyBmaW5lXG4gICAgICAgIGVsdFRvTGlzdGVuT25EYXRhLmxhc3RWYWx1ZSA9IGVsdFRvTGlzdGVuT24udmFsdWVcbiAgICAgIH0pXG4gICAgfVxuICAgIGZvckVhY2goZWx0c1RvTGlzdGVuT24sIGZ1bmN0aW9uKGVsdFRvTGlzdGVuT24pIHtcbiAgICAgIC8qKiBAdHlwZSBFdmVudExpc3RlbmVyICovXG4gICAgICBjb25zdCBldmVudExpc3RlbmVyID0gZnVuY3Rpb24oZXZ0KSB7XG4gICAgICAgIGlmICghYm9keUNvbnRhaW5zKGVsdCkpIHtcbiAgICAgICAgICBlbHRUb0xpc3Rlbk9uLnJlbW92ZUV2ZW50TGlzdGVuZXIodHJpZ2dlclNwZWMudHJpZ2dlciwgZXZlbnRMaXN0ZW5lcilcbiAgICAgICAgICByZXR1cm5cbiAgICAgICAgfVxuICAgICAgICBpZiAoaWdub3JlQm9vc3RlZEFuY2hvckN0cmxDbGljayhlbHQsIGV2dCkpIHtcbiAgICAgICAgICByZXR1cm5cbiAgICAgICAgfVxuICAgICAgICBpZiAoZXhwbGljaXRDYW5jZWwgfHwgc2hvdWxkQ2FuY2VsKGV2dCwgZWx0KSkge1xuICAgICAgICAgIGV2dC5wcmV2ZW50RGVmYXVsdCgpXG4gICAgICAgIH1cbiAgICAgICAgaWYgKG1heWJlRmlsdGVyRXZlbnQodHJpZ2dlclNwZWMsIGVsdCwgZXZ0KSkge1xuICAgICAgICAgIHJldHVyblxuICAgICAgICB9XG4gICAgICAgIGNvbnN0IGV2ZW50RGF0YSA9IGdldEludGVybmFsRGF0YShldnQpXG4gICAgICAgIGV2ZW50RGF0YS50cmlnZ2VyU3BlYyA9IHRyaWdnZXJTcGVjXG4gICAgICAgIGlmIChldmVudERhdGEuaGFuZGxlZEZvciA9PSBudWxsKSB7XG4gICAgICAgICAgZXZlbnREYXRhLmhhbmRsZWRGb3IgPSBbXVxuICAgICAgICB9XG4gICAgICAgIGlmIChldmVudERhdGEuaGFuZGxlZEZvci5pbmRleE9mKGVsdCkgPCAwKSB7XG4gICAgICAgICAgZXZlbnREYXRhLmhhbmRsZWRGb3IucHVzaChlbHQpXG4gICAgICAgICAgaWYgKHRyaWdnZXJTcGVjLmNvbnN1bWUpIHtcbiAgICAgICAgICAgIGV2dC5zdG9wUHJvcGFnYXRpb24oKVxuICAgICAgICAgIH1cbiAgICAgICAgICBpZiAodHJpZ2dlclNwZWMudGFyZ2V0ICYmIGV2dC50YXJnZXQpIHtcbiAgICAgICAgICAgIGlmICghbWF0Y2hlcyhhc0VsZW1lbnQoZXZ0LnRhcmdldCksIHRyaWdnZXJTcGVjLnRhcmdldCkpIHtcbiAgICAgICAgICAgICAgcmV0dXJuXG4gICAgICAgICAgICB9XG4gICAgICAgICAgfVxuICAgICAgICAgIGlmICh0cmlnZ2VyU3BlYy5vbmNlKSB7XG4gICAgICAgICAgICBpZiAoZWxlbWVudERhdGEudHJpZ2dlcmVkT25jZSkge1xuICAgICAgICAgICAgICByZXR1cm5cbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgIGVsZW1lbnREYXRhLnRyaWdnZXJlZE9uY2UgPSB0cnVlXG4gICAgICAgICAgICB9XG4gICAgICAgICAgfVxuICAgICAgICAgIGlmICh0cmlnZ2VyU3BlYy5jaGFuZ2VkKSB7XG4gICAgICAgICAgICBjb25zdCBlbHRUb0xpc3Rlbk9uRGF0YSA9IGdldEludGVybmFsRGF0YShlbHRUb0xpc3Rlbk9uKVxuICAgICAgICAgICAgLy8gQHRzLWlnbm9yZSB2YWx1ZSB3aWxsIGJlIHVuZGVmaW5lZCBmb3Igbm9uLWlucHV0IGVsZW1lbnRzLCB3aGljaCBpcyBmaW5lXG4gICAgICAgICAgICBjb25zdCB2YWx1ZSA9IGVsdFRvTGlzdGVuT24udmFsdWVcbiAgICAgICAgICAgIGlmIChlbHRUb0xpc3Rlbk9uRGF0YS5sYXN0VmFsdWUgPT09IHZhbHVlKSB7XG4gICAgICAgICAgICAgIHJldHVyblxuICAgICAgICAgICAgfVxuICAgICAgICAgICAgZWx0VG9MaXN0ZW5PbkRhdGEubGFzdFZhbHVlID0gdmFsdWVcbiAgICAgICAgICB9XG4gICAgICAgICAgaWYgKGVsZW1lbnREYXRhLmRlbGF5ZWQpIHtcbiAgICAgICAgICAgIGNsZWFyVGltZW91dChlbGVtZW50RGF0YS5kZWxheWVkKVxuICAgICAgICAgIH1cbiAgICAgICAgICBpZiAoZWxlbWVudERhdGEudGhyb3R0bGUpIHtcbiAgICAgICAgICAgIHJldHVyblxuICAgICAgICAgIH1cblxuICAgICAgICAgIGlmICh0cmlnZ2VyU3BlYy50aHJvdHRsZSA+IDApIHtcbiAgICAgICAgICAgIGlmICghZWxlbWVudERhdGEudGhyb3R0bGUpIHtcbiAgICAgICAgICAgICAgdHJpZ2dlckV2ZW50KGVsdCwgJ2h0bXg6dHJpZ2dlcicpXG4gICAgICAgICAgICAgIGhhbmRsZXIoZWx0LCBldnQpXG4gICAgICAgICAgICAgIGVsZW1lbnREYXRhLnRocm90dGxlID0gZ2V0V2luZG93KCkuc2V0VGltZW91dChmdW5jdGlvbigpIHtcbiAgICAgICAgICAgICAgICBlbGVtZW50RGF0YS50aHJvdHRsZSA9IG51bGxcbiAgICAgICAgICAgICAgfSwgdHJpZ2dlclNwZWMudGhyb3R0bGUpXG4gICAgICAgICAgICB9XG4gICAgICAgICAgfSBlbHNlIGlmICh0cmlnZ2VyU3BlYy5kZWxheSA+IDApIHtcbiAgICAgICAgICAgIGVsZW1lbnREYXRhLmRlbGF5ZWQgPSBnZXRXaW5kb3coKS5zZXRUaW1lb3V0KGZ1bmN0aW9uKCkge1xuICAgICAgICAgICAgICB0cmlnZ2VyRXZlbnQoZWx0LCAnaHRteDp0cmlnZ2VyJylcbiAgICAgICAgICAgICAgaGFuZGxlcihlbHQsIGV2dClcbiAgICAgICAgICAgIH0sIHRyaWdnZXJTcGVjLmRlbGF5KVxuICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICB0cmlnZ2VyRXZlbnQoZWx0LCAnaHRteDp0cmlnZ2VyJylcbiAgICAgICAgICAgIGhhbmRsZXIoZWx0LCBldnQpXG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9XG4gICAgICBpZiAobm9kZURhdGEubGlzdGVuZXJJbmZvcyA9PSBudWxsKSB7XG4gICAgICAgIG5vZGVEYXRhLmxpc3RlbmVySW5mb3MgPSBbXVxuICAgICAgfVxuICAgICAgbm9kZURhdGEubGlzdGVuZXJJbmZvcy5wdXNoKHtcbiAgICAgICAgdHJpZ2dlcjogdHJpZ2dlclNwZWMudHJpZ2dlcixcbiAgICAgICAgbGlzdGVuZXI6IGV2ZW50TGlzdGVuZXIsXG4gICAgICAgIG9uOiBlbHRUb0xpc3Rlbk9uXG4gICAgICB9KVxuICAgICAgZWx0VG9MaXN0ZW5Pbi5hZGRFdmVudExpc3RlbmVyKHRyaWdnZXJTcGVjLnRyaWdnZXIsIGV2ZW50TGlzdGVuZXIpXG4gICAgfSlcbiAgfVxuXG4gIGxldCB3aW5kb3dJc1Njcm9sbGluZyA9IGZhbHNlIC8vIHVzZWQgYnkgaW5pdFNjcm9sbEhhbmRsZXJcbiAgbGV0IHNjcm9sbEhhbmRsZXIgPSBudWxsXG4gIGZ1bmN0aW9uIGluaXRTY3JvbGxIYW5kbGVyKCkge1xuICAgIGlmICghc2Nyb2xsSGFuZGxlcikge1xuICAgICAgc2Nyb2xsSGFuZGxlciA9IGZ1bmN0aW9uKCkge1xuICAgICAgICB3aW5kb3dJc1Njcm9sbGluZyA9IHRydWVcbiAgICAgIH1cbiAgICAgIHdpbmRvdy5hZGRFdmVudExpc3RlbmVyKCdzY3JvbGwnLCBzY3JvbGxIYW5kbGVyKVxuICAgICAgc2V0SW50ZXJ2YWwoZnVuY3Rpb24oKSB7XG4gICAgICAgIGlmICh3aW5kb3dJc1Njcm9sbGluZykge1xuICAgICAgICAgIHdpbmRvd0lzU2Nyb2xsaW5nID0gZmFsc2VcbiAgICAgICAgICBmb3JFYWNoKGdldERvY3VtZW50KCkucXVlcnlTZWxlY3RvckFsbChcIltoeC10cmlnZ2VyKj0ncmV2ZWFsZWQnXSxbZGF0YS1oeC10cmlnZ2VyKj0ncmV2ZWFsZWQnXVwiKSwgZnVuY3Rpb24oZWx0KSB7XG4gICAgICAgICAgICBtYXliZVJldmVhbChlbHQpXG4gICAgICAgICAgfSlcbiAgICAgICAgfVxuICAgICAgfSwgMjAwKVxuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBAcGFyYW0ge0VsZW1lbnR9IGVsdFxuICAgKi9cbiAgZnVuY3Rpb24gbWF5YmVSZXZlYWwoZWx0KSB7XG4gICAgaWYgKCFoYXNBdHRyaWJ1dGUoZWx0LCAnZGF0YS1oeC1yZXZlYWxlZCcpICYmIGlzU2Nyb2xsZWRJbnRvVmlldyhlbHQpKSB7XG4gICAgICBlbHQuc2V0QXR0cmlidXRlKCdkYXRhLWh4LXJldmVhbGVkJywgJ3RydWUnKVxuICAgICAgY29uc3Qgbm9kZURhdGEgPSBnZXRJbnRlcm5hbERhdGEoZWx0KVxuICAgICAgaWYgKG5vZGVEYXRhLmluaXRIYXNoKSB7XG4gICAgICAgIHRyaWdnZXJFdmVudChlbHQsICdyZXZlYWxlZCcpXG4gICAgICB9IGVsc2Uge1xuICAgICAgICAvLyBpZiB0aGUgbm9kZSBpc24ndCBpbml0aWFsaXplZCwgd2FpdCBmb3IgaXQgYmVmb3JlIHRyaWdnZXJpbmcgdGhlIHJlcXVlc3RcbiAgICAgICAgZWx0LmFkZEV2ZW50TGlzdGVuZXIoJ2h0bXg6YWZ0ZXJQcm9jZXNzTm9kZScsIGZ1bmN0aW9uKCkgeyB0cmlnZ2VyRXZlbnQoZWx0LCAncmV2ZWFsZWQnKSB9LCB7IG9uY2U6IHRydWUgfSlcbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICAvLz0gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuXG4gIC8qKlxuICAgKiBAcGFyYW0ge0VsZW1lbnR9IGVsdFxuICAgKiBAcGFyYW0ge1RyaWdnZXJIYW5kbGVyfSBoYW5kbGVyXG4gICAqIEBwYXJhbSB7SHRteE5vZGVJbnRlcm5hbERhdGF9IG5vZGVEYXRhXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBkZWxheVxuICAgKi9cbiAgZnVuY3Rpb24gbG9hZEltbWVkaWF0ZWx5KGVsdCwgaGFuZGxlciwgbm9kZURhdGEsIGRlbGF5KSB7XG4gICAgY29uc3QgbG9hZCA9IGZ1bmN0aW9uKCkge1xuICAgICAgaWYgKCFub2RlRGF0YS5sb2FkZWQpIHtcbiAgICAgICAgbm9kZURhdGEubG9hZGVkID0gdHJ1ZVxuICAgICAgICBoYW5kbGVyKGVsdClcbiAgICAgIH1cbiAgICB9XG4gICAgaWYgKGRlbGF5ID4gMCkge1xuICAgICAgZ2V0V2luZG93KCkuc2V0VGltZW91dChsb2FkLCBkZWxheSlcbiAgICB9IGVsc2Uge1xuICAgICAgbG9hZCgpXG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIEBwYXJhbSB7RWxlbWVudH0gZWx0XG4gICAqIEBwYXJhbSB7SHRteE5vZGVJbnRlcm5hbERhdGF9IG5vZGVEYXRhXG4gICAqIEBwYXJhbSB7SHRteFRyaWdnZXJTcGVjaWZpY2F0aW9uW119IHRyaWdnZXJTcGVjc1xuICAgKiBAcmV0dXJucyB7Ym9vbGVhbn1cbiAgICovXG4gIGZ1bmN0aW9uIHByb2Nlc3NWZXJicyhlbHQsIG5vZGVEYXRhLCB0cmlnZ2VyU3BlY3MpIHtcbiAgICBsZXQgZXhwbGljaXRBY3Rpb24gPSBmYWxzZVxuICAgIGZvckVhY2goVkVSQlMsIGZ1bmN0aW9uKHZlcmIpIHtcbiAgICAgIGlmIChoYXNBdHRyaWJ1dGUoZWx0LCAnaHgtJyArIHZlcmIpKSB7XG4gICAgICAgIGNvbnN0IHBhdGggPSBnZXRBdHRyaWJ1dGVWYWx1ZShlbHQsICdoeC0nICsgdmVyYilcbiAgICAgICAgZXhwbGljaXRBY3Rpb24gPSB0cnVlXG4gICAgICAgIG5vZGVEYXRhLnBhdGggPSBwYXRoXG4gICAgICAgIG5vZGVEYXRhLnZlcmIgPSB2ZXJiXG4gICAgICAgIHRyaWdnZXJTcGVjcy5mb3JFYWNoKGZ1bmN0aW9uKHRyaWdnZXJTcGVjKSB7XG4gICAgICAgICAgYWRkVHJpZ2dlckhhbmRsZXIoZWx0LCB0cmlnZ2VyU3BlYywgbm9kZURhdGEsIGZ1bmN0aW9uKG5vZGUsIGV2dCkge1xuICAgICAgICAgICAgY29uc3QgZWx0ID0gYXNFbGVtZW50KG5vZGUpXG4gICAgICAgICAgICBpZiAoY2xvc2VzdChlbHQsIGh0bXguY29uZmlnLmRpc2FibGVTZWxlY3RvcikpIHtcbiAgICAgICAgICAgICAgY2xlYW5VcEVsZW1lbnQoZWx0KVxuICAgICAgICAgICAgICByZXR1cm5cbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGlzc3VlQWpheFJlcXVlc3QodmVyYiwgcGF0aCwgZWx0LCBldnQpXG4gICAgICAgICAgfSlcbiAgICAgICAgfSlcbiAgICAgIH1cbiAgICB9KVxuICAgIHJldHVybiBleHBsaWNpdEFjdGlvblxuICB9XG5cbiAgLyoqXG4gICAqIEBjYWxsYmFjayBUcmlnZ2VySGFuZGxlclxuICAgKiBAcGFyYW0ge05vZGV9IGVsdFxuICAgKiBAcGFyYW0ge0V2ZW50fSBbZXZ0XVxuICAgKi9cblxuICAvKipcbiAgICogQHBhcmFtIHtOb2RlfSBlbHRcbiAgICogQHBhcmFtIHtIdG14VHJpZ2dlclNwZWNpZmljYXRpb259IHRyaWdnZXJTcGVjXG4gICAqIEBwYXJhbSB7SHRteE5vZGVJbnRlcm5hbERhdGF9IG5vZGVEYXRhXG4gICAqIEBwYXJhbSB7VHJpZ2dlckhhbmRsZXJ9IGhhbmRsZXJcbiAgICovXG4gIGZ1bmN0aW9uIGFkZFRyaWdnZXJIYW5kbGVyKGVsdCwgdHJpZ2dlclNwZWMsIG5vZGVEYXRhLCBoYW5kbGVyKSB7XG4gICAgaWYgKHRyaWdnZXJTcGVjLnRyaWdnZXIgPT09ICdyZXZlYWxlZCcpIHtcbiAgICAgIGluaXRTY3JvbGxIYW5kbGVyKClcbiAgICAgIGFkZEV2ZW50TGlzdGVuZXIoZWx0LCBoYW5kbGVyLCBub2RlRGF0YSwgdHJpZ2dlclNwZWMpXG4gICAgICBtYXliZVJldmVhbChhc0VsZW1lbnQoZWx0KSlcbiAgICB9IGVsc2UgaWYgKHRyaWdnZXJTcGVjLnRyaWdnZXIgPT09ICdpbnRlcnNlY3QnKSB7XG4gICAgICBjb25zdCBvYnNlcnZlck9wdGlvbnMgPSB7fVxuICAgICAgaWYgKHRyaWdnZXJTcGVjLnJvb3QpIHtcbiAgICAgICAgb2JzZXJ2ZXJPcHRpb25zLnJvb3QgPSBxdWVyeVNlbGVjdG9yRXh0KGVsdCwgdHJpZ2dlclNwZWMucm9vdClcbiAgICAgIH1cbiAgICAgIGlmICh0cmlnZ2VyU3BlYy50aHJlc2hvbGQpIHtcbiAgICAgICAgb2JzZXJ2ZXJPcHRpb25zLnRocmVzaG9sZCA9IHBhcnNlRmxvYXQodHJpZ2dlclNwZWMudGhyZXNob2xkKVxuICAgICAgfVxuICAgICAgY29uc3Qgb2JzZXJ2ZXIgPSBuZXcgSW50ZXJzZWN0aW9uT2JzZXJ2ZXIoZnVuY3Rpb24oZW50cmllcykge1xuICAgICAgICBmb3IgKGxldCBpID0gMDsgaSA8IGVudHJpZXMubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgICBjb25zdCBlbnRyeSA9IGVudHJpZXNbaV1cbiAgICAgICAgICBpZiAoZW50cnkuaXNJbnRlcnNlY3RpbmcpIHtcbiAgICAgICAgICAgIHRyaWdnZXJFdmVudChlbHQsICdpbnRlcnNlY3QnKVxuICAgICAgICAgICAgYnJlYWtcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIH0sIG9ic2VydmVyT3B0aW9ucylcbiAgICAgIG9ic2VydmVyLm9ic2VydmUoYXNFbGVtZW50KGVsdCkpXG4gICAgICBhZGRFdmVudExpc3RlbmVyKGFzRWxlbWVudChlbHQpLCBoYW5kbGVyLCBub2RlRGF0YSwgdHJpZ2dlclNwZWMpXG4gICAgfSBlbHNlIGlmICh0cmlnZ2VyU3BlYy50cmlnZ2VyID09PSAnbG9hZCcpIHtcbiAgICAgIGlmICghbWF5YmVGaWx0ZXJFdmVudCh0cmlnZ2VyU3BlYywgZWx0LCBtYWtlRXZlbnQoJ2xvYWQnLCB7IGVsdCB9KSkpIHtcbiAgICAgICAgbG9hZEltbWVkaWF0ZWx5KGFzRWxlbWVudChlbHQpLCBoYW5kbGVyLCBub2RlRGF0YSwgdHJpZ2dlclNwZWMuZGVsYXkpXG4gICAgICB9XG4gICAgfSBlbHNlIGlmICh0cmlnZ2VyU3BlYy5wb2xsSW50ZXJ2YWwgPiAwKSB7XG4gICAgICBub2RlRGF0YS5wb2xsaW5nID0gdHJ1ZVxuICAgICAgcHJvY2Vzc1BvbGxpbmcoYXNFbGVtZW50KGVsdCksIGhhbmRsZXIsIHRyaWdnZXJTcGVjKVxuICAgIH0gZWxzZSB7XG4gICAgICBhZGRFdmVudExpc3RlbmVyKGVsdCwgaGFuZGxlciwgbm9kZURhdGEsIHRyaWdnZXJTcGVjKVxuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBAcGFyYW0ge05vZGV9IG5vZGVcbiAgICogQHJldHVybnMge2Jvb2xlYW59XG4gICAqL1xuICBmdW5jdGlvbiBzaG91bGRQcm9jZXNzSHhPbihub2RlKSB7XG4gICAgY29uc3QgZWx0ID0gYXNFbGVtZW50KG5vZGUpXG4gICAgaWYgKCFlbHQpIHtcbiAgICAgIHJldHVybiBmYWxzZVxuICAgIH1cbiAgICBjb25zdCBhdHRyaWJ1dGVzID0gZWx0LmF0dHJpYnV0ZXNcbiAgICBmb3IgKGxldCBqID0gMDsgaiA8IGF0dHJpYnV0ZXMubGVuZ3RoOyBqKyspIHtcbiAgICAgIGNvbnN0IGF0dHJOYW1lID0gYXR0cmlidXRlc1tqXS5uYW1lXG4gICAgICBpZiAoc3RhcnRzV2l0aChhdHRyTmFtZSwgJ2h4LW9uOicpIHx8IHN0YXJ0c1dpdGgoYXR0ck5hbWUsICdkYXRhLWh4LW9uOicpIHx8XG4gICAgICAgIHN0YXJ0c1dpdGgoYXR0ck5hbWUsICdoeC1vbi0nKSB8fCBzdGFydHNXaXRoKGF0dHJOYW1lLCAnZGF0YS1oeC1vbi0nKSkge1xuICAgICAgICByZXR1cm4gdHJ1ZVxuICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4gZmFsc2VcbiAgfVxuXG4gIC8qKlxuICAgKiBAcGFyYW0ge05vZGV9IGVsdFxuICAgKiBAcmV0dXJucyB7RWxlbWVudFtdfVxuICAgKi9cbiAgY29uc3QgSFhfT05fUVVFUlkgPSBuZXcgWFBhdGhFdmFsdWF0b3IoKVxuICAgIC5jcmVhdGVFeHByZXNzaW9uKCcuLy8qW0AqWyBzdGFydHMtd2l0aChuYW1lKCksIFwiaHgtb246XCIpIG9yIHN0YXJ0cy13aXRoKG5hbWUoKSwgXCJkYXRhLWh4LW9uOlwiKSBvcicgK1xuICAgICAgJyBzdGFydHMtd2l0aChuYW1lKCksIFwiaHgtb24tXCIpIG9yIHN0YXJ0cy13aXRoKG5hbWUoKSwgXCJkYXRhLWh4LW9uLVwiKSBdXScpXG5cbiAgZnVuY3Rpb24gcHJvY2Vzc0hYT25Sb290KGVsdCwgZWxlbWVudHMpIHtcbiAgICBpZiAoc2hvdWxkUHJvY2Vzc0h4T24oZWx0KSkge1xuICAgICAgZWxlbWVudHMucHVzaChhc0VsZW1lbnQoZWx0KSlcbiAgICB9XG4gICAgY29uc3QgaXRlciA9IEhYX09OX1FVRVJZLmV2YWx1YXRlKGVsdClcbiAgICBsZXQgbm9kZSA9IG51bGxcbiAgICB3aGlsZSAobm9kZSA9IGl0ZXIuaXRlcmF0ZU5leHQoKSkgZWxlbWVudHMucHVzaChhc0VsZW1lbnQobm9kZSkpXG4gIH1cblxuICBmdW5jdGlvbiBmaW5kSHhPbldpbGRjYXJkRWxlbWVudHMoZWx0KSB7XG4gICAgLyoqIEB0eXBlIHtFbGVtZW50W119ICovXG4gICAgY29uc3QgZWxlbWVudHMgPSBbXVxuICAgIGlmIChlbHQgaW5zdGFuY2VvZiBEb2N1bWVudEZyYWdtZW50KSB7XG4gICAgICBmb3IgKGNvbnN0IGNoaWxkIG9mIGVsdC5jaGlsZE5vZGVzKSB7XG4gICAgICAgIHByb2Nlc3NIWE9uUm9vdChjaGlsZCwgZWxlbWVudHMpXG4gICAgICB9XG4gICAgfSBlbHNlIHtcbiAgICAgIHByb2Nlc3NIWE9uUm9vdChlbHQsIGVsZW1lbnRzKVxuICAgIH1cbiAgICByZXR1cm4gZWxlbWVudHNcbiAgfVxuXG4gIC8qKlxuICAgKiBAcGFyYW0ge0VsZW1lbnR9IGVsdFxuICAgKiBAcmV0dXJucyB7Tm9kZUxpc3RPZjxFbGVtZW50PnxbXX1cbiAgICovXG4gIGZ1bmN0aW9uIGZpbmRFbGVtZW50c1RvUHJvY2VzcyhlbHQpIHtcbiAgICBpZiAoZWx0LnF1ZXJ5U2VsZWN0b3JBbGwpIHtcbiAgICAgIGNvbnN0IGJvb3N0ZWRTZWxlY3RvciA9ICcsIFtoeC1ib29zdF0gYSwgW2RhdGEtaHgtYm9vc3RdIGEsIGFbaHgtYm9vc3RdLCBhW2RhdGEtaHgtYm9vc3RdJ1xuXG4gICAgICBjb25zdCBleHRlbnNpb25TZWxlY3RvcnMgPSBbXVxuICAgICAgZm9yIChjb25zdCBlIGluIGV4dGVuc2lvbnMpIHtcbiAgICAgICAgY29uc3QgZXh0ZW5zaW9uID0gZXh0ZW5zaW9uc1tlXVxuICAgICAgICBpZiAoZXh0ZW5zaW9uLmdldFNlbGVjdG9ycykge1xuICAgICAgICAgIHZhciBzZWxlY3RvcnMgPSBleHRlbnNpb24uZ2V0U2VsZWN0b3JzKClcbiAgICAgICAgICBpZiAoc2VsZWN0b3JzKSB7XG4gICAgICAgICAgICBleHRlbnNpb25TZWxlY3RvcnMucHVzaChzZWxlY3RvcnMpXG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9XG5cbiAgICAgIGNvbnN0IHJlc3VsdHMgPSBlbHQucXVlcnlTZWxlY3RvckFsbChWRVJCX1NFTEVDVE9SICsgYm9vc3RlZFNlbGVjdG9yICsgXCIsIGZvcm0sIFt0eXBlPSdzdWJtaXQnXSxcIiArXG4gICAgICAgICcgW2h4LWV4dF0sIFtkYXRhLWh4LWV4dF0sIFtoeC10cmlnZ2VyXSwgW2RhdGEtaHgtdHJpZ2dlcl0nICsgZXh0ZW5zaW9uU2VsZWN0b3JzLmZsYXQoKS5tYXAocyA9PiAnLCAnICsgcykuam9pbignJykpXG5cbiAgICAgIHJldHVybiByZXN1bHRzXG4gICAgfSBlbHNlIHtcbiAgICAgIHJldHVybiBbXVxuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBIYW5kbGUgc3VibWl0IGJ1dHRvbnMvaW5wdXRzIHRoYXQgaGF2ZSB0aGUgZm9ybSBhdHRyaWJ1dGUgc2V0XG4gICAqIHNlZSBodHRwczovL2RldmVsb3Blci5tb3ppbGxhLm9yZy9kb2NzL1dlYi9IVE1ML0VsZW1lbnQvYnV0dG9uXG4gICAqIEBwYXJhbSB7RXZlbnR9IGV2dFxuICAgKi9cbiAgZnVuY3Rpb24gbWF5YmVTZXRMYXN0QnV0dG9uQ2xpY2tlZChldnQpIHtcbiAgICBjb25zdCBlbHQgPSAvKiogQHR5cGUge0hUTUxCdXR0b25FbGVtZW50fEhUTUxJbnB1dEVsZW1lbnR9ICovIChjbG9zZXN0KGFzRWxlbWVudChldnQudGFyZ2V0KSwgXCJidXR0b24sIGlucHV0W3R5cGU9J3N1Ym1pdCddXCIpKVxuICAgIGNvbnN0IGludGVybmFsRGF0YSA9IGdldFJlbGF0ZWRGb3JtRGF0YShldnQpXG4gICAgaWYgKGludGVybmFsRGF0YSkge1xuICAgICAgaW50ZXJuYWxEYXRhLmxhc3RCdXR0b25DbGlja2VkID0gZWx0XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIEBwYXJhbSB7RXZlbnR9IGV2dFxuICAgKi9cbiAgZnVuY3Rpb24gbWF5YmVVbnNldExhc3RCdXR0b25DbGlja2VkKGV2dCkge1xuICAgIGNvbnN0IGludGVybmFsRGF0YSA9IGdldFJlbGF0ZWRGb3JtRGF0YShldnQpXG4gICAgaWYgKGludGVybmFsRGF0YSkge1xuICAgICAgaW50ZXJuYWxEYXRhLmxhc3RCdXR0b25DbGlja2VkID0gbnVsbFxuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBAcGFyYW0ge0V2ZW50fSBldnRcbiAgICogQHJldHVybnMge0h0bXhOb2RlSW50ZXJuYWxEYXRhfHVuZGVmaW5lZH1cbiAgICovXG4gIGZ1bmN0aW9uIGdldFJlbGF0ZWRGb3JtRGF0YShldnQpIHtcbiAgICBjb25zdCBlbHQgPSBjbG9zZXN0KGFzRWxlbWVudChldnQudGFyZ2V0KSwgXCJidXR0b24sIGlucHV0W3R5cGU9J3N1Ym1pdCddXCIpXG4gICAgaWYgKCFlbHQpIHtcbiAgICAgIHJldHVyblxuICAgIH1cbiAgICBjb25zdCBmb3JtID0gcmVzb2x2ZVRhcmdldCgnIycgKyBnZXRSYXdBdHRyaWJ1dGUoZWx0LCAnZm9ybScpLCBlbHQuZ2V0Um9vdE5vZGUoKSkgfHwgY2xvc2VzdChlbHQsICdmb3JtJylcbiAgICBpZiAoIWZvcm0pIHtcbiAgICAgIHJldHVyblxuICAgIH1cbiAgICByZXR1cm4gZ2V0SW50ZXJuYWxEYXRhKGZvcm0pXG4gIH1cblxuICAvKipcbiAgICogQHBhcmFtIHtFdmVudFRhcmdldH0gZWx0XG4gICAqL1xuICBmdW5jdGlvbiBpbml0QnV0dG9uVHJhY2tpbmcoZWx0KSB7XG4gICAgLy8gbmVlZCB0byBoYW5kbGUgYm90aCBjbGljayBhbmQgZm9jdXMgaW46XG4gICAgLy8gICBmb2N1c2luIC0gaW4gY2FzZSBzb21lb25lIHRhYnMgaW4gdG8gYSBidXR0b24gYW5kIGhpdHMgdGhlIHNwYWNlIGJhclxuICAgIC8vICAgY2xpY2sgLSBvbiBPU1ggYnV0dG9ucyBkbyBub3QgZm9jdXMgb24gY2xpY2sgc2VlIGh0dHBzOi8vYnVncy53ZWJraXQub3JnL3Nob3dfYnVnLmNnaT9pZD0xMzcyNFxuICAgIGVsdC5hZGRFdmVudExpc3RlbmVyKCdjbGljaycsIG1heWJlU2V0TGFzdEJ1dHRvbkNsaWNrZWQpXG4gICAgZWx0LmFkZEV2ZW50TGlzdGVuZXIoJ2ZvY3VzaW4nLCBtYXliZVNldExhc3RCdXR0b25DbGlja2VkKVxuICAgIGVsdC5hZGRFdmVudExpc3RlbmVyKCdmb2N1c291dCcsIG1heWJlVW5zZXRMYXN0QnV0dG9uQ2xpY2tlZClcbiAgfVxuXG4gIC8qKlxuICAgKiBAcGFyYW0ge0VsZW1lbnR9IGVsdFxuICAgKiBAcGFyYW0ge3N0cmluZ30gZXZlbnROYW1lXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBjb2RlXG4gICAqL1xuICBmdW5jdGlvbiBhZGRIeE9uRXZlbnRIYW5kbGVyKGVsdCwgZXZlbnROYW1lLCBjb2RlKSB7XG4gICAgY29uc3Qgbm9kZURhdGEgPSBnZXRJbnRlcm5hbERhdGEoZWx0KVxuICAgIGlmICghQXJyYXkuaXNBcnJheShub2RlRGF0YS5vbkhhbmRsZXJzKSkge1xuICAgICAgbm9kZURhdGEub25IYW5kbGVycyA9IFtdXG4gICAgfVxuICAgIGxldCBmdW5jXG4gICAgLyoqIEB0eXBlIEV2ZW50TGlzdGVuZXIgKi9cbiAgICBjb25zdCBsaXN0ZW5lciA9IGZ1bmN0aW9uKGUpIHtcbiAgICAgIG1heWJlRXZhbChlbHQsIGZ1bmN0aW9uKCkge1xuICAgICAgICBpZiAoZWx0SXNEaXNhYmxlZChlbHQpKSB7XG4gICAgICAgICAgcmV0dXJuXG4gICAgICAgIH1cbiAgICAgICAgaWYgKCFmdW5jKSB7XG4gICAgICAgICAgZnVuYyA9IG5ldyBGdW5jdGlvbignZXZlbnQnLCBjb2RlKVxuICAgICAgICB9XG4gICAgICAgIGZ1bmMuY2FsbChlbHQsIGUpXG4gICAgICB9KVxuICAgIH1cbiAgICBlbHQuYWRkRXZlbnRMaXN0ZW5lcihldmVudE5hbWUsIGxpc3RlbmVyKVxuICAgIG5vZGVEYXRhLm9uSGFuZGxlcnMucHVzaCh7IGV2ZW50OiBldmVudE5hbWUsIGxpc3RlbmVyIH0pXG4gIH1cblxuICAvKipcbiAgICogQHBhcmFtIHtFbGVtZW50fSBlbHRcbiAgICovXG4gIGZ1bmN0aW9uIHByb2Nlc3NIeE9uV2lsZGNhcmQoZWx0KSB7XG4gICAgLy8gd2lwZSBhbnkgcHJldmlvdXMgb24gaGFuZGxlcnMgc28gdGhhdCB0aGlzIGZ1bmN0aW9uIHRha2VzIHByZWNlZGVuY2VcbiAgICBkZUluaXRPbkhhbmRsZXJzKGVsdClcblxuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgZWx0LmF0dHJpYnV0ZXMubGVuZ3RoOyBpKyspIHtcbiAgICAgIGNvbnN0IG5hbWUgPSBlbHQuYXR0cmlidXRlc1tpXS5uYW1lXG4gICAgICBjb25zdCB2YWx1ZSA9IGVsdC5hdHRyaWJ1dGVzW2ldLnZhbHVlXG4gICAgICBpZiAoc3RhcnRzV2l0aChuYW1lLCAnaHgtb24nKSB8fCBzdGFydHNXaXRoKG5hbWUsICdkYXRhLWh4LW9uJykpIHtcbiAgICAgICAgY29uc3QgYWZ0ZXJPblBvc2l0aW9uID0gbmFtZS5pbmRleE9mKCctb24nKSArIDNcbiAgICAgICAgY29uc3QgbmV4dENoYXIgPSBuYW1lLnNsaWNlKGFmdGVyT25Qb3NpdGlvbiwgYWZ0ZXJPblBvc2l0aW9uICsgMSlcbiAgICAgICAgaWYgKG5leHRDaGFyID09PSAnLScgfHwgbmV4dENoYXIgPT09ICc6Jykge1xuICAgICAgICAgIGxldCBldmVudE5hbWUgPSBuYW1lLnNsaWNlKGFmdGVyT25Qb3NpdGlvbiArIDEpXG4gICAgICAgICAgLy8gaWYgdGhlIGV2ZW50TmFtZSBzdGFydHMgd2l0aCBhIGNvbG9uIG9yIGRhc2gsIHByZXBlbmQgXCJodG14XCIgZm9yIHNob3J0aGFuZCBzdXBwb3J0XG4gICAgICAgICAgaWYgKHN0YXJ0c1dpdGgoZXZlbnROYW1lLCAnOicpKSB7XG4gICAgICAgICAgICBldmVudE5hbWUgPSAnaHRteCcgKyBldmVudE5hbWVcbiAgICAgICAgICB9IGVsc2UgaWYgKHN0YXJ0c1dpdGgoZXZlbnROYW1lLCAnLScpKSB7XG4gICAgICAgICAgICBldmVudE5hbWUgPSAnaHRteDonICsgZXZlbnROYW1lLnNsaWNlKDEpXG4gICAgICAgICAgfSBlbHNlIGlmIChzdGFydHNXaXRoKGV2ZW50TmFtZSwgJ2h0bXgtJykpIHtcbiAgICAgICAgICAgIGV2ZW50TmFtZSA9ICdodG14OicgKyBldmVudE5hbWUuc2xpY2UoNSlcbiAgICAgICAgICB9XG5cbiAgICAgICAgICBhZGRIeE9uRXZlbnRIYW5kbGVyKGVsdCwgZXZlbnROYW1lLCB2YWx1ZSlcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBAcGFyYW0ge0VsZW1lbnR8SFRNTElucHV0RWxlbWVudH0gZWx0XG4gICAqL1xuICBmdW5jdGlvbiBpbml0Tm9kZShlbHQpIHtcbiAgICBpZiAoY2xvc2VzdChlbHQsIGh0bXguY29uZmlnLmRpc2FibGVTZWxlY3RvcikpIHtcbiAgICAgIGNsZWFuVXBFbGVtZW50KGVsdClcbiAgICAgIHJldHVyblxuICAgIH1cbiAgICBjb25zdCBub2RlRGF0YSA9IGdldEludGVybmFsRGF0YShlbHQpXG4gICAgaWYgKG5vZGVEYXRhLmluaXRIYXNoICE9PSBhdHRyaWJ1dGVIYXNoKGVsdCkpIHtcbiAgICAgIC8vIGNsZWFuIHVwIGFueSBwcmV2aW91c2x5IHByb2Nlc3NlZCBpbmZvXG4gICAgICBkZUluaXROb2RlKGVsdClcblxuICAgICAgbm9kZURhdGEuaW5pdEhhc2ggPSBhdHRyaWJ1dGVIYXNoKGVsdClcblxuICAgICAgdHJpZ2dlckV2ZW50KGVsdCwgJ2h0bXg6YmVmb3JlUHJvY2Vzc05vZGUnKVxuXG4gICAgICAvLyBAdHMtaWdub3JlIHZhbHVlIHdpbGwgYmUgdW5kZWZpbmVkIGZvciBub24taW5wdXQgZWxlbWVudHMsIHdoaWNoIGlzIGZpbmVcbiAgICAgIGlmIChlbHQudmFsdWUpIHtcbiAgICAgICAgLy8gQHRzLWlnbm9yZVxuICAgICAgICBub2RlRGF0YS5sYXN0VmFsdWUgPSBlbHQudmFsdWVcbiAgICAgIH1cblxuICAgICAgY29uc3QgdHJpZ2dlclNwZWNzID0gZ2V0VHJpZ2dlclNwZWNzKGVsdClcbiAgICAgIGNvbnN0IGhhc0V4cGxpY2l0SHR0cEFjdGlvbiA9IHByb2Nlc3NWZXJicyhlbHQsIG5vZGVEYXRhLCB0cmlnZ2VyU3BlY3MpXG5cbiAgICAgIGlmICghaGFzRXhwbGljaXRIdHRwQWN0aW9uKSB7XG4gICAgICAgIGlmIChnZXRDbG9zZXN0QXR0cmlidXRlVmFsdWUoZWx0LCAnaHgtYm9vc3QnKSA9PT0gJ3RydWUnKSB7XG4gICAgICAgICAgYm9vc3RFbGVtZW50KGVsdCwgbm9kZURhdGEsIHRyaWdnZXJTcGVjcylcbiAgICAgICAgfSBlbHNlIGlmIChoYXNBdHRyaWJ1dGUoZWx0LCAnaHgtdHJpZ2dlcicpKSB7XG4gICAgICAgICAgdHJpZ2dlclNwZWNzLmZvckVhY2goZnVuY3Rpb24odHJpZ2dlclNwZWMpIHtcbiAgICAgICAgICAgIC8vIEZvciBcIm5ha2VkXCIgdHJpZ2dlcnMsIGRvbid0IGRvIGFueXRoaW5nIGF0IGFsbFxuICAgICAgICAgICAgYWRkVHJpZ2dlckhhbmRsZXIoZWx0LCB0cmlnZ2VyU3BlYywgbm9kZURhdGEsIGZ1bmN0aW9uKCkge1xuICAgICAgICAgICAgfSlcbiAgICAgICAgICB9KVxuICAgICAgICB9XG4gICAgICB9XG5cbiAgICAgIC8vIEhhbmRsZSBzdWJtaXQgYnV0dG9ucy9pbnB1dHMgdGhhdCBoYXZlIHRoZSBmb3JtIGF0dHJpYnV0ZSBzZXRcbiAgICAgIC8vIHNlZSBodHRwczovL2RldmVsb3Blci5tb3ppbGxhLm9yZy9kb2NzL1dlYi9IVE1ML0VsZW1lbnQvYnV0dG9uXG4gICAgICBpZiAoZWx0LnRhZ05hbWUgPT09ICdGT1JNJyB8fCAoZ2V0UmF3QXR0cmlidXRlKGVsdCwgJ3R5cGUnKSA9PT0gJ3N1Ym1pdCcgJiYgaGFzQXR0cmlidXRlKGVsdCwgJ2Zvcm0nKSkpIHtcbiAgICAgICAgaW5pdEJ1dHRvblRyYWNraW5nKGVsdClcbiAgICAgIH1cblxuICAgICAgdHJpZ2dlckV2ZW50KGVsdCwgJ2h0bXg6YWZ0ZXJQcm9jZXNzTm9kZScpXG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIFByb2Nlc3NlcyBuZXcgY29udGVudCwgZW5hYmxpbmcgaHRteCBiZWhhdmlvci4gVGhpcyBjYW4gYmUgdXNlZnVsIGlmIHlvdSBoYXZlIGNvbnRlbnQgdGhhdCBpcyBhZGRlZCB0byB0aGUgRE9NIG91dHNpZGUgb2YgdGhlIG5vcm1hbCBodG14IHJlcXVlc3QgY3ljbGUgYnV0IHN0aWxsIHdhbnQgaHRteCBhdHRyaWJ1dGVzIHRvIHdvcmsuXG4gICAqXG4gICAqIEBzZWUgaHR0cHM6Ly9odG14Lm9yZy9hcGkvI3Byb2Nlc3NcbiAgICpcbiAgICogQHBhcmFtIHtFbGVtZW50fHN0cmluZ30gZWx0IGVsZW1lbnQgdG8gcHJvY2Vzc1xuICAgKi9cbiAgZnVuY3Rpb24gcHJvY2Vzc05vZGUoZWx0KSB7XG4gICAgZWx0ID0gcmVzb2x2ZVRhcmdldChlbHQpXG4gICAgaWYgKGNsb3Nlc3QoZWx0LCBodG14LmNvbmZpZy5kaXNhYmxlU2VsZWN0b3IpKSB7XG4gICAgICBjbGVhblVwRWxlbWVudChlbHQpXG4gICAgICByZXR1cm5cbiAgICB9XG4gICAgaW5pdE5vZGUoZWx0KVxuICAgIGZvckVhY2goZmluZEVsZW1lbnRzVG9Qcm9jZXNzKGVsdCksIGZ1bmN0aW9uKGNoaWxkKSB7IGluaXROb2RlKGNoaWxkKSB9KVxuICAgIGZvckVhY2goZmluZEh4T25XaWxkY2FyZEVsZW1lbnRzKGVsdCksIHByb2Nlc3NIeE9uV2lsZGNhcmQpXG4gIH1cblxuICAvLz0gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuICAvLyBFdmVudC9Mb2cgU3VwcG9ydFxuICAvLz0gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuXG4gIC8qKlxuICAgKiBAcGFyYW0ge3N0cmluZ30gc3RyXG4gICAqIEByZXR1cm5zIHtzdHJpbmd9XG4gICAqL1xuICBmdW5jdGlvbiBrZWJhYkV2ZW50TmFtZShzdHIpIHtcbiAgICByZXR1cm4gc3RyLnJlcGxhY2UoLyhbYS16MC05XSkoW0EtWl0pL2csICckMS0kMicpLnRvTG93ZXJDYXNlKClcbiAgfVxuXG4gIC8qKlxuICAgKiBAcGFyYW0ge3N0cmluZ30gZXZlbnROYW1lXG4gICAqIEBwYXJhbSB7YW55fSBkZXRhaWxcbiAgICogQHJldHVybnMge0N1c3RvbUV2ZW50fVxuICAgKi9cbiAgZnVuY3Rpb24gbWFrZUV2ZW50KGV2ZW50TmFtZSwgZGV0YWlsKSB7XG4gICAgbGV0IGV2dFxuICAgIGlmICh3aW5kb3cuQ3VzdG9tRXZlbnQgJiYgdHlwZW9mIHdpbmRvdy5DdXN0b21FdmVudCA9PT0gJ2Z1bmN0aW9uJykge1xuICAgICAgLy8gVE9ETzogYGNvbXBvc2VkOiB0cnVlYCBoZXJlIGlzIGEgaGFjayB0byBtYWtlIGdsb2JhbCBldmVudCBoYW5kbGVycyB3b3JrIHdpdGggZXZlbnRzIGluIHNoYWRvdyBET01cbiAgICAgIC8vIFRoaXMgYnJlYWtzIGV4cGVjdGVkIGVuY2Fwc3VsYXRpb24gYnV0IG5lZWRzIHRvIGJlIGhlcmUgdW50aWwgZGVjaWRlZCBvdGhlcndpc2UgYnkgY29yZSBkZXZzXG4gICAgICBldnQgPSBuZXcgQ3VzdG9tRXZlbnQoZXZlbnROYW1lLCB7IGJ1YmJsZXM6IHRydWUsIGNhbmNlbGFibGU6IHRydWUsIGNvbXBvc2VkOiB0cnVlLCBkZXRhaWwgfSlcbiAgICB9IGVsc2Uge1xuICAgICAgZXZ0ID0gZ2V0RG9jdW1lbnQoKS5jcmVhdGVFdmVudCgnQ3VzdG9tRXZlbnQnKVxuICAgICAgZXZ0LmluaXRDdXN0b21FdmVudChldmVudE5hbWUsIHRydWUsIHRydWUsIGRldGFpbClcbiAgICB9XG4gICAgcmV0dXJuIGV2dFxuICB9XG5cbiAgLyoqXG4gICAqIEBwYXJhbSB7RXZlbnRUYXJnZXR8c3RyaW5nfSBlbHRcbiAgICogQHBhcmFtIHtzdHJpbmd9IGV2ZW50TmFtZVxuICAgKiBAcGFyYW0ge2FueT19IGRldGFpbFxuICAgKi9cbiAgZnVuY3Rpb24gdHJpZ2dlckVycm9yRXZlbnQoZWx0LCBldmVudE5hbWUsIGRldGFpbCkge1xuICAgIHRyaWdnZXJFdmVudChlbHQsIGV2ZW50TmFtZSwgbWVyZ2VPYmplY3RzKHsgZXJyb3I6IGV2ZW50TmFtZSB9LCBkZXRhaWwpKVxuICB9XG5cbiAgLyoqXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBldmVudE5hbWVcbiAgICogQHJldHVybnMge2Jvb2xlYW59XG4gICAqL1xuICBmdW5jdGlvbiBpZ25vcmVFdmVudEZvckxvZ2dpbmcoZXZlbnROYW1lKSB7XG4gICAgcmV0dXJuIGV2ZW50TmFtZSA9PT0gJ2h0bXg6YWZ0ZXJQcm9jZXNzTm9kZSdcbiAgfVxuXG4gIC8qKlxuICAgKiBgd2l0aEV4dGVuc2lvbnNgIGxvY2F0ZXMgYWxsIGFjdGl2ZSBleHRlbnNpb25zIGZvciBhIHByb3ZpZGVkIGVsZW1lbnQsIHRoZW5cbiAgICogZXhlY3V0ZXMgdGhlIHByb3ZpZGVkIGZ1bmN0aW9uIHVzaW5nIGVhY2ggb2YgdGhlIGFjdGl2ZSBleHRlbnNpb25zLiAgSXQgc2hvdWxkXG4gICAqIGJlIGNhbGxlZCBpbnRlcm5hbGx5IGF0IGV2ZXJ5IGV4dGVuZGFibGUgZXhlY3V0aW9uIHBvaW50IGluIGh0bXguXG4gICAqXG4gICAqIEBwYXJhbSB7RWxlbWVudH0gZWx0XG4gICAqIEBwYXJhbSB7KGV4dGVuc2lvbjpIdG14RXh0ZW5zaW9uKSA9PiB2b2lkfSB0b0RvXG4gICAqIEByZXR1cm5zIHZvaWRcbiAgICovXG4gIGZ1bmN0aW9uIHdpdGhFeHRlbnNpb25zKGVsdCwgdG9Ebykge1xuICAgIGZvckVhY2goZ2V0RXh0ZW5zaW9ucyhlbHQpLCBmdW5jdGlvbihleHRlbnNpb24pIHtcbiAgICAgIHRyeSB7XG4gICAgICAgIHRvRG8oZXh0ZW5zaW9uKVxuICAgICAgfSBjYXRjaCAoZSkge1xuICAgICAgICBsb2dFcnJvcihlKVxuICAgICAgfVxuICAgIH0pXG4gIH1cblxuICBmdW5jdGlvbiBsb2dFcnJvcihtc2cpIHtcbiAgICBpZiAoY29uc29sZS5lcnJvcikge1xuICAgICAgY29uc29sZS5lcnJvcihtc2cpXG4gICAgfSBlbHNlIGlmIChjb25zb2xlLmxvZykge1xuICAgICAgY29uc29sZS5sb2coJ0VSUk9SOiAnLCBtc2cpXG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIFRyaWdnZXJzIGEgZ2l2ZW4gZXZlbnQgb24gYW4gZWxlbWVudFxuICAgKlxuICAgKiBAc2VlIGh0dHBzOi8vaHRteC5vcmcvYXBpLyN0cmlnZ2VyXG4gICAqXG4gICAqIEBwYXJhbSB7RXZlbnRUYXJnZXR8c3RyaW5nfSBlbHQgdGhlIGVsZW1lbnQgdG8gdHJpZ2dlciB0aGUgZXZlbnQgb25cbiAgICogQHBhcmFtIHtzdHJpbmd9IGV2ZW50TmFtZSB0aGUgbmFtZSBvZiB0aGUgZXZlbnQgdG8gdHJpZ2dlclxuICAgKiBAcGFyYW0ge2FueT19IGRldGFpbCBkZXRhaWxzIGZvciB0aGUgZXZlbnRcbiAgICogQHJldHVybnMge2Jvb2xlYW59XG4gICAqL1xuICBmdW5jdGlvbiB0cmlnZ2VyRXZlbnQoZWx0LCBldmVudE5hbWUsIGRldGFpbCkge1xuICAgIGVsdCA9IHJlc29sdmVUYXJnZXQoZWx0KVxuICAgIGlmIChkZXRhaWwgPT0gbnVsbCkge1xuICAgICAgZGV0YWlsID0ge31cbiAgICB9XG4gICAgZGV0YWlsLmVsdCA9IGVsdFxuICAgIGNvbnN0IGV2ZW50ID0gbWFrZUV2ZW50KGV2ZW50TmFtZSwgZGV0YWlsKVxuICAgIGlmIChodG14LmxvZ2dlciAmJiAhaWdub3JlRXZlbnRGb3JMb2dnaW5nKGV2ZW50TmFtZSkpIHtcbiAgICAgIGh0bXgubG9nZ2VyKGVsdCwgZXZlbnROYW1lLCBkZXRhaWwpXG4gICAgfVxuICAgIGlmIChkZXRhaWwuZXJyb3IpIHtcbiAgICAgIGxvZ0Vycm9yKGRldGFpbC5lcnJvcilcbiAgICAgIHRyaWdnZXJFdmVudChlbHQsICdodG14OmVycm9yJywgeyBlcnJvckluZm86IGRldGFpbCB9KVxuICAgIH1cbiAgICBsZXQgZXZlbnRSZXN1bHQgPSBlbHQuZGlzcGF0Y2hFdmVudChldmVudClcbiAgICBjb25zdCBrZWJhYk5hbWUgPSBrZWJhYkV2ZW50TmFtZShldmVudE5hbWUpXG4gICAgaWYgKGV2ZW50UmVzdWx0ICYmIGtlYmFiTmFtZSAhPT0gZXZlbnROYW1lKSB7XG4gICAgICBjb25zdCBrZWJhYmVkRXZlbnQgPSBtYWtlRXZlbnQoa2ViYWJOYW1lLCBldmVudC5kZXRhaWwpXG4gICAgICBldmVudFJlc3VsdCA9IGV2ZW50UmVzdWx0ICYmIGVsdC5kaXNwYXRjaEV2ZW50KGtlYmFiZWRFdmVudClcbiAgICB9XG4gICAgd2l0aEV4dGVuc2lvbnMoYXNFbGVtZW50KGVsdCksIGZ1bmN0aW9uKGV4dGVuc2lvbikge1xuICAgICAgZXZlbnRSZXN1bHQgPSBldmVudFJlc3VsdCAmJiAoZXh0ZW5zaW9uLm9uRXZlbnQoZXZlbnROYW1lLCBldmVudCkgIT09IGZhbHNlICYmICFldmVudC5kZWZhdWx0UHJldmVudGVkKVxuICAgIH0pXG4gICAgcmV0dXJuIGV2ZW50UmVzdWx0XG4gIH1cblxuICAvLz0gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuICAvLyBIaXN0b3J5IFN1cHBvcnRcbiAgLy89ID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbiAgbGV0IGN1cnJlbnRQYXRoRm9ySGlzdG9yeSA9IGxvY2F0aW9uLnBhdGhuYW1lICsgbG9jYXRpb24uc2VhcmNoXG5cbiAgLyoqXG4gICAqIEByZXR1cm5zIHtFbGVtZW50fVxuICAgKi9cbiAgZnVuY3Rpb24gZ2V0SGlzdG9yeUVsZW1lbnQoKSB7XG4gICAgY29uc3QgaGlzdG9yeUVsdCA9IGdldERvY3VtZW50KCkucXVlcnlTZWxlY3RvcignW2h4LWhpc3RvcnktZWx0XSxbZGF0YS1oeC1oaXN0b3J5LWVsdF0nKVxuICAgIHJldHVybiBoaXN0b3J5RWx0IHx8IGdldERvY3VtZW50KCkuYm9keVxuICB9XG5cbiAgLyoqXG4gICAqIEBwYXJhbSB7c3RyaW5nfSB1cmxcbiAgICogQHBhcmFtIHtFbGVtZW50fSByb290RWx0XG4gICAqL1xuICBmdW5jdGlvbiBzYXZlVG9IaXN0b3J5Q2FjaGUodXJsLCByb290RWx0KSB7XG4gICAgaWYgKCFjYW5BY2Nlc3NMb2NhbFN0b3JhZ2UoKSkge1xuICAgICAgcmV0dXJuXG4gICAgfVxuXG4gICAgLy8gZ2V0IHN0YXRlIHRvIHNhdmVcbiAgICBjb25zdCBpbm5lckhUTUwgPSBjbGVhbklubmVySHRtbEZvckhpc3Rvcnkocm9vdEVsdClcbiAgICBjb25zdCB0aXRsZSA9IGdldERvY3VtZW50KCkudGl0bGVcbiAgICBjb25zdCBzY3JvbGwgPSB3aW5kb3cuc2Nyb2xsWVxuXG4gICAgaWYgKGh0bXguY29uZmlnLmhpc3RvcnlDYWNoZVNpemUgPD0gMCkge1xuICAgICAgLy8gbWFrZSBzdXJlIHRoYXQgYW4gZXZlbnR1YWxseSBhbHJlYWR5IGV4aXN0aW5nIGNhY2hlIGlzIHB1cmdlZFxuICAgICAgbG9jYWxTdG9yYWdlLnJlbW92ZUl0ZW0oJ2h0bXgtaGlzdG9yeS1jYWNoZScpXG4gICAgICByZXR1cm5cbiAgICB9XG5cbiAgICB1cmwgPSBub3JtYWxpemVQYXRoKHVybClcblxuICAgIGNvbnN0IGhpc3RvcnlDYWNoZSA9IHBhcnNlSlNPTihsb2NhbFN0b3JhZ2UuZ2V0SXRlbSgnaHRteC1oaXN0b3J5LWNhY2hlJykpIHx8IFtdXG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCBoaXN0b3J5Q2FjaGUubGVuZ3RoOyBpKyspIHtcbiAgICAgIGlmIChoaXN0b3J5Q2FjaGVbaV0udXJsID09PSB1cmwpIHtcbiAgICAgICAgaGlzdG9yeUNhY2hlLnNwbGljZShpLCAxKVxuICAgICAgICBicmVha1xuICAgICAgfVxuICAgIH1cblxuICAgIC8qKiBAdHlwZSBIdG14SGlzdG9yeUl0ZW0gKi9cbiAgICBjb25zdCBuZXdIaXN0b3J5SXRlbSA9IHsgdXJsLCBjb250ZW50OiBpbm5lckhUTUwsIHRpdGxlLCBzY3JvbGwgfVxuXG4gICAgdHJpZ2dlckV2ZW50KGdldERvY3VtZW50KCkuYm9keSwgJ2h0bXg6aGlzdG9yeUl0ZW1DcmVhdGVkJywgeyBpdGVtOiBuZXdIaXN0b3J5SXRlbSwgY2FjaGU6IGhpc3RvcnlDYWNoZSB9KVxuXG4gICAgaGlzdG9yeUNhY2hlLnB1c2gobmV3SGlzdG9yeUl0ZW0pXG4gICAgd2hpbGUgKGhpc3RvcnlDYWNoZS5sZW5ndGggPiBodG14LmNvbmZpZy5oaXN0b3J5Q2FjaGVTaXplKSB7XG4gICAgICBoaXN0b3J5Q2FjaGUuc2hpZnQoKVxuICAgIH1cblxuICAgIC8vIGtlZXAgdHJ5aW5nIHRvIHNhdmUgdGhlIGNhY2hlIHVudGlsIGl0IHN1Y2NlZWRzIG9yIGlzIGVtcHR5XG4gICAgd2hpbGUgKGhpc3RvcnlDYWNoZS5sZW5ndGggPiAwKSB7XG4gICAgICB0cnkge1xuICAgICAgICBsb2NhbFN0b3JhZ2Uuc2V0SXRlbSgnaHRteC1oaXN0b3J5LWNhY2hlJywgSlNPTi5zdHJpbmdpZnkoaGlzdG9yeUNhY2hlKSlcbiAgICAgICAgYnJlYWtcbiAgICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgICAgdHJpZ2dlckVycm9yRXZlbnQoZ2V0RG9jdW1lbnQoKS5ib2R5LCAnaHRteDpoaXN0b3J5Q2FjaGVFcnJvcicsIHsgY2F1c2U6IGUsIGNhY2hlOiBoaXN0b3J5Q2FjaGUgfSlcbiAgICAgICAgaGlzdG9yeUNhY2hlLnNoaWZ0KCkgLy8gc2hyaW5rIHRoZSBjYWNoZSBhbmQgcmV0cnlcbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogQHR5cGVkZWYge09iamVjdH0gSHRteEhpc3RvcnlJdGVtXG4gICAqIEBwcm9wZXJ0eSB7c3RyaW5nfSB1cmxcbiAgICogQHByb3BlcnR5IHtzdHJpbmd9IGNvbnRlbnRcbiAgICogQHByb3BlcnR5IHtzdHJpbmd9IHRpdGxlXG4gICAqIEBwcm9wZXJ0eSB7bnVtYmVyfSBzY3JvbGxcbiAgICovXG5cbiAgLyoqXG4gICAqIEBwYXJhbSB7c3RyaW5nfSB1cmxcbiAgICogQHJldHVybnMge0h0bXhIaXN0b3J5SXRlbXxudWxsfVxuICAgKi9cbiAgZnVuY3Rpb24gZ2V0Q2FjaGVkSGlzdG9yeSh1cmwpIHtcbiAgICBpZiAoIWNhbkFjY2Vzc0xvY2FsU3RvcmFnZSgpKSB7XG4gICAgICByZXR1cm4gbnVsbFxuICAgIH1cblxuICAgIHVybCA9IG5vcm1hbGl6ZVBhdGgodXJsKVxuXG4gICAgY29uc3QgaGlzdG9yeUNhY2hlID0gcGFyc2VKU09OKGxvY2FsU3RvcmFnZS5nZXRJdGVtKCdodG14LWhpc3RvcnktY2FjaGUnKSkgfHwgW11cbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IGhpc3RvcnlDYWNoZS5sZW5ndGg7IGkrKykge1xuICAgICAgaWYgKGhpc3RvcnlDYWNoZVtpXS51cmwgPT09IHVybCkge1xuICAgICAgICByZXR1cm4gaGlzdG9yeUNhY2hlW2ldXG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiBudWxsXG4gIH1cblxuICAvKipcbiAgICogQHBhcmFtIHtFbGVtZW50fSBlbHRcbiAgICogQHJldHVybnMge3N0cmluZ31cbiAgICovXG4gIGZ1bmN0aW9uIGNsZWFuSW5uZXJIdG1sRm9ySGlzdG9yeShlbHQpIHtcbiAgICBjb25zdCBjbGFzc05hbWUgPSBodG14LmNvbmZpZy5yZXF1ZXN0Q2xhc3NcbiAgICBjb25zdCBjbG9uZSA9IC8qKiBAdHlwZSBFbGVtZW50ICovIChlbHQuY2xvbmVOb2RlKHRydWUpKVxuICAgIGZvckVhY2goZmluZEFsbChjbG9uZSwgJy4nICsgY2xhc3NOYW1lKSwgZnVuY3Rpb24oY2hpbGQpIHtcbiAgICAgIHJlbW92ZUNsYXNzRnJvbUVsZW1lbnQoY2hpbGQsIGNsYXNzTmFtZSlcbiAgICB9KVxuICAgIC8vIHJlbW92ZSB0aGUgZGlzYWJsZWQgYXR0cmlidXRlIGZvciBhbnkgZWxlbWVudCBkaXNhYmxlZCBkdWUgdG8gYW4gaHRteCByZXF1ZXN0XG4gICAgZm9yRWFjaChmaW5kQWxsKGNsb25lLCAnW2RhdGEtZGlzYWJsZWQtYnktaHRteF0nKSwgZnVuY3Rpb24oY2hpbGQpIHtcbiAgICAgIGNoaWxkLnJlbW92ZUF0dHJpYnV0ZSgnZGlzYWJsZWQnKVxuICAgIH0pXG4gICAgcmV0dXJuIGNsb25lLmlubmVySFRNTFxuICB9XG5cbiAgZnVuY3Rpb24gc2F2ZUN1cnJlbnRQYWdlVG9IaXN0b3J5KCkge1xuICAgIGNvbnN0IGVsdCA9IGdldEhpc3RvcnlFbGVtZW50KClcbiAgICBjb25zdCBwYXRoID0gY3VycmVudFBhdGhGb3JIaXN0b3J5IHx8IGxvY2F0aW9uLnBhdGhuYW1lICsgbG9jYXRpb24uc2VhcmNoXG5cbiAgICAvLyBBbGxvdyBoaXN0b3J5IHNuYXBzaG90IGZlYXR1cmUgdG8gYmUgZGlzYWJsZWQgd2hlcmUgaHgtaGlzdG9yeT1cImZhbHNlXCJcbiAgICAvLyBpcyBwcmVzZW50ICphbnl3aGVyZSogaW4gdGhlIGN1cnJlbnQgZG9jdW1lbnQgd2UncmUgYWJvdXQgdG8gc2F2ZSxcbiAgICAvLyBzbyB3ZSBjYW4gcHJldmVudCBwcml2aWxlZ2VkIGRhdGEgZW50ZXJpbmcgdGhlIGNhY2hlLlxuICAgIC8vIFRoZSBwYWdlIHdpbGwgc3RpbGwgYmUgcmVhY2hhYmxlIGFzIGEgaGlzdG9yeSBlbnRyeSwgYnV0IGh0bXggd2lsbCBmZXRjaCBpdFxuICAgIC8vIGxpdmUgZnJvbSB0aGUgc2VydmVyIG9ucG9wc3RhdGUgcmF0aGVyIHRoYW4gbG9vayBpbiB0aGUgbG9jYWxTdG9yYWdlIGNhY2hlXG4gICAgbGV0IGRpc2FibGVIaXN0b3J5Q2FjaGVcbiAgICB0cnkge1xuICAgICAgZGlzYWJsZUhpc3RvcnlDYWNoZSA9IGdldERvY3VtZW50KCkucXVlcnlTZWxlY3RvcignW2h4LWhpc3Rvcnk9XCJmYWxzZVwiIGldLFtkYXRhLWh4LWhpc3Rvcnk9XCJmYWxzZVwiIGldJylcbiAgICB9IGNhdGNoIChlKSB7XG4gICAgLy8gSUUxMTogaW5zZW5zaXRpdmUgbW9kaWZpZXIgbm90IHN1cHBvcnRlZCBzbyBmYWxsYmFjayB0byBjYXNlIHNlbnNpdGl2ZSBzZWxlY3RvclxuICAgICAgZGlzYWJsZUhpc3RvcnlDYWNoZSA9IGdldERvY3VtZW50KCkucXVlcnlTZWxlY3RvcignW2h4LWhpc3Rvcnk9XCJmYWxzZVwiXSxbZGF0YS1oeC1oaXN0b3J5PVwiZmFsc2VcIl0nKVxuICAgIH1cbiAgICBpZiAoIWRpc2FibGVIaXN0b3J5Q2FjaGUpIHtcbiAgICAgIHRyaWdnZXJFdmVudChnZXREb2N1bWVudCgpLmJvZHksICdodG14OmJlZm9yZUhpc3RvcnlTYXZlJywgeyBwYXRoLCBoaXN0b3J5RWx0OiBlbHQgfSlcbiAgICAgIHNhdmVUb0hpc3RvcnlDYWNoZShwYXRoLCBlbHQpXG4gICAgfVxuXG4gICAgaWYgKGh0bXguY29uZmlnLmhpc3RvcnlFbmFibGVkKSBoaXN0b3J5LnJlcGxhY2VTdGF0ZSh7IGh0bXg6IHRydWUgfSwgZ2V0RG9jdW1lbnQoKS50aXRsZSwgd2luZG93LmxvY2F0aW9uLmhyZWYpXG4gIH1cblxuICAvKipcbiAgICogQHBhcmFtIHtzdHJpbmd9IHBhdGhcbiAgICovXG4gIGZ1bmN0aW9uIHB1c2hVcmxJbnRvSGlzdG9yeShwYXRoKSB7XG4gIC8vIHJlbW92ZSB0aGUgY2FjaGUgYnVzdGVyIHBhcmFtZXRlciwgaWYgYW55XG4gICAgaWYgKGh0bXguY29uZmlnLmdldENhY2hlQnVzdGVyUGFyYW0pIHtcbiAgICAgIHBhdGggPSBwYXRoLnJlcGxhY2UoL29yZ1xcLmh0bXhcXC5jYWNoZS1idXN0ZXI9W14mXSomPy8sICcnKVxuICAgICAgaWYgKGVuZHNXaXRoKHBhdGgsICcmJykgfHwgZW5kc1dpdGgocGF0aCwgJz8nKSkge1xuICAgICAgICBwYXRoID0gcGF0aC5zbGljZSgwLCAtMSlcbiAgICAgIH1cbiAgICB9XG4gICAgaWYgKGh0bXguY29uZmlnLmhpc3RvcnlFbmFibGVkKSB7XG4gICAgICBoaXN0b3J5LnB1c2hTdGF0ZSh7IGh0bXg6IHRydWUgfSwgJycsIHBhdGgpXG4gICAgfVxuICAgIGN1cnJlbnRQYXRoRm9ySGlzdG9yeSA9IHBhdGhcbiAgfVxuXG4gIC8qKlxuICAgKiBAcGFyYW0ge3N0cmluZ30gcGF0aFxuICAgKi9cbiAgZnVuY3Rpb24gcmVwbGFjZVVybEluSGlzdG9yeShwYXRoKSB7XG4gICAgaWYgKGh0bXguY29uZmlnLmhpc3RvcnlFbmFibGVkKSBoaXN0b3J5LnJlcGxhY2VTdGF0ZSh7IGh0bXg6IHRydWUgfSwgJycsIHBhdGgpXG4gICAgY3VycmVudFBhdGhGb3JIaXN0b3J5ID0gcGF0aFxuICB9XG5cbiAgLyoqXG4gICAqIEBwYXJhbSB7SHRteFNldHRsZVRhc2tbXX0gdGFza3NcbiAgICovXG4gIGZ1bmN0aW9uIHNldHRsZUltbWVkaWF0ZWx5KHRhc2tzKSB7XG4gICAgZm9yRWFjaCh0YXNrcywgZnVuY3Rpb24odGFzaykge1xuICAgICAgdGFzay5jYWxsKHVuZGVmaW5lZClcbiAgICB9KVxuICB9XG5cbiAgLyoqXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBwYXRoXG4gICAqL1xuICBmdW5jdGlvbiBsb2FkSGlzdG9yeUZyb21TZXJ2ZXIocGF0aCkge1xuICAgIGNvbnN0IHJlcXVlc3QgPSBuZXcgWE1MSHR0cFJlcXVlc3QoKVxuICAgIGNvbnN0IGRldGFpbHMgPSB7IHBhdGgsIHhocjogcmVxdWVzdCB9XG4gICAgdHJpZ2dlckV2ZW50KGdldERvY3VtZW50KCkuYm9keSwgJ2h0bXg6aGlzdG9yeUNhY2hlTWlzcycsIGRldGFpbHMpXG4gICAgcmVxdWVzdC5vcGVuKCdHRVQnLCBwYXRoLCB0cnVlKVxuICAgIHJlcXVlc3Quc2V0UmVxdWVzdEhlYWRlcignSFgtUmVxdWVzdCcsICd0cnVlJylcbiAgICByZXF1ZXN0LnNldFJlcXVlc3RIZWFkZXIoJ0hYLUhpc3RvcnktUmVzdG9yZS1SZXF1ZXN0JywgJ3RydWUnKVxuICAgIHJlcXVlc3Quc2V0UmVxdWVzdEhlYWRlcignSFgtQ3VycmVudC1VUkwnLCBnZXREb2N1bWVudCgpLmxvY2F0aW9uLmhyZWYpXG4gICAgcmVxdWVzdC5vbmxvYWQgPSBmdW5jdGlvbigpIHtcbiAgICAgIGlmICh0aGlzLnN0YXR1cyA+PSAyMDAgJiYgdGhpcy5zdGF0dXMgPCA0MDApIHtcbiAgICAgICAgdHJpZ2dlckV2ZW50KGdldERvY3VtZW50KCkuYm9keSwgJ2h0bXg6aGlzdG9yeUNhY2hlTWlzc0xvYWQnLCBkZXRhaWxzKVxuICAgICAgICBjb25zdCBmcmFnbWVudCA9IG1ha2VGcmFnbWVudCh0aGlzLnJlc3BvbnNlKVxuICAgICAgICAvKiogQHR5cGUgUGFyZW50Tm9kZSAqL1xuICAgICAgICBjb25zdCBjb250ZW50ID0gZnJhZ21lbnQucXVlcnlTZWxlY3RvcignW2h4LWhpc3RvcnktZWx0XSxbZGF0YS1oeC1oaXN0b3J5LWVsdF0nKSB8fCBmcmFnbWVudFxuICAgICAgICBjb25zdCBoaXN0b3J5RWxlbWVudCA9IGdldEhpc3RvcnlFbGVtZW50KClcbiAgICAgICAgY29uc3Qgc2V0dGxlSW5mbyA9IG1ha2VTZXR0bGVJbmZvKGhpc3RvcnlFbGVtZW50KVxuICAgICAgICBoYW5kbGVUaXRsZShmcmFnbWVudC50aXRsZSlcblxuICAgICAgICBzd2FwSW5uZXJIVE1MKGhpc3RvcnlFbGVtZW50LCBjb250ZW50LCBzZXR0bGVJbmZvKVxuICAgICAgICBzZXR0bGVJbW1lZGlhdGVseShzZXR0bGVJbmZvLnRhc2tzKVxuICAgICAgICBjdXJyZW50UGF0aEZvckhpc3RvcnkgPSBwYXRoXG4gICAgICAgIHRyaWdnZXJFdmVudChnZXREb2N1bWVudCgpLmJvZHksICdodG14Omhpc3RvcnlSZXN0b3JlJywgeyBwYXRoLCBjYWNoZU1pc3M6IHRydWUsIHNlcnZlclJlc3BvbnNlOiB0aGlzLnJlc3BvbnNlIH0pXG4gICAgICB9IGVsc2Uge1xuICAgICAgICB0cmlnZ2VyRXJyb3JFdmVudChnZXREb2N1bWVudCgpLmJvZHksICdodG14Omhpc3RvcnlDYWNoZU1pc3NMb2FkRXJyb3InLCBkZXRhaWxzKVxuICAgICAgfVxuICAgIH1cbiAgICByZXF1ZXN0LnNlbmQoKVxuICB9XG5cbiAgLyoqXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBbcGF0aF1cbiAgICovXG4gIGZ1bmN0aW9uIHJlc3RvcmVIaXN0b3J5KHBhdGgpIHtcbiAgICBzYXZlQ3VycmVudFBhZ2VUb0hpc3RvcnkoKVxuICAgIHBhdGggPSBwYXRoIHx8IGxvY2F0aW9uLnBhdGhuYW1lICsgbG9jYXRpb24uc2VhcmNoXG4gICAgY29uc3QgY2FjaGVkID0gZ2V0Q2FjaGVkSGlzdG9yeShwYXRoKVxuICAgIGlmIChjYWNoZWQpIHtcbiAgICAgIGNvbnN0IGZyYWdtZW50ID0gbWFrZUZyYWdtZW50KGNhY2hlZC5jb250ZW50KVxuICAgICAgY29uc3QgaGlzdG9yeUVsZW1lbnQgPSBnZXRIaXN0b3J5RWxlbWVudCgpXG4gICAgICBjb25zdCBzZXR0bGVJbmZvID0gbWFrZVNldHRsZUluZm8oaGlzdG9yeUVsZW1lbnQpXG4gICAgICBoYW5kbGVUaXRsZShmcmFnbWVudC50aXRsZSlcbiAgICAgIHN3YXBJbm5lckhUTUwoaGlzdG9yeUVsZW1lbnQsIGZyYWdtZW50LCBzZXR0bGVJbmZvKVxuICAgICAgc2V0dGxlSW1tZWRpYXRlbHkoc2V0dGxlSW5mby50YXNrcylcbiAgICAgIGdldFdpbmRvdygpLnNldFRpbWVvdXQoZnVuY3Rpb24oKSB7XG4gICAgICAgIHdpbmRvdy5zY3JvbGxUbygwLCBjYWNoZWQuc2Nyb2xsKVxuICAgICAgfSwgMCkgLy8gbmV4dCAndGljaycsIHNvIGJyb3dzZXIgaGFzIHRpbWUgdG8gcmVuZGVyIGxheW91dFxuICAgICAgY3VycmVudFBhdGhGb3JIaXN0b3J5ID0gcGF0aFxuICAgICAgdHJpZ2dlckV2ZW50KGdldERvY3VtZW50KCkuYm9keSwgJ2h0bXg6aGlzdG9yeVJlc3RvcmUnLCB7IHBhdGgsIGl0ZW06IGNhY2hlZCB9KVxuICAgIH0gZWxzZSB7XG4gICAgICBpZiAoaHRteC5jb25maWcucmVmcmVzaE9uSGlzdG9yeU1pc3MpIHtcbiAgICAgICAgLy8gQHRzLWlnbm9yZTogb3B0aW9uYWwgcGFyYW1ldGVyIGluIHJlbG9hZCgpIGZ1bmN0aW9uIHRocm93cyBlcnJvclxuICAgICAgICAvLyBub2luc3BlY3Rpb24gSlNVbnJlc29sdmVkUmVmZXJlbmNlXG4gICAgICAgIHdpbmRvdy5sb2NhdGlvbi5yZWxvYWQodHJ1ZSlcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIGxvYWRIaXN0b3J5RnJvbVNlcnZlcihwYXRoKVxuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBAcGFyYW0ge0VsZW1lbnR9IGVsdFxuICAgKiBAcmV0dXJucyB7RWxlbWVudFtdfVxuICAgKi9cbiAgZnVuY3Rpb24gYWRkUmVxdWVzdEluZGljYXRvckNsYXNzZXMoZWx0KSB7XG4gICAgbGV0IGluZGljYXRvcnMgPSAvKiogQHR5cGUgRWxlbWVudFtdICovIChmaW5kQXR0cmlidXRlVGFyZ2V0cyhlbHQsICdoeC1pbmRpY2F0b3InKSlcbiAgICBpZiAoaW5kaWNhdG9ycyA9PSBudWxsKSB7XG4gICAgICBpbmRpY2F0b3JzID0gW2VsdF1cbiAgICB9XG4gICAgZm9yRWFjaChpbmRpY2F0b3JzLCBmdW5jdGlvbihpYykge1xuICAgICAgY29uc3QgaW50ZXJuYWxEYXRhID0gZ2V0SW50ZXJuYWxEYXRhKGljKVxuICAgICAgaW50ZXJuYWxEYXRhLnJlcXVlc3RDb3VudCA9IChpbnRlcm5hbERhdGEucmVxdWVzdENvdW50IHx8IDApICsgMVxuICAgICAgaWMuY2xhc3NMaXN0LmFkZC5jYWxsKGljLmNsYXNzTGlzdCwgaHRteC5jb25maWcucmVxdWVzdENsYXNzKVxuICAgIH0pXG4gICAgcmV0dXJuIGluZGljYXRvcnNcbiAgfVxuXG4gIC8qKlxuICAgKiBAcGFyYW0ge0VsZW1lbnR9IGVsdFxuICAgKiBAcmV0dXJucyB7RWxlbWVudFtdfVxuICAgKi9cbiAgZnVuY3Rpb24gZGlzYWJsZUVsZW1lbnRzKGVsdCkge1xuICAgIGxldCBkaXNhYmxlZEVsdHMgPSAvKiogQHR5cGUgRWxlbWVudFtdICovIChmaW5kQXR0cmlidXRlVGFyZ2V0cyhlbHQsICdoeC1kaXNhYmxlZC1lbHQnKSlcbiAgICBpZiAoZGlzYWJsZWRFbHRzID09IG51bGwpIHtcbiAgICAgIGRpc2FibGVkRWx0cyA9IFtdXG4gICAgfVxuICAgIGZvckVhY2goZGlzYWJsZWRFbHRzLCBmdW5jdGlvbihkaXNhYmxlZEVsZW1lbnQpIHtcbiAgICAgIGNvbnN0IGludGVybmFsRGF0YSA9IGdldEludGVybmFsRGF0YShkaXNhYmxlZEVsZW1lbnQpXG4gICAgICBpbnRlcm5hbERhdGEucmVxdWVzdENvdW50ID0gKGludGVybmFsRGF0YS5yZXF1ZXN0Q291bnQgfHwgMCkgKyAxXG4gICAgICBkaXNhYmxlZEVsZW1lbnQuc2V0QXR0cmlidXRlKCdkaXNhYmxlZCcsICcnKVxuICAgICAgZGlzYWJsZWRFbGVtZW50LnNldEF0dHJpYnV0ZSgnZGF0YS1kaXNhYmxlZC1ieS1odG14JywgJycpXG4gICAgfSlcbiAgICByZXR1cm4gZGlzYWJsZWRFbHRzXG4gIH1cblxuICAvKipcbiAgICogQHBhcmFtIHtFbGVtZW50W119IGluZGljYXRvcnNcbiAgICogQHBhcmFtIHtFbGVtZW50W119IGRpc2FibGVkXG4gICAqL1xuICBmdW5jdGlvbiByZW1vdmVSZXF1ZXN0SW5kaWNhdG9ycyhpbmRpY2F0b3JzLCBkaXNhYmxlZCkge1xuICAgIGZvckVhY2goaW5kaWNhdG9ycywgZnVuY3Rpb24oaWMpIHtcbiAgICAgIGNvbnN0IGludGVybmFsRGF0YSA9IGdldEludGVybmFsRGF0YShpYylcbiAgICAgIGludGVybmFsRGF0YS5yZXF1ZXN0Q291bnQgPSAoaW50ZXJuYWxEYXRhLnJlcXVlc3RDb3VudCB8fCAwKSAtIDFcbiAgICAgIGlmIChpbnRlcm5hbERhdGEucmVxdWVzdENvdW50ID09PSAwKSB7XG4gICAgICAgIGljLmNsYXNzTGlzdC5yZW1vdmUuY2FsbChpYy5jbGFzc0xpc3QsIGh0bXguY29uZmlnLnJlcXVlc3RDbGFzcylcbiAgICAgIH1cbiAgICB9KVxuICAgIGZvckVhY2goZGlzYWJsZWQsIGZ1bmN0aW9uKGRpc2FibGVkRWxlbWVudCkge1xuICAgICAgY29uc3QgaW50ZXJuYWxEYXRhID0gZ2V0SW50ZXJuYWxEYXRhKGRpc2FibGVkRWxlbWVudClcbiAgICAgIGludGVybmFsRGF0YS5yZXF1ZXN0Q291bnQgPSAoaW50ZXJuYWxEYXRhLnJlcXVlc3RDb3VudCB8fCAwKSAtIDFcbiAgICAgIGlmIChpbnRlcm5hbERhdGEucmVxdWVzdENvdW50ID09PSAwKSB7XG4gICAgICAgIGRpc2FibGVkRWxlbWVudC5yZW1vdmVBdHRyaWJ1dGUoJ2Rpc2FibGVkJylcbiAgICAgICAgZGlzYWJsZWRFbGVtZW50LnJlbW92ZUF0dHJpYnV0ZSgnZGF0YS1kaXNhYmxlZC1ieS1odG14JylcbiAgICAgIH1cbiAgICB9KVxuICB9XG5cbiAgLy89ID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbiAgLy8gSW5wdXQgVmFsdWUgUHJvY2Vzc2luZ1xuICAvLz0gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuXG4gIC8qKlxuICAgKiBAcGFyYW0ge0VsZW1lbnRbXX0gcHJvY2Vzc2VkXG4gICAqIEBwYXJhbSB7RWxlbWVudH0gZWx0XG4gICAqIEByZXR1cm5zIHtib29sZWFufVxuICAgKi9cbiAgZnVuY3Rpb24gaGF2ZVNlZW5Ob2RlKHByb2Nlc3NlZCwgZWx0KSB7XG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCBwcm9jZXNzZWQubGVuZ3RoOyBpKyspIHtcbiAgICAgIGNvbnN0IG5vZGUgPSBwcm9jZXNzZWRbaV1cbiAgICAgIGlmIChub2RlLmlzU2FtZU5vZGUoZWx0KSkge1xuICAgICAgICByZXR1cm4gdHJ1ZVxuICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4gZmFsc2VcbiAgfVxuXG4gIC8qKlxuICAgKiBAcGFyYW0ge0VsZW1lbnR9IGVsZW1lbnRcbiAgICogQHJldHVybiB7Ym9vbGVhbn1cbiAgICovXG4gIGZ1bmN0aW9uIHNob3VsZEluY2x1ZGUoZWxlbWVudCkge1xuICAgIC8vIENhc3QgdG8gdHJpY2sgdHNjLCB1bmRlZmluZWQgdmFsdWVzIHdpbGwgd29yayBmaW5lIGhlcmVcbiAgICBjb25zdCBlbHQgPSAvKiogQHR5cGUge0hUTUxJbnB1dEVsZW1lbnR9ICovIChlbGVtZW50KVxuICAgIGlmIChlbHQubmFtZSA9PT0gJycgfHwgZWx0Lm5hbWUgPT0gbnVsbCB8fCBlbHQuZGlzYWJsZWQgfHwgY2xvc2VzdChlbHQsICdmaWVsZHNldFtkaXNhYmxlZF0nKSkge1xuICAgICAgcmV0dXJuIGZhbHNlXG4gICAgfVxuICAgIC8vIGlnbm9yZSBcInN1Ym1pdHRlclwiIHR5cGVzIChzZWUgalF1ZXJ5IHNyYy9zZXJpYWxpemUuanMpXG4gICAgaWYgKGVsdC50eXBlID09PSAnYnV0dG9uJyB8fCBlbHQudHlwZSA9PT0gJ3N1Ym1pdCcgfHwgZWx0LnRhZ05hbWUgPT09ICdpbWFnZScgfHwgZWx0LnRhZ05hbWUgPT09ICdyZXNldCcgfHwgZWx0LnRhZ05hbWUgPT09ICdmaWxlJykge1xuICAgICAgcmV0dXJuIGZhbHNlXG4gICAgfVxuICAgIGlmIChlbHQudHlwZSA9PT0gJ2NoZWNrYm94JyB8fCBlbHQudHlwZSA9PT0gJ3JhZGlvJykge1xuICAgICAgcmV0dXJuIGVsdC5jaGVja2VkXG4gICAgfVxuICAgIHJldHVybiB0cnVlXG4gIH1cblxuICAvKiogQHBhcmFtIHtzdHJpbmd9IG5hbWVcbiAgICogQHBhcmFtIHtzdHJpbmd8QXJyYXl8Rm9ybURhdGFFbnRyeVZhbHVlfSB2YWx1ZVxuICAgKiBAcGFyYW0ge0Zvcm1EYXRhfSBmb3JtRGF0YSAqL1xuICBmdW5jdGlvbiBhZGRWYWx1ZVRvRm9ybURhdGEobmFtZSwgdmFsdWUsIGZvcm1EYXRhKSB7XG4gICAgaWYgKG5hbWUgIT0gbnVsbCAmJiB2YWx1ZSAhPSBudWxsKSB7XG4gICAgICBpZiAoQXJyYXkuaXNBcnJheSh2YWx1ZSkpIHtcbiAgICAgICAgdmFsdWUuZm9yRWFjaChmdW5jdGlvbih2KSB7IGZvcm1EYXRhLmFwcGVuZChuYW1lLCB2KSB9KVxuICAgICAgfSBlbHNlIHtcbiAgICAgICAgZm9ybURhdGEuYXBwZW5kKG5hbWUsIHZhbHVlKVxuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIC8qKiBAcGFyYW0ge3N0cmluZ30gbmFtZVxuICAgKiBAcGFyYW0ge3N0cmluZ3xBcnJheX0gdmFsdWVcbiAgICogQHBhcmFtIHtGb3JtRGF0YX0gZm9ybURhdGEgKi9cbiAgZnVuY3Rpb24gcmVtb3ZlVmFsdWVGcm9tRm9ybURhdGEobmFtZSwgdmFsdWUsIGZvcm1EYXRhKSB7XG4gICAgaWYgKG5hbWUgIT0gbnVsbCAmJiB2YWx1ZSAhPSBudWxsKSB7XG4gICAgICBsZXQgdmFsdWVzID0gZm9ybURhdGEuZ2V0QWxsKG5hbWUpXG4gICAgICBpZiAoQXJyYXkuaXNBcnJheSh2YWx1ZSkpIHtcbiAgICAgICAgdmFsdWVzID0gdmFsdWVzLmZpbHRlcih2ID0+IHZhbHVlLmluZGV4T2YodikgPCAwKVxuICAgICAgfSBlbHNlIHtcbiAgICAgICAgdmFsdWVzID0gdmFsdWVzLmZpbHRlcih2ID0+IHYgIT09IHZhbHVlKVxuICAgICAgfVxuICAgICAgZm9ybURhdGEuZGVsZXRlKG5hbWUpXG4gICAgICBmb3JFYWNoKHZhbHVlcywgdiA9PiBmb3JtRGF0YS5hcHBlbmQobmFtZSwgdikpXG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIEBwYXJhbSB7RWxlbWVudFtdfSBwcm9jZXNzZWRcbiAgICogQHBhcmFtIHtGb3JtRGF0YX0gZm9ybURhdGFcbiAgICogQHBhcmFtIHtIdG14RWxlbWVudFZhbGlkYXRpb25FcnJvcltdfSBlcnJvcnNcbiAgICogQHBhcmFtIHtFbGVtZW50fEhUTUxJbnB1dEVsZW1lbnR8SFRNTFNlbGVjdEVsZW1lbnR8SFRNTEZvcm1FbGVtZW50fSBlbHRcbiAgICogQHBhcmFtIHtib29sZWFufSB2YWxpZGF0ZVxuICAgKi9cbiAgZnVuY3Rpb24gcHJvY2Vzc0lucHV0VmFsdWUocHJvY2Vzc2VkLCBmb3JtRGF0YSwgZXJyb3JzLCBlbHQsIHZhbGlkYXRlKSB7XG4gICAgaWYgKGVsdCA9PSBudWxsIHx8IGhhdmVTZWVuTm9kZShwcm9jZXNzZWQsIGVsdCkpIHtcbiAgICAgIHJldHVyblxuICAgIH0gZWxzZSB7XG4gICAgICBwcm9jZXNzZWQucHVzaChlbHQpXG4gICAgfVxuICAgIGlmIChzaG91bGRJbmNsdWRlKGVsdCkpIHtcbiAgICAgIGNvbnN0IG5hbWUgPSBnZXRSYXdBdHRyaWJ1dGUoZWx0LCAnbmFtZScpXG4gICAgICAvLyBAdHMtaWdub3JlIHZhbHVlIHdpbGwgYmUgdW5kZWZpbmVkIGZvciBub24taW5wdXQgZWxlbWVudHMsIHdoaWNoIGlzIGZpbmVcbiAgICAgIGxldCB2YWx1ZSA9IGVsdC52YWx1ZVxuICAgICAgaWYgKGVsdCBpbnN0YW5jZW9mIEhUTUxTZWxlY3RFbGVtZW50ICYmIGVsdC5tdWx0aXBsZSkge1xuICAgICAgICB2YWx1ZSA9IHRvQXJyYXkoZWx0LnF1ZXJ5U2VsZWN0b3JBbGwoJ29wdGlvbjpjaGVja2VkJykpLm1hcChmdW5jdGlvbihlKSB7IHJldHVybiAoLyoqIEB0eXBlIEhUTUxPcHRpb25FbGVtZW50ICovKGUpKS52YWx1ZSB9KVxuICAgICAgfVxuICAgICAgLy8gaW5jbHVkZSBmaWxlIGlucHV0c1xuICAgICAgaWYgKGVsdCBpbnN0YW5jZW9mIEhUTUxJbnB1dEVsZW1lbnQgJiYgZWx0LmZpbGVzKSB7XG4gICAgICAgIHZhbHVlID0gdG9BcnJheShlbHQuZmlsZXMpXG4gICAgICB9XG4gICAgICBhZGRWYWx1ZVRvRm9ybURhdGEobmFtZSwgdmFsdWUsIGZvcm1EYXRhKVxuICAgICAgaWYgKHZhbGlkYXRlKSB7XG4gICAgICAgIHZhbGlkYXRlRWxlbWVudChlbHQsIGVycm9ycylcbiAgICAgIH1cbiAgICB9XG4gICAgaWYgKGVsdCBpbnN0YW5jZW9mIEhUTUxGb3JtRWxlbWVudCkge1xuICAgICAgZm9yRWFjaChlbHQuZWxlbWVudHMsIGZ1bmN0aW9uKGlucHV0KSB7XG4gICAgICAgIGlmIChwcm9jZXNzZWQuaW5kZXhPZihpbnB1dCkgPj0gMCkge1xuICAgICAgICAgIC8vIFRoZSBpbnB1dCBoYXMgYWxyZWFkeSBiZWVuIHByb2Nlc3NlZCBhbmQgYWRkZWQgdG8gdGhlIHZhbHVlcywgYnV0IHRoZSBGb3JtRGF0YSB0aGF0IHdpbGwgYmVcbiAgICAgICAgICAvLyAgY29uc3RydWN0ZWQgcmlnaHQgYWZ0ZXIgb24gdGhlIGZvcm0sIHdpbGwgaW5jbHVkZSBpdCBvbmNlIGFnYWluLiBTbyByZW1vdmUgdGhhdCBpbnB1dCdzIHZhbHVlXG4gICAgICAgICAgLy8gIG5vdyB0byBhdm9pZCBkdXBsaWNhdGVzXG4gICAgICAgICAgcmVtb3ZlVmFsdWVGcm9tRm9ybURhdGEoaW5wdXQubmFtZSwgaW5wdXQudmFsdWUsIGZvcm1EYXRhKVxuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIHByb2Nlc3NlZC5wdXNoKGlucHV0KVxuICAgICAgICB9XG4gICAgICAgIGlmICh2YWxpZGF0ZSkge1xuICAgICAgICAgIHZhbGlkYXRlRWxlbWVudChpbnB1dCwgZXJyb3JzKVxuICAgICAgICB9XG4gICAgICB9KVxuICAgICAgbmV3IEZvcm1EYXRhKGVsdCkuZm9yRWFjaChmdW5jdGlvbih2YWx1ZSwgbmFtZSkge1xuICAgICAgICBpZiAodmFsdWUgaW5zdGFuY2VvZiBGaWxlICYmIHZhbHVlLm5hbWUgPT09ICcnKSB7XG4gICAgICAgICAgcmV0dXJuIC8vIGlnbm9yZSBuby1uYW1lIGZpbGVzXG4gICAgICAgIH1cbiAgICAgICAgYWRkVmFsdWVUb0Zvcm1EYXRhKG5hbWUsIHZhbHVlLCBmb3JtRGF0YSlcbiAgICAgIH0pXG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqXG4gICAqIEBwYXJhbSB7RWxlbWVudH0gZWx0XG4gICAqIEBwYXJhbSB7SHRteEVsZW1lbnRWYWxpZGF0aW9uRXJyb3JbXX0gZXJyb3JzXG4gICAqL1xuICBmdW5jdGlvbiB2YWxpZGF0ZUVsZW1lbnQoZWx0LCBlcnJvcnMpIHtcbiAgICBjb25zdCBlbGVtZW50ID0gLyoqIEB0eXBlIHtIVE1MRWxlbWVudCAmIEVsZW1lbnRJbnRlcm5hbHN9ICovIChlbHQpXG4gICAgaWYgKGVsZW1lbnQud2lsbFZhbGlkYXRlKSB7XG4gICAgICB0cmlnZ2VyRXZlbnQoZWxlbWVudCwgJ2h0bXg6dmFsaWRhdGlvbjp2YWxpZGF0ZScpXG4gICAgICBpZiAoIWVsZW1lbnQuY2hlY2tWYWxpZGl0eSgpKSB7XG4gICAgICAgIGVycm9ycy5wdXNoKHsgZWx0OiBlbGVtZW50LCBtZXNzYWdlOiBlbGVtZW50LnZhbGlkYXRpb25NZXNzYWdlLCB2YWxpZGl0eTogZWxlbWVudC52YWxpZGl0eSB9KVxuICAgICAgICB0cmlnZ2VyRXZlbnQoZWxlbWVudCwgJ2h0bXg6dmFsaWRhdGlvbjpmYWlsZWQnLCB7IG1lc3NhZ2U6IGVsZW1lbnQudmFsaWRhdGlvbk1lc3NhZ2UsIHZhbGlkaXR5OiBlbGVtZW50LnZhbGlkaXR5IH0pXG4gICAgICB9XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIE92ZXJyaWRlIHZhbHVlcyBpbiB0aGUgb25lIEZvcm1EYXRhIHdpdGggdGhvc2UgZnJvbSBhbm90aGVyLlxuICAgKiBAcGFyYW0ge0Zvcm1EYXRhfSByZWNlaXZlciB0aGUgZm9ybWRhdGEgdGhhdCB3aWxsIGJlIG11dGF0ZWRcbiAgICogQHBhcmFtIHtGb3JtRGF0YX0gZG9ub3IgdGhlIGZvcm1kYXRhIHRoYXQgd2lsbCBwcm92aWRlIHRoZSBvdmVycmlkaW5nIHZhbHVlc1xuICAgKiBAcmV0dXJucyB7Rm9ybURhdGF9IHRoZSB7QGxpbmtjb2RlIHJlY2VpdmVyfVxuICAgKi9cbiAgZnVuY3Rpb24gb3ZlcnJpZGVGb3JtRGF0YShyZWNlaXZlciwgZG9ub3IpIHtcbiAgICBmb3IgKGNvbnN0IGtleSBvZiBkb25vci5rZXlzKCkpIHtcbiAgICAgIHJlY2VpdmVyLmRlbGV0ZShrZXkpXG4gICAgfVxuICAgIGRvbm9yLmZvckVhY2goZnVuY3Rpb24odmFsdWUsIGtleSkge1xuICAgICAgcmVjZWl2ZXIuYXBwZW5kKGtleSwgdmFsdWUpXG4gICAgfSlcbiAgICByZXR1cm4gcmVjZWl2ZXJcbiAgfVxuXG4gIC8qKlxuICogQHBhcmFtIHtFbGVtZW50fEhUTUxGb3JtRWxlbWVudH0gZWx0XG4gKiBAcGFyYW0ge0h0dHBWZXJifSB2ZXJiXG4gKiBAcmV0dXJucyB7e2Vycm9yczogSHRteEVsZW1lbnRWYWxpZGF0aW9uRXJyb3JbXSwgZm9ybURhdGE6IEZvcm1EYXRhLCB2YWx1ZXM6IE9iamVjdH19XG4gKi9cbiAgZnVuY3Rpb24gZ2V0SW5wdXRWYWx1ZXMoZWx0LCB2ZXJiKSB7XG4gICAgLyoqIEB0eXBlIEVsZW1lbnRbXSAqL1xuICAgIGNvbnN0IHByb2Nlc3NlZCA9IFtdXG4gICAgY29uc3QgZm9ybURhdGEgPSBuZXcgRm9ybURhdGEoKVxuICAgIGNvbnN0IHByaW9yaXR5Rm9ybURhdGEgPSBuZXcgRm9ybURhdGEoKVxuICAgIC8qKiBAdHlwZSBIdG14RWxlbWVudFZhbGlkYXRpb25FcnJvcltdICovXG4gICAgY29uc3QgZXJyb3JzID0gW11cbiAgICBjb25zdCBpbnRlcm5hbERhdGEgPSBnZXRJbnRlcm5hbERhdGEoZWx0KVxuICAgIGlmIChpbnRlcm5hbERhdGEubGFzdEJ1dHRvbkNsaWNrZWQgJiYgIWJvZHlDb250YWlucyhpbnRlcm5hbERhdGEubGFzdEJ1dHRvbkNsaWNrZWQpKSB7XG4gICAgICBpbnRlcm5hbERhdGEubGFzdEJ1dHRvbkNsaWNrZWQgPSBudWxsXG4gICAgfVxuXG4gICAgLy8gb25seSB2YWxpZGF0ZSB3aGVuIGZvcm0gaXMgZGlyZWN0bHkgc3VibWl0dGVkIGFuZCBub3ZhbGlkYXRlIG9yIGZvcm1ub3ZhbGlkYXRlIGFyZSBub3Qgc2V0XG4gICAgLy8gb3IgaWYgdGhlIGVsZW1lbnQgaGFzIGFuIGV4cGxpY2l0IGh4LXZhbGlkYXRlPVwidHJ1ZVwiIG9uIGl0XG4gICAgbGV0IHZhbGlkYXRlID0gKGVsdCBpbnN0YW5jZW9mIEhUTUxGb3JtRWxlbWVudCAmJiBlbHQubm9WYWxpZGF0ZSAhPT0gdHJ1ZSkgfHwgZ2V0QXR0cmlidXRlVmFsdWUoZWx0LCAnaHgtdmFsaWRhdGUnKSA9PT0gJ3RydWUnXG4gICAgaWYgKGludGVybmFsRGF0YS5sYXN0QnV0dG9uQ2xpY2tlZCkge1xuICAgICAgdmFsaWRhdGUgPSB2YWxpZGF0ZSAmJiBpbnRlcm5hbERhdGEubGFzdEJ1dHRvbkNsaWNrZWQuZm9ybU5vVmFsaWRhdGUgIT09IHRydWVcbiAgICB9XG5cbiAgICAvLyBmb3IgYSBub24tR0VUIGluY2x1ZGUgdGhlIGNsb3Nlc3QgZm9ybVxuICAgIGlmICh2ZXJiICE9PSAnZ2V0Jykge1xuICAgICAgcHJvY2Vzc0lucHV0VmFsdWUocHJvY2Vzc2VkLCBwcmlvcml0eUZvcm1EYXRhLCBlcnJvcnMsIGNsb3Nlc3QoZWx0LCAnZm9ybScpLCB2YWxpZGF0ZSlcbiAgICB9XG5cbiAgICAvLyBpbmNsdWRlIHRoZSBlbGVtZW50IGl0c2VsZlxuICAgIHByb2Nlc3NJbnB1dFZhbHVlKHByb2Nlc3NlZCwgZm9ybURhdGEsIGVycm9ycywgZWx0LCB2YWxpZGF0ZSlcblxuICAgIC8vIGlmIGEgYnV0dG9uIG9yIHN1Ym1pdCB3YXMgY2xpY2tlZCBsYXN0LCBpbmNsdWRlIGl0cyB2YWx1ZVxuICAgIGlmIChpbnRlcm5hbERhdGEubGFzdEJ1dHRvbkNsaWNrZWQgfHwgZWx0LnRhZ05hbWUgPT09ICdCVVRUT04nIHx8XG4gICAgKGVsdC50YWdOYW1lID09PSAnSU5QVVQnICYmIGdldFJhd0F0dHJpYnV0ZShlbHQsICd0eXBlJykgPT09ICdzdWJtaXQnKSkge1xuICAgICAgY29uc3QgYnV0dG9uID0gaW50ZXJuYWxEYXRhLmxhc3RCdXR0b25DbGlja2VkIHx8ICgvKiogQHR5cGUgSFRNTElucHV0RWxlbWVudHxIVE1MQnV0dG9uRWxlbWVudCAqLyhlbHQpKVxuICAgICAgY29uc3QgbmFtZSA9IGdldFJhd0F0dHJpYnV0ZShidXR0b24sICduYW1lJylcbiAgICAgIGFkZFZhbHVlVG9Gb3JtRGF0YShuYW1lLCBidXR0b24udmFsdWUsIHByaW9yaXR5Rm9ybURhdGEpXG4gICAgfVxuXG4gICAgLy8gaW5jbHVkZSBhbnkgZXhwbGljaXQgaW5jbHVkZXNcbiAgICBjb25zdCBpbmNsdWRlcyA9IGZpbmRBdHRyaWJ1dGVUYXJnZXRzKGVsdCwgJ2h4LWluY2x1ZGUnKVxuICAgIGZvckVhY2goaW5jbHVkZXMsIGZ1bmN0aW9uKG5vZGUpIHtcbiAgICAgIHByb2Nlc3NJbnB1dFZhbHVlKHByb2Nlc3NlZCwgZm9ybURhdGEsIGVycm9ycywgYXNFbGVtZW50KG5vZGUpLCB2YWxpZGF0ZSlcbiAgICAgIC8vIGlmIGEgbm9uLWZvcm0gaXMgaW5jbHVkZWQsIGluY2x1ZGUgYW55IGlucHV0IHZhbHVlcyB3aXRoaW4gaXRcbiAgICAgIGlmICghbWF0Y2hlcyhub2RlLCAnZm9ybScpKSB7XG4gICAgICAgIGZvckVhY2goYXNQYXJlbnROb2RlKG5vZGUpLnF1ZXJ5U2VsZWN0b3JBbGwoSU5QVVRfU0VMRUNUT1IpLCBmdW5jdGlvbihkZXNjZW5kYW50KSB7XG4gICAgICAgICAgcHJvY2Vzc0lucHV0VmFsdWUocHJvY2Vzc2VkLCBmb3JtRGF0YSwgZXJyb3JzLCBkZXNjZW5kYW50LCB2YWxpZGF0ZSlcbiAgICAgICAgfSlcbiAgICAgIH1cbiAgICB9KVxuXG4gICAgLy8gdmFsdWVzIGZyb20gYSA8Zm9ybT4gdGFrZSBwcmVjZWRlbmNlLCBvdmVycmlkaW5nIHRoZSByZWd1bGFyIHZhbHVlc1xuICAgIG92ZXJyaWRlRm9ybURhdGEoZm9ybURhdGEsIHByaW9yaXR5Rm9ybURhdGEpXG5cbiAgICByZXR1cm4geyBlcnJvcnMsIGZvcm1EYXRhLCB2YWx1ZXM6IGZvcm1EYXRhUHJveHkoZm9ybURhdGEpIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBAcGFyYW0ge3N0cmluZ30gcmV0dXJuU3RyXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBuYW1lXG4gICAqIEBwYXJhbSB7YW55fSByZWFsVmFsdWVcbiAgICogQHJldHVybnMge3N0cmluZ31cbiAgICovXG4gIGZ1bmN0aW9uIGFwcGVuZFBhcmFtKHJldHVyblN0ciwgbmFtZSwgcmVhbFZhbHVlKSB7XG4gICAgaWYgKHJldHVyblN0ciAhPT0gJycpIHtcbiAgICAgIHJldHVyblN0ciArPSAnJidcbiAgICB9XG4gICAgaWYgKFN0cmluZyhyZWFsVmFsdWUpID09PSAnW29iamVjdCBPYmplY3RdJykge1xuICAgICAgcmVhbFZhbHVlID0gSlNPTi5zdHJpbmdpZnkocmVhbFZhbHVlKVxuICAgIH1cbiAgICBjb25zdCBzID0gZW5jb2RlVVJJQ29tcG9uZW50KHJlYWxWYWx1ZSlcbiAgICByZXR1cm5TdHIgKz0gZW5jb2RlVVJJQ29tcG9uZW50KG5hbWUpICsgJz0nICsgc1xuICAgIHJldHVybiByZXR1cm5TdHJcbiAgfVxuXG4gIC8qKlxuICAgKiBAcGFyYW0ge0Zvcm1EYXRhfE9iamVjdH0gdmFsdWVzXG4gICAqIEByZXR1cm5zIHN0cmluZ1xuICAgKi9cbiAgZnVuY3Rpb24gdXJsRW5jb2RlKHZhbHVlcykge1xuICAgIHZhbHVlcyA9IGZvcm1EYXRhRnJvbU9iamVjdCh2YWx1ZXMpXG4gICAgbGV0IHJldHVyblN0ciA9ICcnXG4gICAgdmFsdWVzLmZvckVhY2goZnVuY3Rpb24odmFsdWUsIGtleSkge1xuICAgICAgcmV0dXJuU3RyID0gYXBwZW5kUGFyYW0ocmV0dXJuU3RyLCBrZXksIHZhbHVlKVxuICAgIH0pXG4gICAgcmV0dXJuIHJldHVyblN0clxuICB9XG5cbiAgLy89ID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbiAgLy8gQWpheFxuICAvLz0gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuXG4gIC8qKlxuICogQHBhcmFtIHtFbGVtZW50fSBlbHRcbiAqIEBwYXJhbSB7RWxlbWVudH0gdGFyZ2V0XG4gKiBAcGFyYW0ge3N0cmluZ30gcHJvbXB0XG4gKiBAcmV0dXJucyB7SHRteEhlYWRlclNwZWNpZmljYXRpb259XG4gKi9cbiAgZnVuY3Rpb24gZ2V0SGVhZGVycyhlbHQsIHRhcmdldCwgcHJvbXB0KSB7XG4gICAgLyoqIEB0eXBlIEh0bXhIZWFkZXJTcGVjaWZpY2F0aW9uICovXG4gICAgY29uc3QgaGVhZGVycyA9IHtcbiAgICAgICdIWC1SZXF1ZXN0JzogJ3RydWUnLFxuICAgICAgJ0hYLVRyaWdnZXInOiBnZXRSYXdBdHRyaWJ1dGUoZWx0LCAnaWQnKSxcbiAgICAgICdIWC1UcmlnZ2VyLU5hbWUnOiBnZXRSYXdBdHRyaWJ1dGUoZWx0LCAnbmFtZScpLFxuICAgICAgJ0hYLVRhcmdldCc6IGdldEF0dHJpYnV0ZVZhbHVlKHRhcmdldCwgJ2lkJyksXG4gICAgICAnSFgtQ3VycmVudC1VUkwnOiBnZXREb2N1bWVudCgpLmxvY2F0aW9uLmhyZWZcbiAgICB9XG4gICAgZ2V0VmFsdWVzRm9yRWxlbWVudChlbHQsICdoeC1oZWFkZXJzJywgZmFsc2UsIGhlYWRlcnMpXG4gICAgaWYgKHByb21wdCAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICBoZWFkZXJzWydIWC1Qcm9tcHQnXSA9IHByb21wdFxuICAgIH1cbiAgICBpZiAoZ2V0SW50ZXJuYWxEYXRhKGVsdCkuYm9vc3RlZCkge1xuICAgICAgaGVhZGVyc1snSFgtQm9vc3RlZCddID0gJ3RydWUnXG4gICAgfVxuICAgIHJldHVybiBoZWFkZXJzXG4gIH1cblxuICAvKipcbiAqIGZpbHRlclZhbHVlcyB0YWtlcyBhbiBvYmplY3QgY29udGFpbmluZyBmb3JtIGlucHV0IHZhbHVlc1xuICogYW5kIHJldHVybnMgYSBuZXcgb2JqZWN0IHRoYXQgb25seSBjb250YWlucyBrZXlzIHRoYXQgYXJlXG4gKiBzcGVjaWZpZWQgYnkgdGhlIGNsb3Nlc3QgXCJoeC1wYXJhbXNcIiBhdHRyaWJ1dGVcbiAqIEBwYXJhbSB7Rm9ybURhdGF9IGlucHV0VmFsdWVzXG4gKiBAcGFyYW0ge0VsZW1lbnR9IGVsdFxuICogQHJldHVybnMge0Zvcm1EYXRhfVxuICovXG4gIGZ1bmN0aW9uIGZpbHRlclZhbHVlcyhpbnB1dFZhbHVlcywgZWx0KSB7XG4gICAgY29uc3QgcGFyYW1zVmFsdWUgPSBnZXRDbG9zZXN0QXR0cmlidXRlVmFsdWUoZWx0LCAnaHgtcGFyYW1zJylcbiAgICBpZiAocGFyYW1zVmFsdWUpIHtcbiAgICAgIGlmIChwYXJhbXNWYWx1ZSA9PT0gJ25vbmUnKSB7XG4gICAgICAgIHJldHVybiBuZXcgRm9ybURhdGEoKVxuICAgICAgfSBlbHNlIGlmIChwYXJhbXNWYWx1ZSA9PT0gJyonKSB7XG4gICAgICAgIHJldHVybiBpbnB1dFZhbHVlc1xuICAgICAgfSBlbHNlIGlmIChwYXJhbXNWYWx1ZS5pbmRleE9mKCdub3QgJykgPT09IDApIHtcbiAgICAgICAgZm9yRWFjaChwYXJhbXNWYWx1ZS5zdWJzdHIoNCkuc3BsaXQoJywnKSwgZnVuY3Rpb24obmFtZSkge1xuICAgICAgICAgIG5hbWUgPSBuYW1lLnRyaW0oKVxuICAgICAgICAgIGlucHV0VmFsdWVzLmRlbGV0ZShuYW1lKVxuICAgICAgICB9KVxuICAgICAgICByZXR1cm4gaW5wdXRWYWx1ZXNcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIGNvbnN0IG5ld1ZhbHVlcyA9IG5ldyBGb3JtRGF0YSgpXG4gICAgICAgIGZvckVhY2gocGFyYW1zVmFsdWUuc3BsaXQoJywnKSwgZnVuY3Rpb24obmFtZSkge1xuICAgICAgICAgIG5hbWUgPSBuYW1lLnRyaW0oKVxuICAgICAgICAgIGlmIChpbnB1dFZhbHVlcy5oYXMobmFtZSkpIHtcbiAgICAgICAgICAgIGlucHV0VmFsdWVzLmdldEFsbChuYW1lKS5mb3JFYWNoKGZ1bmN0aW9uKHZhbHVlKSB7IG5ld1ZhbHVlcy5hcHBlbmQobmFtZSwgdmFsdWUpIH0pXG4gICAgICAgICAgfVxuICAgICAgICB9KVxuICAgICAgICByZXR1cm4gbmV3VmFsdWVzXG4gICAgICB9XG4gICAgfSBlbHNlIHtcbiAgICAgIHJldHVybiBpbnB1dFZhbHVlc1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBAcGFyYW0ge0VsZW1lbnR9IGVsdFxuICAgKiBAcmV0dXJuIHtib29sZWFufVxuICAgKi9cbiAgZnVuY3Rpb24gaXNBbmNob3JMaW5rKGVsdCkge1xuICAgIHJldHVybiAhIWdldFJhd0F0dHJpYnV0ZShlbHQsICdocmVmJykgJiYgZ2V0UmF3QXR0cmlidXRlKGVsdCwgJ2hyZWYnKS5pbmRleE9mKCcjJykgPj0gMFxuICB9XG5cbiAgLyoqXG4gKiBAcGFyYW0ge0VsZW1lbnR9IGVsdFxuICogQHBhcmFtIHtIdG14U3dhcFN0eWxlfSBbc3dhcEluZm9PdmVycmlkZV1cbiAqIEByZXR1cm5zIHtIdG14U3dhcFNwZWNpZmljYXRpb259XG4gKi9cbiAgZnVuY3Rpb24gZ2V0U3dhcFNwZWNpZmljYXRpb24oZWx0LCBzd2FwSW5mb092ZXJyaWRlKSB7XG4gICAgY29uc3Qgc3dhcEluZm8gPSBzd2FwSW5mb092ZXJyaWRlIHx8IGdldENsb3Nlc3RBdHRyaWJ1dGVWYWx1ZShlbHQsICdoeC1zd2FwJylcbiAgICAvKiogQHR5cGUgSHRteFN3YXBTcGVjaWZpY2F0aW9uICovXG4gICAgY29uc3Qgc3dhcFNwZWMgPSB7XG4gICAgICBzd2FwU3R5bGU6IGdldEludGVybmFsRGF0YShlbHQpLmJvb3N0ZWQgPyAnaW5uZXJIVE1MJyA6IGh0bXguY29uZmlnLmRlZmF1bHRTd2FwU3R5bGUsXG4gICAgICBzd2FwRGVsYXk6IGh0bXguY29uZmlnLmRlZmF1bHRTd2FwRGVsYXksXG4gICAgICBzZXR0bGVEZWxheTogaHRteC5jb25maWcuZGVmYXVsdFNldHRsZURlbGF5XG4gICAgfVxuICAgIGlmIChodG14LmNvbmZpZy5zY3JvbGxJbnRvVmlld09uQm9vc3QgJiYgZ2V0SW50ZXJuYWxEYXRhKGVsdCkuYm9vc3RlZCAmJiAhaXNBbmNob3JMaW5rKGVsdCkpIHtcbiAgICAgIHN3YXBTcGVjLnNob3cgPSAndG9wJ1xuICAgIH1cbiAgICBpZiAoc3dhcEluZm8pIHtcbiAgICAgIGNvbnN0IHNwbGl0ID0gc3BsaXRPbldoaXRlc3BhY2Uoc3dhcEluZm8pXG4gICAgICBpZiAoc3BsaXQubGVuZ3RoID4gMCkge1xuICAgICAgICBmb3IgKGxldCBpID0gMDsgaSA8IHNwbGl0Lmxlbmd0aDsgaSsrKSB7XG4gICAgICAgICAgY29uc3QgdmFsdWUgPSBzcGxpdFtpXVxuICAgICAgICAgIGlmICh2YWx1ZS5pbmRleE9mKCdzd2FwOicpID09PSAwKSB7XG4gICAgICAgICAgICBzd2FwU3BlYy5zd2FwRGVsYXkgPSBwYXJzZUludGVydmFsKHZhbHVlLnN1YnN0cig1KSlcbiAgICAgICAgICB9IGVsc2UgaWYgKHZhbHVlLmluZGV4T2YoJ3NldHRsZTonKSA9PT0gMCkge1xuICAgICAgICAgICAgc3dhcFNwZWMuc2V0dGxlRGVsYXkgPSBwYXJzZUludGVydmFsKHZhbHVlLnN1YnN0cig3KSlcbiAgICAgICAgICB9IGVsc2UgaWYgKHZhbHVlLmluZGV4T2YoJ3RyYW5zaXRpb246JykgPT09IDApIHtcbiAgICAgICAgICAgIHN3YXBTcGVjLnRyYW5zaXRpb24gPSB2YWx1ZS5zdWJzdHIoMTEpID09PSAndHJ1ZSdcbiAgICAgICAgICB9IGVsc2UgaWYgKHZhbHVlLmluZGV4T2YoJ2lnbm9yZVRpdGxlOicpID09PSAwKSB7XG4gICAgICAgICAgICBzd2FwU3BlYy5pZ25vcmVUaXRsZSA9IHZhbHVlLnN1YnN0cigxMikgPT09ICd0cnVlJ1xuICAgICAgICAgIH0gZWxzZSBpZiAodmFsdWUuaW5kZXhPZignc2Nyb2xsOicpID09PSAwKSB7XG4gICAgICAgICAgICBjb25zdCBzY3JvbGxTcGVjID0gdmFsdWUuc3Vic3RyKDcpXG4gICAgICAgICAgICB2YXIgc3BsaXRTcGVjID0gc2Nyb2xsU3BlYy5zcGxpdCgnOicpXG4gICAgICAgICAgICBjb25zdCBzY3JvbGxWYWwgPSBzcGxpdFNwZWMucG9wKClcbiAgICAgICAgICAgIHZhciBzZWxlY3RvclZhbCA9IHNwbGl0U3BlYy5sZW5ndGggPiAwID8gc3BsaXRTcGVjLmpvaW4oJzonKSA6IG51bGxcbiAgICAgICAgICAgIC8vIEB0cy1pZ25vcmVcbiAgICAgICAgICAgIHN3YXBTcGVjLnNjcm9sbCA9IHNjcm9sbFZhbFxuICAgICAgICAgICAgc3dhcFNwZWMuc2Nyb2xsVGFyZ2V0ID0gc2VsZWN0b3JWYWxcbiAgICAgICAgICB9IGVsc2UgaWYgKHZhbHVlLmluZGV4T2YoJ3Nob3c6JykgPT09IDApIHtcbiAgICAgICAgICAgIGNvbnN0IHNob3dTcGVjID0gdmFsdWUuc3Vic3RyKDUpXG4gICAgICAgICAgICB2YXIgc3BsaXRTcGVjID0gc2hvd1NwZWMuc3BsaXQoJzonKVxuICAgICAgICAgICAgY29uc3Qgc2hvd1ZhbCA9IHNwbGl0U3BlYy5wb3AoKVxuICAgICAgICAgICAgdmFyIHNlbGVjdG9yVmFsID0gc3BsaXRTcGVjLmxlbmd0aCA+IDAgPyBzcGxpdFNwZWMuam9pbignOicpIDogbnVsbFxuICAgICAgICAgICAgc3dhcFNwZWMuc2hvdyA9IHNob3dWYWxcbiAgICAgICAgICAgIHN3YXBTcGVjLnNob3dUYXJnZXQgPSBzZWxlY3RvclZhbFxuICAgICAgICAgIH0gZWxzZSBpZiAodmFsdWUuaW5kZXhPZignZm9jdXMtc2Nyb2xsOicpID09PSAwKSB7XG4gICAgICAgICAgICBjb25zdCBmb2N1c1Njcm9sbFZhbCA9IHZhbHVlLnN1YnN0cignZm9jdXMtc2Nyb2xsOicubGVuZ3RoKVxuICAgICAgICAgICAgc3dhcFNwZWMuZm9jdXNTY3JvbGwgPSBmb2N1c1Njcm9sbFZhbCA9PSAndHJ1ZSdcbiAgICAgICAgICB9IGVsc2UgaWYgKGkgPT0gMCkge1xuICAgICAgICAgICAgc3dhcFNwZWMuc3dhcFN0eWxlID0gdmFsdWVcbiAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgbG9nRXJyb3IoJ1Vua25vd24gbW9kaWZpZXIgaW4gaHgtc3dhcDogJyArIHZhbHVlKVxuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4gc3dhcFNwZWNcbiAgfVxuXG4gIC8qKlxuICAgKiBAcGFyYW0ge0VsZW1lbnR9IGVsdFxuICAgKiBAcmV0dXJuIHtib29sZWFufVxuICAgKi9cbiAgZnVuY3Rpb24gdXNlc0Zvcm1EYXRhKGVsdCkge1xuICAgIHJldHVybiBnZXRDbG9zZXN0QXR0cmlidXRlVmFsdWUoZWx0LCAnaHgtZW5jb2RpbmcnKSA9PT0gJ211bHRpcGFydC9mb3JtLWRhdGEnIHx8XG4gICAgKG1hdGNoZXMoZWx0LCAnZm9ybScpICYmIGdldFJhd0F0dHJpYnV0ZShlbHQsICdlbmN0eXBlJykgPT09ICdtdWx0aXBhcnQvZm9ybS1kYXRhJylcbiAgfVxuXG4gIC8qKlxuICAgKiBAcGFyYW0ge1hNTEh0dHBSZXF1ZXN0fSB4aHJcbiAgICogQHBhcmFtIHtFbGVtZW50fSBlbHRcbiAgICogQHBhcmFtIHtGb3JtRGF0YX0gZmlsdGVyZWRQYXJhbWV0ZXJzXG4gICAqIEByZXR1cm5zIHsqfHN0cmluZ3xudWxsfVxuICAgKi9cbiAgZnVuY3Rpb24gZW5jb2RlUGFyYW1zRm9yQm9keSh4aHIsIGVsdCwgZmlsdGVyZWRQYXJhbWV0ZXJzKSB7XG4gICAgbGV0IGVuY29kZWRQYXJhbWV0ZXJzID0gbnVsbFxuICAgIHdpdGhFeHRlbnNpb25zKGVsdCwgZnVuY3Rpb24oZXh0ZW5zaW9uKSB7XG4gICAgICBpZiAoZW5jb2RlZFBhcmFtZXRlcnMgPT0gbnVsbCkge1xuICAgICAgICBlbmNvZGVkUGFyYW1ldGVycyA9IGV4dGVuc2lvbi5lbmNvZGVQYXJhbWV0ZXJzKHhociwgZmlsdGVyZWRQYXJhbWV0ZXJzLCBlbHQpXG4gICAgICB9XG4gICAgfSlcbiAgICBpZiAoZW5jb2RlZFBhcmFtZXRlcnMgIT0gbnVsbCkge1xuICAgICAgcmV0dXJuIGVuY29kZWRQYXJhbWV0ZXJzXG4gICAgfSBlbHNlIHtcbiAgICAgIGlmICh1c2VzRm9ybURhdGEoZWx0KSkge1xuICAgICAgICAvLyBGb3JjZSBjb252ZXJzaW9uIHRvIGFuIGFjdHVhbCBGb3JtRGF0YSBvYmplY3QgaW4gY2FzZSBmaWx0ZXJlZFBhcmFtZXRlcnMgaXMgYSBmb3JtRGF0YVByb3h5XG4gICAgICAgIC8vIFNlZSBodHRwczovL2dpdGh1Yi5jb20vYmlnc2t5c29mdHdhcmUvaHRteC9pc3N1ZXMvMjMxN1xuICAgICAgICByZXR1cm4gb3ZlcnJpZGVGb3JtRGF0YShuZXcgRm9ybURhdGEoKSwgZm9ybURhdGFGcm9tT2JqZWN0KGZpbHRlcmVkUGFyYW1ldGVycykpXG4gICAgICB9IGVsc2Uge1xuICAgICAgICByZXR1cm4gdXJsRW5jb2RlKGZpbHRlcmVkUGFyYW1ldGVycylcbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICAvKipcbiAqXG4gKiBAcGFyYW0ge0VsZW1lbnR9IHRhcmdldFxuICogQHJldHVybnMge0h0bXhTZXR0bGVJbmZvfVxuICovXG4gIGZ1bmN0aW9uIG1ha2VTZXR0bGVJbmZvKHRhcmdldCkge1xuICAgIHJldHVybiB7IHRhc2tzOiBbXSwgZWx0czogW3RhcmdldF0gfVxuICB9XG5cbiAgLyoqXG4gICAqIEBwYXJhbSB7RWxlbWVudFtdfSBjb250ZW50XG4gICAqIEBwYXJhbSB7SHRteFN3YXBTcGVjaWZpY2F0aW9ufSBzd2FwU3BlY1xuICAgKi9cbiAgZnVuY3Rpb24gdXBkYXRlU2Nyb2xsU3RhdGUoY29udGVudCwgc3dhcFNwZWMpIHtcbiAgICBjb25zdCBmaXJzdCA9IGNvbnRlbnRbMF1cbiAgICBjb25zdCBsYXN0ID0gY29udGVudFtjb250ZW50Lmxlbmd0aCAtIDFdXG4gICAgaWYgKHN3YXBTcGVjLnNjcm9sbCkge1xuICAgICAgdmFyIHRhcmdldCA9IG51bGxcbiAgICAgIGlmIChzd2FwU3BlYy5zY3JvbGxUYXJnZXQpIHtcbiAgICAgICAgdGFyZ2V0ID0gYXNFbGVtZW50KHF1ZXJ5U2VsZWN0b3JFeHQoZmlyc3QsIHN3YXBTcGVjLnNjcm9sbFRhcmdldCkpXG4gICAgICB9XG4gICAgICBpZiAoc3dhcFNwZWMuc2Nyb2xsID09PSAndG9wJyAmJiAoZmlyc3QgfHwgdGFyZ2V0KSkge1xuICAgICAgICB0YXJnZXQgPSB0YXJnZXQgfHwgZmlyc3RcbiAgICAgICAgdGFyZ2V0LnNjcm9sbFRvcCA9IDBcbiAgICAgIH1cbiAgICAgIGlmIChzd2FwU3BlYy5zY3JvbGwgPT09ICdib3R0b20nICYmIChsYXN0IHx8IHRhcmdldCkpIHtcbiAgICAgICAgdGFyZ2V0ID0gdGFyZ2V0IHx8IGxhc3RcbiAgICAgICAgdGFyZ2V0LnNjcm9sbFRvcCA9IHRhcmdldC5zY3JvbGxIZWlnaHRcbiAgICAgIH1cbiAgICB9XG4gICAgaWYgKHN3YXBTcGVjLnNob3cpIHtcbiAgICAgIHZhciB0YXJnZXQgPSBudWxsXG4gICAgICBpZiAoc3dhcFNwZWMuc2hvd1RhcmdldCkge1xuICAgICAgICBsZXQgdGFyZ2V0U3RyID0gc3dhcFNwZWMuc2hvd1RhcmdldFxuICAgICAgICBpZiAoc3dhcFNwZWMuc2hvd1RhcmdldCA9PT0gJ3dpbmRvdycpIHtcbiAgICAgICAgICB0YXJnZXRTdHIgPSAnYm9keSdcbiAgICAgICAgfVxuICAgICAgICB0YXJnZXQgPSBhc0VsZW1lbnQocXVlcnlTZWxlY3RvckV4dChmaXJzdCwgdGFyZ2V0U3RyKSlcbiAgICAgIH1cbiAgICAgIGlmIChzd2FwU3BlYy5zaG93ID09PSAndG9wJyAmJiAoZmlyc3QgfHwgdGFyZ2V0KSkge1xuICAgICAgICB0YXJnZXQgPSB0YXJnZXQgfHwgZmlyc3RcbiAgICAgICAgLy8gQHRzLWlnbm9yZSBGb3Igc29tZSByZWFzb24gdHNjIGRvZXNuJ3QgcmVjb2duaXplIFwiaW5zdGFudFwiIGFzIGEgdmFsaWQgb3B0aW9uIGZvciBub3dcbiAgICAgICAgdGFyZ2V0LnNjcm9sbEludG9WaWV3KHsgYmxvY2s6ICdzdGFydCcsIGJlaGF2aW9yOiBodG14LmNvbmZpZy5zY3JvbGxCZWhhdmlvciB9KVxuICAgICAgfVxuICAgICAgaWYgKHN3YXBTcGVjLnNob3cgPT09ICdib3R0b20nICYmIChsYXN0IHx8IHRhcmdldCkpIHtcbiAgICAgICAgdGFyZ2V0ID0gdGFyZ2V0IHx8IGxhc3RcbiAgICAgICAgLy8gQHRzLWlnbm9yZSBGb3Igc29tZSByZWFzb24gdHNjIGRvZXNuJ3QgcmVjb2duaXplIFwiaW5zdGFudFwiIGFzIGEgdmFsaWQgb3B0aW9uIGZvciBub3dcbiAgICAgICAgdGFyZ2V0LnNjcm9sbEludG9WaWV3KHsgYmxvY2s6ICdlbmQnLCBiZWhhdmlvcjogaHRteC5jb25maWcuc2Nyb2xsQmVoYXZpb3IgfSlcbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICAvKipcbiAqIEBwYXJhbSB7RWxlbWVudH0gZWx0XG4gKiBAcGFyYW0ge3N0cmluZ30gYXR0clxuICogQHBhcmFtIHtib29sZWFuPX0gZXZhbEFzRGVmYXVsdFxuICogQHBhcmFtIHtPYmplY3Q9fSB2YWx1ZXNcbiAqIEByZXR1cm5zIHtPYmplY3R9XG4gKi9cbiAgZnVuY3Rpb24gZ2V0VmFsdWVzRm9yRWxlbWVudChlbHQsIGF0dHIsIGV2YWxBc0RlZmF1bHQsIHZhbHVlcykge1xuICAgIGlmICh2YWx1ZXMgPT0gbnVsbCkge1xuICAgICAgdmFsdWVzID0ge31cbiAgICB9XG4gICAgaWYgKGVsdCA9PSBudWxsKSB7XG4gICAgICByZXR1cm4gdmFsdWVzXG4gICAgfVxuICAgIGNvbnN0IGF0dHJpYnV0ZVZhbHVlID0gZ2V0QXR0cmlidXRlVmFsdWUoZWx0LCBhdHRyKVxuICAgIGlmIChhdHRyaWJ1dGVWYWx1ZSkge1xuICAgICAgbGV0IHN0ciA9IGF0dHJpYnV0ZVZhbHVlLnRyaW0oKVxuICAgICAgbGV0IGV2YWx1YXRlVmFsdWUgPSBldmFsQXNEZWZhdWx0XG4gICAgICBpZiAoc3RyID09PSAndW5zZXQnKSB7XG4gICAgICAgIHJldHVybiBudWxsXG4gICAgICB9XG4gICAgICBpZiAoc3RyLmluZGV4T2YoJ2phdmFzY3JpcHQ6JykgPT09IDApIHtcbiAgICAgICAgc3RyID0gc3RyLnN1YnN0cigxMSlcbiAgICAgICAgZXZhbHVhdGVWYWx1ZSA9IHRydWVcbiAgICAgIH0gZWxzZSBpZiAoc3RyLmluZGV4T2YoJ2pzOicpID09PSAwKSB7XG4gICAgICAgIHN0ciA9IHN0ci5zdWJzdHIoMylcbiAgICAgICAgZXZhbHVhdGVWYWx1ZSA9IHRydWVcbiAgICAgIH1cbiAgICAgIGlmIChzdHIuaW5kZXhPZigneycpICE9PSAwKSB7XG4gICAgICAgIHN0ciA9ICd7JyArIHN0ciArICd9J1xuICAgICAgfVxuICAgICAgbGV0IHZhcnNWYWx1ZXNcbiAgICAgIGlmIChldmFsdWF0ZVZhbHVlKSB7XG4gICAgICAgIHZhcnNWYWx1ZXMgPSBtYXliZUV2YWwoZWx0LCBmdW5jdGlvbigpIHsgcmV0dXJuIEZ1bmN0aW9uKCdyZXR1cm4gKCcgKyBzdHIgKyAnKScpKCkgfSwge30pXG4gICAgICB9IGVsc2Uge1xuICAgICAgICB2YXJzVmFsdWVzID0gcGFyc2VKU09OKHN0cilcbiAgICAgIH1cbiAgICAgIGZvciAoY29uc3Qga2V5IGluIHZhcnNWYWx1ZXMpIHtcbiAgICAgICAgaWYgKHZhcnNWYWx1ZXMuaGFzT3duUHJvcGVydHkoa2V5KSkge1xuICAgICAgICAgIGlmICh2YWx1ZXNba2V5XSA9PSBudWxsKSB7XG4gICAgICAgICAgICB2YWx1ZXNba2V5XSA9IHZhcnNWYWx1ZXNba2V5XVxuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4gZ2V0VmFsdWVzRm9yRWxlbWVudChhc0VsZW1lbnQocGFyZW50RWx0KGVsdCkpLCBhdHRyLCBldmFsQXNEZWZhdWx0LCB2YWx1ZXMpXG4gIH1cblxuICAvKipcbiAgICogQHBhcmFtIHtFdmVudFRhcmdldHxzdHJpbmd9IGVsdFxuICAgKiBAcGFyYW0geygpID0+IGFueX0gdG9FdmFsXG4gICAqIEBwYXJhbSB7YW55PX0gZGVmYXVsdFZhbFxuICAgKiBAcmV0dXJucyB7YW55fVxuICAgKi9cbiAgZnVuY3Rpb24gbWF5YmVFdmFsKGVsdCwgdG9FdmFsLCBkZWZhdWx0VmFsKSB7XG4gICAgaWYgKGh0bXguY29uZmlnLmFsbG93RXZhbCkge1xuICAgICAgcmV0dXJuIHRvRXZhbCgpXG4gICAgfSBlbHNlIHtcbiAgICAgIHRyaWdnZXJFcnJvckV2ZW50KGVsdCwgJ2h0bXg6ZXZhbERpc2FsbG93ZWRFcnJvcicpXG4gICAgICByZXR1cm4gZGVmYXVsdFZhbFxuICAgIH1cbiAgfVxuXG4gIC8qKlxuICogQHBhcmFtIHtFbGVtZW50fSBlbHRcbiAqIEBwYXJhbSB7Kj99IGV4cHJlc3Npb25WYXJzXG4gKiBAcmV0dXJuc1xuICovXG4gIGZ1bmN0aW9uIGdldEhYVmFyc0ZvckVsZW1lbnQoZWx0LCBleHByZXNzaW9uVmFycykge1xuICAgIHJldHVybiBnZXRWYWx1ZXNGb3JFbGVtZW50KGVsdCwgJ2h4LXZhcnMnLCB0cnVlLCBleHByZXNzaW9uVmFycylcbiAgfVxuXG4gIC8qKlxuICogQHBhcmFtIHtFbGVtZW50fSBlbHRcbiAqIEBwYXJhbSB7Kj99IGV4cHJlc3Npb25WYXJzXG4gKiBAcmV0dXJuc1xuICovXG4gIGZ1bmN0aW9uIGdldEhYVmFsc0ZvckVsZW1lbnQoZWx0LCBleHByZXNzaW9uVmFycykge1xuICAgIHJldHVybiBnZXRWYWx1ZXNGb3JFbGVtZW50KGVsdCwgJ2h4LXZhbHMnLCBmYWxzZSwgZXhwcmVzc2lvblZhcnMpXG4gIH1cblxuICAvKipcbiAqIEBwYXJhbSB7RWxlbWVudH0gZWx0XG4gKiBAcmV0dXJucyB7Rm9ybURhdGF9XG4gKi9cbiAgZnVuY3Rpb24gZ2V0RXhwcmVzc2lvblZhcnMoZWx0KSB7XG4gICAgcmV0dXJuIG1lcmdlT2JqZWN0cyhnZXRIWFZhcnNGb3JFbGVtZW50KGVsdCksIGdldEhYVmFsc0ZvckVsZW1lbnQoZWx0KSlcbiAgfVxuXG4gIC8qKlxuICAgKiBAcGFyYW0ge1hNTEh0dHBSZXF1ZXN0fSB4aHJcbiAgICogQHBhcmFtIHtzdHJpbmd9IGhlYWRlclxuICAgKiBAcGFyYW0ge3N0cmluZ3xudWxsfSBoZWFkZXJWYWx1ZVxuICAgKi9cbiAgZnVuY3Rpb24gc2FmZWx5U2V0SGVhZGVyVmFsdWUoeGhyLCBoZWFkZXIsIGhlYWRlclZhbHVlKSB7XG4gICAgaWYgKGhlYWRlclZhbHVlICE9PSBudWxsKSB7XG4gICAgICB0cnkge1xuICAgICAgICB4aHIuc2V0UmVxdWVzdEhlYWRlcihoZWFkZXIsIGhlYWRlclZhbHVlKVxuICAgICAgfSBjYXRjaCAoZSkge1xuICAgICAgLy8gT24gYW4gZXhjZXB0aW9uLCB0cnkgdG8gc2V0IHRoZSBoZWFkZXIgVVJJIGVuY29kZWQgaW5zdGVhZFxuICAgICAgICB4aHIuc2V0UmVxdWVzdEhlYWRlcihoZWFkZXIsIGVuY29kZVVSSUNvbXBvbmVudChoZWFkZXJWYWx1ZSkpXG4gICAgICAgIHhoci5zZXRSZXF1ZXN0SGVhZGVyKGhlYWRlciArICctVVJJLUF1dG9FbmNvZGVkJywgJ3RydWUnKVxuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBAcGFyYW0ge1hNTEh0dHBSZXF1ZXN0fSB4aHJcbiAgICogQHJldHVybiB7c3RyaW5nfVxuICAgKi9cbiAgZnVuY3Rpb24gZ2V0UGF0aEZyb21SZXNwb25zZSh4aHIpIHtcbiAgLy8gTkI6IElFMTEgZG9lcyBub3Qgc3VwcG9ydCB0aGlzIHN0dWZmXG4gICAgaWYgKHhoci5yZXNwb25zZVVSTCAmJiB0eXBlb2YgKFVSTCkgIT09ICd1bmRlZmluZWQnKSB7XG4gICAgICB0cnkge1xuICAgICAgICBjb25zdCB1cmwgPSBuZXcgVVJMKHhoci5yZXNwb25zZVVSTClcbiAgICAgICAgcmV0dXJuIHVybC5wYXRobmFtZSArIHVybC5zZWFyY2hcbiAgICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgICAgdHJpZ2dlckVycm9yRXZlbnQoZ2V0RG9jdW1lbnQoKS5ib2R5LCAnaHRteDpiYWRSZXNwb25zZVVybCcsIHsgdXJsOiB4aHIucmVzcG9uc2VVUkwgfSlcbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogQHBhcmFtIHtYTUxIdHRwUmVxdWVzdH0geGhyXG4gICAqIEBwYXJhbSB7UmVnRXhwfSByZWdleHBcbiAgICogQHJldHVybiB7Ym9vbGVhbn1cbiAgICovXG4gIGZ1bmN0aW9uIGhhc0hlYWRlcih4aHIsIHJlZ2V4cCkge1xuICAgIHJldHVybiByZWdleHAudGVzdCh4aHIuZ2V0QWxsUmVzcG9uc2VIZWFkZXJzKCkpXG4gIH1cblxuICAvKipcbiAgICogSXNzdWVzIGFuIGh0bXgtc3R5bGUgQUpBWCByZXF1ZXN0XG4gICAqXG4gICAqIEBzZWUgaHR0cHM6Ly9odG14Lm9yZy9hcGkvI2FqYXhcbiAgICpcbiAgICogQHBhcmFtIHtIdHRwVmVyYn0gdmVyYlxuICAgKiBAcGFyYW0ge3N0cmluZ30gcGF0aCB0aGUgVVJMIHBhdGggdG8gbWFrZSB0aGUgQUpBWFxuICAgKiBAcGFyYW0ge0VsZW1lbnR8c3RyaW5nfEh0bXhBamF4SGVscGVyQ29udGV4dH0gY29udGV4dCB0aGUgZWxlbWVudCB0byB0YXJnZXQgKGRlZmF1bHRzIHRvIHRoZSAqKmJvZHkqKikgfCBhIHNlbGVjdG9yIGZvciB0aGUgdGFyZ2V0IHwgYSBjb250ZXh0IG9iamVjdCB0aGF0IGNvbnRhaW5zIGFueSBvZiB0aGUgZm9sbG93aW5nXG4gICAqIEByZXR1cm4ge1Byb21pc2U8dm9pZD59IFByb21pc2UgdGhhdCByZXNvbHZlcyBpbW1lZGlhdGVseSBpZiBubyByZXF1ZXN0IGlzIHNlbnQsIG9yIHdoZW4gdGhlIHJlcXVlc3QgaXMgY29tcGxldGVcbiAgICovXG4gIGZ1bmN0aW9uIGFqYXhIZWxwZXIodmVyYiwgcGF0aCwgY29udGV4dCkge1xuICAgIHZlcmIgPSAoLyoqIEB0eXBlIEh0dHBWZXJiICovKHZlcmIudG9Mb3dlckNhc2UoKSkpXG4gICAgaWYgKGNvbnRleHQpIHtcbiAgICAgIGlmIChjb250ZXh0IGluc3RhbmNlb2YgRWxlbWVudCB8fCB0eXBlb2YgY29udGV4dCA9PT0gJ3N0cmluZycpIHtcbiAgICAgICAgcmV0dXJuIGlzc3VlQWpheFJlcXVlc3QodmVyYiwgcGF0aCwgbnVsbCwgbnVsbCwge1xuICAgICAgICAgIHRhcmdldE92ZXJyaWRlOiByZXNvbHZlVGFyZ2V0KGNvbnRleHQpLFxuICAgICAgICAgIHJldHVyblByb21pc2U6IHRydWVcbiAgICAgICAgfSlcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHJldHVybiBpc3N1ZUFqYXhSZXF1ZXN0KHZlcmIsIHBhdGgsIHJlc29sdmVUYXJnZXQoY29udGV4dC5zb3VyY2UpLCBjb250ZXh0LmV2ZW50LFxuICAgICAgICAgIHtcbiAgICAgICAgICAgIGhhbmRsZXI6IGNvbnRleHQuaGFuZGxlcixcbiAgICAgICAgICAgIGhlYWRlcnM6IGNvbnRleHQuaGVhZGVycyxcbiAgICAgICAgICAgIHZhbHVlczogY29udGV4dC52YWx1ZXMsXG4gICAgICAgICAgICB0YXJnZXRPdmVycmlkZTogcmVzb2x2ZVRhcmdldChjb250ZXh0LnRhcmdldCksXG4gICAgICAgICAgICBzd2FwT3ZlcnJpZGU6IGNvbnRleHQuc3dhcCxcbiAgICAgICAgICAgIHNlbGVjdDogY29udGV4dC5zZWxlY3QsXG4gICAgICAgICAgICByZXR1cm5Qcm9taXNlOiB0cnVlXG4gICAgICAgICAgfSlcbiAgICAgIH1cbiAgICB9IGVsc2Uge1xuICAgICAgcmV0dXJuIGlzc3VlQWpheFJlcXVlc3QodmVyYiwgcGF0aCwgbnVsbCwgbnVsbCwge1xuICAgICAgICByZXR1cm5Qcm9taXNlOiB0cnVlXG4gICAgICB9KVxuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBAcGFyYW0ge0VsZW1lbnR9IGVsdFxuICAgKiBAcmV0dXJuIHtFbGVtZW50W119XG4gICAqL1xuICBmdW5jdGlvbiBoaWVyYXJjaHlGb3JFbHQoZWx0KSB7XG4gICAgY29uc3QgYXJyID0gW11cbiAgICB3aGlsZSAoZWx0KSB7XG4gICAgICBhcnIucHVzaChlbHQpXG4gICAgICBlbHQgPSBlbHQucGFyZW50RWxlbWVudFxuICAgIH1cbiAgICByZXR1cm4gYXJyXG4gIH1cblxuICAvKipcbiAgICogQHBhcmFtIHtFbGVtZW50fSBlbHRcbiAgICogQHBhcmFtIHtzdHJpbmd9IHBhdGhcbiAgICogQHBhcmFtIHtIdG14UmVxdWVzdENvbmZpZ30gcmVxdWVzdENvbmZpZ1xuICAgKiBAcmV0dXJuIHtib29sZWFufVxuICAgKi9cbiAgZnVuY3Rpb24gdmVyaWZ5UGF0aChlbHQsIHBhdGgsIHJlcXVlc3RDb25maWcpIHtcbiAgICBsZXQgc2FtZUhvc3RcbiAgICBsZXQgdXJsXG4gICAgaWYgKHR5cGVvZiBVUkwgPT09ICdmdW5jdGlvbicpIHtcbiAgICAgIHVybCA9IG5ldyBVUkwocGF0aCwgZG9jdW1lbnQubG9jYXRpb24uaHJlZilcbiAgICAgIGNvbnN0IG9yaWdpbiA9IGRvY3VtZW50LmxvY2F0aW9uLm9yaWdpblxuICAgICAgc2FtZUhvc3QgPSBvcmlnaW4gPT09IHVybC5vcmlnaW5cbiAgICB9IGVsc2Uge1xuICAgIC8vIElFMTEgZG9lc24ndCBzdXBwb3J0IFVSTFxuICAgICAgdXJsID0gcGF0aFxuICAgICAgc2FtZUhvc3QgPSBzdGFydHNXaXRoKHBhdGgsIGRvY3VtZW50LmxvY2F0aW9uLm9yaWdpbilcbiAgICB9XG5cbiAgICBpZiAoaHRteC5jb25maWcuc2VsZlJlcXVlc3RzT25seSkge1xuICAgICAgaWYgKCFzYW1lSG9zdCkge1xuICAgICAgICByZXR1cm4gZmFsc2VcbiAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIHRyaWdnZXJFdmVudChlbHQsICdodG14OnZhbGlkYXRlVXJsJywgbWVyZ2VPYmplY3RzKHsgdXJsLCBzYW1lSG9zdCB9LCByZXF1ZXN0Q29uZmlnKSlcbiAgfVxuXG4gIC8qKlxuICAgKiBAcGFyYW0ge09iamVjdHxGb3JtRGF0YX0gb2JqXG4gICAqIEByZXR1cm4ge0Zvcm1EYXRhfVxuICAgKi9cbiAgZnVuY3Rpb24gZm9ybURhdGFGcm9tT2JqZWN0KG9iaikge1xuICAgIGlmIChvYmogaW5zdGFuY2VvZiBGb3JtRGF0YSkgcmV0dXJuIG9ialxuICAgIGNvbnN0IGZvcm1EYXRhID0gbmV3IEZvcm1EYXRhKClcbiAgICBmb3IgKGNvbnN0IGtleSBpbiBvYmopIHtcbiAgICAgIGlmIChvYmouaGFzT3duUHJvcGVydHkoa2V5KSkge1xuICAgICAgICBpZiAodHlwZW9mIG9ialtrZXldLmZvckVhY2ggPT09ICdmdW5jdGlvbicpIHtcbiAgICAgICAgICBvYmpba2V5XS5mb3JFYWNoKGZ1bmN0aW9uKHYpIHsgZm9ybURhdGEuYXBwZW5kKGtleSwgdikgfSlcbiAgICAgICAgfSBlbHNlIGlmICh0eXBlb2Ygb2JqW2tleV0gPT09ICdvYmplY3QnICYmICEob2JqW2tleV0gaW5zdGFuY2VvZiBCbG9iKSkge1xuICAgICAgICAgIGZvcm1EYXRhLmFwcGVuZChrZXksIEpTT04uc3RyaW5naWZ5KG9ialtrZXldKSlcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBmb3JtRGF0YS5hcHBlbmQoa2V5LCBvYmpba2V5XSlcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4gZm9ybURhdGFcbiAgfVxuXG4gIC8qKlxuICAgKiBAcGFyYW0ge0Zvcm1EYXRhfSBmb3JtRGF0YVxuICAgKiBAcGFyYW0ge3N0cmluZ30gbmFtZVxuICAgKiBAcGFyYW0ge0FycmF5fSBhcnJheVxuICAgKiBAcmV0dXJucyB7QXJyYXl9XG4gICAqL1xuICBmdW5jdGlvbiBmb3JtRGF0YUFycmF5UHJveHkoZm9ybURhdGEsIG5hbWUsIGFycmF5KSB7XG4gICAgLy8gbXV0YXRpbmcgdGhlIGFycmF5IHNob3VsZCBtdXRhdGUgdGhlIHVuZGVybHlpbmcgZm9ybSBkYXRhXG4gICAgcmV0dXJuIG5ldyBQcm94eShhcnJheSwge1xuICAgICAgZ2V0OiBmdW5jdGlvbih0YXJnZXQsIGtleSkge1xuICAgICAgICBpZiAodHlwZW9mIGtleSA9PT0gJ251bWJlcicpIHJldHVybiB0YXJnZXRba2V5XVxuICAgICAgICBpZiAoa2V5ID09PSAnbGVuZ3RoJykgcmV0dXJuIHRhcmdldC5sZW5ndGhcbiAgICAgICAgaWYgKGtleSA9PT0gJ3B1c2gnKSB7XG4gICAgICAgICAgcmV0dXJuIGZ1bmN0aW9uKHZhbHVlKSB7XG4gICAgICAgICAgICB0YXJnZXQucHVzaCh2YWx1ZSlcbiAgICAgICAgICAgIGZvcm1EYXRhLmFwcGVuZChuYW1lLCB2YWx1ZSlcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgaWYgKHR5cGVvZiB0YXJnZXRba2V5XSA9PT0gJ2Z1bmN0aW9uJykge1xuICAgICAgICAgIHJldHVybiBmdW5jdGlvbigpIHtcbiAgICAgICAgICAgIHRhcmdldFtrZXldLmFwcGx5KHRhcmdldCwgYXJndW1lbnRzKVxuICAgICAgICAgICAgZm9ybURhdGEuZGVsZXRlKG5hbWUpXG4gICAgICAgICAgICB0YXJnZXQuZm9yRWFjaChmdW5jdGlvbih2KSB7IGZvcm1EYXRhLmFwcGVuZChuYW1lLCB2KSB9KVxuICAgICAgICAgIH1cbiAgICAgICAgfVxuXG4gICAgICAgIGlmICh0YXJnZXRba2V5XSAmJiB0YXJnZXRba2V5XS5sZW5ndGggPT09IDEpIHtcbiAgICAgICAgICByZXR1cm4gdGFyZ2V0W2tleV1bMF1cbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICByZXR1cm4gdGFyZ2V0W2tleV1cbiAgICAgICAgfVxuICAgICAgfSxcbiAgICAgIHNldDogZnVuY3Rpb24odGFyZ2V0LCBpbmRleCwgdmFsdWUpIHtcbiAgICAgICAgdGFyZ2V0W2luZGV4XSA9IHZhbHVlXG4gICAgICAgIGZvcm1EYXRhLmRlbGV0ZShuYW1lKVxuICAgICAgICB0YXJnZXQuZm9yRWFjaChmdW5jdGlvbih2KSB7IGZvcm1EYXRhLmFwcGVuZChuYW1lLCB2KSB9KVxuICAgICAgICByZXR1cm4gdHJ1ZVxuICAgICAgfVxuICAgIH0pXG4gIH1cblxuICAvKipcbiAgICogQHBhcmFtIHtGb3JtRGF0YX0gZm9ybURhdGFcbiAgICogQHJldHVybnMge09iamVjdH1cbiAgICovXG4gIGZ1bmN0aW9uIGZvcm1EYXRhUHJveHkoZm9ybURhdGEpIHtcbiAgICByZXR1cm4gbmV3IFByb3h5KGZvcm1EYXRhLCB7XG4gICAgICBnZXQ6IGZ1bmN0aW9uKHRhcmdldCwgbmFtZSkge1xuICAgICAgICBpZiAodHlwZW9mIG5hbWUgPT09ICdzeW1ib2wnKSB7XG4gICAgICAgICAgLy8gRm9yd2FyZCBzeW1ib2wgY2FsbHMgdG8gdGhlIEZvcm1EYXRhIGl0c2VsZiBkaXJlY3RseVxuICAgICAgICAgIHJldHVybiBSZWZsZWN0LmdldCh0YXJnZXQsIG5hbWUpXG4gICAgICAgIH1cbiAgICAgICAgaWYgKG5hbWUgPT09ICd0b0pTT04nKSB7XG4gICAgICAgICAgLy8gU3VwcG9ydCBKU09OLnN0cmluZ2lmeSBjYWxsIG9uIHByb3h5XG4gICAgICAgICAgcmV0dXJuICgpID0+IE9iamVjdC5mcm9tRW50cmllcyhmb3JtRGF0YSlcbiAgICAgICAgfVxuICAgICAgICBpZiAobmFtZSBpbiB0YXJnZXQpIHtcbiAgICAgICAgICAvLyBXcmFwIGluIGZ1bmN0aW9uIHdpdGggYXBwbHkgdG8gY29ycmVjdGx5IGJpbmQgdGhlIEZvcm1EYXRhIGNvbnRleHQsIGFzIGEgZGlyZWN0IGNhbGwgd291bGQgcmVzdWx0IGluIGFuIGlsbGVnYWwgaW52b2NhdGlvbiBlcnJvclxuICAgICAgICAgIGlmICh0eXBlb2YgdGFyZ2V0W25hbWVdID09PSAnZnVuY3Rpb24nKSB7XG4gICAgICAgICAgICByZXR1cm4gZnVuY3Rpb24oKSB7XG4gICAgICAgICAgICAgIHJldHVybiBmb3JtRGF0YVtuYW1lXS5hcHBseShmb3JtRGF0YSwgYXJndW1lbnRzKVxuICAgICAgICAgICAgfVxuICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICByZXR1cm4gdGFyZ2V0W25hbWVdXG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIGNvbnN0IGFycmF5ID0gZm9ybURhdGEuZ2V0QWxsKG5hbWUpXG4gICAgICAgIC8vIFRob3NlIDIgdW5kZWZpbmVkICYgc2luZ2xlIHZhbHVlIHJldHVybnMgYXJlIGZvciByZXRyby1jb21wYXRpYmlsaXR5IGFzIHdlIHdlcmVuJ3QgdXNpbmcgRm9ybURhdGEgYmVmb3JlXG4gICAgICAgIGlmIChhcnJheS5sZW5ndGggPT09IDApIHtcbiAgICAgICAgICByZXR1cm4gdW5kZWZpbmVkXG4gICAgICAgIH0gZWxzZSBpZiAoYXJyYXkubGVuZ3RoID09PSAxKSB7XG4gICAgICAgICAgcmV0dXJuIGFycmF5WzBdXG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgcmV0dXJuIGZvcm1EYXRhQXJyYXlQcm94eSh0YXJnZXQsIG5hbWUsIGFycmF5KVxuICAgICAgICB9XG4gICAgICB9LFxuICAgICAgc2V0OiBmdW5jdGlvbih0YXJnZXQsIG5hbWUsIHZhbHVlKSB7XG4gICAgICAgIGlmICh0eXBlb2YgbmFtZSAhPT0gJ3N0cmluZycpIHtcbiAgICAgICAgICByZXR1cm4gZmFsc2VcbiAgICAgICAgfVxuICAgICAgICB0YXJnZXQuZGVsZXRlKG5hbWUpXG4gICAgICAgIGlmICh0eXBlb2YgdmFsdWUuZm9yRWFjaCA9PT0gJ2Z1bmN0aW9uJykge1xuICAgICAgICAgIHZhbHVlLmZvckVhY2goZnVuY3Rpb24odikgeyB0YXJnZXQuYXBwZW5kKG5hbWUsIHYpIH0pXG4gICAgICAgIH0gZWxzZSBpZiAodHlwZW9mIHZhbHVlID09PSAnb2JqZWN0JyAmJiAhKHZhbHVlIGluc3RhbmNlb2YgQmxvYikpIHtcbiAgICAgICAgICB0YXJnZXQuYXBwZW5kKG5hbWUsIEpTT04uc3RyaW5naWZ5KHZhbHVlKSlcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICB0YXJnZXQuYXBwZW5kKG5hbWUsIHZhbHVlKVxuICAgICAgICB9XG4gICAgICAgIHJldHVybiB0cnVlXG4gICAgICB9LFxuICAgICAgZGVsZXRlUHJvcGVydHk6IGZ1bmN0aW9uKHRhcmdldCwgbmFtZSkge1xuICAgICAgICBpZiAodHlwZW9mIG5hbWUgPT09ICdzdHJpbmcnKSB7XG4gICAgICAgICAgdGFyZ2V0LmRlbGV0ZShuYW1lKVxuICAgICAgICB9XG4gICAgICAgIHJldHVybiB0cnVlXG4gICAgICB9LFxuICAgICAgLy8gU3VwcG9ydCBPYmplY3QuYXNzaWduIGNhbGwgZnJvbSBwcm94eVxuICAgICAgb3duS2V5czogZnVuY3Rpb24odGFyZ2V0KSB7XG4gICAgICAgIHJldHVybiBSZWZsZWN0Lm93bktleXMoT2JqZWN0LmZyb21FbnRyaWVzKHRhcmdldCkpXG4gICAgICB9LFxuICAgICAgZ2V0T3duUHJvcGVydHlEZXNjcmlwdG9yOiBmdW5jdGlvbih0YXJnZXQsIHByb3ApIHtcbiAgICAgICAgcmV0dXJuIFJlZmxlY3QuZ2V0T3duUHJvcGVydHlEZXNjcmlwdG9yKE9iamVjdC5mcm9tRW50cmllcyh0YXJnZXQpLCBwcm9wKVxuICAgICAgfVxuICAgIH0pXG4gIH1cblxuICAvKipcbiAgICogQHBhcmFtIHtIdHRwVmVyYn0gdmVyYlxuICAgKiBAcGFyYW0ge3N0cmluZ30gcGF0aFxuICAgKiBAcGFyYW0ge0VsZW1lbnR9IGVsdFxuICAgKiBAcGFyYW0ge0V2ZW50fSBldmVudFxuICAgKiBAcGFyYW0ge0h0bXhBamF4RXRjfSBbZXRjXVxuICAgKiBAcGFyYW0ge2Jvb2xlYW59IFtjb25maXJtZWRdXG4gICAqIEByZXR1cm4ge1Byb21pc2U8dm9pZD59XG4gICAqL1xuICBmdW5jdGlvbiBpc3N1ZUFqYXhSZXF1ZXN0KHZlcmIsIHBhdGgsIGVsdCwgZXZlbnQsIGV0YywgY29uZmlybWVkKSB7XG4gICAgbGV0IHJlc29sdmUgPSBudWxsXG4gICAgbGV0IHJlamVjdCA9IG51bGxcbiAgICBldGMgPSBldGMgIT0gbnVsbCA/IGV0YyA6IHt9XG4gICAgaWYgKGV0Yy5yZXR1cm5Qcm9taXNlICYmIHR5cGVvZiBQcm9taXNlICE9PSAndW5kZWZpbmVkJykge1xuICAgICAgdmFyIHByb21pc2UgPSBuZXcgUHJvbWlzZShmdW5jdGlvbihfcmVzb2x2ZSwgX3JlamVjdCkge1xuICAgICAgICByZXNvbHZlID0gX3Jlc29sdmVcbiAgICAgICAgcmVqZWN0ID0gX3JlamVjdFxuICAgICAgfSlcbiAgICB9XG4gICAgaWYgKGVsdCA9PSBudWxsKSB7XG4gICAgICBlbHQgPSBnZXREb2N1bWVudCgpLmJvZHlcbiAgICB9XG4gICAgY29uc3QgcmVzcG9uc2VIYW5kbGVyID0gZXRjLmhhbmRsZXIgfHwgaGFuZGxlQWpheFJlc3BvbnNlXG4gICAgY29uc3Qgc2VsZWN0ID0gZXRjLnNlbGVjdCB8fCBudWxsXG5cbiAgICBpZiAoIWJvZHlDb250YWlucyhlbHQpKSB7XG4gICAgLy8gZG8gbm90IGlzc3VlIHJlcXVlc3RzIGZvciBlbGVtZW50cyByZW1vdmVkIGZyb20gdGhlIERPTVxuICAgICAgbWF5YmVDYWxsKHJlc29sdmUpXG4gICAgICByZXR1cm4gcHJvbWlzZVxuICAgIH1cbiAgICBjb25zdCB0YXJnZXQgPSBldGMudGFyZ2V0T3ZlcnJpZGUgfHwgYXNFbGVtZW50KGdldFRhcmdldChlbHQpKVxuICAgIGlmICh0YXJnZXQgPT0gbnVsbCB8fCB0YXJnZXQgPT0gRFVNTVlfRUxUKSB7XG4gICAgICB0cmlnZ2VyRXJyb3JFdmVudChlbHQsICdodG14OnRhcmdldEVycm9yJywgeyB0YXJnZXQ6IGdldEF0dHJpYnV0ZVZhbHVlKGVsdCwgJ2h4LXRhcmdldCcpIH0pXG4gICAgICBtYXliZUNhbGwocmVqZWN0KVxuICAgICAgcmV0dXJuIHByb21pc2VcbiAgICB9XG5cbiAgICBsZXQgZWx0RGF0YSA9IGdldEludGVybmFsRGF0YShlbHQpXG4gICAgY29uc3Qgc3VibWl0dGVyID0gZWx0RGF0YS5sYXN0QnV0dG9uQ2xpY2tlZFxuXG4gICAgaWYgKHN1Ym1pdHRlcikge1xuICAgICAgY29uc3QgYnV0dG9uUGF0aCA9IGdldFJhd0F0dHJpYnV0ZShzdWJtaXR0ZXIsICdmb3JtYWN0aW9uJylcbiAgICAgIGlmIChidXR0b25QYXRoICE9IG51bGwpIHtcbiAgICAgICAgcGF0aCA9IGJ1dHRvblBhdGhcbiAgICAgIH1cblxuICAgICAgY29uc3QgYnV0dG9uVmVyYiA9IGdldFJhd0F0dHJpYnV0ZShzdWJtaXR0ZXIsICdmb3JtbWV0aG9kJylcbiAgICAgIGlmIChidXR0b25WZXJiICE9IG51bGwpIHtcbiAgICAgIC8vIGlnbm9yZSBidXR0b25zIHdpdGggZm9ybW1ldGhvZD1cImRpYWxvZ1wiXG4gICAgICAgIGlmIChidXR0b25WZXJiLnRvTG93ZXJDYXNlKCkgIT09ICdkaWFsb2cnKSB7XG4gICAgICAgICAgdmVyYiA9ICgvKiogQHR5cGUgSHR0cFZlcmIgKi8oYnV0dG9uVmVyYikpXG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG5cbiAgICBjb25zdCBjb25maXJtUXVlc3Rpb24gPSBnZXRDbG9zZXN0QXR0cmlidXRlVmFsdWUoZWx0LCAnaHgtY29uZmlybScpXG4gICAgLy8gYWxsb3cgZXZlbnQtYmFzZWQgY29uZmlybWF0aW9uIHcvIGEgY2FsbGJhY2tcbiAgICBpZiAoY29uZmlybWVkID09PSB1bmRlZmluZWQpIHtcbiAgICAgIGNvbnN0IGlzc3VlUmVxdWVzdCA9IGZ1bmN0aW9uKHNraXBDb25maXJtYXRpb24pIHtcbiAgICAgICAgcmV0dXJuIGlzc3VlQWpheFJlcXVlc3QodmVyYiwgcGF0aCwgZWx0LCBldmVudCwgZXRjLCAhIXNraXBDb25maXJtYXRpb24pXG4gICAgICB9XG4gICAgICBjb25zdCBjb25maXJtRGV0YWlscyA9IHsgdGFyZ2V0LCBlbHQsIHBhdGgsIHZlcmIsIHRyaWdnZXJpbmdFdmVudDogZXZlbnQsIGV0YywgaXNzdWVSZXF1ZXN0LCBxdWVzdGlvbjogY29uZmlybVF1ZXN0aW9uIH1cbiAgICAgIGlmICh0cmlnZ2VyRXZlbnQoZWx0LCAnaHRteDpjb25maXJtJywgY29uZmlybURldGFpbHMpID09PSBmYWxzZSkge1xuICAgICAgICBtYXliZUNhbGwocmVzb2x2ZSlcbiAgICAgICAgcmV0dXJuIHByb21pc2VcbiAgICAgIH1cbiAgICB9XG5cbiAgICBsZXQgc3luY0VsdCA9IGVsdFxuICAgIGxldCBzeW5jU3RyYXRlZ3kgPSBnZXRDbG9zZXN0QXR0cmlidXRlVmFsdWUoZWx0LCAnaHgtc3luYycpXG4gICAgbGV0IHF1ZXVlU3RyYXRlZ3kgPSBudWxsXG4gICAgbGV0IGFib3J0YWJsZSA9IGZhbHNlXG4gICAgaWYgKHN5bmNTdHJhdGVneSkge1xuICAgICAgY29uc3Qgc3luY1N0cmluZ3MgPSBzeW5jU3RyYXRlZ3kuc3BsaXQoJzonKVxuICAgICAgY29uc3Qgc2VsZWN0b3IgPSBzeW5jU3RyaW5nc1swXS50cmltKClcbiAgICAgIGlmIChzZWxlY3RvciA9PT0gJ3RoaXMnKSB7XG4gICAgICAgIHN5bmNFbHQgPSBmaW5kVGhpc0VsZW1lbnQoZWx0LCAnaHgtc3luYycpXG4gICAgICB9IGVsc2Uge1xuICAgICAgICBzeW5jRWx0ID0gYXNFbGVtZW50KHF1ZXJ5U2VsZWN0b3JFeHQoZWx0LCBzZWxlY3RvcikpXG4gICAgICB9XG4gICAgICAvLyBkZWZhdWx0IHRvIHRoZSBkcm9wIHN0cmF0ZWd5XG4gICAgICBzeW5jU3RyYXRlZ3kgPSAoc3luY1N0cmluZ3NbMV0gfHwgJ2Ryb3AnKS50cmltKClcbiAgICAgIGVsdERhdGEgPSBnZXRJbnRlcm5hbERhdGEoc3luY0VsdClcbiAgICAgIGlmIChzeW5jU3RyYXRlZ3kgPT09ICdkcm9wJyAmJiBlbHREYXRhLnhociAmJiBlbHREYXRhLmFib3J0YWJsZSAhPT0gdHJ1ZSkge1xuICAgICAgICBtYXliZUNhbGwocmVzb2x2ZSlcbiAgICAgICAgcmV0dXJuIHByb21pc2VcbiAgICAgIH0gZWxzZSBpZiAoc3luY1N0cmF0ZWd5ID09PSAnYWJvcnQnKSB7XG4gICAgICAgIGlmIChlbHREYXRhLnhocikge1xuICAgICAgICAgIG1heWJlQ2FsbChyZXNvbHZlKVxuICAgICAgICAgIHJldHVybiBwcm9taXNlXG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgYWJvcnRhYmxlID0gdHJ1ZVxuICAgICAgICB9XG4gICAgICB9IGVsc2UgaWYgKHN5bmNTdHJhdGVneSA9PT0gJ3JlcGxhY2UnKSB7XG4gICAgICAgIHRyaWdnZXJFdmVudChzeW5jRWx0LCAnaHRteDphYm9ydCcpIC8vIGFib3J0IHRoZSBjdXJyZW50IHJlcXVlc3QgYW5kIGNvbnRpbnVlXG4gICAgICB9IGVsc2UgaWYgKHN5bmNTdHJhdGVneS5pbmRleE9mKCdxdWV1ZScpID09PSAwKSB7XG4gICAgICAgIGNvbnN0IHF1ZXVlU3RyQXJyYXkgPSBzeW5jU3RyYXRlZ3kuc3BsaXQoJyAnKVxuICAgICAgICBxdWV1ZVN0cmF0ZWd5ID0gKHF1ZXVlU3RyQXJyYXlbMV0gfHwgJ2xhc3QnKS50cmltKClcbiAgICAgIH1cbiAgICB9XG5cbiAgICBpZiAoZWx0RGF0YS54aHIpIHtcbiAgICAgIGlmIChlbHREYXRhLmFib3J0YWJsZSkge1xuICAgICAgICB0cmlnZ2VyRXZlbnQoc3luY0VsdCwgJ2h0bXg6YWJvcnQnKSAvLyBhYm9ydCB0aGUgY3VycmVudCByZXF1ZXN0IGFuZCBjb250aW51ZVxuICAgICAgfSBlbHNlIHtcbiAgICAgICAgaWYgKHF1ZXVlU3RyYXRlZ3kgPT0gbnVsbCkge1xuICAgICAgICAgIGlmIChldmVudCkge1xuICAgICAgICAgICAgY29uc3QgZXZlbnREYXRhID0gZ2V0SW50ZXJuYWxEYXRhKGV2ZW50KVxuICAgICAgICAgICAgaWYgKGV2ZW50RGF0YSAmJiBldmVudERhdGEudHJpZ2dlclNwZWMgJiYgZXZlbnREYXRhLnRyaWdnZXJTcGVjLnF1ZXVlKSB7XG4gICAgICAgICAgICAgIHF1ZXVlU3RyYXRlZ3kgPSBldmVudERhdGEudHJpZ2dlclNwZWMucXVldWVcbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9XG4gICAgICAgICAgaWYgKHF1ZXVlU3RyYXRlZ3kgPT0gbnVsbCkge1xuICAgICAgICAgICAgcXVldWVTdHJhdGVneSA9ICdsYXN0J1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICBpZiAoZWx0RGF0YS5xdWV1ZWRSZXF1ZXN0cyA9PSBudWxsKSB7XG4gICAgICAgICAgZWx0RGF0YS5xdWV1ZWRSZXF1ZXN0cyA9IFtdXG4gICAgICAgIH1cbiAgICAgICAgaWYgKHF1ZXVlU3RyYXRlZ3kgPT09ICdmaXJzdCcgJiYgZWx0RGF0YS5xdWV1ZWRSZXF1ZXN0cy5sZW5ndGggPT09IDApIHtcbiAgICAgICAgICBlbHREYXRhLnF1ZXVlZFJlcXVlc3RzLnB1c2goZnVuY3Rpb24oKSB7XG4gICAgICAgICAgICBpc3N1ZUFqYXhSZXF1ZXN0KHZlcmIsIHBhdGgsIGVsdCwgZXZlbnQsIGV0YylcbiAgICAgICAgICB9KVxuICAgICAgICB9IGVsc2UgaWYgKHF1ZXVlU3RyYXRlZ3kgPT09ICdhbGwnKSB7XG4gICAgICAgICAgZWx0RGF0YS5xdWV1ZWRSZXF1ZXN0cy5wdXNoKGZ1bmN0aW9uKCkge1xuICAgICAgICAgICAgaXNzdWVBamF4UmVxdWVzdCh2ZXJiLCBwYXRoLCBlbHQsIGV2ZW50LCBldGMpXG4gICAgICAgICAgfSlcbiAgICAgICAgfSBlbHNlIGlmIChxdWV1ZVN0cmF0ZWd5ID09PSAnbGFzdCcpIHtcbiAgICAgICAgICBlbHREYXRhLnF1ZXVlZFJlcXVlc3RzID0gW10gLy8gZHVtcCBleGlzdGluZyBxdWV1ZVxuICAgICAgICAgIGVsdERhdGEucXVldWVkUmVxdWVzdHMucHVzaChmdW5jdGlvbigpIHtcbiAgICAgICAgICAgIGlzc3VlQWpheFJlcXVlc3QodmVyYiwgcGF0aCwgZWx0LCBldmVudCwgZXRjKVxuICAgICAgICAgIH0pXG4gICAgICAgIH1cbiAgICAgICAgbWF5YmVDYWxsKHJlc29sdmUpXG4gICAgICAgIHJldHVybiBwcm9taXNlXG4gICAgICB9XG4gICAgfVxuXG4gICAgY29uc3QgeGhyID0gbmV3IFhNTEh0dHBSZXF1ZXN0KClcbiAgICBlbHREYXRhLnhociA9IHhoclxuICAgIGVsdERhdGEuYWJvcnRhYmxlID0gYWJvcnRhYmxlXG4gICAgY29uc3QgZW5kUmVxdWVzdExvY2sgPSBmdW5jdGlvbigpIHtcbiAgICAgIGVsdERhdGEueGhyID0gbnVsbFxuICAgICAgZWx0RGF0YS5hYm9ydGFibGUgPSBmYWxzZVxuICAgICAgaWYgKGVsdERhdGEucXVldWVkUmVxdWVzdHMgIT0gbnVsbCAmJlxuICAgICAgZWx0RGF0YS5xdWV1ZWRSZXF1ZXN0cy5sZW5ndGggPiAwKSB7XG4gICAgICAgIGNvbnN0IHF1ZXVlZFJlcXVlc3QgPSBlbHREYXRhLnF1ZXVlZFJlcXVlc3RzLnNoaWZ0KClcbiAgICAgICAgcXVldWVkUmVxdWVzdCgpXG4gICAgICB9XG4gICAgfVxuICAgIGNvbnN0IHByb21wdFF1ZXN0aW9uID0gZ2V0Q2xvc2VzdEF0dHJpYnV0ZVZhbHVlKGVsdCwgJ2h4LXByb21wdCcpXG4gICAgaWYgKHByb21wdFF1ZXN0aW9uKSB7XG4gICAgICB2YXIgcHJvbXB0UmVzcG9uc2UgPSBwcm9tcHQocHJvbXB0UXVlc3Rpb24pXG4gICAgICAvLyBwcm9tcHQgcmV0dXJucyBudWxsIGlmIGNhbmNlbGxlZCBhbmQgZW1wdHkgc3RyaW5nIGlmIGFjY2VwdGVkIHdpdGggbm8gZW50cnlcbiAgICAgIGlmIChwcm9tcHRSZXNwb25zZSA9PT0gbnVsbCB8fFxuICAgICAgIXRyaWdnZXJFdmVudChlbHQsICdodG14OnByb21wdCcsIHsgcHJvbXB0OiBwcm9tcHRSZXNwb25zZSwgdGFyZ2V0IH0pKSB7XG4gICAgICAgIG1heWJlQ2FsbChyZXNvbHZlKVxuICAgICAgICBlbmRSZXF1ZXN0TG9jaygpXG4gICAgICAgIHJldHVybiBwcm9taXNlXG4gICAgICB9XG4gICAgfVxuXG4gICAgaWYgKGNvbmZpcm1RdWVzdGlvbiAmJiAhY29uZmlybWVkKSB7XG4gICAgICBpZiAoIWNvbmZpcm0oY29uZmlybVF1ZXN0aW9uKSkge1xuICAgICAgICBtYXliZUNhbGwocmVzb2x2ZSlcbiAgICAgICAgZW5kUmVxdWVzdExvY2soKVxuICAgICAgICByZXR1cm4gcHJvbWlzZVxuICAgICAgfVxuICAgIH1cblxuICAgIGxldCBoZWFkZXJzID0gZ2V0SGVhZGVycyhlbHQsIHRhcmdldCwgcHJvbXB0UmVzcG9uc2UpXG5cbiAgICBpZiAodmVyYiAhPT0gJ2dldCcgJiYgIXVzZXNGb3JtRGF0YShlbHQpKSB7XG4gICAgICBoZWFkZXJzWydDb250ZW50LVR5cGUnXSA9ICdhcHBsaWNhdGlvbi94LXd3dy1mb3JtLXVybGVuY29kZWQnXG4gICAgfVxuXG4gICAgaWYgKGV0Yy5oZWFkZXJzKSB7XG4gICAgICBoZWFkZXJzID0gbWVyZ2VPYmplY3RzKGhlYWRlcnMsIGV0Yy5oZWFkZXJzKVxuICAgIH1cbiAgICBjb25zdCByZXN1bHRzID0gZ2V0SW5wdXRWYWx1ZXMoZWx0LCB2ZXJiKVxuICAgIGxldCBlcnJvcnMgPSByZXN1bHRzLmVycm9yc1xuICAgIGNvbnN0IHJhd0Zvcm1EYXRhID0gcmVzdWx0cy5mb3JtRGF0YVxuICAgIGlmIChldGMudmFsdWVzKSB7XG4gICAgICBvdmVycmlkZUZvcm1EYXRhKHJhd0Zvcm1EYXRhLCBmb3JtRGF0YUZyb21PYmplY3QoZXRjLnZhbHVlcykpXG4gICAgfVxuICAgIGNvbnN0IGV4cHJlc3Npb25WYXJzID0gZm9ybURhdGFGcm9tT2JqZWN0KGdldEV4cHJlc3Npb25WYXJzKGVsdCkpXG4gICAgY29uc3QgYWxsRm9ybURhdGEgPSBvdmVycmlkZUZvcm1EYXRhKHJhd0Zvcm1EYXRhLCBleHByZXNzaW9uVmFycylcbiAgICBsZXQgZmlsdGVyZWRGb3JtRGF0YSA9IGZpbHRlclZhbHVlcyhhbGxGb3JtRGF0YSwgZWx0KVxuXG4gICAgaWYgKGh0bXguY29uZmlnLmdldENhY2hlQnVzdGVyUGFyYW0gJiYgdmVyYiA9PT0gJ2dldCcpIHtcbiAgICAgIGZpbHRlcmVkRm9ybURhdGEuc2V0KCdvcmcuaHRteC5jYWNoZS1idXN0ZXInLCBnZXRSYXdBdHRyaWJ1dGUodGFyZ2V0LCAnaWQnKSB8fCAndHJ1ZScpXG4gICAgfVxuXG4gICAgLy8gYmVoYXZpb3Igb2YgYW5jaG9ycyB3LyBlbXB0eSBocmVmIGlzIHRvIHVzZSB0aGUgY3VycmVudCBVUkxcbiAgICBpZiAocGF0aCA9PSBudWxsIHx8IHBhdGggPT09ICcnKSB7XG4gICAgICBwYXRoID0gZ2V0RG9jdW1lbnQoKS5sb2NhdGlvbi5ocmVmXG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogQHR5cGUge09iamVjdH1cbiAgICAgKiBAcHJvcGVydHkge2Jvb2xlYW59IFtjcmVkZW50aWFsc11cbiAgICAgKiBAcHJvcGVydHkge251bWJlcn0gW3RpbWVvdXRdXG4gICAgICogQHByb3BlcnR5IHtib29sZWFufSBbbm9IZWFkZXJzXVxuICAgICAqL1xuICAgIGNvbnN0IHJlcXVlc3RBdHRyVmFsdWVzID0gZ2V0VmFsdWVzRm9yRWxlbWVudChlbHQsICdoeC1yZXF1ZXN0JylcblxuICAgIGNvbnN0IGVsdElzQm9vc3RlZCA9IGdldEludGVybmFsRGF0YShlbHQpLmJvb3N0ZWRcblxuICAgIGxldCB1c2VVcmxQYXJhbXMgPSBodG14LmNvbmZpZy5tZXRob2RzVGhhdFVzZVVybFBhcmFtcy5pbmRleE9mKHZlcmIpID49IDBcblxuICAgIC8qKiBAdHlwZSBIdG14UmVxdWVzdENvbmZpZyAqL1xuICAgIGNvbnN0IHJlcXVlc3RDb25maWcgPSB7XG4gICAgICBib29zdGVkOiBlbHRJc0Jvb3N0ZWQsXG4gICAgICB1c2VVcmxQYXJhbXMsXG4gICAgICBmb3JtRGF0YTogZmlsdGVyZWRGb3JtRGF0YSxcbiAgICAgIHBhcmFtZXRlcnM6IGZvcm1EYXRhUHJveHkoZmlsdGVyZWRGb3JtRGF0YSksXG4gICAgICB1bmZpbHRlcmVkRm9ybURhdGE6IGFsbEZvcm1EYXRhLFxuICAgICAgdW5maWx0ZXJlZFBhcmFtZXRlcnM6IGZvcm1EYXRhUHJveHkoYWxsRm9ybURhdGEpLFxuICAgICAgaGVhZGVycyxcbiAgICAgIHRhcmdldCxcbiAgICAgIHZlcmIsXG4gICAgICBlcnJvcnMsXG4gICAgICB3aXRoQ3JlZGVudGlhbHM6IGV0Yy5jcmVkZW50aWFscyB8fCByZXF1ZXN0QXR0clZhbHVlcy5jcmVkZW50aWFscyB8fCBodG14LmNvbmZpZy53aXRoQ3JlZGVudGlhbHMsXG4gICAgICB0aW1lb3V0OiBldGMudGltZW91dCB8fCByZXF1ZXN0QXR0clZhbHVlcy50aW1lb3V0IHx8IGh0bXguY29uZmlnLnRpbWVvdXQsXG4gICAgICBwYXRoLFxuICAgICAgdHJpZ2dlcmluZ0V2ZW50OiBldmVudFxuICAgIH1cblxuICAgIGlmICghdHJpZ2dlckV2ZW50KGVsdCwgJ2h0bXg6Y29uZmlnUmVxdWVzdCcsIHJlcXVlc3RDb25maWcpKSB7XG4gICAgICBtYXliZUNhbGwocmVzb2x2ZSlcbiAgICAgIGVuZFJlcXVlc3RMb2NrKClcbiAgICAgIHJldHVybiBwcm9taXNlXG4gICAgfVxuXG4gICAgLy8gY29weSBvdXQgaW4gY2FzZSB0aGUgb2JqZWN0IHdhcyBvdmVyd3JpdHRlblxuICAgIHBhdGggPSByZXF1ZXN0Q29uZmlnLnBhdGhcbiAgICB2ZXJiID0gcmVxdWVzdENvbmZpZy52ZXJiXG4gICAgaGVhZGVycyA9IHJlcXVlc3RDb25maWcuaGVhZGVyc1xuICAgIGZpbHRlcmVkRm9ybURhdGEgPSBmb3JtRGF0YUZyb21PYmplY3QocmVxdWVzdENvbmZpZy5wYXJhbWV0ZXJzKVxuICAgIGVycm9ycyA9IHJlcXVlc3RDb25maWcuZXJyb3JzXG4gICAgdXNlVXJsUGFyYW1zID0gcmVxdWVzdENvbmZpZy51c2VVcmxQYXJhbXNcblxuICAgIGlmIChlcnJvcnMgJiYgZXJyb3JzLmxlbmd0aCA+IDApIHtcbiAgICAgIHRyaWdnZXJFdmVudChlbHQsICdodG14OnZhbGlkYXRpb246aGFsdGVkJywgcmVxdWVzdENvbmZpZylcbiAgICAgIG1heWJlQ2FsbChyZXNvbHZlKVxuICAgICAgZW5kUmVxdWVzdExvY2soKVxuICAgICAgcmV0dXJuIHByb21pc2VcbiAgICB9XG5cbiAgICBjb25zdCBzcGxpdFBhdGggPSBwYXRoLnNwbGl0KCcjJylcbiAgICBjb25zdCBwYXRoTm9BbmNob3IgPSBzcGxpdFBhdGhbMF1cbiAgICBjb25zdCBhbmNob3IgPSBzcGxpdFBhdGhbMV1cblxuICAgIGxldCBmaW5hbFBhdGggPSBwYXRoXG4gICAgaWYgKHVzZVVybFBhcmFtcykge1xuICAgICAgZmluYWxQYXRoID0gcGF0aE5vQW5jaG9yXG4gICAgICBjb25zdCBoYXNWYWx1ZXMgPSAhZmlsdGVyZWRGb3JtRGF0YS5rZXlzKCkubmV4dCgpLmRvbmVcbiAgICAgIGlmIChoYXNWYWx1ZXMpIHtcbiAgICAgICAgaWYgKGZpbmFsUGF0aC5pbmRleE9mKCc/JykgPCAwKSB7XG4gICAgICAgICAgZmluYWxQYXRoICs9ICc/J1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIGZpbmFsUGF0aCArPSAnJidcbiAgICAgICAgfVxuICAgICAgICBmaW5hbFBhdGggKz0gdXJsRW5jb2RlKGZpbHRlcmVkRm9ybURhdGEpXG4gICAgICAgIGlmIChhbmNob3IpIHtcbiAgICAgICAgICBmaW5hbFBhdGggKz0gJyMnICsgYW5jaG9yXG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG5cbiAgICBpZiAoIXZlcmlmeVBhdGgoZWx0LCBmaW5hbFBhdGgsIHJlcXVlc3RDb25maWcpKSB7XG4gICAgICB0cmlnZ2VyRXJyb3JFdmVudChlbHQsICdodG14OmludmFsaWRQYXRoJywgcmVxdWVzdENvbmZpZylcbiAgICAgIG1heWJlQ2FsbChyZWplY3QpXG4gICAgICByZXR1cm4gcHJvbWlzZVxuICAgIH1cblxuICAgIHhoci5vcGVuKHZlcmIudG9VcHBlckNhc2UoKSwgZmluYWxQYXRoLCB0cnVlKVxuICAgIHhoci5vdmVycmlkZU1pbWVUeXBlKCd0ZXh0L2h0bWwnKVxuICAgIHhoci53aXRoQ3JlZGVudGlhbHMgPSByZXF1ZXN0Q29uZmlnLndpdGhDcmVkZW50aWFsc1xuICAgIHhoci50aW1lb3V0ID0gcmVxdWVzdENvbmZpZy50aW1lb3V0XG5cbiAgICAvLyByZXF1ZXN0IGhlYWRlcnNcbiAgICBpZiAocmVxdWVzdEF0dHJWYWx1ZXMubm9IZWFkZXJzKSB7XG4gICAgLy8gaWdub3JlIGFsbCBoZWFkZXJzXG4gICAgfSBlbHNlIHtcbiAgICAgIGZvciAoY29uc3QgaGVhZGVyIGluIGhlYWRlcnMpIHtcbiAgICAgICAgaWYgKGhlYWRlcnMuaGFzT3duUHJvcGVydHkoaGVhZGVyKSkge1xuICAgICAgICAgIGNvbnN0IGhlYWRlclZhbHVlID0gaGVhZGVyc1toZWFkZXJdXG4gICAgICAgICAgc2FmZWx5U2V0SGVhZGVyVmFsdWUoeGhyLCBoZWFkZXIsIGhlYWRlclZhbHVlKVxuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuXG4gICAgLyoqIEB0eXBlIHtIdG14UmVzcG9uc2VJbmZvfSAqL1xuICAgIGNvbnN0IHJlc3BvbnNlSW5mbyA9IHtcbiAgICAgIHhocixcbiAgICAgIHRhcmdldCxcbiAgICAgIHJlcXVlc3RDb25maWcsXG4gICAgICBldGMsXG4gICAgICBib29zdGVkOiBlbHRJc0Jvb3N0ZWQsXG4gICAgICBzZWxlY3QsXG4gICAgICBwYXRoSW5mbzoge1xuICAgICAgICByZXF1ZXN0UGF0aDogcGF0aCxcbiAgICAgICAgZmluYWxSZXF1ZXN0UGF0aDogZmluYWxQYXRoLFxuICAgICAgICByZXNwb25zZVBhdGg6IG51bGwsXG4gICAgICAgIGFuY2hvclxuICAgICAgfVxuICAgIH1cblxuICAgIHhoci5vbmxvYWQgPSBmdW5jdGlvbigpIHtcbiAgICAgIHRyeSB7XG4gICAgICAgIGNvbnN0IGhpZXJhcmNoeSA9IGhpZXJhcmNoeUZvckVsdChlbHQpXG4gICAgICAgIHJlc3BvbnNlSW5mby5wYXRoSW5mby5yZXNwb25zZVBhdGggPSBnZXRQYXRoRnJvbVJlc3BvbnNlKHhocilcbiAgICAgICAgcmVzcG9uc2VIYW5kbGVyKGVsdCwgcmVzcG9uc2VJbmZvKVxuICAgICAgICBpZiAocmVzcG9uc2VJbmZvLmtlZXBJbmRpY2F0b3JzICE9PSB0cnVlKSB7XG4gICAgICAgICAgcmVtb3ZlUmVxdWVzdEluZGljYXRvcnMoaW5kaWNhdG9ycywgZGlzYWJsZUVsdHMpXG4gICAgICAgIH1cbiAgICAgICAgdHJpZ2dlckV2ZW50KGVsdCwgJ2h0bXg6YWZ0ZXJSZXF1ZXN0JywgcmVzcG9uc2VJbmZvKVxuICAgICAgICB0cmlnZ2VyRXZlbnQoZWx0LCAnaHRteDphZnRlck9uTG9hZCcsIHJlc3BvbnNlSW5mbylcbiAgICAgICAgLy8gaWYgdGhlIGJvZHkgbm8gbG9uZ2VyIGNvbnRhaW5zIHRoZSBlbGVtZW50LCB0cmlnZ2VyIHRoZSBldmVudCBvbiB0aGUgY2xvc2VzdCBwYXJlbnRcbiAgICAgICAgLy8gcmVtYWluaW5nIGluIHRoZSBET01cbiAgICAgICAgaWYgKCFib2R5Q29udGFpbnMoZWx0KSkge1xuICAgICAgICAgIGxldCBzZWNvbmRhcnlUcmlnZ2VyRWx0ID0gbnVsbFxuICAgICAgICAgIHdoaWxlIChoaWVyYXJjaHkubGVuZ3RoID4gMCAmJiBzZWNvbmRhcnlUcmlnZ2VyRWx0ID09IG51bGwpIHtcbiAgICAgICAgICAgIGNvbnN0IHBhcmVudEVsdEluSGllcmFyY2h5ID0gaGllcmFyY2h5LnNoaWZ0KClcbiAgICAgICAgICAgIGlmIChib2R5Q29udGFpbnMocGFyZW50RWx0SW5IaWVyYXJjaHkpKSB7XG4gICAgICAgICAgICAgIHNlY29uZGFyeVRyaWdnZXJFbHQgPSBwYXJlbnRFbHRJbkhpZXJhcmNoeVxuICAgICAgICAgICAgfVxuICAgICAgICAgIH1cbiAgICAgICAgICBpZiAoc2Vjb25kYXJ5VHJpZ2dlckVsdCkge1xuICAgICAgICAgICAgdHJpZ2dlckV2ZW50KHNlY29uZGFyeVRyaWdnZXJFbHQsICdodG14OmFmdGVyUmVxdWVzdCcsIHJlc3BvbnNlSW5mbylcbiAgICAgICAgICAgIHRyaWdnZXJFdmVudChzZWNvbmRhcnlUcmlnZ2VyRWx0LCAnaHRteDphZnRlck9uTG9hZCcsIHJlc3BvbnNlSW5mbylcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgbWF5YmVDYWxsKHJlc29sdmUpXG4gICAgICAgIGVuZFJlcXVlc3RMb2NrKClcbiAgICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgICAgdHJpZ2dlckVycm9yRXZlbnQoZWx0LCAnaHRteDpvbkxvYWRFcnJvcicsIG1lcmdlT2JqZWN0cyh7IGVycm9yOiBlIH0sIHJlc3BvbnNlSW5mbykpXG4gICAgICAgIHRocm93IGVcbiAgICAgIH1cbiAgICB9XG4gICAgeGhyLm9uZXJyb3IgPSBmdW5jdGlvbigpIHtcbiAgICAgIHJlbW92ZVJlcXVlc3RJbmRpY2F0b3JzKGluZGljYXRvcnMsIGRpc2FibGVFbHRzKVxuICAgICAgdHJpZ2dlckVycm9yRXZlbnQoZWx0LCAnaHRteDphZnRlclJlcXVlc3QnLCByZXNwb25zZUluZm8pXG4gICAgICB0cmlnZ2VyRXJyb3JFdmVudChlbHQsICdodG14OnNlbmRFcnJvcicsIHJlc3BvbnNlSW5mbylcbiAgICAgIG1heWJlQ2FsbChyZWplY3QpXG4gICAgICBlbmRSZXF1ZXN0TG9jaygpXG4gICAgfVxuICAgIHhoci5vbmFib3J0ID0gZnVuY3Rpb24oKSB7XG4gICAgICByZW1vdmVSZXF1ZXN0SW5kaWNhdG9ycyhpbmRpY2F0b3JzLCBkaXNhYmxlRWx0cylcbiAgICAgIHRyaWdnZXJFcnJvckV2ZW50KGVsdCwgJ2h0bXg6YWZ0ZXJSZXF1ZXN0JywgcmVzcG9uc2VJbmZvKVxuICAgICAgdHJpZ2dlckVycm9yRXZlbnQoZWx0LCAnaHRteDpzZW5kQWJvcnQnLCByZXNwb25zZUluZm8pXG4gICAgICBtYXliZUNhbGwocmVqZWN0KVxuICAgICAgZW5kUmVxdWVzdExvY2soKVxuICAgIH1cbiAgICB4aHIub250aW1lb3V0ID0gZnVuY3Rpb24oKSB7XG4gICAgICByZW1vdmVSZXF1ZXN0SW5kaWNhdG9ycyhpbmRpY2F0b3JzLCBkaXNhYmxlRWx0cylcbiAgICAgIHRyaWdnZXJFcnJvckV2ZW50KGVsdCwgJ2h0bXg6YWZ0ZXJSZXF1ZXN0JywgcmVzcG9uc2VJbmZvKVxuICAgICAgdHJpZ2dlckVycm9yRXZlbnQoZWx0LCAnaHRteDp0aW1lb3V0JywgcmVzcG9uc2VJbmZvKVxuICAgICAgbWF5YmVDYWxsKHJlamVjdClcbiAgICAgIGVuZFJlcXVlc3RMb2NrKClcbiAgICB9XG4gICAgaWYgKCF0cmlnZ2VyRXZlbnQoZWx0LCAnaHRteDpiZWZvcmVSZXF1ZXN0JywgcmVzcG9uc2VJbmZvKSkge1xuICAgICAgbWF5YmVDYWxsKHJlc29sdmUpXG4gICAgICBlbmRSZXF1ZXN0TG9jaygpXG4gICAgICByZXR1cm4gcHJvbWlzZVxuICAgIH1cbiAgICB2YXIgaW5kaWNhdG9ycyA9IGFkZFJlcXVlc3RJbmRpY2F0b3JDbGFzc2VzKGVsdClcbiAgICB2YXIgZGlzYWJsZUVsdHMgPSBkaXNhYmxlRWxlbWVudHMoZWx0KVxuXG4gICAgZm9yRWFjaChbJ2xvYWRzdGFydCcsICdsb2FkZW5kJywgJ3Byb2dyZXNzJywgJ2Fib3J0J10sIGZ1bmN0aW9uKGV2ZW50TmFtZSkge1xuICAgICAgZm9yRWFjaChbeGhyLCB4aHIudXBsb2FkXSwgZnVuY3Rpb24odGFyZ2V0KSB7XG4gICAgICAgIHRhcmdldC5hZGRFdmVudExpc3RlbmVyKGV2ZW50TmFtZSwgZnVuY3Rpb24oZXZlbnQpIHtcbiAgICAgICAgICB0cmlnZ2VyRXZlbnQoZWx0LCAnaHRteDp4aHI6JyArIGV2ZW50TmFtZSwge1xuICAgICAgICAgICAgbGVuZ3RoQ29tcHV0YWJsZTogZXZlbnQubGVuZ3RoQ29tcHV0YWJsZSxcbiAgICAgICAgICAgIGxvYWRlZDogZXZlbnQubG9hZGVkLFxuICAgICAgICAgICAgdG90YWw6IGV2ZW50LnRvdGFsXG4gICAgICAgICAgfSlcbiAgICAgICAgfSlcbiAgICAgIH0pXG4gICAgfSlcbiAgICB0cmlnZ2VyRXZlbnQoZWx0LCAnaHRteDpiZWZvcmVTZW5kJywgcmVzcG9uc2VJbmZvKVxuICAgIGNvbnN0IHBhcmFtcyA9IHVzZVVybFBhcmFtcyA/IG51bGwgOiBlbmNvZGVQYXJhbXNGb3JCb2R5KHhociwgZWx0LCBmaWx0ZXJlZEZvcm1EYXRhKVxuICAgIHhoci5zZW5kKHBhcmFtcylcbiAgICByZXR1cm4gcHJvbWlzZVxuICB9XG5cbiAgLyoqXG4gICAqIEB0eXBlZGVmIHtPYmplY3R9IEh0bXhIaXN0b3J5VXBkYXRlXG4gICAqIEBwcm9wZXJ0eSB7c3RyaW5nfG51bGx9IFt0eXBlXVxuICAgKiBAcHJvcGVydHkge3N0cmluZ3xudWxsfSBbcGF0aF1cbiAgICovXG5cbiAgLyoqXG4gICAqIEBwYXJhbSB7RWxlbWVudH0gZWx0XG4gICAqIEBwYXJhbSB7SHRteFJlc3BvbnNlSW5mb30gcmVzcG9uc2VJbmZvXG4gICAqIEByZXR1cm4ge0h0bXhIaXN0b3J5VXBkYXRlfVxuICAgKi9cbiAgZnVuY3Rpb24gZGV0ZXJtaW5lSGlzdG9yeVVwZGF0ZXMoZWx0LCByZXNwb25zZUluZm8pIHtcbiAgICBjb25zdCB4aHIgPSByZXNwb25zZUluZm8ueGhyXG5cbiAgICAvLz0gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4gICAgLy8gRmlyc3QgY29uc3VsdCByZXNwb25zZSBoZWFkZXJzXG4gICAgLy89ID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuICAgIGxldCBwYXRoRnJvbUhlYWRlcnMgPSBudWxsXG4gICAgbGV0IHR5cGVGcm9tSGVhZGVycyA9IG51bGxcbiAgICBpZiAoaGFzSGVhZGVyKHhociwgL0hYLVB1c2g6L2kpKSB7XG4gICAgICBwYXRoRnJvbUhlYWRlcnMgPSB4aHIuZ2V0UmVzcG9uc2VIZWFkZXIoJ0hYLVB1c2gnKVxuICAgICAgdHlwZUZyb21IZWFkZXJzID0gJ3B1c2gnXG4gICAgfSBlbHNlIGlmIChoYXNIZWFkZXIoeGhyLCAvSFgtUHVzaC1Vcmw6L2kpKSB7XG4gICAgICBwYXRoRnJvbUhlYWRlcnMgPSB4aHIuZ2V0UmVzcG9uc2VIZWFkZXIoJ0hYLVB1c2gtVXJsJylcbiAgICAgIHR5cGVGcm9tSGVhZGVycyA9ICdwdXNoJ1xuICAgIH0gZWxzZSBpZiAoaGFzSGVhZGVyKHhociwgL0hYLVJlcGxhY2UtVXJsOi9pKSkge1xuICAgICAgcGF0aEZyb21IZWFkZXJzID0geGhyLmdldFJlc3BvbnNlSGVhZGVyKCdIWC1SZXBsYWNlLVVybCcpXG4gICAgICB0eXBlRnJvbUhlYWRlcnMgPSAncmVwbGFjZSdcbiAgICB9XG5cbiAgICAvLyBpZiB0aGVyZSB3YXMgYSByZXNwb25zZSBoZWFkZXIsIHRoYXQgaGFzIHByaW9yaXR5XG4gICAgaWYgKHBhdGhGcm9tSGVhZGVycykge1xuICAgICAgaWYgKHBhdGhGcm9tSGVhZGVycyA9PT0gJ2ZhbHNlJykge1xuICAgICAgICByZXR1cm4ge31cbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHJldHVybiB7XG4gICAgICAgICAgdHlwZTogdHlwZUZyb21IZWFkZXJzLFxuICAgICAgICAgIHBhdGg6IHBhdGhGcm9tSGVhZGVyc1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuXG4gICAgLy89ID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuICAgIC8vIE5leHQgcmVzb2x2ZSB2aWEgRE9NIHZhbHVlc1xuICAgIC8vPSA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbiAgICBjb25zdCByZXF1ZXN0UGF0aCA9IHJlc3BvbnNlSW5mby5wYXRoSW5mby5maW5hbFJlcXVlc3RQYXRoXG4gICAgY29uc3QgcmVzcG9uc2VQYXRoID0gcmVzcG9uc2VJbmZvLnBhdGhJbmZvLnJlc3BvbnNlUGF0aFxuXG4gICAgY29uc3QgcHVzaFVybCA9IGdldENsb3Nlc3RBdHRyaWJ1dGVWYWx1ZShlbHQsICdoeC1wdXNoLXVybCcpXG4gICAgY29uc3QgcmVwbGFjZVVybCA9IGdldENsb3Nlc3RBdHRyaWJ1dGVWYWx1ZShlbHQsICdoeC1yZXBsYWNlLXVybCcpXG4gICAgY29uc3QgZWxlbWVudElzQm9vc3RlZCA9IGdldEludGVybmFsRGF0YShlbHQpLmJvb3N0ZWRcblxuICAgIGxldCBzYXZlVHlwZSA9IG51bGxcbiAgICBsZXQgcGF0aCA9IG51bGxcblxuICAgIGlmIChwdXNoVXJsKSB7XG4gICAgICBzYXZlVHlwZSA9ICdwdXNoJ1xuICAgICAgcGF0aCA9IHB1c2hVcmxcbiAgICB9IGVsc2UgaWYgKHJlcGxhY2VVcmwpIHtcbiAgICAgIHNhdmVUeXBlID0gJ3JlcGxhY2UnXG4gICAgICBwYXRoID0gcmVwbGFjZVVybFxuICAgIH0gZWxzZSBpZiAoZWxlbWVudElzQm9vc3RlZCkge1xuICAgICAgc2F2ZVR5cGUgPSAncHVzaCdcbiAgICAgIHBhdGggPSByZXNwb25zZVBhdGggfHwgcmVxdWVzdFBhdGggLy8gaWYgdGhlcmUgaXMgbm8gcmVzcG9uc2UgcGF0aCwgZ28gd2l0aCB0aGUgb3JpZ2luYWwgcmVxdWVzdCBwYXRoXG4gICAgfVxuXG4gICAgaWYgKHBhdGgpIHtcbiAgICAvLyBmYWxzZSBpbmRpY2F0ZXMgbm8gcHVzaCwgcmV0dXJuIGVtcHR5IG9iamVjdFxuICAgICAgaWYgKHBhdGggPT09ICdmYWxzZScpIHtcbiAgICAgICAgcmV0dXJuIHt9XG4gICAgICB9XG5cbiAgICAgIC8vIHRydWUgaW5kaWNhdGVzIHdlIHdhbnQgdG8gZm9sbG93IHdoZXJldmVyIHRoZSBzZXJ2ZXIgZW5kZWQgdXAgc2VuZGluZyB1c1xuICAgICAgaWYgKHBhdGggPT09ICd0cnVlJykge1xuICAgICAgICBwYXRoID0gcmVzcG9uc2VQYXRoIHx8IHJlcXVlc3RQYXRoIC8vIGlmIHRoZXJlIGlzIG5vIHJlc3BvbnNlIHBhdGgsIGdvIHdpdGggdGhlIG9yaWdpbmFsIHJlcXVlc3QgcGF0aFxuICAgICAgfVxuXG4gICAgICAvLyByZXN0b3JlIGFueSBhbmNob3IgYXNzb2NpYXRlZCB3aXRoIHRoZSByZXF1ZXN0XG4gICAgICBpZiAocmVzcG9uc2VJbmZvLnBhdGhJbmZvLmFuY2hvciAmJiBwYXRoLmluZGV4T2YoJyMnKSA9PT0gLTEpIHtcbiAgICAgICAgcGF0aCA9IHBhdGggKyAnIycgKyByZXNwb25zZUluZm8ucGF0aEluZm8uYW5jaG9yXG4gICAgICB9XG5cbiAgICAgIHJldHVybiB7XG4gICAgICAgIHR5cGU6IHNhdmVUeXBlLFxuICAgICAgICBwYXRoXG4gICAgICB9XG4gICAgfSBlbHNlIHtcbiAgICAgIHJldHVybiB7fVxuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBAcGFyYW0ge0h0bXhSZXNwb25zZUhhbmRsaW5nQ29uZmlnfSByZXNwb25zZUhhbmRsaW5nQ29uZmlnXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBzdGF0dXNcbiAgICogQHJldHVybiB7Ym9vbGVhbn1cbiAgICovXG4gIGZ1bmN0aW9uIGNvZGVNYXRjaGVzKHJlc3BvbnNlSGFuZGxpbmdDb25maWcsIHN0YXR1cykge1xuICAgIHZhciByZWdFeHAgPSBuZXcgUmVnRXhwKHJlc3BvbnNlSGFuZGxpbmdDb25maWcuY29kZSlcbiAgICByZXR1cm4gcmVnRXhwLnRlc3Qoc3RhdHVzLnRvU3RyaW5nKDEwKSlcbiAgfVxuXG4gIC8qKlxuICAgKiBAcGFyYW0ge1hNTEh0dHBSZXF1ZXN0fSB4aHJcbiAgICogQHJldHVybiB7SHRteFJlc3BvbnNlSGFuZGxpbmdDb25maWd9XG4gICAqL1xuICBmdW5jdGlvbiByZXNvbHZlUmVzcG9uc2VIYW5kbGluZyh4aHIpIHtcbiAgICBmb3IgKHZhciBpID0gMDsgaSA8IGh0bXguY29uZmlnLnJlc3BvbnNlSGFuZGxpbmcubGVuZ3RoOyBpKyspIHtcbiAgICAgIC8qKiBAdHlwZSBIdG14UmVzcG9uc2VIYW5kbGluZ0NvbmZpZyAqL1xuICAgICAgdmFyIHJlc3BvbnNlSGFuZGxpbmdFbGVtZW50ID0gaHRteC5jb25maWcucmVzcG9uc2VIYW5kbGluZ1tpXVxuICAgICAgaWYgKGNvZGVNYXRjaGVzKHJlc3BvbnNlSGFuZGxpbmdFbGVtZW50LCB4aHIuc3RhdHVzKSkge1xuICAgICAgICByZXR1cm4gcmVzcG9uc2VIYW5kbGluZ0VsZW1lbnRcbiAgICAgIH1cbiAgICB9XG4gICAgLy8gbm8gbWF0Y2hlcywgcmV0dXJuIG5vIHN3YXBcbiAgICByZXR1cm4ge1xuICAgICAgc3dhcDogZmFsc2VcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogQHBhcmFtIHtzdHJpbmd9IHRpdGxlXG4gICAqL1xuICBmdW5jdGlvbiBoYW5kbGVUaXRsZSh0aXRsZSkge1xuICAgIGlmICh0aXRsZSkge1xuICAgICAgY29uc3QgdGl0bGVFbHQgPSBmaW5kKCd0aXRsZScpXG4gICAgICBpZiAodGl0bGVFbHQpIHtcbiAgICAgICAgdGl0bGVFbHQuaW5uZXJIVE1MID0gdGl0bGVcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHdpbmRvdy5kb2N1bWVudC50aXRsZSA9IHRpdGxlXG4gICAgICB9XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIEBwYXJhbSB7RWxlbWVudH0gZWx0XG4gICAqIEBwYXJhbSB7SHRteFJlc3BvbnNlSW5mb30gcmVzcG9uc2VJbmZvXG4gICAqL1xuICBmdW5jdGlvbiBoYW5kbGVBamF4UmVzcG9uc2UoZWx0LCByZXNwb25zZUluZm8pIHtcbiAgICBjb25zdCB4aHIgPSByZXNwb25zZUluZm8ueGhyXG4gICAgbGV0IHRhcmdldCA9IHJlc3BvbnNlSW5mby50YXJnZXRcbiAgICBjb25zdCBldGMgPSByZXNwb25zZUluZm8uZXRjXG4gICAgY29uc3QgcmVzcG9uc2VJbmZvU2VsZWN0ID0gcmVzcG9uc2VJbmZvLnNlbGVjdFxuXG4gICAgaWYgKCF0cmlnZ2VyRXZlbnQoZWx0LCAnaHRteDpiZWZvcmVPbkxvYWQnLCByZXNwb25zZUluZm8pKSByZXR1cm5cblxuICAgIGlmIChoYXNIZWFkZXIoeGhyLCAvSFgtVHJpZ2dlcjovaSkpIHtcbiAgICAgIGhhbmRsZVRyaWdnZXJIZWFkZXIoeGhyLCAnSFgtVHJpZ2dlcicsIGVsdClcbiAgICB9XG5cbiAgICBpZiAoaGFzSGVhZGVyKHhociwgL0hYLUxvY2F0aW9uOi9pKSkge1xuICAgICAgc2F2ZUN1cnJlbnRQYWdlVG9IaXN0b3J5KClcbiAgICAgIGxldCByZWRpcmVjdFBhdGggPSB4aHIuZ2V0UmVzcG9uc2VIZWFkZXIoJ0hYLUxvY2F0aW9uJylcbiAgICAgIC8qKiBAdHlwZSB7SHRteEFqYXhIZWxwZXJDb250ZXh0JntwYXRoOnN0cmluZ319ICovXG4gICAgICB2YXIgcmVkaXJlY3RTd2FwU3BlY1xuICAgICAgaWYgKHJlZGlyZWN0UGF0aC5pbmRleE9mKCd7JykgPT09IDApIHtcbiAgICAgICAgcmVkaXJlY3RTd2FwU3BlYyA9IHBhcnNlSlNPTihyZWRpcmVjdFBhdGgpXG4gICAgICAgIC8vIHdoYXQncyB0aGUgYmVzdCB3YXkgdG8gdGhyb3cgYW4gZXJyb3IgaWYgdGhlIHVzZXIgZGlkbid0IGluY2x1ZGUgdGhpc1xuICAgICAgICByZWRpcmVjdFBhdGggPSByZWRpcmVjdFN3YXBTcGVjLnBhdGhcbiAgICAgICAgZGVsZXRlIHJlZGlyZWN0U3dhcFNwZWMucGF0aFxuICAgICAgfVxuICAgICAgYWpheEhlbHBlcignZ2V0JywgcmVkaXJlY3RQYXRoLCByZWRpcmVjdFN3YXBTcGVjKS50aGVuKGZ1bmN0aW9uKCkge1xuICAgICAgICBwdXNoVXJsSW50b0hpc3RvcnkocmVkaXJlY3RQYXRoKVxuICAgICAgfSlcbiAgICAgIHJldHVyblxuICAgIH1cblxuICAgIGNvbnN0IHNob3VsZFJlZnJlc2ggPSBoYXNIZWFkZXIoeGhyLCAvSFgtUmVmcmVzaDovaSkgJiYgeGhyLmdldFJlc3BvbnNlSGVhZGVyKCdIWC1SZWZyZXNoJykgPT09ICd0cnVlJ1xuXG4gICAgaWYgKGhhc0hlYWRlcih4aHIsIC9IWC1SZWRpcmVjdDovaSkpIHtcbiAgICAgIHJlc3BvbnNlSW5mby5rZWVwSW5kaWNhdG9ycyA9IHRydWVcbiAgICAgIGxvY2F0aW9uLmhyZWYgPSB4aHIuZ2V0UmVzcG9uc2VIZWFkZXIoJ0hYLVJlZGlyZWN0JylcbiAgICAgIHNob3VsZFJlZnJlc2ggJiYgbG9jYXRpb24ucmVsb2FkKClcbiAgICAgIHJldHVyblxuICAgIH1cblxuICAgIGlmIChzaG91bGRSZWZyZXNoKSB7XG4gICAgICByZXNwb25zZUluZm8ua2VlcEluZGljYXRvcnMgPSB0cnVlXG4gICAgICBsb2NhdGlvbi5yZWxvYWQoKVxuICAgICAgcmV0dXJuXG4gICAgfVxuXG4gICAgaWYgKGhhc0hlYWRlcih4aHIsIC9IWC1SZXRhcmdldDovaSkpIHtcbiAgICAgIGlmICh4aHIuZ2V0UmVzcG9uc2VIZWFkZXIoJ0hYLVJldGFyZ2V0JykgPT09ICd0aGlzJykge1xuICAgICAgICByZXNwb25zZUluZm8udGFyZ2V0ID0gZWx0XG4gICAgICB9IGVsc2Uge1xuICAgICAgICByZXNwb25zZUluZm8udGFyZ2V0ID0gYXNFbGVtZW50KHF1ZXJ5U2VsZWN0b3JFeHQoZWx0LCB4aHIuZ2V0UmVzcG9uc2VIZWFkZXIoJ0hYLVJldGFyZ2V0JykpKVxuICAgICAgfVxuICAgIH1cblxuICAgIGNvbnN0IGhpc3RvcnlVcGRhdGUgPSBkZXRlcm1pbmVIaXN0b3J5VXBkYXRlcyhlbHQsIHJlc3BvbnNlSW5mbylcblxuICAgIGNvbnN0IHJlc3BvbnNlSGFuZGxpbmcgPSByZXNvbHZlUmVzcG9uc2VIYW5kbGluZyh4aHIpXG4gICAgY29uc3Qgc2hvdWxkU3dhcCA9IHJlc3BvbnNlSGFuZGxpbmcuc3dhcFxuICAgIGxldCBpc0Vycm9yID0gISFyZXNwb25zZUhhbmRsaW5nLmVycm9yXG4gICAgbGV0IGlnbm9yZVRpdGxlID0gaHRteC5jb25maWcuaWdub3JlVGl0bGUgfHwgcmVzcG9uc2VIYW5kbGluZy5pZ25vcmVUaXRsZVxuICAgIGxldCBzZWxlY3RPdmVycmlkZSA9IHJlc3BvbnNlSGFuZGxpbmcuc2VsZWN0XG4gICAgaWYgKHJlc3BvbnNlSGFuZGxpbmcudGFyZ2V0KSB7XG4gICAgICByZXNwb25zZUluZm8udGFyZ2V0ID0gYXNFbGVtZW50KHF1ZXJ5U2VsZWN0b3JFeHQoZWx0LCByZXNwb25zZUhhbmRsaW5nLnRhcmdldCkpXG4gICAgfVxuICAgIHZhciBzd2FwT3ZlcnJpZGUgPSBldGMuc3dhcE92ZXJyaWRlXG4gICAgaWYgKHN3YXBPdmVycmlkZSA9PSBudWxsICYmIHJlc3BvbnNlSGFuZGxpbmcuc3dhcE92ZXJyaWRlKSB7XG4gICAgICBzd2FwT3ZlcnJpZGUgPSByZXNwb25zZUhhbmRsaW5nLnN3YXBPdmVycmlkZVxuICAgIH1cblxuICAgIC8vIHJlc3BvbnNlIGhlYWRlcnMgb3ZlcnJpZGUgcmVzcG9uc2UgaGFuZGxpbmcgY29uZmlnXG4gICAgaWYgKGhhc0hlYWRlcih4aHIsIC9IWC1SZXRhcmdldDovaSkpIHtcbiAgICAgIGlmICh4aHIuZ2V0UmVzcG9uc2VIZWFkZXIoJ0hYLVJldGFyZ2V0JykgPT09ICd0aGlzJykge1xuICAgICAgICByZXNwb25zZUluZm8udGFyZ2V0ID0gZWx0XG4gICAgICB9IGVsc2Uge1xuICAgICAgICByZXNwb25zZUluZm8udGFyZ2V0ID0gYXNFbGVtZW50KHF1ZXJ5U2VsZWN0b3JFeHQoZWx0LCB4aHIuZ2V0UmVzcG9uc2VIZWFkZXIoJ0hYLVJldGFyZ2V0JykpKVxuICAgICAgfVxuICAgIH1cbiAgICBpZiAoaGFzSGVhZGVyKHhociwgL0hYLVJlc3dhcDovaSkpIHtcbiAgICAgIHN3YXBPdmVycmlkZSA9IHhoci5nZXRSZXNwb25zZUhlYWRlcignSFgtUmVzd2FwJylcbiAgICB9XG5cbiAgICB2YXIgc2VydmVyUmVzcG9uc2UgPSB4aHIucmVzcG9uc2VcbiAgICAvKiogQHR5cGUgSHRteEJlZm9yZVN3YXBEZXRhaWxzICovXG4gICAgdmFyIGJlZm9yZVN3YXBEZXRhaWxzID0gbWVyZ2VPYmplY3RzKHtcbiAgICAgIHNob3VsZFN3YXAsXG4gICAgICBzZXJ2ZXJSZXNwb25zZSxcbiAgICAgIGlzRXJyb3IsXG4gICAgICBpZ25vcmVUaXRsZSxcbiAgICAgIHNlbGVjdE92ZXJyaWRlXG4gICAgfSwgcmVzcG9uc2VJbmZvKVxuXG4gICAgaWYgKHJlc3BvbnNlSGFuZGxpbmcuZXZlbnQgJiYgIXRyaWdnZXJFdmVudCh0YXJnZXQsIHJlc3BvbnNlSGFuZGxpbmcuZXZlbnQsIGJlZm9yZVN3YXBEZXRhaWxzKSkgcmV0dXJuXG5cbiAgICBpZiAoIXRyaWdnZXJFdmVudCh0YXJnZXQsICdodG14OmJlZm9yZVN3YXAnLCBiZWZvcmVTd2FwRGV0YWlscykpIHJldHVyblxuXG4gICAgdGFyZ2V0ID0gYmVmb3JlU3dhcERldGFpbHMudGFyZ2V0IC8vIGFsbG93IHJlLXRhcmdldGluZ1xuICAgIHNlcnZlclJlc3BvbnNlID0gYmVmb3JlU3dhcERldGFpbHMuc2VydmVyUmVzcG9uc2UgLy8gYWxsb3cgdXBkYXRpbmcgY29udGVudFxuICAgIGlzRXJyb3IgPSBiZWZvcmVTd2FwRGV0YWlscy5pc0Vycm9yIC8vIGFsbG93IHVwZGF0aW5nIGVycm9yXG4gICAgaWdub3JlVGl0bGUgPSBiZWZvcmVTd2FwRGV0YWlscy5pZ25vcmVUaXRsZSAvLyBhbGxvdyB1cGRhdGluZyBpZ25vcmluZyB0aXRsZVxuICAgIHNlbGVjdE92ZXJyaWRlID0gYmVmb3JlU3dhcERldGFpbHMuc2VsZWN0T3ZlcnJpZGUgLy8gYWxsb3cgdXBkYXRpbmcgc2VsZWN0IG92ZXJyaWRlXG5cbiAgICByZXNwb25zZUluZm8udGFyZ2V0ID0gdGFyZ2V0IC8vIE1ha2UgdXBkYXRlZCB0YXJnZXQgYXZhaWxhYmxlIHRvIHJlc3BvbnNlIGV2ZW50c1xuICAgIHJlc3BvbnNlSW5mby5mYWlsZWQgPSBpc0Vycm9yIC8vIE1ha2UgZmFpbGVkIHByb3BlcnR5IGF2YWlsYWJsZSB0byByZXNwb25zZSBldmVudHNcbiAgICByZXNwb25zZUluZm8uc3VjY2Vzc2Z1bCA9ICFpc0Vycm9yIC8vIE1ha2Ugc3VjY2Vzc2Z1bCBwcm9wZXJ0eSBhdmFpbGFibGUgdG8gcmVzcG9uc2UgZXZlbnRzXG5cbiAgICBpZiAoYmVmb3JlU3dhcERldGFpbHMuc2hvdWxkU3dhcCkge1xuICAgICAgaWYgKHhoci5zdGF0dXMgPT09IDI4Nikge1xuICAgICAgICBjYW5jZWxQb2xsaW5nKGVsdClcbiAgICAgIH1cblxuICAgICAgd2l0aEV4dGVuc2lvbnMoZWx0LCBmdW5jdGlvbihleHRlbnNpb24pIHtcbiAgICAgICAgc2VydmVyUmVzcG9uc2UgPSBleHRlbnNpb24udHJhbnNmb3JtUmVzcG9uc2Uoc2VydmVyUmVzcG9uc2UsIHhociwgZWx0KVxuICAgICAgfSlcblxuICAgICAgLy8gU2F2ZSBjdXJyZW50IHBhZ2UgaWYgdGhlcmUgd2lsbCBiZSBhIGhpc3RvcnkgdXBkYXRlXG4gICAgICBpZiAoaGlzdG9yeVVwZGF0ZS50eXBlKSB7XG4gICAgICAgIHNhdmVDdXJyZW50UGFnZVRvSGlzdG9yeSgpXG4gICAgICB9XG5cbiAgICAgIGlmIChoYXNIZWFkZXIoeGhyLCAvSFgtUmVzd2FwOi9pKSkge1xuICAgICAgICBzd2FwT3ZlcnJpZGUgPSB4aHIuZ2V0UmVzcG9uc2VIZWFkZXIoJ0hYLVJlc3dhcCcpXG4gICAgICB9XG4gICAgICB2YXIgc3dhcFNwZWMgPSBnZXRTd2FwU3BlY2lmaWNhdGlvbihlbHQsIHN3YXBPdmVycmlkZSlcblxuICAgICAgaWYgKCFzd2FwU3BlYy5oYXNPd25Qcm9wZXJ0eSgnaWdub3JlVGl0bGUnKSkge1xuICAgICAgICBzd2FwU3BlYy5pZ25vcmVUaXRsZSA9IGlnbm9yZVRpdGxlXG4gICAgICB9XG5cbiAgICAgIHRhcmdldC5jbGFzc0xpc3QuYWRkKGh0bXguY29uZmlnLnN3YXBwaW5nQ2xhc3MpXG5cbiAgICAgIC8vIG9wdGlvbmFsIHRyYW5zaXRpb24gQVBJIHByb21pc2UgY2FsbGJhY2tzXG4gICAgICBsZXQgc2V0dGxlUmVzb2x2ZSA9IG51bGxcbiAgICAgIGxldCBzZXR0bGVSZWplY3QgPSBudWxsXG5cbiAgICAgIGlmIChyZXNwb25zZUluZm9TZWxlY3QpIHtcbiAgICAgICAgc2VsZWN0T3ZlcnJpZGUgPSByZXNwb25zZUluZm9TZWxlY3RcbiAgICAgIH1cblxuICAgICAgaWYgKGhhc0hlYWRlcih4aHIsIC9IWC1SZXNlbGVjdDovaSkpIHtcbiAgICAgICAgc2VsZWN0T3ZlcnJpZGUgPSB4aHIuZ2V0UmVzcG9uc2VIZWFkZXIoJ0hYLVJlc2VsZWN0JylcbiAgICAgIH1cblxuICAgICAgY29uc3Qgc2VsZWN0T09CID0gZ2V0Q2xvc2VzdEF0dHJpYnV0ZVZhbHVlKGVsdCwgJ2h4LXNlbGVjdC1vb2InKVxuICAgICAgY29uc3Qgc2VsZWN0ID0gZ2V0Q2xvc2VzdEF0dHJpYnV0ZVZhbHVlKGVsdCwgJ2h4LXNlbGVjdCcpXG5cbiAgICAgIGxldCBkb1N3YXAgPSBmdW5jdGlvbigpIHtcbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICAvLyBpZiB3ZSBuZWVkIHRvIHNhdmUgaGlzdG9yeSwgZG8gc28sIGJlZm9yZSBzd2FwcGluZyBzbyB0aGF0IHJlbGF0aXZlIHJlc291cmNlcyBoYXZlIHRoZSBjb3JyZWN0IGJhc2UgVVJMXG4gICAgICAgICAgaWYgKGhpc3RvcnlVcGRhdGUudHlwZSkge1xuICAgICAgICAgICAgdHJpZ2dlckV2ZW50KGdldERvY3VtZW50KCkuYm9keSwgJ2h0bXg6YmVmb3JlSGlzdG9yeVVwZGF0ZScsIG1lcmdlT2JqZWN0cyh7IGhpc3Rvcnk6IGhpc3RvcnlVcGRhdGUgfSwgcmVzcG9uc2VJbmZvKSlcbiAgICAgICAgICAgIGlmIChoaXN0b3J5VXBkYXRlLnR5cGUgPT09ICdwdXNoJykge1xuICAgICAgICAgICAgICBwdXNoVXJsSW50b0hpc3RvcnkoaGlzdG9yeVVwZGF0ZS5wYXRoKVxuICAgICAgICAgICAgICB0cmlnZ2VyRXZlbnQoZ2V0RG9jdW1lbnQoKS5ib2R5LCAnaHRteDpwdXNoZWRJbnRvSGlzdG9yeScsIHsgcGF0aDogaGlzdG9yeVVwZGF0ZS5wYXRoIH0pXG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICByZXBsYWNlVXJsSW5IaXN0b3J5KGhpc3RvcnlVcGRhdGUucGF0aClcbiAgICAgICAgICAgICAgdHJpZ2dlckV2ZW50KGdldERvY3VtZW50KCkuYm9keSwgJ2h0bXg6cmVwbGFjZWRJbkhpc3RvcnknLCB7IHBhdGg6IGhpc3RvcnlVcGRhdGUucGF0aCB9KVxuICAgICAgICAgICAgfVxuICAgICAgICAgIH1cblxuICAgICAgICAgIHN3YXAodGFyZ2V0LCBzZXJ2ZXJSZXNwb25zZSwgc3dhcFNwZWMsIHtcbiAgICAgICAgICAgIHNlbGVjdDogc2VsZWN0T3ZlcnJpZGUgfHwgc2VsZWN0LFxuICAgICAgICAgICAgc2VsZWN0T09CLFxuICAgICAgICAgICAgZXZlbnRJbmZvOiByZXNwb25zZUluZm8sXG4gICAgICAgICAgICBhbmNob3I6IHJlc3BvbnNlSW5mby5wYXRoSW5mby5hbmNob3IsXG4gICAgICAgICAgICBjb250ZXh0RWxlbWVudDogZWx0LFxuICAgICAgICAgICAgYWZ0ZXJTd2FwQ2FsbGJhY2s6IGZ1bmN0aW9uKCkge1xuICAgICAgICAgICAgICBpZiAoaGFzSGVhZGVyKHhociwgL0hYLVRyaWdnZXItQWZ0ZXItU3dhcDovaSkpIHtcbiAgICAgICAgICAgICAgICBsZXQgZmluYWxFbHQgPSBlbHRcbiAgICAgICAgICAgICAgICBpZiAoIWJvZHlDb250YWlucyhlbHQpKSB7XG4gICAgICAgICAgICAgICAgICBmaW5hbEVsdCA9IGdldERvY3VtZW50KCkuYm9keVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICBoYW5kbGVUcmlnZ2VySGVhZGVyKHhociwgJ0hYLVRyaWdnZXItQWZ0ZXItU3dhcCcsIGZpbmFsRWx0KVxuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9LFxuICAgICAgICAgICAgYWZ0ZXJTZXR0bGVDYWxsYmFjazogZnVuY3Rpb24oKSB7XG4gICAgICAgICAgICAgIGlmIChoYXNIZWFkZXIoeGhyLCAvSFgtVHJpZ2dlci1BZnRlci1TZXR0bGU6L2kpKSB7XG4gICAgICAgICAgICAgICAgbGV0IGZpbmFsRWx0ID0gZWx0XG4gICAgICAgICAgICAgICAgaWYgKCFib2R5Q29udGFpbnMoZWx0KSkge1xuICAgICAgICAgICAgICAgICAgZmluYWxFbHQgPSBnZXREb2N1bWVudCgpLmJvZHlcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgaGFuZGxlVHJpZ2dlckhlYWRlcih4aHIsICdIWC1UcmlnZ2VyLUFmdGVyLVNldHRsZScsIGZpbmFsRWx0KVxuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIG1heWJlQ2FsbChzZXR0bGVSZXNvbHZlKVxuICAgICAgICAgICAgfVxuICAgICAgICAgIH0pXG4gICAgICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgICAgICB0cmlnZ2VyRXJyb3JFdmVudChlbHQsICdodG14OnN3YXBFcnJvcicsIHJlc3BvbnNlSW5mbylcbiAgICAgICAgICBtYXliZUNhbGwoc2V0dGxlUmVqZWN0KVxuICAgICAgICAgIHRocm93IGVcbiAgICAgICAgfVxuICAgICAgfVxuXG4gICAgICBsZXQgc2hvdWxkVHJhbnNpdGlvbiA9IGh0bXguY29uZmlnLmdsb2JhbFZpZXdUcmFuc2l0aW9uc1xuICAgICAgaWYgKHN3YXBTcGVjLmhhc093blByb3BlcnR5KCd0cmFuc2l0aW9uJykpIHtcbiAgICAgICAgc2hvdWxkVHJhbnNpdGlvbiA9IHN3YXBTcGVjLnRyYW5zaXRpb25cbiAgICAgIH1cblxuICAgICAgaWYgKHNob3VsZFRyYW5zaXRpb24gJiZcbiAgICAgICAgICAgICAgdHJpZ2dlckV2ZW50KGVsdCwgJ2h0bXg6YmVmb3JlVHJhbnNpdGlvbicsIHJlc3BvbnNlSW5mbykgJiZcbiAgICAgICAgICAgICAgdHlwZW9mIFByb21pc2UgIT09ICd1bmRlZmluZWQnICYmXG4gICAgICAgICAgICAgIC8vIEB0cy1pZ25vcmUgZXhwZXJpbWVudGFsIGZlYXR1cmUgYXRtXG4gICAgICAgICAgICAgIGRvY3VtZW50LnN0YXJ0Vmlld1RyYW5zaXRpb24pIHtcbiAgICAgICAgY29uc3Qgc2V0dGxlUHJvbWlzZSA9IG5ldyBQcm9taXNlKGZ1bmN0aW9uKF9yZXNvbHZlLCBfcmVqZWN0KSB7XG4gICAgICAgICAgc2V0dGxlUmVzb2x2ZSA9IF9yZXNvbHZlXG4gICAgICAgICAgc2V0dGxlUmVqZWN0ID0gX3JlamVjdFxuICAgICAgICB9KVxuICAgICAgICAvLyB3cmFwIHRoZSBvcmlnaW5hbCBkb1N3YXAoKSBpbiBhIGNhbGwgdG8gc3RhcnRWaWV3VHJhbnNpdGlvbigpXG4gICAgICAgIGNvbnN0IGlubmVyRG9Td2FwID0gZG9Td2FwXG4gICAgICAgIGRvU3dhcCA9IGZ1bmN0aW9uKCkge1xuICAgICAgICAgIC8vIEB0cy1pZ25vcmUgZXhwZXJpbWVudGFsIGZlYXR1cmUgYXRtXG4gICAgICAgICAgZG9jdW1lbnQuc3RhcnRWaWV3VHJhbnNpdGlvbihmdW5jdGlvbigpIHtcbiAgICAgICAgICAgIGlubmVyRG9Td2FwKClcbiAgICAgICAgICAgIHJldHVybiBzZXR0bGVQcm9taXNlXG4gICAgICAgICAgfSlcbiAgICAgICAgfVxuICAgICAgfVxuXG4gICAgICBpZiAoc3dhcFNwZWMuc3dhcERlbGF5ID4gMCkge1xuICAgICAgICBnZXRXaW5kb3coKS5zZXRUaW1lb3V0KGRvU3dhcCwgc3dhcFNwZWMuc3dhcERlbGF5KVxuICAgICAgfSBlbHNlIHtcbiAgICAgICAgZG9Td2FwKClcbiAgICAgIH1cbiAgICB9XG4gICAgaWYgKGlzRXJyb3IpIHtcbiAgICAgIHRyaWdnZXJFcnJvckV2ZW50KGVsdCwgJ2h0bXg6cmVzcG9uc2VFcnJvcicsIG1lcmdlT2JqZWN0cyh7IGVycm9yOiAnUmVzcG9uc2UgU3RhdHVzIEVycm9yIENvZGUgJyArIHhoci5zdGF0dXMgKyAnIGZyb20gJyArIHJlc3BvbnNlSW5mby5wYXRoSW5mby5yZXF1ZXN0UGF0aCB9LCByZXNwb25zZUluZm8pKVxuICAgIH1cbiAgfVxuXG4gIC8vPSA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4gIC8vIEV4dGVuc2lvbnMgQVBJXG4gIC8vPSA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG5cbiAgLyoqIEB0eXBlIHtPYmplY3Q8c3RyaW5nLCBIdG14RXh0ZW5zaW9uPn0gKi9cbiAgY29uc3QgZXh0ZW5zaW9ucyA9IHt9XG5cbiAgLyoqXG4gICAqIGV4dGVuc2lvbkJhc2UgZGVmaW5lcyB0aGUgZGVmYXVsdCBmdW5jdGlvbnMgZm9yIGFsbCBleHRlbnNpb25zLlxuICAgKiBAcmV0dXJucyB7SHRteEV4dGVuc2lvbn1cbiAgICovXG4gIGZ1bmN0aW9uIGV4dGVuc2lvbkJhc2UoKSB7XG4gICAgcmV0dXJuIHtcbiAgICAgIGluaXQ6IGZ1bmN0aW9uKGFwaSkgeyByZXR1cm4gbnVsbCB9LFxuICAgICAgZ2V0U2VsZWN0b3JzOiBmdW5jdGlvbigpIHsgcmV0dXJuIG51bGwgfSxcbiAgICAgIG9uRXZlbnQ6IGZ1bmN0aW9uKG5hbWUsIGV2dCkgeyByZXR1cm4gdHJ1ZSB9LFxuICAgICAgdHJhbnNmb3JtUmVzcG9uc2U6IGZ1bmN0aW9uKHRleHQsIHhociwgZWx0KSB7IHJldHVybiB0ZXh0IH0sXG4gICAgICBpc0lubGluZVN3YXA6IGZ1bmN0aW9uKHN3YXBTdHlsZSkgeyByZXR1cm4gZmFsc2UgfSxcbiAgICAgIGhhbmRsZVN3YXA6IGZ1bmN0aW9uKHN3YXBTdHlsZSwgdGFyZ2V0LCBmcmFnbWVudCwgc2V0dGxlSW5mbykgeyByZXR1cm4gZmFsc2UgfSxcbiAgICAgIGVuY29kZVBhcmFtZXRlcnM6IGZ1bmN0aW9uKHhociwgcGFyYW1ldGVycywgZWx0KSB7IHJldHVybiBudWxsIH1cbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogZGVmaW5lRXh0ZW5zaW9uIGluaXRpYWxpemVzIHRoZSBleHRlbnNpb24gYW5kIGFkZHMgaXQgdG8gdGhlIGh0bXggcmVnaXN0cnlcbiAgICpcbiAgICogQHNlZSBodHRwczovL2h0bXgub3JnL2FwaS8jZGVmaW5lRXh0ZW5zaW9uXG4gICAqXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBuYW1lIHRoZSBleHRlbnNpb24gbmFtZVxuICAgKiBAcGFyYW0ge0h0bXhFeHRlbnNpb259IGV4dGVuc2lvbiB0aGUgZXh0ZW5zaW9uIGRlZmluaXRpb25cbiAgICovXG4gIGZ1bmN0aW9uIGRlZmluZUV4dGVuc2lvbihuYW1lLCBleHRlbnNpb24pIHtcbiAgICBpZiAoZXh0ZW5zaW9uLmluaXQpIHtcbiAgICAgIGV4dGVuc2lvbi5pbml0KGludGVybmFsQVBJKVxuICAgIH1cbiAgICBleHRlbnNpb25zW25hbWVdID0gbWVyZ2VPYmplY3RzKGV4dGVuc2lvbkJhc2UoKSwgZXh0ZW5zaW9uKVxuICB9XG5cbiAgLyoqXG4gICAqIHJlbW92ZUV4dGVuc2lvbiByZW1vdmVzIGFuIGV4dGVuc2lvbiBmcm9tIHRoZSBodG14IHJlZ2lzdHJ5XG4gICAqXG4gICAqIEBzZWUgaHR0cHM6Ly9odG14Lm9yZy9hcGkvI3JlbW92ZUV4dGVuc2lvblxuICAgKlxuICAgKiBAcGFyYW0ge3N0cmluZ30gbmFtZVxuICAgKi9cbiAgZnVuY3Rpb24gcmVtb3ZlRXh0ZW5zaW9uKG5hbWUpIHtcbiAgICBkZWxldGUgZXh0ZW5zaW9uc1tuYW1lXVxuICB9XG5cbiAgLyoqXG4gICAqIGdldEV4dGVuc2lvbnMgc2VhcmNoZXMgdXAgdGhlIERPTSB0cmVlIHRvIHJldHVybiBhbGwgZXh0ZW5zaW9ucyB0aGF0IGNhbiBiZSBhcHBsaWVkIHRvIGEgZ2l2ZW4gZWxlbWVudFxuICAgKlxuICAgKiBAcGFyYW0ge0VsZW1lbnR9IGVsdFxuICAgKiBAcGFyYW0ge0h0bXhFeHRlbnNpb25bXT19IGV4dGVuc2lvbnNUb1JldHVyblxuICAgKiBAcGFyYW0ge3N0cmluZ1tdPX0gZXh0ZW5zaW9uc1RvSWdub3JlXG4gICAqIEByZXR1cm5zIHtIdG14RXh0ZW5zaW9uW119XG4gICAqL1xuICBmdW5jdGlvbiBnZXRFeHRlbnNpb25zKGVsdCwgZXh0ZW5zaW9uc1RvUmV0dXJuLCBleHRlbnNpb25zVG9JZ25vcmUpIHtcbiAgICBpZiAoZXh0ZW5zaW9uc1RvUmV0dXJuID09IHVuZGVmaW5lZCkge1xuICAgICAgZXh0ZW5zaW9uc1RvUmV0dXJuID0gW11cbiAgICB9XG4gICAgaWYgKGVsdCA9PSB1bmRlZmluZWQpIHtcbiAgICAgIHJldHVybiBleHRlbnNpb25zVG9SZXR1cm5cbiAgICB9XG4gICAgaWYgKGV4dGVuc2lvbnNUb0lnbm9yZSA9PSB1bmRlZmluZWQpIHtcbiAgICAgIGV4dGVuc2lvbnNUb0lnbm9yZSA9IFtdXG4gICAgfVxuICAgIGNvbnN0IGV4dGVuc2lvbnNGb3JFbGVtZW50ID0gZ2V0QXR0cmlidXRlVmFsdWUoZWx0LCAnaHgtZXh0JylcbiAgICBpZiAoZXh0ZW5zaW9uc0ZvckVsZW1lbnQpIHtcbiAgICAgIGZvckVhY2goZXh0ZW5zaW9uc0ZvckVsZW1lbnQuc3BsaXQoJywnKSwgZnVuY3Rpb24oZXh0ZW5zaW9uTmFtZSkge1xuICAgICAgICBleHRlbnNpb25OYW1lID0gZXh0ZW5zaW9uTmFtZS5yZXBsYWNlKC8gL2csICcnKVxuICAgICAgICBpZiAoZXh0ZW5zaW9uTmFtZS5zbGljZSgwLCA3KSA9PSAnaWdub3JlOicpIHtcbiAgICAgICAgICBleHRlbnNpb25zVG9JZ25vcmUucHVzaChleHRlbnNpb25OYW1lLnNsaWNlKDcpKVxuICAgICAgICAgIHJldHVyblxuICAgICAgICB9XG4gICAgICAgIGlmIChleHRlbnNpb25zVG9JZ25vcmUuaW5kZXhPZihleHRlbnNpb25OYW1lKSA8IDApIHtcbiAgICAgICAgICBjb25zdCBleHRlbnNpb24gPSBleHRlbnNpb25zW2V4dGVuc2lvbk5hbWVdXG4gICAgICAgICAgaWYgKGV4dGVuc2lvbiAmJiBleHRlbnNpb25zVG9SZXR1cm4uaW5kZXhPZihleHRlbnNpb24pIDwgMCkge1xuICAgICAgICAgICAgZXh0ZW5zaW9uc1RvUmV0dXJuLnB1c2goZXh0ZW5zaW9uKVxuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgfSlcbiAgICB9XG4gICAgcmV0dXJuIGdldEV4dGVuc2lvbnMoYXNFbGVtZW50KHBhcmVudEVsdChlbHQpKSwgZXh0ZW5zaW9uc1RvUmV0dXJuLCBleHRlbnNpb25zVG9JZ25vcmUpXG4gIH1cblxuICAvLz0gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuICAvLyBJbml0aWFsaXphdGlvblxuICAvLz0gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuICB2YXIgaXNSZWFkeSA9IGZhbHNlXG4gIGdldERvY3VtZW50KCkuYWRkRXZlbnRMaXN0ZW5lcignRE9NQ29udGVudExvYWRlZCcsIGZ1bmN0aW9uKCkge1xuICAgIGlzUmVhZHkgPSB0cnVlXG4gIH0pXG5cbiAgLyoqXG4gICAqIEV4ZWN1dGUgYSBmdW5jdGlvbiBub3cgaWYgRE9NQ29udGVudExvYWRlZCBoYXMgZmlyZWQsIG90aGVyd2lzZSBsaXN0ZW4gZm9yIGl0LlxuICAgKlxuICAgKiBUaGlzIGZ1bmN0aW9uIHVzZXMgaXNSZWFkeSBiZWNhdXNlIHRoZXJlIGlzIG5vIHJlbGlhYmxlIHdheSB0byBhc2sgdGhlIGJyb3dzZXIgd2hldGhlclxuICAgKiB0aGUgRE9NQ29udGVudExvYWRlZCBldmVudCBoYXMgYWxyZWFkeSBiZWVuIGZpcmVkOyB0aGVyZSdzIGEgZ2FwIGJldHdlZW4gRE9NQ29udGVudExvYWRlZFxuICAgKiBmaXJpbmcgYW5kIHJlYWR5c3RhdGU9Y29tcGxldGUuXG4gICAqL1xuICBmdW5jdGlvbiByZWFkeShmbikge1xuICAgIC8vIENoZWNraW5nIHJlYWR5U3RhdGUgaGVyZSBpcyBhIGZhaWxzYWZlIGluIGNhc2UgdGhlIGh0bXggc2NyaXB0IHRhZyBlbnRlcmVkIHRoZSBET00gYnlcbiAgICAvLyBzb21lIG1lYW5zIG90aGVyIHRoYW4gdGhlIGluaXRpYWwgcGFnZSBsb2FkLlxuICAgIGlmIChpc1JlYWR5IHx8IGdldERvY3VtZW50KCkucmVhZHlTdGF0ZSA9PT0gJ2NvbXBsZXRlJykge1xuICAgICAgZm4oKVxuICAgIH0gZWxzZSB7XG4gICAgICBnZXREb2N1bWVudCgpLmFkZEV2ZW50TGlzdGVuZXIoJ0RPTUNvbnRlbnRMb2FkZWQnLCBmbilcbiAgICB9XG4gIH1cblxuICBmdW5jdGlvbiBpbnNlcnRJbmRpY2F0b3JTdHlsZXMoKSB7XG4gICAgaWYgKGh0bXguY29uZmlnLmluY2x1ZGVJbmRpY2F0b3JTdHlsZXMgIT09IGZhbHNlKSB7XG4gICAgICBjb25zdCBub25jZUF0dHJpYnV0ZSA9IGh0bXguY29uZmlnLmlubGluZVN0eWxlTm9uY2UgPyBgIG5vbmNlPVwiJHtodG14LmNvbmZpZy5pbmxpbmVTdHlsZU5vbmNlfVwiYCA6ICcnXG4gICAgICBnZXREb2N1bWVudCgpLmhlYWQuaW5zZXJ0QWRqYWNlbnRIVE1MKCdiZWZvcmVlbmQnLFxuICAgICAgICAnPHN0eWxlJyArIG5vbmNlQXR0cmlidXRlICsgJz5cXFxuICAgICAgLicgKyBodG14LmNvbmZpZy5pbmRpY2F0b3JDbGFzcyArICd7b3BhY2l0eTowfVxcXG4gICAgICAuJyArIGh0bXguY29uZmlnLnJlcXVlc3RDbGFzcyArICcgLicgKyBodG14LmNvbmZpZy5pbmRpY2F0b3JDbGFzcyArICd7b3BhY2l0eToxOyB0cmFuc2l0aW9uOiBvcGFjaXR5IDIwMG1zIGVhc2UtaW47fVxcXG4gICAgICAuJyArIGh0bXguY29uZmlnLnJlcXVlc3RDbGFzcyArICcuJyArIGh0bXguY29uZmlnLmluZGljYXRvckNsYXNzICsgJ3tvcGFjaXR5OjE7IHRyYW5zaXRpb246IG9wYWNpdHkgMjAwbXMgZWFzZS1pbjt9XFxcbiAgICAgIDwvc3R5bGU+JylcbiAgICB9XG4gIH1cblxuICBmdW5jdGlvbiBnZXRNZXRhQ29uZmlnKCkge1xuICAgIC8qKiBAdHlwZSBIVE1MTWV0YUVsZW1lbnQgKi9cbiAgICBjb25zdCBlbGVtZW50ID0gZ2V0RG9jdW1lbnQoKS5xdWVyeVNlbGVjdG9yKCdtZXRhW25hbWU9XCJodG14LWNvbmZpZ1wiXScpXG4gICAgaWYgKGVsZW1lbnQpIHtcbiAgICAgIHJldHVybiBwYXJzZUpTT04oZWxlbWVudC5jb250ZW50KVxuICAgIH0gZWxzZSB7XG4gICAgICByZXR1cm4gbnVsbFxuICAgIH1cbiAgfVxuXG4gIGZ1bmN0aW9uIG1lcmdlTWV0YUNvbmZpZygpIHtcbiAgICBjb25zdCBtZXRhQ29uZmlnID0gZ2V0TWV0YUNvbmZpZygpXG4gICAgaWYgKG1ldGFDb25maWcpIHtcbiAgICAgIGh0bXguY29uZmlnID0gbWVyZ2VPYmplY3RzKGh0bXguY29uZmlnLCBtZXRhQ29uZmlnKVxuICAgIH1cbiAgfVxuXG4gIC8vIGluaXRpYWxpemUgdGhlIGRvY3VtZW50XG4gIHJlYWR5KGZ1bmN0aW9uKCkge1xuICAgIG1lcmdlTWV0YUNvbmZpZygpXG4gICAgaW5zZXJ0SW5kaWNhdG9yU3R5bGVzKClcbiAgICBsZXQgYm9keSA9IGdldERvY3VtZW50KCkuYm9keVxuICAgIHByb2Nlc3NOb2RlKGJvZHkpXG4gICAgY29uc3QgcmVzdG9yZWRFbHRzID0gZ2V0RG9jdW1lbnQoKS5xdWVyeVNlbGVjdG9yQWxsKFxuICAgICAgXCJbaHgtdHJpZ2dlcj0ncmVzdG9yZWQnXSxbZGF0YS1oeC10cmlnZ2VyPSdyZXN0b3JlZCddXCJcbiAgICApXG4gICAgYm9keS5hZGRFdmVudExpc3RlbmVyKCdodG14OmFib3J0JywgZnVuY3Rpb24oZXZ0KSB7XG4gICAgICBjb25zdCB0YXJnZXQgPSBldnQudGFyZ2V0XG4gICAgICBjb25zdCBpbnRlcm5hbERhdGEgPSBnZXRJbnRlcm5hbERhdGEodGFyZ2V0KVxuICAgICAgaWYgKGludGVybmFsRGF0YSAmJiBpbnRlcm5hbERhdGEueGhyKSB7XG4gICAgICAgIGludGVybmFsRGF0YS54aHIuYWJvcnQoKVxuICAgICAgfVxuICAgIH0pXG4gICAgLyoqIEB0eXBlIHsoZXY6IFBvcFN0YXRlRXZlbnQpID0+IGFueX0gKi9cbiAgICBjb25zdCBvcmlnaW5hbFBvcHN0YXRlID0gd2luZG93Lm9ucG9wc3RhdGUgPyB3aW5kb3cub25wb3BzdGF0ZS5iaW5kKHdpbmRvdykgOiBudWxsXG4gICAgLyoqIEB0eXBlIHsoZXY6IFBvcFN0YXRlRXZlbnQpID0+IGFueX0gKi9cbiAgICB3aW5kb3cub25wb3BzdGF0ZSA9IGZ1bmN0aW9uKGV2ZW50KSB7XG4gICAgICBpZiAoZXZlbnQuc3RhdGUgJiYgZXZlbnQuc3RhdGUuaHRteCkge1xuICAgICAgICByZXN0b3JlSGlzdG9yeSgpXG4gICAgICAgIGZvckVhY2gocmVzdG9yZWRFbHRzLCBmdW5jdGlvbihlbHQpIHtcbiAgICAgICAgICB0cmlnZ2VyRXZlbnQoZWx0LCAnaHRteDpyZXN0b3JlZCcsIHtcbiAgICAgICAgICAgIGRvY3VtZW50OiBnZXREb2N1bWVudCgpLFxuICAgICAgICAgICAgdHJpZ2dlckV2ZW50XG4gICAgICAgICAgfSlcbiAgICAgICAgfSlcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIGlmIChvcmlnaW5hbFBvcHN0YXRlKSB7XG4gICAgICAgICAgb3JpZ2luYWxQb3BzdGF0ZShldmVudClcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cbiAgICBnZXRXaW5kb3coKS5zZXRUaW1lb3V0KGZ1bmN0aW9uKCkge1xuICAgICAgdHJpZ2dlckV2ZW50KGJvZHksICdodG14OmxvYWQnLCB7fSkgLy8gZ2l2ZSByZWFkeSBoYW5kbGVycyBhIGNoYW5jZSB0byBsb2FkIHVwIGJlZm9yZSBmaXJpbmcgdGhpcyBldmVudFxuICAgICAgYm9keSA9IG51bGwgLy8ga2lsbCByZWZlcmVuY2UgZm9yIGdjXG4gICAgfSwgMClcbiAgfSlcblxuICByZXR1cm4gaHRteFxufSkoKVxuXG4vKiogQHR5cGVkZWYgeydnZXQnfCdoZWFkJ3wncG9zdCd8J3B1dCd8J2RlbGV0ZSd8J2Nvbm5lY3QnfCdvcHRpb25zJ3wndHJhY2UnfCdwYXRjaCd9IEh0dHBWZXJiICovXG5cbi8qKlxuICogQHR5cGVkZWYge09iamVjdH0gU3dhcE9wdGlvbnNcbiAqIEBwcm9wZXJ0eSB7c3RyaW5nfSBbc2VsZWN0XVxuICogQHByb3BlcnR5IHtzdHJpbmd9IFtzZWxlY3RPT0JdXG4gKiBAcHJvcGVydHkgeyp9IFtldmVudEluZm9dXG4gKiBAcHJvcGVydHkge3N0cmluZ30gW2FuY2hvcl1cbiAqIEBwcm9wZXJ0eSB7RWxlbWVudH0gW2NvbnRleHRFbGVtZW50XVxuICogQHByb3BlcnR5IHtzd2FwQ2FsbGJhY2t9IFthZnRlclN3YXBDYWxsYmFja11cbiAqIEBwcm9wZXJ0eSB7c3dhcENhbGxiYWNrfSBbYWZ0ZXJTZXR0bGVDYWxsYmFja11cbiAqL1xuXG4vKipcbiAqIEBjYWxsYmFjayBzd2FwQ2FsbGJhY2tcbiAqL1xuXG4vKipcbiAqIEB0eXBlZGVmIHsnaW5uZXJIVE1MJyB8ICdvdXRlckhUTUwnIHwgJ2JlZm9yZWJlZ2luJyB8ICdhZnRlcmJlZ2luJyB8ICdiZWZvcmVlbmQnIHwgJ2FmdGVyZW5kJyB8ICdkZWxldGUnIHwgJ25vbmUnIHwgc3RyaW5nfSBIdG14U3dhcFN0eWxlXG4gKi9cblxuLyoqXG4gKiBAdHlwZWRlZiBIdG14U3dhcFNwZWNpZmljYXRpb25cbiAqIEBwcm9wZXJ0eSB7SHRteFN3YXBTdHlsZX0gc3dhcFN0eWxlXG4gKiBAcHJvcGVydHkge251bWJlcn0gc3dhcERlbGF5XG4gKiBAcHJvcGVydHkge251bWJlcn0gc2V0dGxlRGVsYXlcbiAqIEBwcm9wZXJ0eSB7Ym9vbGVhbn0gW3RyYW5zaXRpb25dXG4gKiBAcHJvcGVydHkge2Jvb2xlYW59IFtpZ25vcmVUaXRsZV1cbiAqIEBwcm9wZXJ0eSB7c3RyaW5nfSBbaGVhZF1cbiAqIEBwcm9wZXJ0eSB7J3RvcCcgfCAnYm90dG9tJ30gW3Njcm9sbF1cbiAqIEBwcm9wZXJ0eSB7c3RyaW5nfSBbc2Nyb2xsVGFyZ2V0XVxuICogQHByb3BlcnR5IHtzdHJpbmd9IFtzaG93XVxuICogQHByb3BlcnR5IHtzdHJpbmd9IFtzaG93VGFyZ2V0XVxuICogQHByb3BlcnR5IHtib29sZWFufSBbZm9jdXNTY3JvbGxdXG4gKi9cblxuLyoqXG4gKiBAdHlwZWRlZiB7KCh0aGlzOk5vZGUsIGV2dDpFdmVudCkgPT4gYm9vbGVhbikgJiB7c291cmNlOiBzdHJpbmd9fSBDb25kaXRpb25hbEZ1bmN0aW9uXG4gKi9cblxuLyoqXG4gKiBAdHlwZWRlZiB7T2JqZWN0fSBIdG14VHJpZ2dlclNwZWNpZmljYXRpb25cbiAqIEBwcm9wZXJ0eSB7c3RyaW5nfSB0cmlnZ2VyXG4gKiBAcHJvcGVydHkge251bWJlcn0gW3BvbGxJbnRlcnZhbF1cbiAqIEBwcm9wZXJ0eSB7Q29uZGl0aW9uYWxGdW5jdGlvbn0gW2V2ZW50RmlsdGVyXVxuICogQHByb3BlcnR5IHtib29sZWFufSBbY2hhbmdlZF1cbiAqIEBwcm9wZXJ0eSB7Ym9vbGVhbn0gW29uY2VdXG4gKiBAcHJvcGVydHkge2Jvb2xlYW59IFtjb25zdW1lXVxuICogQHByb3BlcnR5IHtudW1iZXJ9IFtkZWxheV1cbiAqIEBwcm9wZXJ0eSB7c3RyaW5nfSBbZnJvbV1cbiAqIEBwcm9wZXJ0eSB7c3RyaW5nfSBbdGFyZ2V0XVxuICogQHByb3BlcnR5IHtudW1iZXJ9IFt0aHJvdHRsZV1cbiAqIEBwcm9wZXJ0eSB7c3RyaW5nfSBbcXVldWVdXG4gKiBAcHJvcGVydHkge3N0cmluZ30gW3Jvb3RdXG4gKiBAcHJvcGVydHkge3N0cmluZ30gW3RocmVzaG9sZF1cbiAqL1xuXG4vKipcbiAqIEB0eXBlZGVmIHt7ZWx0OiBFbGVtZW50LCBtZXNzYWdlOiBzdHJpbmcsIHZhbGlkaXR5OiBWYWxpZGl0eVN0YXRlfX0gSHRteEVsZW1lbnRWYWxpZGF0aW9uRXJyb3JcbiAqL1xuXG4vKipcbiAqIEB0eXBlZGVmIHtSZWNvcmQ8c3RyaW5nLCBzdHJpbmc+fSBIdG14SGVhZGVyU3BlY2lmaWNhdGlvblxuICogQHByb3BlcnR5IHsndHJ1ZSd9IEhYLVJlcXVlc3RcbiAqIEBwcm9wZXJ0eSB7c3RyaW5nfG51bGx9IEhYLVRyaWdnZXJcbiAqIEBwcm9wZXJ0eSB7c3RyaW5nfG51bGx9IEhYLVRyaWdnZXItTmFtZVxuICogQHByb3BlcnR5IHtzdHJpbmd8bnVsbH0gSFgtVGFyZ2V0XG4gKiBAcHJvcGVydHkge3N0cmluZ30gSFgtQ3VycmVudC1VUkxcbiAqIEBwcm9wZXJ0eSB7c3RyaW5nfSBbSFgtUHJvbXB0XVxuICogQHByb3BlcnR5IHsndHJ1ZSd9IFtIWC1Cb29zdGVkXVxuICogQHByb3BlcnR5IHtzdHJpbmd9IFtDb250ZW50LVR5cGVdXG4gKiBAcHJvcGVydHkgeyd0cnVlJ30gW0hYLUhpc3RvcnktUmVzdG9yZS1SZXF1ZXN0XVxuICovXG5cbi8qKiBAdHlwZWRlZiBIdG14QWpheEhlbHBlckNvbnRleHRcbiAqIEBwcm9wZXJ0eSB7RWxlbWVudHxzdHJpbmd9IFtzb3VyY2VdXG4gKiBAcHJvcGVydHkge0V2ZW50fSBbZXZlbnRdXG4gKiBAcHJvcGVydHkge0h0bXhBamF4SGFuZGxlcn0gW2hhbmRsZXJdXG4gKiBAcHJvcGVydHkge0VsZW1lbnR8c3RyaW5nfSBbdGFyZ2V0XVxuICogQHByb3BlcnR5IHtIdG14U3dhcFN0eWxlfSBbc3dhcF1cbiAqIEBwcm9wZXJ0eSB7T2JqZWN0fEZvcm1EYXRhfSBbdmFsdWVzXVxuICogQHByb3BlcnR5IHtSZWNvcmQ8c3RyaW5nLHN0cmluZz59IFtoZWFkZXJzXVxuICogQHByb3BlcnR5IHtzdHJpbmd9IFtzZWxlY3RdXG4gKi9cblxuLyoqXG4gKiBAdHlwZWRlZiB7T2JqZWN0fSBIdG14UmVxdWVzdENvbmZpZ1xuICogQHByb3BlcnR5IHtib29sZWFufSBib29zdGVkXG4gKiBAcHJvcGVydHkge2Jvb2xlYW59IHVzZVVybFBhcmFtc1xuICogQHByb3BlcnR5IHtGb3JtRGF0YX0gZm9ybURhdGFcbiAqIEBwcm9wZXJ0eSB7T2JqZWN0fSBwYXJhbWV0ZXJzIGZvcm1EYXRhIHByb3h5XG4gKiBAcHJvcGVydHkge0Zvcm1EYXRhfSB1bmZpbHRlcmVkRm9ybURhdGFcbiAqIEBwcm9wZXJ0eSB7T2JqZWN0fSB1bmZpbHRlcmVkUGFyYW1ldGVycyB1bmZpbHRlcmVkRm9ybURhdGEgcHJveHlcbiAqIEBwcm9wZXJ0eSB7SHRteEhlYWRlclNwZWNpZmljYXRpb259IGhlYWRlcnNcbiAqIEBwcm9wZXJ0eSB7RWxlbWVudH0gdGFyZ2V0XG4gKiBAcHJvcGVydHkge0h0dHBWZXJifSB2ZXJiXG4gKiBAcHJvcGVydHkge0h0bXhFbGVtZW50VmFsaWRhdGlvbkVycm9yW119IGVycm9yc1xuICogQHByb3BlcnR5IHtib29sZWFufSB3aXRoQ3JlZGVudGlhbHNcbiAqIEBwcm9wZXJ0eSB7bnVtYmVyfSB0aW1lb3V0XG4gKiBAcHJvcGVydHkge3N0cmluZ30gcGF0aFxuICogQHByb3BlcnR5IHtFdmVudH0gdHJpZ2dlcmluZ0V2ZW50XG4gKi9cblxuLyoqXG4gKiBAdHlwZWRlZiB7T2JqZWN0fSBIdG14UmVzcG9uc2VJbmZvXG4gKiBAcHJvcGVydHkge1hNTEh0dHBSZXF1ZXN0fSB4aHJcbiAqIEBwcm9wZXJ0eSB7RWxlbWVudH0gdGFyZ2V0XG4gKiBAcHJvcGVydHkge0h0bXhSZXF1ZXN0Q29uZmlnfSByZXF1ZXN0Q29uZmlnXG4gKiBAcHJvcGVydHkge0h0bXhBamF4RXRjfSBldGNcbiAqIEBwcm9wZXJ0eSB7Ym9vbGVhbn0gYm9vc3RlZFxuICogQHByb3BlcnR5IHtzdHJpbmd9IHNlbGVjdFxuICogQHByb3BlcnR5IHt7cmVxdWVzdFBhdGg6IHN0cmluZywgZmluYWxSZXF1ZXN0UGF0aDogc3RyaW5nLCByZXNwb25zZVBhdGg6IHN0cmluZ3xudWxsLCBhbmNob3I6IHN0cmluZ319IHBhdGhJbmZvXG4gKiBAcHJvcGVydHkge2Jvb2xlYW59IFtmYWlsZWRdXG4gKiBAcHJvcGVydHkge2Jvb2xlYW59IFtzdWNjZXNzZnVsXVxuICogQHByb3BlcnR5IHtib29sZWFufSBba2VlcEluZGljYXRvcnNdXG4gKi9cblxuLyoqXG4gKiBAdHlwZWRlZiB7T2JqZWN0fSBIdG14QWpheEV0Y1xuICogQHByb3BlcnR5IHtib29sZWFufSBbcmV0dXJuUHJvbWlzZV1cbiAqIEBwcm9wZXJ0eSB7SHRteEFqYXhIYW5kbGVyfSBbaGFuZGxlcl1cbiAqIEBwcm9wZXJ0eSB7c3RyaW5nfSBbc2VsZWN0XVxuICogQHByb3BlcnR5IHtFbGVtZW50fSBbdGFyZ2V0T3ZlcnJpZGVdXG4gKiBAcHJvcGVydHkge0h0bXhTd2FwU3R5bGV9IFtzd2FwT3ZlcnJpZGVdXG4gKiBAcHJvcGVydHkge1JlY29yZDxzdHJpbmcsc3RyaW5nPn0gW2hlYWRlcnNdXG4gKiBAcHJvcGVydHkge09iamVjdHxGb3JtRGF0YX0gW3ZhbHVlc11cbiAqIEBwcm9wZXJ0eSB7Ym9vbGVhbn0gW2NyZWRlbnRpYWxzXVxuICogQHByb3BlcnR5IHtudW1iZXJ9IFt0aW1lb3V0XVxuICovXG5cbi8qKlxuICogQHR5cGVkZWYge09iamVjdH0gSHRteFJlc3BvbnNlSGFuZGxpbmdDb25maWdcbiAqIEBwcm9wZXJ0eSB7c3RyaW5nfSBbY29kZV1cbiAqIEBwcm9wZXJ0eSB7Ym9vbGVhbn0gc3dhcFxuICogQHByb3BlcnR5IHtib29sZWFufSBbZXJyb3JdXG4gKiBAcHJvcGVydHkge2Jvb2xlYW59IFtpZ25vcmVUaXRsZV1cbiAqIEBwcm9wZXJ0eSB7c3RyaW5nfSBbc2VsZWN0XVxuICogQHByb3BlcnR5IHtzdHJpbmd9IFt0YXJnZXRdXG4gKiBAcHJvcGVydHkge3N0cmluZ30gW3N3YXBPdmVycmlkZV1cbiAqIEBwcm9wZXJ0eSB7c3RyaW5nfSBbZXZlbnRdXG4gKi9cblxuLyoqXG4gKiBAdHlwZWRlZiB7SHRteFJlc3BvbnNlSW5mbyAmIHtzaG91bGRTd2FwOiBib29sZWFuLCBzZXJ2ZXJSZXNwb25zZTogYW55LCBpc0Vycm9yOiBib29sZWFuLCBpZ25vcmVUaXRsZTogYm9vbGVhbiwgc2VsZWN0T3ZlcnJpZGU6c3RyaW5nfX0gSHRteEJlZm9yZVN3YXBEZXRhaWxzXG4gKi9cblxuLyoqXG4gKiBAY2FsbGJhY2sgSHRteEFqYXhIYW5kbGVyXG4gKiBAcGFyYW0ge0VsZW1lbnR9IGVsdFxuICogQHBhcmFtIHtIdG14UmVzcG9uc2VJbmZvfSByZXNwb25zZUluZm9cbiAqL1xuXG4vKipcbiAqIEB0eXBlZGVmIHsoKCkgPT4gdm9pZCl9IEh0bXhTZXR0bGVUYXNrXG4gKi9cblxuLyoqXG4gKiBAdHlwZWRlZiB7T2JqZWN0fSBIdG14U2V0dGxlSW5mb1xuICogQHByb3BlcnR5IHtIdG14U2V0dGxlVGFza1tdfSB0YXNrc1xuICogQHByb3BlcnR5IHtFbGVtZW50W119IGVsdHNcbiAqIEBwcm9wZXJ0eSB7c3RyaW5nfSBbdGl0bGVdXG4gKi9cblxuLyoqXG4gKiBAc2VlIGh0dHBzOi8vZ2l0aHViLmNvbS9iaWdza3lzb2Z0d2FyZS9odG14LWV4dGVuc2lvbnMvYmxvYi9tYWluL1JFQURNRS5tZFxuICogQHR5cGVkZWYge09iamVjdH0gSHRteEV4dGVuc2lvblxuICogQHByb3BlcnR5IHsoYXBpOiBhbnkpID0+IHZvaWR9IGluaXRcbiAqIEBwcm9wZXJ0eSB7KG5hbWU6IHN0cmluZywgZXZlbnQ6IEV2ZW50fEN1c3RvbUV2ZW50KSA9PiBib29sZWFufSBvbkV2ZW50XG4gKiBAcHJvcGVydHkgeyh0ZXh0OiBzdHJpbmcsIHhocjogWE1MSHR0cFJlcXVlc3QsIGVsdDogRWxlbWVudCkgPT4gc3RyaW5nfSB0cmFuc2Zvcm1SZXNwb25zZVxuICogQHByb3BlcnR5IHsoc3dhcFN0eWxlOiBIdG14U3dhcFN0eWxlKSA9PiBib29sZWFufSBpc0lubGluZVN3YXBcbiAqIEBwcm9wZXJ0eSB7KHN3YXBTdHlsZTogSHRteFN3YXBTdHlsZSwgdGFyZ2V0OiBOb2RlLCBmcmFnbWVudDogTm9kZSwgc2V0dGxlSW5mbzogSHRteFNldHRsZUluZm8pID0+IGJvb2xlYW58Tm9kZVtdfSBoYW5kbGVTd2FwXG4gKiBAcHJvcGVydHkgeyh4aHI6IFhNTEh0dHBSZXF1ZXN0LCBwYXJhbWV0ZXJzOiBGb3JtRGF0YSwgZWx0OiBOb2RlKSA9PiAqfHN0cmluZ3xudWxsfSBlbmNvZGVQYXJhbWV0ZXJzXG4gKiBAcHJvcGVydHkgeygpID0+IHN0cmluZ1tdfG51bGx9IGdldFNlbGVjdG9yc1xuICovXG5leHBvcnQgZGVmYXVsdCBodG14XG4iLCJpbXBvcnQgaHRteCBmcm9tIFwiaHRteC5vcmdcIjtcblxuZnVuY3Rpb24gZGVwZW5kc09uKHBhdGhTcGVjOiBhbnksIHVybDogc3RyaW5nKSB7XG4gIGlmIChwYXRoU3BlYyA9PT0gXCJpZ25vcmVcIikge1xuICAgIHJldHVybiBmYWxzZTtcbiAgfVxuICBjb25zdCBkZXBlbmRlbmN5UGF0aCA9IHBhdGhTcGVjLnNwbGl0KFwiL1wiKTtcbiAgY29uc3QgdXJsUGF0aCA9IHVybC5zcGxpdChcIi9cIik7XG4gIGZvciAobGV0IGkgPSAwOyBpIDwgdXJsUGF0aC5sZW5ndGg7IGkrKykge1xuICAgIGNvbnN0IGRlcGVuZGVuY3lFbGVtZW50ID0gZGVwZW5kZW5jeVBhdGguc2hpZnQoKTtcbiAgICBjb25zdCBwYXRoRWxlbWVudCA9IHVybFBhdGhbaV07XG4gICAgaWYgKGRlcGVuZGVuY3lFbGVtZW50ICE9PSBwYXRoRWxlbWVudCAmJiBkZXBlbmRlbmN5RWxlbWVudCAhPT0gXCIqXCIpIHtcbiAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9XG4gICAgaWYgKFxuICAgICAgZGVwZW5kZW5jeVBhdGgubGVuZ3RoID09PSAwIHx8XG4gICAgICAoZGVwZW5kZW5jeVBhdGgubGVuZ3RoID09PSAxICYmIGRlcGVuZGVuY3lQYXRoWzBdID09PSBcIlwiKVxuICAgICkge1xuICAgICAgcmV0dXJuIHRydWU7XG4gICAgfVxuICB9XG4gIHJldHVybiBmYWxzZTtcbn1cblxuZnVuY3Rpb24gcmVmcmVzaFBhdGgocGF0aDogc3RyaW5nKSB7XG4gIGNvbnN0IGVsdHNXaXRoRGVwcyA9IGh0bXguZmluZEFsbChcIltwYXRoLWRlcHNdXCIpO1xuICBmb3IgKGxldCBpID0gMDsgaSA8IGVsdHNXaXRoRGVwcy5sZW5ndGg7IGkrKykge1xuICAgIGNvbnN0IGVsdCA9IGVsdHNXaXRoRGVwc1tpXTtcbiAgICBpZiAoZGVwZW5kc09uKGVsdC5nZXRBdHRyaWJ1dGUoXCJwYXRoLWRlcHNcIiksIHBhdGgpKSB7XG4gICAgICBodG14LnRyaWdnZXIoZWx0LCBcInBhdGgtZGVwc1wiLCBudWxsKTtcbiAgICB9XG4gIH1cbn1cblxuaHRteC5kZWZpbmVFeHRlbnNpb24oXCJwYXRoLWRlcHNcIiwge1xuICAvLyBAdHMtaWdub3JlXG4gIG9uRXZlbnQ6IGZ1bmN0aW9uIChuYW1lLCBldnQpIHtcbiAgICBpZiAoIShldnQgaW5zdGFuY2VvZiBDdXN0b21FdmVudCkpIHtcbiAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9XG4gICAgaWYgKG5hbWUgPT09IFwiaHRteDpiZWZvcmVPbkxvYWRcIikge1xuICAgICAgY29uc3QgY29uZmlnID0gZXZ0LmRldGFpbC5yZXF1ZXN0Q29uZmlnO1xuICAgICAgLy8gbXV0YXRpbmcgY2FsbFxuICAgICAgaWYgKFxuICAgICAgICBjb25maWcgJiZcbiAgICAgICAgY29uZmlnLnZlcmIgIT09IFwiZ2V0XCIgJiZcbiAgICAgICAgZXZ0LnRhcmdldCAhPSBudWxsICYmXG4gICAgICAgIGV2dC50YXJnZXQgaW5zdGFuY2VvZiBFbGVtZW50ICYmXG4gICAgICAgIGV2dC50YXJnZXQuZ2V0QXR0cmlidXRlKFwicGF0aC1kZXBzXCIpICE9PSBcImlnbm9yZVwiXG4gICAgICApIHtcbiAgICAgICAgcmVmcmVzaFBhdGgoY29uZmlnLnBhdGgpO1xuICAgICAgfVxuICAgIH1cbiAgfSxcbn0pO1xuIiwiaW1wb3J0IGh0bXgsIHtIdG14U2V0dGxlSW5mbywgSHRteFN3YXBTdHlsZX0gZnJvbSBcImh0bXgub3JnXCI7XG5cbmZ1bmN0aW9uIGtlYmFiRXZlbnROYW1lKHN0cjogc3RyaW5nKSB7XG4gICAgcmV0dXJuIHN0ci5yZXBsYWNlKC8oW2EtejAtOV0pKFtBLVpdKS9nLCAnJDEtJDInKS50b0xvd2VyQ2FzZSgpXG59XG5cbmNvbnN0IGlnbm9yZWRFdmVudHMgPSBbJ2h0bXg6YmVmb3JlUHJvY2Vzc05vZGUnLCAnaHRteDphZnRlclByb2Nlc3NOb2RlJywgJ2h0bXg6YmVmb3JlU3dhcCcsICdodG14OmFmdGVyU3dhcCcsICdodG14OmJlZm9yZU9uTG9hZCcsICdodG14OmFmdGVyT25Mb2FkJywgJ2h0bXg6Y29uZmlnUmVxdWVzdCcsICdodG14OmNvbmZpZ1Jlc3BvbnNlJywgJ2h0bXg6cmVzcG9uc2VFcnJvciddO1xuXG5mdW5jdGlvbiBtYWtlRXZlbnQoZXZlbnROYW1lOiBzdHJpbmcsIGRldGFpbDogYW55KSB7XG4gICAgbGV0IGV2dFxuICAgIGlmICh3aW5kb3cuQ3VzdG9tRXZlbnQgJiYgdHlwZW9mIHdpbmRvdy5DdXN0b21FdmVudCA9PT0gJ2Z1bmN0aW9uJykge1xuICAgICAgICAvLyBUT0RPOiBgY29tcG9zZWQ6IHRydWVgIGhlcmUgaXMgYSBoYWNrIHRvIG1ha2UgZ2xvYmFsIGV2ZW50IGhhbmRsZXJzIHdvcmsgd2l0aCBldmVudHMgaW4gc2hhZG93IERPTVxuICAgICAgICBldnQgPSBuZXcgQ3VzdG9tRXZlbnQoZXZlbnROYW1lLCB7IGJ1YmJsZXM6IGZhbHNlLCBjYW5jZWxhYmxlOiB0cnVlLCBjb21wb3NlZDogdHJ1ZSwgZGV0YWlsIH0pXG4gICAgfSBlbHNlIHtcbiAgICAgICAgZXZ0ID0gZG9jdW1lbnQuY3JlYXRlRXZlbnQoJ0N1c3RvbUV2ZW50JylcbiAgICAgICAgZXZ0LmluaXRDdXN0b21FdmVudChldmVudE5hbWUsIHRydWUsIHRydWUsIGRldGFpbClcbiAgICB9XG4gICAgcmV0dXJuIGV2dFxufVxuXG5mdW5jdGlvbiB0cmlnZ2VyQ2hpbGRyZW4odGFyZ2V0OiBIVE1MRWxlbWVudCwgbmFtZTogc3RyaW5nLCBldmVudDogQ3VzdG9tRXZlbnQsIHRyaWdnZXJlZDogU2V0PEhUTUxFbGVtZW50Pikge1xuICAgIGlmKGlnbm9yZWRFdmVudHMuaW5jbHVkZXMobmFtZSkpIHtcbiAgICAgICAgcmV0dXJuXG4gICAgfVxuICAgIGlmICh0YXJnZXQgJiYgdGFyZ2V0LmNoaWxkcmVuKSB7XG4gICAgICAgIEFycmF5LmZyb20odGFyZ2V0LmNoaWxkcmVuKS5mb3JFYWNoKChlKSA9PiB7XG4gICAgICAgICAgICBjb25zdCBrZWhhYiA9IGtlYmFiRXZlbnROYW1lKG5hbWUpO1xuICAgICAgICAgICAgY29uc3QgZXZlbnROYW1lID0ga2VoYWIucmVwbGFjZShcImh0bXg6XCIsIFwiaHgtb246OlwiKVxuICAgICAgICAgICAgaWYgKCF0cmlnZ2VyZWQuaGFzKGUgYXMgSFRNTEVsZW1lbnQpKSB7XG4gICAgICAgICAgICAgICAgaWYoZS5oYXNBdHRyaWJ1dGUoZXZlbnROYW1lKSkge1xuICAgICAgICAgICAgICAgICAgICBjb25zdCBuZXdFdmVudCA9IG1ha2VFdmVudChldmVudE5hbWUucmVwbGFjZShcImh4LW9uOjpcIiwgXCJodG14OlwiKSwgZXZlbnQuZGV0YWlsKVxuICAgICAgICAgICAgICAgICAgICBuZXdFdmVudC5kZXRhaWwubWV0YSA9ICd0cmlnZ2VyLWNoaWxkcmVuJ1xuICAgICAgICAgICAgICAgICAgICBlLmRpc3BhdGNoRXZlbnQobmV3RXZlbnQpXG4gICAgICAgICAgICAgICAgICAgIHRyaWdnZXJlZC5hZGQoZSBhcyBIVE1MRWxlbWVudCk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIGlmIChlLmNoaWxkcmVuKSB7XG4gICAgICAgICAgICAgICAgICAgIHRyaWdnZXJDaGlsZHJlbihlIGFzIEhUTUxFbGVtZW50LCBuYW1lLCBldmVudCwgdHJpZ2dlcmVkKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgIH0pO1xuICAgIH1cbn1cblxuXG5odG14LmRlZmluZUV4dGVuc2lvbihcInRyaWdnZXItY2hpbGRyZW5cIiwge1xuICAgIG9uRXZlbnQ6IChuYW1lLCBldnQ6IEV2ZW50IHwgQ3VzdG9tRXZlbnQpID0+IHtcbiAgICAgICAgaWYgKCEoZXZ0IGluc3RhbmNlb2YgQ3VzdG9tRXZlbnQpKSB7XG4gICAgICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgICAgIH1cbiAgICAgICAgaWYoZXZ0LmRldGFpbC5tZXRhID09PSAndHJpZ2dlci1jaGlsZHJlbicpIHtcbiAgICAgICAgICAgIHJldHVybiBmYWxzZTtcbiAgICAgICAgfVxuICAgICAgICBjb25zdCB0cmlnZ2VyZWQgPSBuZXcgU2V0PEhUTUxFbGVtZW50PigpO1xuICAgICAgICBjb25zdCB0YXJnZXQgPSBldnQudGFyZ2V0IGFzIEhUTUxFbGVtZW50IHx8IGV2dC5kZXRhaWwudGFyZ2V0IGFzIEhUTUxFbGVtZW50O1xuICAgICAgICB0cmlnZ2VyQ2hpbGRyZW4odGFyZ2V0LCBuYW1lLCBldnQsIHRyaWdnZXJlZCk7XG4gICAgICAgIHJldHVybiB0cnVlO1xuICAgIH0sXG4gICAgaW5pdDogZnVuY3Rpb24gKGFwaTogYW55KTogdm9pZCB7XG4gICAgfSxcbiAgICB0cmFuc2Zvcm1SZXNwb25zZTogZnVuY3Rpb24gKFxuICAgICAgICB0ZXh0OiBzdHJpbmcsXG4gICAgICAgIHhocjogWE1MSHR0cFJlcXVlc3QsXG4gICAgICAgIGVsdDogRWxlbWVudCxcbiAgICApOiBzdHJpbmcge1xuICAgICAgICByZXR1cm4gdGV4dDtcbiAgICB9LFxuICAgIGlzSW5saW5lU3dhcDogZnVuY3Rpb24gKHN3YXBTdHlsZTogSHRteFN3YXBTdHlsZSk6IGJvb2xlYW4ge1xuICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgfSxcbiAgICBoYW5kbGVTd2FwOiBmdW5jdGlvbiAoXG4gICAgICAgIHN3YXBTdHlsZTogSHRteFN3YXBTdHlsZSxcbiAgICAgICAgdGFyZ2V0OiBOb2RlLFxuICAgICAgICBmcmFnbWVudDogTm9kZSxcbiAgICAgICAgc2V0dGxlSW5mbzogSHRteFNldHRsZUluZm8sXG4gICAgKTogYm9vbGVhbiB8IE5vZGVbXSB7XG4gICAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9LFxuICAgIGVuY29kZVBhcmFtZXRlcnM6IGZ1bmN0aW9uIChcbiAgICAgICAgeGhyOiBYTUxIdHRwUmVxdWVzdCxcbiAgICAgICAgcGFyYW1ldGVyczogRm9ybURhdGEsXG4gICAgICAgIGVsdDogTm9kZSxcbiAgICApIHtcbiAgICB9LFxuICAgIGdldFNlbGVjdG9yczogZnVuY3Rpb24gKCk6IHN0cmluZ1tdIHwgbnVsbCB7XG4gICAgICAgIHJldHVybiBudWxsO1xuICAgIH0sXG59KTtcbiIsImltcG9ydCBodG14IGZyb20gXCJodG14Lm9yZ1wiO1xuXG5odG14LmRlZmluZUV4dGVuc2lvbihcImRlYnVnXCIsIHtcbiAgLy8gQHRzLWlnbm9yZVxuICBvbkV2ZW50OiBmdW5jdGlvbiAobmFtZSwgZXZ0KSB7XG4gICAgaWYgKGNvbnNvbGUuZGVidWcpIHtcbiAgICAgIGNvbnNvbGUuZGVidWcobmFtZSwgZXZ0KTtcbiAgICB9IGVsc2UgaWYgKGNvbnNvbGUpIHtcbiAgICAgIGNvbnNvbGUubG9nKFwiREVCVUc6XCIsIG5hbWUsIGV2dCk7XG4gICAgfSBlbHNlIHtcbiAgICAgIC8vIG5vb3BcbiAgICB9XG4gIH0sXG59KTtcbiIsImltcG9ydCBodG14IGZyb20gXCJodG14Lm9yZ1wiO1xuY29uc3QgY29uZmlnOiBhbnkgPSBodG14LmNvbmZpZztcblxuLyoqIEB0eXBlIHtpbXBvcnQoXCIuLi9odG14XCIpLkh0bXhJbnRlcm5hbEFwaX0gKi9cbmxldCBhcGk6IGFueTtcblxuY29uc3QgYXR0clByZWZpeCA9IFwiaHgtdGFyZ2V0LVwiO1xuXG4vLyBJRTExIGRvZXNuJ3Qgc3VwcG9ydCBzdHJpbmcuc3RhcnRzV2l0aFxuZnVuY3Rpb24gc3RhcnRzV2l0aChzdHI6IHN0cmluZywgcHJlZml4OiBzdHJpbmcpIHtcbiAgcmV0dXJuIHN0ci5zdWJzdHJpbmcoMCwgcHJlZml4Lmxlbmd0aCkgPT09IHByZWZpeDtcbn1cblxuLyoqXG4gKiBAcGFyYW0ge0hUTUxFbGVtZW50fSBlbHRcbiAqIEBwYXJhbSByZXNwQ29kZU51bWJlclxuICogQHJldHVybnMge0hUTUxFbGVtZW50IHwgbnVsbH1cbiAqL1xuZnVuY3Rpb24gZ2V0UmVzcENvZGVUYXJnZXQoZWx0OiBFbGVtZW50LCByZXNwQ29kZU51bWJlcjogbnVtYmVyKSB7XG4gIGlmICghZWx0IHx8ICFyZXNwQ29kZU51bWJlcikgcmV0dXJuIG51bGw7XG5cbiAgY29uc3QgcmVzcENvZGUgPSByZXNwQ29kZU51bWJlci50b1N0cmluZygpO1xuXG4gIC8vICcqJyBpcyB0aGUgb3JpZ2luYWwgc3ludGF4LCBhcyB0aGUgb2J2aW91cyBjaGFyYWN0ZXIgZm9yIGEgd2lsZGNhcmQuXG4gIC8vIFRoZSAneCcgYWx0ZXJuYXRpdmUgd2FzIGFkZGVkIGZvciBtYXhpbXVtIGNvbXBhdGliaWxpdHkgd2l0aCBIVE1MXG4gIC8vIHRlbXBsYXRpbmcgZW5naW5lcywgZHVlIHRvIGFtYmlndWl0eSBhcm91bmQgd2hpY2ggY2hhcmFjdGVycyBhcmVcbiAgLy8gc3VwcG9ydGVkIGluIEhUTUwgYXR0cmlidXRlcy5cbiAgLy9cbiAgLy8gU3RhcnQgd2l0aCB0aGUgbW9zdCBzcGVjaWZpYyBwb3NzaWJsZSBhdHRyaWJ1dGUgYW5kIGdlbmVyYWxpemUgZnJvbVxuICAvLyB0aGVyZS5cbiAgY29uc3QgYXR0clBvc3NpYmlsaXRpZXMgPSBbXG4gICAgcmVzcENvZGUsXG5cbiAgICByZXNwQ29kZS5zdWJzdHIoMCwgMikgKyBcIipcIixcbiAgICByZXNwQ29kZS5zdWJzdHIoMCwgMikgKyBcInhcIixcblxuICAgIHJlc3BDb2RlLnN1YnN0cigwLCAxKSArIFwiKlwiLFxuICAgIHJlc3BDb2RlLnN1YnN0cigwLCAxKSArIFwieFwiLFxuICAgIHJlc3BDb2RlLnN1YnN0cigwLCAxKSArIFwiKipcIixcbiAgICByZXNwQ29kZS5zdWJzdHIoMCwgMSkgKyBcInh4XCIsXG5cbiAgICBcIipcIixcbiAgICBcInhcIixcbiAgICBcIioqKlwiLFxuICAgIFwieHh4XCIsXG4gIF07XG4gIGlmIChzdGFydHNXaXRoKHJlc3BDb2RlLCBcIjRcIikgfHwgc3RhcnRzV2l0aChyZXNwQ29kZSwgXCI1XCIpKSB7XG4gICAgYXR0clBvc3NpYmlsaXRpZXMucHVzaChcImVycm9yXCIpO1xuICB9XG5cbiAgZm9yIChsZXQgaSA9IDA7IGkgPCBhdHRyUG9zc2liaWxpdGllcy5sZW5ndGg7IGkrKykge1xuICAgIGNvbnN0IGF0dHIgPSBhdHRyUHJlZml4ICsgYXR0clBvc3NpYmlsaXRpZXNbaV07XG4gICAgY29uc3QgYXR0clZhbHVlID0gYXBpLmdldENsb3Nlc3RBdHRyaWJ1dGVWYWx1ZShlbHQsIGF0dHIpO1xuICAgIGlmIChhdHRyVmFsdWUpIHtcbiAgICAgIGlmIChhdHRyVmFsdWUgPT09IFwidGhpc1wiKSB7XG4gICAgICAgIHJldHVybiBhcGkuZmluZFRoaXNFbGVtZW50KGVsdCwgYXR0cik7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICByZXR1cm4gYXBpLnF1ZXJ5U2VsZWN0b3JFeHQoZWx0LCBhdHRyVmFsdWUpO1xuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIHJldHVybiBudWxsO1xufVxuXG4vKiogQHBhcmFtIHtFdmVudH0gZXZ0ICovXG5mdW5jdGlvbiBoYW5kbGVFcnJvckZsYWcoZXZ0OiBDdXN0b21FdmVudCkge1xuICBpZiAoZXZ0LmRldGFpbC5pc0Vycm9yKSB7XG4gICAgaWYgKGNvbmZpZy5yZXNwb25zZVRhcmdldFVuc2V0c0Vycm9yKSB7XG4gICAgICBldnQuZGV0YWlsLmlzRXJyb3IgPSBmYWxzZTtcbiAgICB9XG4gIH0gZWxzZSBpZiAoY29uZmlnLnJlc3BvbnNlVGFyZ2V0U2V0c0Vycm9yKSB7XG4gICAgZXZ0LmRldGFpbC5pc0Vycm9yID0gdHJ1ZTtcbiAgfVxufVxuXG5odG14LmRlZmluZUV4dGVuc2lvbihcInJlc3BvbnNlLXRhcmdldHNcIiwge1xuICAvLyBAdHMtaWdub3JlXG4gIGluaXQ6IChhcGlSZWYpID0+IHtcbiAgICBhcGkgPSBhcGlSZWY7XG5cbiAgICBpZiAoY29uZmlnLnJlc3BvbnNlVGFyZ2V0VW5zZXRzRXJyb3IgPT09IHVuZGVmaW5lZCkge1xuICAgICAgY29uZmlnLnJlc3BvbnNlVGFyZ2V0VW5zZXRzRXJyb3IgPSB0cnVlO1xuICAgIH1cbiAgICBpZiAoY29uZmlnLnJlc3BvbnNlVGFyZ2V0U2V0c0Vycm9yID09PSB1bmRlZmluZWQpIHtcbiAgICAgIGNvbmZpZy5yZXNwb25zZVRhcmdldFNldHNFcnJvciA9IGZhbHNlO1xuICAgIH1cbiAgICBpZiAoY29uZmlnLnJlc3BvbnNlVGFyZ2V0UHJlZmVyc0V4aXN0aW5nID09PSB1bmRlZmluZWQpIHtcbiAgICAgIGNvbmZpZy5yZXNwb25zZVRhcmdldFByZWZlcnNFeGlzdGluZyA9IGZhbHNlO1xuICAgIH1cbiAgICBpZiAoY29uZmlnLnJlc3BvbnNlVGFyZ2V0UHJlZmVyc1JldGFyZ2V0SGVhZGVyID09PSB1bmRlZmluZWQpIHtcbiAgICAgIGNvbmZpZy5yZXNwb25zZVRhcmdldFByZWZlcnNSZXRhcmdldEhlYWRlciA9IHRydWU7XG4gICAgfVxuICB9LFxuXG4gIC8vIEB0cy1pZ25vcmVcbiAgb25FdmVudDogKG5hbWUsIGV2dCkgPT4ge1xuICAgIGlmICghKGV2dCBpbnN0YW5jZW9mIEN1c3RvbUV2ZW50KSkge1xuICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIH1cbiAgICBpZiAoXG4gICAgICBuYW1lID09PSBcImh0bXg6YmVmb3JlU3dhcFwiICYmXG4gICAgICBldnQuZGV0YWlsLnhociAmJlxuICAgICAgZXZ0LmRldGFpbC54aHIuc3RhdHVzICE9PSAyMDBcbiAgICApIHtcbiAgICAgIGlmIChldnQuZGV0YWlsLnRhcmdldCkge1xuICAgICAgICBpZiAoY29uZmlnLnJlc3BvbnNlVGFyZ2V0UHJlZmVyc0V4aXN0aW5nKSB7XG4gICAgICAgICAgZXZ0LmRldGFpbC5zaG91bGRTd2FwID0gdHJ1ZTtcbiAgICAgICAgICBoYW5kbGVFcnJvckZsYWcoZXZ0KTtcbiAgICAgICAgICByZXR1cm4gdHJ1ZTtcbiAgICAgICAgfVxuICAgICAgICBpZiAoXG4gICAgICAgICAgY29uZmlnLnJlc3BvbnNlVGFyZ2V0UHJlZmVyc1JldGFyZ2V0SGVhZGVyICYmXG4gICAgICAgICAgZXZ0LmRldGFpbC54aHIuZ2V0QWxsUmVzcG9uc2VIZWFkZXJzKCkubWF0Y2goL0hYLVJldGFyZ2V0Oi9pKVxuICAgICAgICApIHtcbiAgICAgICAgICBldnQuZGV0YWlsLnNob3VsZFN3YXAgPSB0cnVlO1xuICAgICAgICAgIGhhbmRsZUVycm9yRmxhZyhldnQpO1xuICAgICAgICAgIHJldHVybiB0cnVlO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgICBpZiAoIWV2dC5kZXRhaWwucmVxdWVzdENvbmZpZykge1xuICAgICAgICByZXR1cm4gdHJ1ZTtcbiAgICAgIH1cbiAgICAgIGNvbnN0IHRhcmdldCA9IGdldFJlc3BDb2RlVGFyZ2V0KFxuICAgICAgICBldnQuZGV0YWlsLnJlcXVlc3RDb25maWcuZWx0LFxuICAgICAgICBldnQuZGV0YWlsLnhoci5zdGF0dXMsXG4gICAgICApO1xuICAgICAgaWYgKHRhcmdldCkge1xuICAgICAgICBoYW5kbGVFcnJvckZsYWcoZXZ0KTtcbiAgICAgICAgZXZ0LmRldGFpbC5zaG91bGRTd2FwID0gdHJ1ZTtcbiAgICAgICAgZXZ0LmRldGFpbC50YXJnZXQgPSB0YXJnZXQ7XG4gICAgICB9XG4gICAgICByZXR1cm4gdHJ1ZTtcbiAgICB9XG4gIH0sXG59KTtcbiIsImltcG9ydCBodG14IGZyb20gXCJodG14Lm9yZ1wiO1xuXG5odG14LmRlZmluZUV4dGVuc2lvbihcIm11dGF0aW9uLWVycm9yXCIsIHtcbiAgLy8gQHRzLWlnbm9yZVxuICBvbkV2ZW50OiAobmFtZSwgZXZ0KSA9PiB7XG4gICAgaWYgKCEoZXZ0IGluc3RhbmNlb2YgQ3VzdG9tRXZlbnQpKSB7XG4gICAgICByZXR1cm4gZmFsc2U7XG4gICAgfVxuICAgIGlmIChuYW1lID09PSBcImh0bXg6YWZ0ZXJSZXF1ZXN0XCIpIHtcbiAgICAgIGlmICghZXZ0LmRldGFpbCB8fCAhZXZ0LmRldGFpbC54aHIpIHtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuICAgICAgY29uc3Qgc3RhdHVzID0gZXZ0LmRldGFpbC54aHIuc3RhdHVzO1xuICAgICAgaWYgKHN0YXR1cyA+PSA0MDApIHtcbiAgICAgICAgaHRteC5maW5kQWxsKFwiW2h4LW9uXFxcXDpcXFxcOm11dGF0aW9uLWVycm9yXVwiKS5mb3JFYWNoKChlbGVtZW50KSA9PiB7XG4gICAgICAgICAgaHRteC50cmlnZ2VyKGVsZW1lbnQsIFwiaHRteDptdXRhdGlvbi1lcnJvclwiLCB7IHN0YXR1cyB9KTtcbiAgICAgICAgfSk7XG4gICAgICB9XG4gICAgfVxuICB9LFxufSk7XG4iLCJpbXBvcnQgaHRteCBmcm9tIFwiaHRteC5vcmdcIjtcbmltcG9ydCB7Y3JlYXRlV2ViU29ja2V0Q2xpZW50fSBmcm9tIFwiLi4vdXRpbC93c1wiO1xuXG5sZXQgbGFzdFZlcnNpb24gPSBcIlwiO1xuXG5odG14LmRlZmluZUV4dGVuc2lvbihcImxpdmVyZWxvYWRcIiwge1xuICAgIGluaXQ6IGZ1bmN0aW9uICgpIHtcblxuICAgICAgICBsZXQgZW5hYmxlZCA9IGZhbHNlXG4gICAgICAgIGZvciAoY29uc3QgZWxlbWVudCBvZiBBcnJheS5mcm9tKGh0bXguZmluZEFsbChcIltoeC1leHRdXCIpKSkge1xuICAgICAgICAgICAgY29uc3QgdmFsdWUgPSBlbGVtZW50LmdldEF0dHJpYnV0ZShcImh4LWV4dFwiKTtcbiAgICAgICAgICAgIGlmKHZhbHVlPy5zcGxpdChcIiBcIikuaW5jbHVkZXMoXCJsaXZlcmVsb2FkXCIpKSB7XG4gICAgICAgICAgICAgICAgZW5hYmxlZCA9IHRydWVcbiAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuXG4gICAgICAgIGlmKCFlbmFibGVkKSB7XG4gICAgICAgICAgICByZXR1cm5cbiAgICAgICAgfVxuXG4gICAgICAgIGNvbnNvbGUubG9nKCdsaXZlcmVsb2FkIGV4dGVuc2lvbiBpbml0aWFsaXplZC4nKTtcbiAgICAgICAgLy8gQ3JlYXRlIGEgbmV3IEV2ZW50U291cmNlIG9iamVjdCBhbmQgcG9pbnQgaXQgdG8geW91ciBTU0UgZW5kcG9pbnRcbiAgICAgICAgY29uc3QgZXZlbnRTb3VyY2UgPSBuZXcgRXZlbnRTb3VyY2UoJy9kZXYvbGl2ZXJlbG9hZCcpO1xuICAgICAgICAvLyBMaXN0ZW4gZm9yIG1lc3NhZ2VzIGZyb20gdGhlIHNlcnZlclxuICAgICAgICBldmVudFNvdXJjZS5vbm1lc3NhZ2UgPSBmdW5jdGlvbihldmVudCkge1xuICAgICAgICAgICAgY29uc3QgbWVzc2FnZSA9IGV2ZW50LmRhdGFcbiAgICAgICAgICAgIC8vIExvZyB0aGUgbWVzc2FnZSBkYXRhIHJlY2VpdmVkIGZyb20gdGhlIHNlcnZlclxuICAgICAgICAgICAgaWYobGFzdFZlcnNpb24gPT09IFwiXCIpIHtcbiAgICAgICAgICAgICAgICBsYXN0VmVyc2lvbiA9IG1lc3NhZ2U7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBpZihsYXN0VmVyc2lvbiAhPT0gbWVzc2FnZSkge1xuICAgICAgICAgICAgICAgIGxhc3RWZXJzaW9uID0gbWVzc2FnZTtcbiAgICAgICAgICAgICAgICByZWxvYWQoKVxuICAgICAgICAgICAgfVxuICAgICAgICB9O1xuICAgICAgICAvLyBIYW5kbGUgZXJyb3JzIChlLmcuLCB3aGVuIHRoZSBjb25uZWN0aW9uIGlzIGNsb3NlZClcbiAgICAgICAgZXZlbnRTb3VyY2Uub25lcnJvciA9IGZ1bmN0aW9uKGVycm9yKSB7XG4gICAgICAgICAgICBjb25zb2xlLmVycm9yKCdFdmVudFNvdXJjZSBlcnJvcjonLCBlcnJvcik7XG4gICAgICAgIH07XG5cbiAgICB9LFxuICAgIC8vIEB0cy1pZ25vcmVcbiAgICBvbkV2ZW50OiBmdW5jdGlvbiAobmFtZSwgZXZ0KSB7XG5cbiAgICB9LFxufSk7XG5cbmZ1bmN0aW9uIHJlbG9hZCgpIHtcbiAgICB3aW5kb3cubG9jYXRpb24ucmVsb2FkKClcbn0iLCJpbXBvcnQgaHRteCBmcm9tIFwiaHRteC5vcmdcIjtcblxuY29uc3QgZXZhbEZ1bmNSZWdleCA9L19fZXZhbF9bQS1aYS16MC05XStcXChbYS16XStcXCkvZ21cblxuaHRteC5kZWZpbmVFeHRlbnNpb24oXCJodG1nb1wiLCB7XG4gICAgLy8gQHRzLWlnbm9yZVxuICAgIG9uRXZlbnQ6IGZ1bmN0aW9uIChuYW1lLCBldnQpIHtcbiAgICAgICBpZihuYW1lID09PSBcImh0bXg6YmVmb3JlQ2xlYW51cEVsZW1lbnRcIiAmJiBldnQudGFyZ2V0KSB7XG4gICAgICAgICAgIHJlbW92ZUFzc29jaWF0ZWRTY3JpcHRzKGV2dC50YXJnZXQgYXMgSFRNTEVsZW1lbnQpO1xuICAgICAgIH1cbiAgICB9LFxufSk7XG5cbmV4cG9ydCBmdW5jdGlvbiByZW1vdmVBc3NvY2lhdGVkU2NyaXB0cyhlbGVtZW50OiBIVE1MRWxlbWVudCkge1xuICAgY29uc3QgYXR0cmlidXRlcyA9IEFycmF5LmZyb20oZWxlbWVudC5hdHRyaWJ1dGVzKVxuICAgIGZvciAobGV0IGF0dHJpYnV0ZSBvZiBhdHRyaWJ1dGVzKSB7XG4gICAgICAgY29uc3QgbWF0Y2hlcyA9IGF0dHJpYnV0ZS52YWx1ZS5tYXRjaChldmFsRnVuY1JlZ2V4KSB8fCBbXVxuICAgICAgICBmb3IgKGxldCBtYXRjaCBvZiBtYXRjaGVzKSB7XG4gICAgICAgICAgICBjb25zdCBpZCA9IG1hdGNoLnJlcGxhY2UoXCIoKVwiLCBcIlwiKS5yZXBsYWNlKFwiKHRoaXMpXCIsIFwiXCIpLnJlcGxhY2UoXCI7XCIsIFwiXCIpXG4gICAgICAgICAgICBjb25zdCBlbGUgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZChpZClcbiAgICAgICAgICAgIGlmKGVsZSAmJiBlbGUudGFnTmFtZSA9PT0gXCJTQ1JJUFRcIikge1xuICAgICAgICAgICAgICAgIGNvbnNvbGUuZGVidWcoXCJyZW1vdmluZyBhc3NvY2lhdGVkIHNjcmlwdCB3aXRoIGlkXCIsIGlkKVxuICAgICAgICAgICAgICAgIGVsZS5yZW1vdmUoKVxuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgfVxufVxuIiwiaW1wb3J0IGh0bXggZnJvbSAnaHRteC5vcmcnXG5pbXBvcnQge3JlbW92ZUFzc29jaWF0ZWRTY3JpcHRzfSBmcm9tIFwiLi9odG1nb1wiO1xuXG5sZXQgYXBpIDogYW55ID0gbnVsbDtcbmxldCBwcm9jZXNzZWQgPSBuZXcgU2V0PHN0cmluZz4oKVxuXG5odG14LmRlZmluZUV4dGVuc2lvbihcInNzZVwiLCB7XG4gICAgaW5pdDogZnVuY3Rpb24gKGFwaVJlZikge1xuICAgICAgICBhcGkgPSBhcGlSZWY7XG4gICAgfSxcbiAgICAvLyBAdHMtaWdub3JlXG4gICAgb25FdmVudDogZnVuY3Rpb24gKG5hbWUsIGV2dCkge1xuICAgICAgICBjb25zdCB0YXJnZXQgPSBldnQudGFyZ2V0O1xuICAgICAgICBpZighKHRhcmdldCBpbnN0YW5jZW9mIEhUTUxFbGVtZW50KSkge1xuICAgICAgICAgICAgcmV0dXJuXG4gICAgICAgIH1cblxuICAgICAgICBpZihuYW1lID09PSAnaHRteDpiZWZvcmVDbGVhbnVwRWxlbWVudCcpIHtcbiAgICAgICAgICAgIHJlbW92ZUFzc29jaWF0ZWRTY3JpcHRzKHRhcmdldCk7XG4gICAgICAgIH1cblxuICAgICAgICBpZihuYW1lID09PSAnaHRteDpiZWZvcmVQcm9jZXNzTm9kZScpIHtcbiAgICAgICAgICAgIGNvbnN0IGVsZW1lbnRzID0gZG9jdW1lbnQucXVlcnlTZWxlY3RvckFsbCgnW3NzZS1jb25uZWN0XScpO1xuICAgICAgICAgICAgZm9yIChsZXQgZWxlbWVudCBvZiBBcnJheS5mcm9tKGVsZW1lbnRzKSkge1xuICAgICAgICAgICAgICAgIGNvbnN0IHVybCA9IGVsZW1lbnQuZ2V0QXR0cmlidXRlKFwic3NlLWNvbm5lY3RcIikhO1xuICAgICAgICAgICAgICAgIGlmKHVybCAmJiAhcHJvY2Vzc2VkLmhhcyh1cmwpKSB7XG4gICAgICAgICAgICAgICAgICAgIGNvbm5lY3RFdmVudFNvdXJjZShlbGVtZW50LCB1cmwpXG4gICAgICAgICAgICAgICAgICAgIHByb2Nlc3NlZC5hZGQodXJsKVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgIH1cbn0pXG5cbmZ1bmN0aW9uIGNvbm5lY3RFdmVudFNvdXJjZShlbGU6IEVsZW1lbnQsIHVybDogc3RyaW5nKSB7XG4gICAgaWYoIXVybCkge1xuICAgICAgICByZXR1cm5cbiAgICB9XG4gICAgY29uc29sZS5pbmZvKCdDb25uZWN0aW5nIHRvIEV2ZW50U291cmNlJywgdXJsKVxuICAgIGNvbnN0IGV2ZW50U291cmNlID0gbmV3IEV2ZW50U291cmNlKHVybCk7XG5cbiAgICBldmVudFNvdXJjZS5vbm9wZW4gPSBmdW5jdGlvbihldmVudCkge1xuICAgICAgICBjb25zb2xlLmxvZygnRXZlbnRTb3VyY2Ugb3BlbjonLCBldmVudCk7XG4gICAgICAgIGh0bXgudHJpZ2dlcihlbGUsIFwiaHRteDpzc2VPcGVuXCIsIHtldmVudDogZXZlbnR9KTtcbiAgICB9XG5cbiAgICBldmVudFNvdXJjZS5vbmVycm9yID0gZnVuY3Rpb24oZXZlbnQpIHtcbiAgICAgICAgaHRteC50cmlnZ2VyKGVsZSwgXCJodG14OnNzZUVycm9yXCIsIHtldmVudDogZXZlbnR9KTtcbiAgICAgICAgaWYgKGV2ZW50U291cmNlLnJlYWR5U3RhdGUgPT0gRXZlbnRTb3VyY2UuQ0xPU0VEKSB7XG4gICAgICAgICAgICBodG14LnRyaWdnZXIoZWxlLCBcImh0bXg6c3NlQ2xvc2VcIiwge2V2ZW50OiBldmVudH0pO1xuICAgICAgICB9XG4gICAgfVxuXG4gICAgZXZlbnRTb3VyY2Uub25tZXNzYWdlID0gZnVuY3Rpb24oZXZlbnQpIHtcbiAgICAgICAgY29uc29sZS5sb2coJ0V2ZW50U291cmNlIG1lc3NhZ2U6JywgZXZlbnQuZGF0YSk7XG4gICAgICAgIGh0bXgudHJpZ2dlcihlbGUsIFwiaHRteDpzc2VCZWZvcmVNZXNzYWdlXCIsIHtldmVudDogZXZlbnR9KTtcbiAgICAgICAgY29uc3QgcmVzcG9uc2UgPSBldmVudC5kYXRhXG4gICAgICAgIGNvbnN0IGZyYWdtZW50ID0gYXBpLm1ha2VGcmFnbWVudChyZXNwb25zZSkgYXMgRG9jdW1lbnRGcmFnbWVudDtcbiAgICAgICAgY29uc3QgY2hpbGRyZW4gPSBBcnJheS5mcm9tKGZyYWdtZW50LmNoaWxkcmVuKTtcbiAgICAgICAgZm9yIChsZXQgY2hpbGQgb2YgY2hpbGRyZW4pIHtcbiAgICAgICAgICAgIGFwaS5vb2JTd2FwKGFwaS5nZXRBdHRyaWJ1dGVWYWx1ZShjaGlsZCwgJ2h4LXN3YXAtb29iJykgfHwgJ3RydWUnLCBjaGlsZCwge3Rhc2tzOiBbXX0pO1xuICAgICAgICAgICAgLy8gc3VwcG9ydCBodG1nbyBldmFsX18gc2NyaXB0c1xuICAgICAgICAgICAgaWYoY2hpbGQudGFnTmFtZSA9PT0gJ1NDUklQVCcgJiYgY2hpbGQuaWQuc3RhcnRzV2l0aChcIl9fZXZhbFwiKSkge1xuICAgICAgICAgICAgICAgIGRvY3VtZW50LmJvZHkuYXBwZW5kQ2hpbGQoY2hpbGQpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIGh0bXgudHJpZ2dlcihlbGUsIFwiaHRteDpzc2VBZnRlck1lc3NhZ2VcIiwge2V2ZW50OiBldmVudH0pO1xuICAgIH1cbn1cbiIsImltcG9ydCBodG14IGZyb20gXCJodG14Lm9yZ1wiO1xuaW1wb3J0IFwiLi9odG14ZXh0ZW5zaW9ucy9wYXRoZGVwc1wiO1xuaW1wb3J0IFwiLi9odG14ZXh0ZW5zaW9ucy90cmlnZ2VyLWNoaWxkcmVuXCI7XG5pbXBvcnQgXCIuL2h0bXhleHRlbnNpb25zL2RlYnVnXCI7XG5pbXBvcnQgXCIuL2h0bXhleHRlbnNpb25zL3Jlc3BvbnNlLXRhcmdldHNcIjtcbmltcG9ydCBcIi4vaHRteGV4dGVuc2lvbnMvbXV0YXRpb24tZXJyb3JcIjtcbmltcG9ydCBcIi4vaHRteGV4dGVuc2lvbnMvbGl2ZXJlbG9hZFwiXG5pbXBvcnQgXCIuL2h0bXhleHRlbnNpb25zL2h0bWdvXCI7XG5pbXBvcnQgXCIuL2h0bXhleHRlbnNpb25zL3NzZVwiXG5cbmZ1bmN0aW9uIHdhdGNoVXJsKGNhbGxiYWNrOiAob2xkVXJsOiBzdHJpbmcsIG5ld1VybDogc3RyaW5nKSA9PiB2b2lkKSB7XG4gIGxldCBsYXN0VXJsID0gd2luZG93LmxvY2F0aW9uLmhyZWY7XG4gIHNldEludGVydmFsKCgpID0+IHtcbiAgICBpZiAod2luZG93LmxvY2F0aW9uLmhyZWYgIT09IGxhc3RVcmwpIHtcbiAgICAgIGNhbGxiYWNrKGxhc3RVcmwsIHdpbmRvdy5sb2NhdGlvbi5ocmVmKTtcbiAgICAgIGxhc3RVcmwgPSB3aW5kb3cubG9jYXRpb24uaHJlZjtcbiAgICB9XG4gIH0sIDEwMCk7XG59XG5cbndhdGNoVXJsKChfLCBuZXdVcmwpID0+IHtcbiAgb25VcmxDaGFuZ2UobmV3VXJsKTtcbn0pO1xuXG5mdW5jdGlvbiBvblVybENoYW5nZShuZXdVcmw6IHN0cmluZykge1xuICBsZXQgdXJsID0gbmV3IFVSTChuZXdVcmwpO1xuXG4gIGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3JBbGwoXCJbaHgtdHJpZ2dlcl1cIikuZm9yRWFjaChmdW5jdGlvbiAoZWxlbWVudCkge1xuICAgIGNvbnN0IHRyaWdnZXJzID0gZWxlbWVudC5nZXRBdHRyaWJ1dGUoXCJoeC10cmlnZ2VyXCIpO1xuICAgIGlmICghdHJpZ2dlcnMpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgY29uc3Qgc3BsaXQgPSB0cmlnZ2Vycy5zcGxpdChcIiwgXCIpO1xuICAgIGlmIChzcGxpdC5maW5kKChzKSA9PiBzID09PSBcInVybFwiKSkge1xuICAgICAgaHRteC5zd2FwKGVsZW1lbnQsIFwidXJsXCIsIHtcbiAgICAgICAgc3dhcFN0eWxlOiBcIm91dGVySFRNTFwiLFxuICAgICAgICBzd2FwRGVsYXk6IDAsXG4gICAgICAgIHNldHRsZURlbGF5OiAwLFxuICAgICAgfSk7XG4gICAgfSBlbHNlIHtcbiAgICAgIGZvciAobGV0IFtrZXksIHZhbHVlc10gb2YgdXJsLnNlYXJjaFBhcmFtcykge1xuICAgICAgICBsZXQgZXZlbnROYW1lID0gXCJxczpcIiArIGtleTtcbiAgICAgICAgaWYgKHRyaWdnZXJzLmluY2x1ZGVzKGV2ZW50TmFtZSkpIHtcbiAgICAgICAgICBjb25zb2xlLmxvZyhcInRyaWdnZXJpbmdcIiwgZXZlbnROYW1lKTtcbiAgICAgICAgICBodG14LnRyaWdnZXIoZWxlbWVudCwgZXZlbnROYW1lLCBudWxsKTtcbiAgICAgICAgICBicmVhaztcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cbiAgfSk7XG5cbiAgZG9jdW1lbnQucXVlcnlTZWxlY3RvckFsbChcIltoeC1tYXRjaC1xcF1cIikuZm9yRWFjaCgoZWwpID0+IHtcbiAgICBsZXQgaGFzTWF0Y2ggPSBmYWxzZTtcbiAgICBmb3IgKGxldCBuYW1lIG9mIGVsLmdldEF0dHJpYnV0ZU5hbWVzKCkpIHtcbiAgICAgIGlmIChuYW1lLnN0YXJ0c1dpdGgoXCJoeC1tYXRjaC1xcC1tYXBwaW5nOlwiKSkge1xuICAgICAgICBsZXQgbWF0Y2ggPSBuYW1lLnJlcGxhY2UoXCJoeC1tYXRjaC1xcC1tYXBwaW5nOlwiLCBcIlwiKTtcbiAgICAgICAgbGV0IHZhbHVlID0gdXJsLnNlYXJjaFBhcmFtcy5nZXQobWF0Y2gpO1xuICAgICAgICBpZiAodmFsdWUpIHtcbiAgICAgICAgICBodG14LnN3YXAoZWwsIGVsLmdldEF0dHJpYnV0ZShuYW1lKSA/PyBcIlwiLCB7XG4gICAgICAgICAgICBzd2FwU3R5bGU6IFwiaW5uZXJIVE1MXCIsXG4gICAgICAgICAgICBzd2FwRGVsYXk6IDAsXG4gICAgICAgICAgICBzZXR0bGVEZWxheTogMCxcbiAgICAgICAgICB9KTtcbiAgICAgICAgICBoYXNNYXRjaCA9IHRydWU7XG4gICAgICAgICAgYnJlYWs7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG4gICAgaWYgKCFoYXNNYXRjaCkge1xuICAgICAgbGV0IGRlZmF1bHRLZXkgPSBlbC5nZXRBdHRyaWJ1dGUoXCJoeC1tYXRjaC1xcC1kZWZhdWx0XCIpO1xuICAgICAgaWYgKGRlZmF1bHRLZXkpIHtcbiAgICAgICAgaHRteC5zd2FwKFxuICAgICAgICAgIGVsLFxuICAgICAgICAgIGVsLmdldEF0dHJpYnV0ZShcImh4LW1hdGNoLXFwLW1hcHBpbmc6XCIgKyBkZWZhdWx0S2V5KSA/PyBcIlwiLFxuICAgICAgICAgIHsgc3dhcFN0eWxlOiBcImlubmVySFRNTFwiLCBzd2FwRGVsYXk6IDAsIHNldHRsZURlbGF5OiAwIH0sXG4gICAgICAgICk7XG4gICAgICB9XG4gICAgfVxuICB9KTtcbn1cbiJdfQ== \ No newline at end of file +var ne=function(){let htmx={onLoad:null,process:null,on:null,off:null,trigger:null,ajax:null,find:null,findAll:null,closest:null,values:function(e,t){return getInputValues(e,t||"post").values},remove:null,addClass:null,removeClass:null,toggleClass:null,takeClass:null,swap:null,defineExtension:null,removeExtension:null,logAll:null,logNone:null,logger:null,config:{historyEnabled:!0,historyCacheSize:10,refreshOnHistoryMiss:!1,defaultSwapStyle:"innerHTML",defaultSwapDelay:0,defaultSettleDelay:20,includeIndicatorStyles:!0,indicatorClass:"htmx-indicator",requestClass:"htmx-request",addedClass:"htmx-added",settlingClass:"htmx-settling",swappingClass:"htmx-swapping",allowEval:!0,allowScriptTags:!0,inlineScriptNonce:"",inlineStyleNonce:"",attributesToSettle:["class","style","width","height"],withCredentials:!1,timeout:0,wsReconnectDelay:"full-jitter",wsBinaryType:"blob",disableSelector:"[hx-disable], [data-hx-disable]",scrollBehavior:"instant",defaultFocusScroll:!1,getCacheBusterParam:!1,globalViewTransitions:!1,methodsThatUseUrlParams:["get","delete"],selfRequestsOnly:!0,ignoreTitle:!1,scrollIntoViewOnBoost:!0,triggerSpecsCache:null,disableInheritance:!1,responseHandling:[{code:"204",swap:!1},{code:"[23]..",swap:!0},{code:"[45]..",swap:!1,error:!0}],allowNestedOobSwaps:!0},parseInterval:null,_:null,version:"2.0.2"};htmx.onLoad=onLoadHelper,htmx.process=processNode,htmx.on=addEventListenerImpl,htmx.off=removeEventListenerImpl,htmx.trigger=triggerEvent,htmx.ajax=ajaxHelper,htmx.find=find,htmx.findAll=findAll,htmx.closest=closest,htmx.remove=removeElement,htmx.addClass=addClassToElement,htmx.removeClass=removeClassFromElement,htmx.toggleClass=toggleClassOnElement,htmx.takeClass=takeClassForElement,htmx.swap=swap,htmx.defineExtension=defineExtension,htmx.removeExtension=removeExtension,htmx.logAll=logAll,htmx.logNone=logNone,htmx.parseInterval=parseInterval,htmx._=internalEval;let internalAPI={addTriggerHandler,bodyContains,canAccessLocalStorage,findThisElement,filterValues,swap,hasAttribute,getAttributeValue,getClosestAttributeValue,getClosestMatch,getExpressionVars,getHeaders,getInputValues,getInternalData,getSwapSpecification,getTriggerSpecs,getTarget,makeFragment,mergeObjects,makeSettleInfo,oobSwap,querySelectorExt,settleImmediately,shouldCancel,triggerEvent,triggerErrorEvent,withExtensions},VERBS=["get","post","put","delete","patch"],VERB_SELECTOR=VERBS.map(function(e){return "[hx-"+e+"], [data-hx-"+e+"]"}).join(", "),HEAD_TAG_REGEX=makeTagRegEx("head");function makeTagRegEx(e,t=!1){return new RegExp(`<${e}(\\s[^>]*>|>)([\\s\\S]*?)<\\/${e}>`,t?"gim":"im")}function parseInterval(e){if(e==null)return;let t=NaN;return e.slice(-2)=="ms"?t=parseFloat(e.slice(0,-2)):e.slice(-1)=="s"?t=parseFloat(e.slice(0,-1))*1e3:e.slice(-1)=="m"?t=parseFloat(e.slice(0,-1))*1e3*60:t=parseFloat(e),isNaN(t)?void 0:t}function getRawAttribute(e,t){return e instanceof Element&&e.getAttribute(t)}function hasAttribute(e,t){return !!e.hasAttribute&&(e.hasAttribute(t)||e.hasAttribute("data-"+t))}function getAttributeValue(e,t){return getRawAttribute(e,t)||getRawAttribute(e,"data-"+t)}function parentElt(e){let t=e.parentElement;return !t&&e.parentNode instanceof ShadowRoot?e.parentNode:t}function getDocument(){return document}function getRootNode(e,t){return e.getRootNode?e.getRootNode({composed:t}):getDocument()}function getClosestMatch(e,t){for(;e&&!t(e);)e=parentElt(e);return e||null}function getAttributeValueWithDisinheritance(e,t,n){let r=getAttributeValue(t,n),o=getAttributeValue(t,"hx-disinherit");var i=getAttributeValue(t,"hx-inherit");if(e!==t){if(htmx.config.disableInheritance)return i&&(i==="*"||i.split(" ").indexOf(n)>=0)?r:null;if(o&&(o==="*"||o.split(" ").indexOf(n)>=0))return "unset"}return r}function getClosestAttributeValue(e,t){let n=null;if(getClosestMatch(e,function(r){return !!(n=getAttributeValueWithDisinheritance(e,asElement(r),t))}),n!=="unset")return n}function matches(e,t){let n=e instanceof Element&&(e.matches||e.matchesSelector||e.msMatchesSelector||e.mozMatchesSelector||e.webkitMatchesSelector||e.oMatchesSelector);return !!n&&n.call(e,t)}function getStartTag(e){let n=/<([a-z][^\/\0>\x20\t\r\n\f]*)/i.exec(e);return n?n[1].toLowerCase():""}function parseHTML(e){return new DOMParser().parseFromString(e,"text/html")}function takeChildrenFor(e,t){for(;t.childNodes.length>0;)e.append(t.childNodes[0]);}function duplicateScript(e){let t=getDocument().createElement("script");return forEach(e.attributes,function(n){t.setAttribute(n.name,n.value);}),t.textContent=e.textContent,t.async=!1,htmx.config.inlineScriptNonce&&(t.nonce=htmx.config.inlineScriptNonce),t}function isJavaScriptScriptNode(e){return e.matches("script")&&(e.type==="text/javascript"||e.type==="module"||e.type==="")}function normalizeScriptTags(e){Array.from(e.querySelectorAll("script")).forEach(t=>{if(isJavaScriptScriptNode(t)){let n=duplicateScript(t),r=t.parentNode;try{r.insertBefore(n,t);}catch(o){logError(o);}finally{t.remove();}}});}function makeFragment(e){let t=e.replace(HEAD_TAG_REGEX,""),n=getStartTag(t),r;if(n==="html"){r=new DocumentFragment;let i=parseHTML(e);takeChildrenFor(r,i.body),r.title=i.title;}else if(n==="body"){r=new DocumentFragment;let i=parseHTML(t);takeChildrenFor(r,i.body),r.title=i.title;}else {let i=parseHTML('");r=i.querySelector("template").content,r.title=i.title;var o=r.querySelector("title");o&&o.parentNode===r&&(o.remove(),r.title=o.innerText);}return r&&(htmx.config.allowScriptTags?normalizeScriptTags(r):r.querySelectorAll("script").forEach(i=>i.remove())),r}function maybeCall(e){e&&e();}function isType(e,t){return Object.prototype.toString.call(e)==="[object "+t+"]"}function isFunction(e){return typeof e=="function"}function isRawObject(e){return isType(e,"Object")}function getInternalData(e){let t="htmx-internal-data",n=e[t];return n||(n=e[t]={}),n}function toArray(e){let t=[];if(e)for(let n=0;n=0}function bodyContains(e){let t=e.getRootNode&&e.getRootNode();return t&&t instanceof window.ShadowRoot?getDocument().body.contains(t.host):getDocument().body.contains(e)}function splitOnWhitespace(e){return e.trim().split(/\s+/)}function mergeObjects(e,t){for(let n in t)t.hasOwnProperty(n)&&(e[n]=t[n]);return e}function parseJSON(e){try{return JSON.parse(e)}catch(t){return logError(t),null}}function canAccessLocalStorage(){let e="htmx:localStorageTest";try{return localStorage.setItem(e,e),localStorage.removeItem(e),!0}catch{return !1}}function normalizePath(e){try{let t=new URL(e);return t&&(e=t.pathname+t.search),/^\/$/.test(e)||(e=e.replace(/\/+$/,"")),e}catch{return e}}function internalEval(str){return maybeEval(getDocument().body,function(){return eval(str)})}function onLoadHelper(e){return htmx.on("htmx:load",function(n){e(n.detail.elt);})}function logAll(){htmx.logger=function(e,t,n){console&&console.log(t,e,n);};}function logNone(){htmx.logger=null;}function find(e,t){return typeof e!="string"?e.querySelector(t):find(getDocument(),e)}function findAll(e,t){return typeof e!="string"?e.querySelectorAll(t):findAll(getDocument(),e)}function getWindow(){return window}function removeElement(e,t){e=resolveTarget(e),t?getWindow().setTimeout(function(){removeElement(e),e=null;},t):parentElt(e).removeChild(e);}function asElement(e){return e instanceof Element?e:null}function asHtmlElement(e){return e instanceof HTMLElement?e:null}function asString(e){return typeof e=="string"?e:null}function asParentNode(e){return e instanceof Element||e instanceof Document||e instanceof DocumentFragment?e:null}function addClassToElement(e,t,n){e=asElement(resolveTarget(e)),e&&(n?getWindow().setTimeout(function(){addClassToElement(e,t),e=null;},n):e.classList&&e.classList.add(t));}function removeClassFromElement(e,t,n){let r=asElement(resolveTarget(e));r&&(n?getWindow().setTimeout(function(){removeClassFromElement(r,t),r=null;},n):r.classList&&(r.classList.remove(t),r.classList.length===0&&r.removeAttribute("class")));}function toggleClassOnElement(e,t){e=resolveTarget(e),e.classList.toggle(t);}function takeClassForElement(e,t){e=resolveTarget(e),forEach(e.parentElement.children,function(n){removeClassFromElement(n,t);}),addClassToElement(asElement(e),t);}function closest(e,t){if(e=asElement(resolveTarget(e)),e&&e.closest)return e.closest(t);do if(e==null||matches(e,t))return e;while(e=e&&asElement(parentElt(e)));return null}function startsWith(e,t){return e.substring(0,t.length)===t}function endsWith(e,t){return e.substring(e.length-t.length)===t}function normalizeSelector(e){let t=e.trim();return startsWith(t,"<")&&endsWith(t,"/>")?t.substring(1,t.length-2):t}function querySelectorAllExt(e,t,n){return e=resolveTarget(e),t.indexOf("closest ")===0?[closest(asElement(e),normalizeSelector(t.substr(8)))]:t.indexOf("find ")===0?[find(asParentNode(e),normalizeSelector(t.substr(5)))]:t==="next"?[asElement(e).nextElementSibling]:t.indexOf("next ")===0?[scanForwardQuery(e,normalizeSelector(t.substr(5)),!!n)]:t==="previous"?[asElement(e).previousElementSibling]:t.indexOf("previous ")===0?[scanBackwardsQuery(e,normalizeSelector(t.substr(9)),!!n)]:t==="document"?[document]:t==="window"?[window]:t==="body"?[document.body]:t==="root"?[getRootNode(e,!!n)]:t.indexOf("global ")===0?querySelectorAllExt(e,t.slice(7),!0):toArray(asParentNode(getRootNode(e,!!n)).querySelectorAll(normalizeSelector(t)))}var scanForwardQuery=function(e,t,n){let r=asParentNode(getRootNode(e,n)).querySelectorAll(t);for(let o=0;o=0;o--){let i=r[o];if(i.compareDocumentPosition(e)===Node.DOCUMENT_POSITION_FOLLOWING)return i}};function querySelectorExt(e,t){return typeof e!="string"?querySelectorAllExt(e,t)[0]:querySelectorAllExt(getDocument().body,e)[0]}function resolveTarget(e,t){return typeof e=="string"?find(asParentNode(t)||document,e):e}function processEventArgs(e,t,n){return isFunction(t)?{target:getDocument().body,event:asString(e),listener:t}:{target:resolveTarget(e),event:asString(t),listener:n}}function addEventListenerImpl(e,t,n){return ready(function(){let o=processEventArgs(e,t,n);o.target.addEventListener(o.event,o.listener);}),isFunction(t)?t:n}function removeEventListenerImpl(e,t,n){return ready(function(){let r=processEventArgs(e,t,n);r.target.removeEventListener(r.event,r.listener);}),isFunction(t)?t:n}let DUMMY_ELT=getDocument().createElement("output");function findAttributeTargets(e,t){let n=getClosestAttributeValue(e,t);if(n){if(n==="this")return [findThisElement(e,t)];{let r=querySelectorAllExt(e,n);return r.length===0?(logError('The selector "'+n+'" on '+t+" returned no matches!"),[DUMMY_ELT]):r}}}function findThisElement(e,t){return asElement(getClosestMatch(e,function(n){return getAttributeValue(asElement(n),t)!=null}))}function getTarget(e){let t=getClosestAttributeValue(e,"hx-target");return t?t==="this"?findThisElement(e,"hx-target"):querySelectorExt(e,t):getInternalData(e).boosted?getDocument().body:e}function shouldSettleAttribute(e){let t=htmx.config.attributesToSettle;for(let n=0;n0?(o=e.substr(0,e.indexOf(":")),r=e.substr(e.indexOf(":")+1,e.length)):o=e);let i=getDocument().querySelectorAll(r);return i?(forEach(i,function(s){let l,a=t.cloneNode(!0);l=getDocument().createDocumentFragment(),l.appendChild(a),isInlineSwap(o,s)||(l=asParentNode(a));let u={shouldSwap:!0,target:s,fragment:l};triggerEvent(s,"htmx:oobBeforeSwap",u)&&(s=u.target,u.shouldSwap&&swapWithStyle(o,s,s,l,n),forEach(n.elts,function(f){triggerEvent(f,"htmx:oobAfterSwap",u);}));}),t.parentNode.removeChild(t)):(t.parentNode.removeChild(t),triggerErrorEvent(getDocument().body,"htmx:oobErrorNoTarget",{content:t})),e}function handlePreservedElements(e){forEach(findAll(e,"[hx-preserve], [data-hx-preserve]"),function(t){let n=getAttributeValue(t,"id"),r=getDocument().getElementById(n);r!=null&&t.parentNode.replaceChild(r,t);});}function handleAttributes(e,t,n){forEach(t.querySelectorAll("[id]"),function(r){let o=getRawAttribute(r,"id");if(o&&o.length>0){let i=o.replace("'","\\'"),s=r.tagName.replace(":","\\:"),l=asParentNode(e),a=l&&l.querySelector(s+"[id='"+i+"']");if(a&&a!==l){let u=r.cloneNode();cloneAttributes(r,a),n.tasks.push(function(){cloneAttributes(r,u);});}}});}function makeAjaxLoadTask(e){return function(){removeClassFromElement(e,htmx.config.addedClass),processNode(asElement(e)),processFocus(asParentNode(e)),triggerEvent(e,"htmx:load");}}function processFocus(e){let t="[autofocus]",n=asHtmlElement(matches(e,t)?e:e.querySelector(t));n?.focus();}function insertNodesBefore(e,t,n,r){for(handleAttributes(e,n,r);n.childNodes.length>0;){let o=n.firstChild;addClassToElement(asElement(o),htmx.config.addedClass),e.insertBefore(o,t),o.nodeType!==Node.TEXT_NODE&&o.nodeType!==Node.COMMENT_NODE&&r.tasks.push(makeAjaxLoadTask(o));}}function stringHash(e,t){let n=0;for(;n0}function swap(e,t,n,r){r||(r={}),e=resolveTarget(e);let o=document.activeElement,i={};try{i={elt:o,start:o?o.selectionStart:null,end:o?o.selectionEnd:null};}catch{}let s=makeSettleInfo(e);if(n.swapStyle==="textContent")e.textContent=t;else {let a=makeFragment(t);if(s.title=a.title,r.selectOOB){let u=r.selectOOB.split(",");for(let f=0;f0?getWindow().setTimeout(l,n.settleDelay):l();}function handleTriggerHeader(e,t,n){let r=e.getResponseHeader(t);if(r.indexOf("{")===0){let o=parseJSON(r);for(let i in o)if(o.hasOwnProperty(i)){let s=o[i];isRawObject(s)?n=s.target!==void 0?s.target:n:s={value:s},triggerEvent(n,i,s);}}else {let o=r.split(",");for(let i=0;i0;){let s=t[0];if(s==="]"){if(r--,r===0){i===null&&(o=o+"true"),t.shift(),o+=")})";try{let l=maybeEval(e,function(){return Function(o)()},function(){return !0});return l.source=o,l}catch(l){return triggerErrorEvent(getDocument().body,"htmx:syntax:error",{error:l,source:o}),null}}}else s==="["&&r++;isPossibleRelativeReference(s,i,n)?o+="(("+n+"."+s+") ? ("+n+"."+s+") : (window."+s+"))":o=o+s,i=t.shift();}}}function consumeUntil(e,t){let n="";for(;e.length>0&&!t.test(e[0]);)n+=e.shift();return n}function consumeCSSSelector(e){let t;return e.length>0&&COMBINED_SELECTOR_START.test(e[0])?(e.shift(),t=consumeUntil(e,COMBINED_SELECTOR_END).trim(),e.shift()):t=consumeUntil(e,WHITESPACE_OR_COMMA),t}let INPUT_SELECTOR="input, textarea, select";function parseAndCacheTrigger(e,t,n){let r=[],o=tokenizeString(t);do{consumeUntil(o,NOT_WHITESPACE);let l=o.length,a=consumeUntil(o,/[,\[\s]/);if(a!=="")if(a==="every"){let u={trigger:"every"};consumeUntil(o,NOT_WHITESPACE),u.pollInterval=parseInterval(consumeUntil(o,/[,\[\s]/)),consumeUntil(o,NOT_WHITESPACE);var i=maybeGenerateConditional(e,o,"event");i&&(u.eventFilter=i),r.push(u);}else {let u={trigger:a};var i=maybeGenerateConditional(e,o,"event");for(i&&(u.eventFilter=i);o.length>0&&o[0]!==",";){consumeUntil(o,NOT_WHITESPACE);let c=o.shift();if(c==="changed")u.changed=!0;else if(c==="once")u.once=!0;else if(c==="consume")u.consume=!0;else if(c==="delay"&&o[0]===":")o.shift(),u.delay=parseInterval(consumeUntil(o,WHITESPACE_OR_COMMA));else if(c==="from"&&o[0]===":"){if(o.shift(),COMBINED_SELECTOR_START.test(o[0]))var s=consumeCSSSelector(o);else {var s=consumeUntil(o,WHITESPACE_OR_COMMA);if(s==="closest"||s==="find"||s==="next"||s==="previous"){o.shift();let b=consumeCSSSelector(o);b.length>0&&(s+=" "+b);}}u.from=s;}else c==="target"&&o[0]===":"?(o.shift(),u.target=consumeCSSSelector(o)):c==="throttle"&&o[0]===":"?(o.shift(),u.throttle=parseInterval(consumeUntil(o,WHITESPACE_OR_COMMA))):c==="queue"&&o[0]===":"?(o.shift(),u.queue=consumeUntil(o,WHITESPACE_OR_COMMA)):c==="root"&&o[0]===":"?(o.shift(),u[c]=consumeCSSSelector(o)):c==="threshold"&&o[0]===":"?(o.shift(),u[c]=consumeUntil(o,WHITESPACE_OR_COMMA)):triggerErrorEvent(e,"htmx:syntax:error",{token:o.shift()});}r.push(u);}o.length===l&&triggerErrorEvent(e,"htmx:syntax:error",{token:o.shift()}),consumeUntil(o,NOT_WHITESPACE);}while(o[0]===","&&o.shift());return n&&(n[t]=r),r}function getTriggerSpecs(e){let t=getAttributeValue(e,"hx-trigger"),n=[];if(t){let r=htmx.config.triggerSpecsCache;n=r&&r[t]||parseAndCacheTrigger(e,t,r);}return n.length>0?n:matches(e,"form")?[{trigger:"submit"}]:matches(e,'input[type="button"], input[type="submit"]')?[{trigger:"click"}]:matches(e,INPUT_SELECTOR)?[{trigger:"change"}]:[{trigger:"click"}]}function cancelPolling(e){getInternalData(e).cancelled=!0;}function processPolling(e,t,n){let r=getInternalData(e);r.timeout=getWindow().setTimeout(function(){bodyContains(e)&&r.cancelled!==!0&&(maybeFilterEvent(n,e,makeEvent("hx:poll:trigger",{triggerSpec:n,target:e}))||t(e),processPolling(e,t,n));},n.pollInterval);}function isLocalLink(e){return location.hostname===e.hostname&&getRawAttribute(e,"href")&&getRawAttribute(e,"href").indexOf("#")!==0}function eltIsDisabled(e){return closest(e,htmx.config.disableSelector)}function boostElement(e,t,n){if(e instanceof HTMLAnchorElement&&isLocalLink(e)&&(e.target===""||e.target==="_self")||e.tagName==="FORM"&&String(getRawAttribute(e,"method")).toLowerCase()!=="dialog"){t.boosted=!0;let r,o;if(e.tagName==="A")r="get",o=getRawAttribute(e,"href");else {let i=getRawAttribute(e,"method");r=i?i.toLowerCase():"get",o=getRawAttribute(e,"action");}n.forEach(function(i){addEventListener(e,function(s,l){let a=asElement(s);if(eltIsDisabled(a)){cleanUpElement(a);return}issueAjaxRequest(r,o,a,l);},t,i,!0);});}}function shouldCancel(e,t){let n=asElement(t);return n?!!((e.type==="submit"||e.type==="click")&&(n.tagName==="FORM"||matches(n,'input[type="submit"], button')&&closest(n,"form")!==null||n instanceof HTMLAnchorElement&&n.href&&(n.getAttribute("href")==="#"||n.getAttribute("href").indexOf("#")!==0))):!1}function ignoreBoostedAnchorCtrlClick(e,t){return getInternalData(e).boosted&&e instanceof HTMLAnchorElement&&t.type==="click"&&(t.ctrlKey||t.metaKey)}function maybeFilterEvent(e,t,n){let r=e.eventFilter;if(r)try{return r.call(t,n)!==!0}catch(o){let i=r.source;return triggerErrorEvent(getDocument().body,"htmx:eventFilter:error",{error:o,source:i}),!0}return !1}function addEventListener(e,t,n,r,o){let i=getInternalData(e),s;r.from?s=querySelectorAllExt(e,r.from):s=[e],r.changed&&s.forEach(function(l){let a=getInternalData(l);a.lastValue=l.value;}),forEach(s,function(l){let a=function(u){if(!bodyContains(e)){l.removeEventListener(r.trigger,a);return}if(ignoreBoostedAnchorCtrlClick(e,u)||((o||shouldCancel(u,e))&&u.preventDefault(),maybeFilterEvent(r,e,u)))return;let f=getInternalData(u);if(f.triggerSpec=r,f.handledFor==null&&(f.handledFor=[]),f.handledFor.indexOf(e)<0){if(f.handledFor.push(e),r.consume&&u.stopPropagation(),r.target&&u.target&&!matches(asElement(u.target),r.target))return;if(r.once){if(i.triggeredOnce)return;i.triggeredOnce=!0;}if(r.changed){let c=getInternalData(l),d=l.value;if(c.lastValue===d)return;c.lastValue=d;}if(i.delayed&&clearTimeout(i.delayed),i.throttle)return;r.throttle>0?i.throttle||(triggerEvent(e,"htmx:trigger"),t(e,u),i.throttle=getWindow().setTimeout(function(){i.throttle=null;},r.throttle)):r.delay>0?i.delayed=getWindow().setTimeout(function(){triggerEvent(e,"htmx:trigger"),t(e,u);},r.delay):(triggerEvent(e,"htmx:trigger"),t(e,u));}};n.listenerInfos==null&&(n.listenerInfos=[]),n.listenerInfos.push({trigger:r.trigger,listener:a,on:l}),l.addEventListener(r.trigger,a);});}let windowIsScrolling=!1,scrollHandler=null;function initScrollHandler(){scrollHandler||(scrollHandler=function(){windowIsScrolling=!0;},window.addEventListener("scroll",scrollHandler),setInterval(function(){windowIsScrolling&&(windowIsScrolling=!1,forEach(getDocument().querySelectorAll("[hx-trigger*='revealed'],[data-hx-trigger*='revealed']"),function(e){maybeReveal(e);}));},200));}function maybeReveal(e){!hasAttribute(e,"data-hx-revealed")&&isScrolledIntoView(e)&&(e.setAttribute("data-hx-revealed","true"),getInternalData(e).initHash?triggerEvent(e,"revealed"):e.addEventListener("htmx:afterProcessNode",function(){triggerEvent(e,"revealed");},{once:!0}));}function loadImmediately(e,t,n,r){let o=function(){n.loaded||(n.loaded=!0,t(e));};r>0?getWindow().setTimeout(o,r):o();}function processVerbs(e,t,n){let r=!1;return forEach(VERBS,function(o){if(hasAttribute(e,"hx-"+o)){let i=getAttributeValue(e,"hx-"+o);r=!0,t.path=i,t.verb=o,n.forEach(function(s){addTriggerHandler(e,s,t,function(l,a){let u=asElement(l);if(closest(u,htmx.config.disableSelector)){cleanUpElement(u);return}issueAjaxRequest(o,i,u,a);});});}}),r}function addTriggerHandler(e,t,n,r){if(t.trigger==="revealed")initScrollHandler(),addEventListener(e,r,n,t),maybeReveal(asElement(e));else if(t.trigger==="intersect"){let o={};t.root&&(o.root=querySelectorExt(e,t.root)),t.threshold&&(o.threshold=parseFloat(t.threshold)),new IntersectionObserver(function(s){for(let l=0;l0?(n.polling=!0,processPolling(asElement(e),r,t)):addEventListener(e,r,n,t);}function shouldProcessHxOn(e){let t=asElement(e);if(!t)return !1;let n=t.attributes;for(let r=0;r", "+i).join(""))}else return []}function maybeSetLastButtonClicked(e){let t=closest(asElement(e.target),"button, input[type='submit']"),n=getRelatedFormData(e);n&&(n.lastButtonClicked=t);}function maybeUnsetLastButtonClicked(e){let t=getRelatedFormData(e);t&&(t.lastButtonClicked=null);}function getRelatedFormData(e){let t=closest(asElement(e.target),"button, input[type='submit']");if(!t)return;let n=resolveTarget("#"+getRawAttribute(t,"form"),t.getRootNode())||closest(t,"form");if(n)return getInternalData(n)}function initButtonTracking(e){e.addEventListener("click",maybeSetLastButtonClicked),e.addEventListener("focusin",maybeSetLastButtonClicked),e.addEventListener("focusout",maybeUnsetLastButtonClicked);}function addHxOnEventHandler(e,t,n){let r=getInternalData(e);Array.isArray(r.onHandlers)||(r.onHandlers=[]);let o,i=function(s){maybeEval(e,function(){eltIsDisabled(e)||(o||(o=new Function("event",n)),o.call(e,s));});};e.addEventListener(t,i),r.onHandlers.push({event:t,listener:i});}function processHxOnWildcard(e){deInitOnHandlers(e);for(let t=0;thtmx.config.historyCacheSize;)i.shift();for(;i.length>0;)try{localStorage.setItem("htmx-history-cache",JSON.stringify(i));break}catch(l){triggerErrorEvent(getDocument().body,"htmx:historyCacheError",{cause:l,cache:i}),i.shift();}}function getCachedHistory(e){if(!canAccessLocalStorage())return null;e=normalizePath(e);let t=parseJSON(localStorage.getItem("htmx-history-cache"))||[];for(let n=0;n=200&&this.status<400){triggerEvent(getDocument().body,"htmx:historyCacheMissLoad",n);let r=makeFragment(this.response),o=r.querySelector("[hx-history-elt],[data-hx-history-elt]")||r,i=getHistoryElement(),s=makeSettleInfo(i);handleTitle(r.title),swapInnerHTML(i,o,s),settleImmediately(s.tasks),currentPathForHistory=e,triggerEvent(getDocument().body,"htmx:historyRestore",{path:e,cacheMiss:!0,serverResponse:this.response});}else triggerErrorEvent(getDocument().body,"htmx:historyCacheMissLoadError",n);},t.send();}function restoreHistory(e){saveCurrentPageToHistory(),e=e||location.pathname+location.search;let t=getCachedHistory(e);if(t){let n=makeFragment(t.content),r=getHistoryElement(),o=makeSettleInfo(r);handleTitle(n.title),swapInnerHTML(r,n,o),settleImmediately(o.tasks),getWindow().setTimeout(function(){window.scrollTo(0,t.scroll);},0),currentPathForHistory=e,triggerEvent(getDocument().body,"htmx:historyRestore",{path:e,item:t});}else htmx.config.refreshOnHistoryMiss?window.location.reload(!0):loadHistoryFromServer(e);}function addRequestIndicatorClasses(e){let t=findAttributeTargets(e,"hx-indicator");return t==null&&(t=[e]),forEach(t,function(n){let r=getInternalData(n);r.requestCount=(r.requestCount||0)+1,n.classList.add.call(n.classList,htmx.config.requestClass);}),t}function disableElements(e){let t=findAttributeTargets(e,"hx-disabled-elt");return t==null&&(t=[]),forEach(t,function(n){let r=getInternalData(n);r.requestCount=(r.requestCount||0)+1,n.setAttribute("disabled",""),n.setAttribute("data-disabled-by-htmx","");}),t}function removeRequestIndicators(e,t){forEach(e,function(n){let r=getInternalData(n);r.requestCount=(r.requestCount||0)-1,r.requestCount===0&&n.classList.remove.call(n.classList,htmx.config.requestClass);}),forEach(t,function(n){let r=getInternalData(n);r.requestCount=(r.requestCount||0)-1,r.requestCount===0&&(n.removeAttribute("disabled"),n.removeAttribute("data-disabled-by-htmx"));});}function haveSeenNode(e,t){for(let n=0;nt.indexOf(o)<0):r=r.filter(o=>o!==t),n.delete(e),forEach(r,o=>n.append(e,o));}}function processInputValue(e,t,n,r,o){if(!(r==null||haveSeenNode(e,r))){if(e.push(r),shouldInclude(r)){let i=getRawAttribute(r,"name"),s=r.value;r instanceof HTMLSelectElement&&r.multiple&&(s=toArray(r.querySelectorAll("option:checked")).map(function(l){return l.value})),r instanceof HTMLInputElement&&r.files&&(s=toArray(r.files)),addValueToFormData(i,s,t),o&&validateElement(r,n);}r instanceof HTMLFormElement&&(forEach(r.elements,function(i){e.indexOf(i)>=0?removeValueFromFormData(i.name,i.value,t):e.push(i),o&&validateElement(i,n);}),new FormData(r).forEach(function(i,s){i instanceof File&&i.name===""||addValueToFormData(s,i,t);}));}}function validateElement(e,t){let n=e;n.willValidate&&(triggerEvent(n,"htmx:validation:validate"),n.checkValidity()||(t.push({elt:n,message:n.validationMessage,validity:n.validity}),triggerEvent(n,"htmx:validation:failed",{message:n.validationMessage,validity:n.validity})));}function overrideFormData(e,t){for(let n of t.keys())e.delete(n);return t.forEach(function(n,r){e.append(r,n);}),e}function getInputValues(e,t){let n=[],r=new FormData,o=new FormData,i=[],s=getInternalData(e);s.lastButtonClicked&&!bodyContains(s.lastButtonClicked)&&(s.lastButtonClicked=null);let l=e instanceof HTMLFormElement&&e.noValidate!==!0||getAttributeValue(e,"hx-validate")==="true";if(s.lastButtonClicked&&(l=l&&s.lastButtonClicked.formNoValidate!==!0),t!=="get"&&processInputValue(n,o,i,closest(e,"form"),l),processInputValue(n,r,i,e,l),s.lastButtonClicked||e.tagName==="BUTTON"||e.tagName==="INPUT"&&getRawAttribute(e,"type")==="submit"){let u=s.lastButtonClicked||e,f=getRawAttribute(u,"name");addValueToFormData(f,u.value,o);}let a=findAttributeTargets(e,"hx-include");return forEach(a,function(u){processInputValue(n,r,i,asElement(u),l),matches(u,"form")||forEach(asParentNode(u).querySelectorAll(INPUT_SELECTOR),function(f){processInputValue(n,r,i,f,l);});}),overrideFormData(r,o),{errors:i,formData:r,values:formDataProxy(r)}}function appendParam(e,t,n){e!==""&&(e+="&"),String(n)==="[object Object]"&&(n=JSON.stringify(n));let r=encodeURIComponent(n);return e+=encodeURIComponent(t)+"="+r,e}function urlEncode(e){e=formDataFromObject(e);let t="";return e.forEach(function(n,r){t=appendParam(t,r,n);}),t}function getHeaders(e,t,n){let r={"HX-Request":"true","HX-Trigger":getRawAttribute(e,"id"),"HX-Trigger-Name":getRawAttribute(e,"name"),"HX-Target":getAttributeValue(t,"id"),"HX-Current-URL":getDocument().location.href};return getValuesForElement(e,"hx-headers",!1,r),n!==void 0&&(r["HX-Prompt"]=n),getInternalData(e).boosted&&(r["HX-Boosted"]="true"),r}function filterValues(e,t){let n=getClosestAttributeValue(t,"hx-params");if(n){if(n==="none")return new FormData;if(n==="*")return e;if(n.indexOf("not ")===0)return forEach(n.substr(4).split(","),function(r){r=r.trim(),e.delete(r);}),e;{let r=new FormData;return forEach(n.split(","),function(o){o=o.trim(),e.has(o)&&e.getAll(o).forEach(function(i){r.append(o,i);});}),r}}else return e}function isAnchorLink(e){return !!getRawAttribute(e,"href")&&getRawAttribute(e,"href").indexOf("#")>=0}function getSwapSpecification(e,t){let n=t||getClosestAttributeValue(e,"hx-swap"),r={swapStyle:getInternalData(e).boosted?"innerHTML":htmx.config.defaultSwapStyle,swapDelay:htmx.config.defaultSwapDelay,settleDelay:htmx.config.defaultSettleDelay};if(htmx.config.scrollIntoViewOnBoost&&getInternalData(e).boosted&&!isAnchorLink(e)&&(r.show="top"),n){let s=splitOnWhitespace(n);if(s.length>0)for(let l=0;l0?o.join(":"):null;r.scroll=f,r.scrollTarget=i;}else if(a.indexOf("show:")===0){var o=a.substr(5).split(":");let c=o.pop();var i=o.length>0?o.join(":"):null;r.show=c,r.showTarget=i;}else if(a.indexOf("focus-scroll:")===0){let u=a.substr(13);r.focusScroll=u=="true";}else l==0?r.swapStyle=a:logError("Unknown modifier in hx-swap: "+a);}}return r}function usesFormData(e){return getClosestAttributeValue(e,"hx-encoding")==="multipart/form-data"||matches(e,"form")&&getRawAttribute(e,"enctype")==="multipart/form-data"}function encodeParamsForBody(e,t,n){let r=null;return withExtensions(t,function(o){r==null&&(r=o.encodeParameters(e,n,t));}),r??(usesFormData(t)?overrideFormData(new FormData,formDataFromObject(n)):urlEncode(n))}function makeSettleInfo(e){return {tasks:[],elts:[e]}}function updateScrollState(e,t){let n=e[0],r=e[e.length-1];if(t.scroll){var o=null;t.scrollTarget&&(o=asElement(querySelectorExt(n,t.scrollTarget))),t.scroll==="top"&&(n||o)&&(o=o||n,o.scrollTop=0),t.scroll==="bottom"&&(r||o)&&(o=o||r,o.scrollTop=o.scrollHeight);}if(t.show){var o=null;if(t.showTarget){let s=t.showTarget;t.showTarget==="window"&&(s="body"),o=asElement(querySelectorExt(n,s));}t.show==="top"&&(n||o)&&(o=o||n,o.scrollIntoView({block:"start",behavior:htmx.config.scrollBehavior})),t.show==="bottom"&&(r||o)&&(o=o||r,o.scrollIntoView({block:"end",behavior:htmx.config.scrollBehavior}));}}function getValuesForElement(e,t,n,r){if(r==null&&(r={}),e==null)return r;let o=getAttributeValue(e,t);if(o){let i=o.trim(),s=n;if(i==="unset")return null;i.indexOf("javascript:")===0?(i=i.substr(11),s=!0):i.indexOf("js:")===0&&(i=i.substr(3),s=!0),i.indexOf("{")!==0&&(i="{"+i+"}");let l;s?l=maybeEval(e,function(){return Function("return ("+i+")")()},{}):l=parseJSON(i);for(let a in l)l.hasOwnProperty(a)&&r[a]==null&&(r[a]=l[a]);}return getValuesForElement(asElement(parentElt(e)),t,n,r)}function maybeEval(e,t,n){return htmx.config.allowEval?t():(triggerErrorEvent(e,"htmx:evalDisallowedError"),n)}function getHXVarsForElement(e,t){return getValuesForElement(e,"hx-vars",!0,t)}function getHXValsForElement(e,t){return getValuesForElement(e,"hx-vals",!1,t)}function getExpressionVars(e){return mergeObjects(getHXVarsForElement(e),getHXValsForElement(e))}function safelySetHeaderValue(e,t,n){if(n!==null)try{e.setRequestHeader(t,n);}catch{e.setRequestHeader(t,encodeURIComponent(n)),e.setRequestHeader(t+"-URI-AutoEncoded","true");}}function getPathFromResponse(e){if(e.responseURL&&typeof URL<"u")try{let t=new URL(e.responseURL);return t.pathname+t.search}catch{triggerErrorEvent(getDocument().body,"htmx:badResponseUrl",{url:e.responseURL});}}function hasHeader(e,t){return t.test(e.getAllResponseHeaders())}function ajaxHelper(e,t,n){return e=e.toLowerCase(),n?n instanceof Element||typeof n=="string"?issueAjaxRequest(e,t,null,null,{targetOverride:resolveTarget(n),returnPromise:!0}):issueAjaxRequest(e,t,resolveTarget(n.source),n.event,{handler:n.handler,headers:n.headers,values:n.values,targetOverride:resolveTarget(n.target),swapOverride:n.swap,select:n.select,returnPromise:!0}):issueAjaxRequest(e,t,null,null,{returnPromise:!0})}function hierarchyForElt(e){let t=[];for(;e;)t.push(e),e=e.parentElement;return t}function verifyPath(e,t,n){let r,o;return typeof URL=="function"?(o=new URL(t,document.location.href),r=document.location.origin===o.origin):(o=t,r=startsWith(t,document.location.origin)),htmx.config.selfRequestsOnly&&!r?!1:triggerEvent(e,"htmx:validateUrl",mergeObjects({url:o,sameHost:r},n))}function formDataFromObject(e){if(e instanceof FormData)return e;let t=new FormData;for(let n in e)e.hasOwnProperty(n)&&(typeof e[n].forEach=="function"?e[n].forEach(function(r){t.append(n,r);}):typeof e[n]=="object"&&!(e[n]instanceof Blob)?t.append(n,JSON.stringify(e[n])):t.append(n,e[n]));return t}function formDataArrayProxy(e,t,n){return new Proxy(n,{get:function(r,o){return typeof o=="number"?r[o]:o==="length"?r.length:o==="push"?function(i){r.push(i),e.append(t,i);}:typeof r[o]=="function"?function(){r[o].apply(r,arguments),e.delete(t),r.forEach(function(i){e.append(t,i);});}:r[o]&&r[o].length===1?r[o][0]:r[o]},set:function(r,o,i){return r[o]=i,e.delete(t),r.forEach(function(s){e.append(t,s);}),!0}})}function formDataProxy(e){return new Proxy(e,{get:function(t,n){if(typeof n=="symbol")return Reflect.get(t,n);if(n==="toJSON")return ()=>Object.fromEntries(e);if(n in t)return typeof t[n]=="function"?function(){return e[n].apply(e,arguments)}:t[n];let r=e.getAll(n);if(r.length!==0)return r.length===1?r[0]:formDataArrayProxy(t,n,r)},set:function(t,n,r){return typeof n!="string"?!1:(t.delete(n),typeof r.forEach=="function"?r.forEach(function(o){t.append(n,o);}):typeof r=="object"&&!(r instanceof Blob)?t.append(n,JSON.stringify(r)):t.append(n,r),!0)},deleteProperty:function(t,n){return typeof n=="string"&&t.delete(n),!0},ownKeys:function(t){return Reflect.ownKeys(Object.fromEntries(t))},getOwnPropertyDescriptor:function(t,n){return Reflect.getOwnPropertyDescriptor(Object.fromEntries(t),n)}})}function issueAjaxRequest(e,t,n,r,o,i){let s=null,l=null;if(o=o??{},o.returnPromise&&typeof Promise<"u")var a=new Promise(function(g,E){s=g,l=E;});n==null&&(n=getDocument().body);let u=o.handler||handleAjaxResponse,f=o.select||null;if(!bodyContains(n))return maybeCall(s),a;let c=o.targetOverride||asElement(getTarget(n));if(c==null||c==DUMMY_ELT)return triggerErrorEvent(n,"htmx:targetError",{target:getAttributeValue(n,"hx-target")}),maybeCall(l),a;let d=getInternalData(n),b=d.lastButtonClicked;if(b){let g=getRawAttribute(b,"formaction");g!=null&&(t=g);let E=getRawAttribute(b,"formmethod");E!=null&&E.toLowerCase()!=="dialog"&&(e=E);}let S=getClosestAttributeValue(n,"hx-confirm");if(i===void 0&&triggerEvent(n,"htmx:confirm",{target:c,elt:n,path:t,verb:e,triggeringEvent:r,etc:o,issueRequest:function(O){return issueAjaxRequest(e,t,n,r,o,!!O)},question:S})===!1)return maybeCall(s),a;let A=n,p=getClosestAttributeValue(n,"hx-sync"),x=null,T=!1;if(p){let g=p.split(":"),E=g[0].trim();if(E==="this"?A=findThisElement(n,"hx-sync"):A=asElement(querySelectorExt(n,E)),p=(g[1]||"drop").trim(),d=getInternalData(A),p==="drop"&&d.xhr&&d.abortable!==!0)return maybeCall(s),a;if(p==="abort"){if(d.xhr)return maybeCall(s),a;T=!0;}else p==="replace"?triggerEvent(A,"htmx:abort"):p.indexOf("queue")===0&&(x=(p.split(" ")[1]||"last").trim());}if(d.xhr)if(d.abortable)triggerEvent(A,"htmx:abort");else {if(x==null){if(r){let g=getInternalData(r);g&&g.triggerSpec&&g.triggerSpec.queue&&(x=g.triggerSpec.queue);}x==null&&(x="last");}return d.queuedRequests==null&&(d.queuedRequests=[]),x==="first"&&d.queuedRequests.length===0?d.queuedRequests.push(function(){issueAjaxRequest(e,t,n,r,o);}):x==="all"?d.queuedRequests.push(function(){issueAjaxRequest(e,t,n,r,o);}):x==="last"&&(d.queuedRequests=[],d.queuedRequests.push(function(){issueAjaxRequest(e,t,n,r,o);})),maybeCall(s),a}let m=new XMLHttpRequest;d.xhr=m,d.abortable=T;let H=function(){d.xhr=null,d.abortable=!1,d.queuedRequests!=null&&d.queuedRequests.length>0&&d.queuedRequests.shift()();},N=getClosestAttributeValue(n,"hx-prompt");if(N){var I=prompt(N);if(I===null||!triggerEvent(n,"htmx:prompt",{prompt:I,target:c}))return maybeCall(s),H(),a}if(S&&!i&&!confirm(S))return maybeCall(s),H(),a;let R=getHeaders(n,c,I);e!=="get"&&!usesFormData(n)&&(R["Content-Type"]="application/x-www-form-urlencoded"),o.headers&&(R=mergeObjects(R,o.headers));let v=getInputValues(n,e),q=v.errors,F=v.formData;o.values&&overrideFormData(F,formDataFromObject(o.values));let _=formDataFromObject(getExpressionVars(n)),W=overrideFormData(F,_),L=filterValues(W,n);htmx.config.getCacheBusterParam&&e==="get"&&L.set("org.htmx.cache-buster",getRawAttribute(c,"id")||"true"),(t==null||t==="")&&(t=getDocument().location.href);let X=getValuesForElement(n,"hx-request"),Y=getInternalData(n).boosted,M=htmx.config.methodsThatUseUrlParams.indexOf(e)>=0,w={boosted:Y,useUrlParams:M,formData:L,parameters:formDataProxy(L),unfilteredFormData:W,unfilteredParameters:formDataProxy(W),headers:R,target:c,verb:e,errors:q,withCredentials:o.credentials||X.credentials||htmx.config.withCredentials,timeout:o.timeout||X.timeout||htmx.config.timeout,path:t,triggeringEvent:r};if(!triggerEvent(n,"htmx:configRequest",w))return maybeCall(s),H(),a;if(t=w.path,e=w.verb,R=w.headers,L=formDataFromObject(w.parameters),q=w.errors,M=w.useUrlParams,q&&q.length>0)return triggerEvent(n,"htmx:validation:halted",w),maybeCall(s),H(),a;let G=t.split("#"),ee=G[0],j=G[1],D=t;if(M&&(D=ee,!L.keys().next().done&&(D.indexOf("?")<0?D+="?":D+="&",D+=urlEncode(L),j&&(D+="#"+j))),!verifyPath(n,D,w))return triggerErrorEvent(n,"htmx:invalidPath",w),maybeCall(l),a;if(m.open(e.toUpperCase(),D,!0),m.overrideMimeType("text/html"),m.withCredentials=w.withCredentials,m.timeout=w.timeout,!X.noHeaders){for(let g in R)if(R.hasOwnProperty(g)){let E=R[g];safelySetHeaderValue(m,g,E);}}let y={xhr:m,target:c,requestConfig:w,etc:o,boosted:Y,select:f,pathInfo:{requestPath:t,finalRequestPath:D,responsePath:null,anchor:j}};if(m.onload=function(){try{let g=hierarchyForElt(n);if(y.pathInfo.responsePath=getPathFromResponse(m),u(n,y),y.keepIndicators!==!0&&removeRequestIndicators(V,k),triggerEvent(n,"htmx:afterRequest",y),triggerEvent(n,"htmx:afterOnLoad",y),!bodyContains(n)){let E=null;for(;g.length>0&&E==null;){let O=g.shift();bodyContains(O)&&(E=O);}E&&(triggerEvent(E,"htmx:afterRequest",y),triggerEvent(E,"htmx:afterOnLoad",y));}maybeCall(s),H();}catch(g){throw triggerErrorEvent(n,"htmx:onLoadError",mergeObjects({error:g},y)),g}},m.onerror=function(){removeRequestIndicators(V,k),triggerErrorEvent(n,"htmx:afterRequest",y),triggerErrorEvent(n,"htmx:sendError",y),maybeCall(l),H();},m.onabort=function(){removeRequestIndicators(V,k),triggerErrorEvent(n,"htmx:afterRequest",y),triggerErrorEvent(n,"htmx:sendAbort",y),maybeCall(l),H();},m.ontimeout=function(){removeRequestIndicators(V,k),triggerErrorEvent(n,"htmx:afterRequest",y),triggerErrorEvent(n,"htmx:timeout",y),maybeCall(l),H();},!triggerEvent(n,"htmx:beforeRequest",y))return maybeCall(s),H(),a;var V=addRequestIndicatorClasses(n),k=disableElements(n);forEach(["loadstart","loadend","progress","abort"],function(g){forEach([m,m.upload],function(E){E.addEventListener(g,function(O){triggerEvent(n,"htmx:xhr:"+g,{lengthComputable:O.lengthComputable,loaded:O.loaded,total:O.total});});});}),triggerEvent(n,"htmx:beforeSend",y);let te=M?null:encodeParamsForBody(m,n,L);return m.send(te),a}function determineHistoryUpdates(e,t){let n=t.xhr,r=null,o=null;if(hasHeader(n,/HX-Push:/i)?(r=n.getResponseHeader("HX-Push"),o="push"):hasHeader(n,/HX-Push-Url:/i)?(r=n.getResponseHeader("HX-Push-Url"),o="push"):hasHeader(n,/HX-Replace-Url:/i)&&(r=n.getResponseHeader("HX-Replace-Url"),o="replace"),r)return r==="false"?{}:{type:o,path:r};let i=t.pathInfo.finalRequestPath,s=t.pathInfo.responsePath,l=getClosestAttributeValue(e,"hx-push-url"),a=getClosestAttributeValue(e,"hx-replace-url"),u=getInternalData(e).boosted,f=null,c=null;return l?(f="push",c=l):a?(f="replace",c=a):u&&(f="push",c=s||i),c?c==="false"?{}:(c==="true"&&(c=s||i),t.pathInfo.anchor&&c.indexOf("#")===-1&&(c=c+"#"+t.pathInfo.anchor),{type:f,path:c}):{}}function codeMatches(e,t){var n=new RegExp(e.code);return n.test(t.toString(10))}function resolveResponseHandling(e){for(var t=0;t0?getWindow().setTimeout(I,x.swapDelay):I();}c&&triggerErrorEvent(e,"htmx:responseError",mergeObjects({error:"Response Status Error Code "+n.status+" from "+t.pathInfo.requestPath},t));}}let extensions={};function extensionBase(){return {init:function(e){return null},getSelectors:function(){return null},onEvent:function(e,t){return !0},transformResponse:function(e,t,n){return e},isInlineSwap:function(e){return !1},handleSwap:function(e,t,n,r){return !1},encodeParameters:function(e,t,n){return null}}}function defineExtension(e,t){t.init&&t.init(internalAPI),extensions[e]=mergeObjects(extensionBase(),t);}function removeExtension(e){delete extensions[e];}function getExtensions(e,t,n){if(t==null&&(t=[]),e==null)return t;n==null&&(n=[]);let r=getAttributeValue(e,"hx-ext");return r&&forEach(r.split(","),function(o){if(o=o.replace(/ /g,""),o.slice(0,7)=="ignore:"){n.push(o.slice(7));return}if(n.indexOf(o)<0){let i=extensions[o];i&&t.indexOf(i)<0&&t.push(i);}}),getExtensions(asElement(parentElt(e)),t,n)}var isReady=!1;getDocument().addEventListener("DOMContentLoaded",function(){isReady=!0;});function ready(e){isReady||getDocument().readyState==="complete"?e():getDocument().addEventListener("DOMContentLoaded",e);}function insertIndicatorStyles(){if(htmx.config.includeIndicatorStyles!==!1){let e=htmx.config.inlineStyleNonce?` nonce="${htmx.config.inlineStyleNonce}"`:"";getDocument().head.insertAdjacentHTML("beforeend"," ."+htmx.config.indicatorClass+"{opacity:0} ."+htmx.config.requestClass+" ."+htmx.config.indicatorClass+"{opacity:1; transition: opacity 200ms ease-in;} ."+htmx.config.requestClass+"."+htmx.config.indicatorClass+"{opacity:1; transition: opacity 200ms ease-in;} ");}}function getMetaConfig(){let e=getDocument().querySelector('meta[name="htmx-config"]');return e?parseJSON(e.content):null}function mergeMetaConfig(){let e=getMetaConfig();e&&(htmx.config=mergeObjects(htmx.config,e));}return ready(function(){mergeMetaConfig(),insertIndicatorStyles();let e=getDocument().body;processNode(e);let t=getDocument().querySelectorAll("[hx-trigger='restored'],[data-hx-trigger='restored']");e.addEventListener("htmx:abort",function(r){let o=r.target,i=getInternalData(o);i&&i.xhr&&i.xhr.abort();});let n=window.onpopstate?window.onpopstate.bind(window):null;window.onpopstate=function(r){r.state&&r.state.htmx?(restoreHistory(),forEach(t,function(o){triggerEvent(o,"htmx:restored",{document:getDocument(),triggerEvent});})):n&&n(r);},getWindow().setTimeout(function(){triggerEvent(e,"htmx:load",{}),e=null;},0);}),htmx}(),h=ne;function re(e,t){if(e==="ignore")return !1;let n=e.split("/"),r=t.split("/");for(let o=0;o{let s=ie(t).replace("htmx:","hx-on::");r.has(o)||(o.hasAttribute(s)&&setTimeout(()=>{let l=ae(s.replace("hx-on::","htmx:"),{...n.detail,target:o});l.detail.meta="trigger-children",o.dispatchEvent(l),r.add(o);},1),o.children&&$(o,t,n,r));});}h.defineExtension("trigger-children",{onEvent:(e,t)=>{if(!(t instanceof CustomEvent)||t.detail.meta==="trigger-children")return !1;let n=new Set,r=t.target||t.detail.target;return $(r,e,t,n),!0}});h.defineExtension("debug",{onEvent:function(e,t){console.debug?console.debug(e,t):console&&console.log("DEBUG:",e,t);}});var C=h.config,B,le="hx-target-";function Q(e,t){return e.substring(0,t.length)===t}function ue(e,t){if(!e||!t)return null;let n=t.toString(),r=[n,n.substr(0,2)+"*",n.substr(0,2)+"x",n.substr(0,1)+"*",n.substr(0,1)+"x",n.substr(0,1)+"**",n.substr(0,1)+"xx","*","x","***","xxx"];(Q(n,"4")||Q(n,"5"))&&r.push("error");for(let o=0;o{B=e,C.responseTargetUnsetsError===void 0&&(C.responseTargetUnsetsError=!0),C.responseTargetSetsError===void 0&&(C.responseTargetSetsError=!1),C.responseTargetPrefersExisting===void 0&&(C.responseTargetPrefersExisting=!1),C.responseTargetPrefersRetargetHeader===void 0&&(C.responseTargetPrefersRetargetHeader=!0);},onEvent:(e,t)=>{if(!(t instanceof CustomEvent))return !1;if(e==="htmx:beforeSwap"&&t.detail.xhr&&t.detail.xhr.status!==200){if(t.detail.target&&(C.responseTargetPrefersExisting||C.responseTargetPrefersRetargetHeader&&t.detail.xhr.getAllResponseHeaders().match(/HX-Retarget:/i)))return t.detail.shouldSwap=!0,z(t),!0;if(!t.detail.requestConfig)return !0;let n=ue(t.detail.requestConfig.elt,t.detail.xhr.status);return n&&(z(t),t.detail.shouldSwap=!0,t.detail.target=n),!0}}});h.defineExtension("mutation-error",{onEvent:(e,t)=>{if(!(t instanceof CustomEvent))return !1;if(e==="htmx:afterRequest"){if(!t.detail||!t.detail.xhr)return;let n=t.detail.xhr.status;n>=400&&document.querySelectorAll("*").forEach(r=>{r.hasAttribute("hx-on::on-mutation-error")&&h.trigger(r,"htmx:on-mutation-error",{status:n});});}}});var U="";h.defineExtension("livereload",{init:function(){let e=!1;for(let n of Array.from(h.findAll("[hx-ext]")))if(n.getAttribute("hx-ext")?.split(" ").includes("livereload")){e=!0;break}if(!e)return;console.log("livereload extension initialized.");let t=new EventSource("/dev/livereload");t.onmessage=function(n){let r=n.data;U===""&&(U=r),U!==r&&(U=r,ce());},t.onerror=function(n){console.error("EventSource error:",n);};},onEvent:function(e,t){}});function ce(){window.location.reload();}var fe=/__eval_[A-Za-z0-9]+\([a-z]+\)/gm;h.defineExtension("htmgo",{onEvent:function(e,t){e==="htmx:beforeCleanupElement"&&t.target&&J(t.target),e==="htmx:load"&&t.target&&K(t.target);}});function K(e){if(e==null||!(e instanceof HTMLElement))return;["SCRIPT","LINK","STYLE","META","BASE","TITLE","HEAD","HTML","BODY"].includes(e.tagName)||e.hasAttribute("onload")&&e.onload(new Event("load")),e.querySelectorAll("[onload]").forEach(K);}function J(e){let t=Array.from(e.attributes);for(let n of t){let r=n.value.match(fe)||[];for(let o of r){let i=o.replace("()","").replace("(this)","").replace(";",""),s=document.getElementById(i);s&&s.tagName==="SCRIPT"&&(console.debug("removing associated script with id",i),s.remove());}}}var P=null,Z=new Set;h.defineExtension("sse",{init:function(e){P=e;},onEvent:function(e,t){let n=t.target;if(n instanceof HTMLElement&&(e==="htmx:beforeCleanupElement"&&J(n),e==="htmx:beforeProcessNode")){let r=document.querySelectorAll("[sse-connect]");for(let o of Array.from(r)){let i=o.getAttribute("sse-connect");i&&!Z.has(i)&&(de(o,i),Z.add(i));}}}});function de(e,t){if(!t)return;console.info("Connecting to EventSource",t);let n=new EventSource(t);n.addEventListener("close",function(r){h.trigger(e,"htmx:sseClose",{event:r});}),n.onopen=function(r){h.trigger(e,"htmx:sseOpen",{event:r});},n.onerror=function(r){h.trigger(e,"htmx:sseError",{event:r}),n.readyState==EventSource.CLOSED&&h.trigger(e,"htmx:sseClose",{event:r});},n.onmessage=function(r){let o=P.makeSettleInfo(e);h.trigger(e,"htmx:sseBeforeMessage",{event:r});let i=r.data,s=P.makeFragment(i),l=Array.from(s.children);for(let a of l)P.oobSwap(P.getAttributeValue(a,"hx-swap-oob")||"true",a,o),a.tagName==="SCRIPT"&&a.id.startsWith("__eval")&&document.body.appendChild(a);h.trigger(e,"htmx:sseAfterMessage",{event:r});};}window.htmx=h;function he(e){let t=window.location.href;setInterval(()=>{window.location.href!==t&&(e(t,window.location.href),t=window.location.href);},101);}he((e,t)=>{ge(t);});function ge(e){let t=new URL(e);document.querySelectorAll("[hx-trigger]").forEach(function(n){let r=n.getAttribute("hx-trigger");if(!r)return;if(r.split(", ").find(i=>i==="url"))h.swap(n,"url",{swapStyle:"outerHTML",swapDelay:0,settleDelay:0});else for(let[i,s]of t.searchParams){let l="qs:"+i;if(r.includes(l)){console.log("triggering",l),h.trigger(n,l,null);break}}}),document.querySelectorAll("[hx-match-qp]").forEach(n=>{let r=!1;for(let o of n.getAttributeNames())if(o.startsWith("hx-match-qp-mapping:")){let i=o.replace("hx-match-qp-mapping:","");if(t.searchParams.get(i)){h.swap(n,n.getAttribute(o)??"",{swapStyle:"innerHTML",swapDelay:0,settleDelay:0}),r=!0;break}}if(!r){let o=n.getAttribute("hx-match-qp-default");o&&h.swap(n,n.getAttribute("hx-match-qp-mapping:"+o)??"",{swapStyle:"innerHTML",swapDelay:0,settleDelay:0});}});}document.addEventListener("htmx:beforeSwap",function(e){e instanceof CustomEvent&&(e.detail.xhr.status===422||e.detail.xhr.status===400)&&(e.detail.shouldSwap=!0,e.detail.isError=!1);}); diff --git a/framework/assets/js/htmgo.ts b/framework/assets/js/htmgo.ts index 898d00b..7b33524 100644 --- a/framework/assets/js/htmgo.ts +++ b/framework/assets/js/htmgo.ts @@ -8,6 +8,9 @@ import "./htmxextensions/livereload" import "./htmxextensions/htmgo"; import "./htmxextensions/sse" +// @ts-ignore +window.htmx = htmx; + function watchUrl(callback: (oldUrl: string, newUrl: string) => void) { let lastUrl = window.location.href; setInterval(() => { @@ -15,7 +18,7 @@ function watchUrl(callback: (oldUrl: string, newUrl: string) => void) { callback(lastUrl, window.location.href); lastUrl = window.location.href; } - }, 100); + }, 101); } watchUrl((_, newUrl) => { @@ -78,3 +81,17 @@ function onUrlChange(newUrl: string) { } }); } + +/* + 400s should allow swapping by default, as it's useful to show error messages + */ +document.addEventListener('htmx:beforeSwap', function(evt) { + if(evt instanceof CustomEvent) { + // Allow 422 and 400 responses to swap + // We treat these as form validation errors + if (evt.detail.xhr.status === 422 || evt.detail.xhr.status === 400) { + evt.detail.shouldSwap = true; + evt.detail.isError = false; + } + } +}); diff --git a/framework/assets/js/htmxextensions/htmgo.ts b/framework/assets/js/htmxextensions/htmgo.ts index 6b0ae7e..a711734 100644 --- a/framework/assets/js/htmxextensions/htmgo.ts +++ b/framework/assets/js/htmxextensions/htmgo.ts @@ -8,9 +8,30 @@ htmx.defineExtension("htmgo", { if(name === "htmx:beforeCleanupElement" && evt.target) { removeAssociatedScripts(evt.target as HTMLElement); } + if(name === "htmx:load" && evt.target) { + invokeOnLoad(evt.target as HTMLElement); + } }, }); +/** + * Browser doesn't support onload for all elements, so we need to manually trigger it + * this is useful for locality of behavior + */ +function invokeOnLoad(element : Element) { + if(element == null || !(element instanceof HTMLElement)) { + return + } + const ignored = ['SCRIPT', 'LINK', 'STYLE', 'META', 'BASE', 'TITLE', 'HEAD', 'HTML', 'BODY']; + if(!ignored.includes(element.tagName)) { + if(element.hasAttribute("onload")) { + element.onload!(new Event("load")); + } + } + // check its children + element.querySelectorAll('[onload]').forEach(invokeOnLoad) +} + export function removeAssociatedScripts(element: HTMLElement) { const attributes = Array.from(element.attributes) for (let attribute of attributes) { diff --git a/framework/assets/js/htmxextensions/livereload.ts b/framework/assets/js/htmxextensions/livereload.ts index 8dbc9c6..ebde77d 100644 --- a/framework/assets/js/htmxextensions/livereload.ts +++ b/framework/assets/js/htmxextensions/livereload.ts @@ -1,5 +1,4 @@ import htmx from "htmx.org"; -import {createWebSocketClient} from "../util/ws"; let lastVersion = ""; @@ -48,4 +47,4 @@ htmx.defineExtension("livereload", { function reload() { window.location.reload() -} \ No newline at end of file +} diff --git a/framework/assets/js/htmxextensions/mutation-error.ts b/framework/assets/js/htmxextensions/mutation-error.ts index caf9af1..99b16cc 100644 --- a/framework/assets/js/htmxextensions/mutation-error.ts +++ b/framework/assets/js/htmxextensions/mutation-error.ts @@ -12,8 +12,10 @@ htmx.defineExtension("mutation-error", { } const status = evt.detail.xhr.status; if (status >= 400) { - htmx.findAll("[hx-on\\:\\:mutation-error]").forEach((element) => { - htmx.trigger(element, "htmx:mutation-error", { status }); + document.querySelectorAll("*").forEach((element) => { + if (element.hasAttribute("hx-on::on-mutation-error")) { + htmx.trigger(element, "htmx:on-mutation-error", { status }); + } }); } } diff --git a/framework/assets/js/htmxextensions/sse.ts b/framework/assets/js/htmxextensions/sse.ts index 16cfe79..b4b7a19 100644 --- a/framework/assets/js/htmxextensions/sse.ts +++ b/framework/assets/js/htmxextensions/sse.ts @@ -39,8 +39,11 @@ function connectEventSource(ele: Element, url: string) { console.info('Connecting to EventSource', url) const eventSource = new EventSource(url); + eventSource.addEventListener("close", function(event) { + htmx.trigger(ele, "htmx:sseClose", {event: event}); + }) + eventSource.onopen = function(event) { - console.log('EventSource open:', event); htmx.trigger(ele, "htmx:sseOpen", {event: event}); } @@ -52,13 +55,13 @@ function connectEventSource(ele: Element, url: string) { } eventSource.onmessage = function(event) { - console.log('EventSource message:', event.data); + const settleInfo = api.makeSettleInfo(ele); htmx.trigger(ele, "htmx:sseBeforeMessage", {event: event}); const response = event.data const fragment = api.makeFragment(response) as DocumentFragment; const children = Array.from(fragment.children); for (let child of children) { - api.oobSwap(api.getAttributeValue(child, 'hx-swap-oob') || 'true', child, {tasks: []}); + api.oobSwap(api.getAttributeValue(child, 'hx-swap-oob') || 'true', child, settleInfo); // support htmgo eval__ scripts if(child.tagName === 'SCRIPT' && child.id.startsWith("__eval")) { document.body.appendChild(child); diff --git a/framework/assets/js/htmxextensions/trigger-children.ts b/framework/assets/js/htmxextensions/trigger-children.ts index f39fa23..c32c738 100644 --- a/framework/assets/js/htmxextensions/trigger-children.ts +++ b/framework/assets/js/htmxextensions/trigger-children.ts @@ -1,10 +1,10 @@ -import htmx, {HtmxSettleInfo, HtmxSwapStyle} from "htmx.org"; +import htmx from "htmx.org"; function kebabEventName(str: string) { return str.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase() } -const ignoredEvents = ['htmx:beforeProcessNode', 'htmx:afterProcessNode', 'htmx:beforeSwap', 'htmx:afterSwap', 'htmx:beforeOnLoad', 'htmx:afterOnLoad', 'htmx:configRequest', 'htmx:configResponse', 'htmx:responseError']; +const ignoredEvents = ['htmx:beforeProcessNode', 'htmx:afterProcessNode', 'htmx:configRequest', 'htmx:configResponse', 'htmx:responseError']; function makeEvent(eventName: string, detail: any) { let evt @@ -28,10 +28,15 @@ function triggerChildren(target: HTMLElement, name: string, event: CustomEvent, const eventName = kehab.replace("htmx:", "hx-on::") if (!triggered.has(e as HTMLElement)) { if(e.hasAttribute(eventName)) { - const newEvent = makeEvent(eventName.replace("hx-on::", "htmx:"), event.detail) - newEvent.detail.meta = 'trigger-children' - e.dispatchEvent(newEvent) - triggered.add(e as HTMLElement); + setTimeout(() => { + const newEvent = makeEvent(eventName.replace("hx-on::", "htmx:"), { + ...event.detail, + target: e, + }) + newEvent.detail.meta = 'trigger-children' + e.dispatchEvent(newEvent) + triggered.add(e as HTMLElement); + }, 1) } if (e.children) { triggerChildren(e as HTMLElement, name, event, triggered); @@ -42,6 +47,7 @@ function triggerChildren(target: HTMLElement, name: string, event: CustomEvent, } +// @ts-ignore htmx.defineExtension("trigger-children", { onEvent: (name, evt: Event | CustomEvent) => { if (!(evt instanceof CustomEvent)) { @@ -55,33 +61,4 @@ htmx.defineExtension("trigger-children", { triggerChildren(target, name, evt, triggered); return true; }, - init: function (api: any): void { - }, - transformResponse: function ( - text: string, - xhr: XMLHttpRequest, - elt: Element, - ): string { - return text; - }, - isInlineSwap: function (swapStyle: HtmxSwapStyle): boolean { - return false; - }, - handleSwap: function ( - swapStyle: HtmxSwapStyle, - target: Node, - fragment: Node, - settleInfo: HtmxSettleInfo, - ): boolean | Node[] { - return false; - }, - encodeParameters: function ( - xhr: XMLHttpRequest, - parameters: FormData, - elt: Node, - ) { - }, - getSelectors: function (): string[] | null { - return null; - }, }); diff --git a/framework/assets/js/util/ws.ts b/framework/assets/js/util/ws.ts deleted file mode 100644 index 446289f..0000000 --- a/framework/assets/js/util/ws.ts +++ /dev/null @@ -1,46 +0,0 @@ -type WsOpts = { - url: string; - reconnectInterval?: number; - onOpen?: () => void; - onMessage: (message: string) => void; - onError?: (error: Event) => void; - onClose?: () => void; -} - -export function createWebSocketClient(opts: WsOpts) { - let socket: WebSocket | null = null; - const connect = (tries: number) => { - console.log('connecting to ws', opts.url, 'attempt', tries) - socket = new WebSocket(opts.url); - // Handle incoming messages - socket.onmessage = (event) => { - opts.onMessage(event.data) - }; - // Handle connection errors - socket.onerror = (error) => { - try { - socket?.close() - } catch(ex) { - // noop - } - socket = null - let interval = tries * (opts.reconnectInterval || 1000); - setTimeout(() => connect(tries + 1), interval); - }; - // Handle connection close and attempt reconnection - socket.onclose = () => { - socket = null; - let interval = tries * (opts.reconnectInterval || 1000); - setTimeout(() => connect(tries + 1), interval); - }; - }; - connect(1); - const sendMessage = (message: string) => { - if (socket && socket.readyState === WebSocket.OPEN) { - socket.send(message); - } else { - setTimeout(() => sendMessage(message), 100); - } - }; - return { sendMessage }; -} \ No newline at end of file diff --git a/framework/config/project.go b/framework/config/project.go new file mode 100644 index 0000000..33f6cfc --- /dev/null +++ b/framework/config/project.go @@ -0,0 +1,82 @@ +package config + +import ( + "gopkg.in/yaml.v3" + "log/slog" + "os" + "path" + "strings" +) + +type ProjectConfig struct { + Tailwind bool `yaml:"tailwind"` + WatchIgnore []string `yaml:"watch_ignore"` + WatchFiles []string `yaml:"watch_files"` + AutomaticPageRoutingIgnore []string `yaml:"automatic_page_routing_ignore"` + AutomaticPartialRoutingIgnore []string `yaml:"automatic_partial_routing_ignore"` +} + +func DefaultProjectConfig() *ProjectConfig { + return &ProjectConfig{ + Tailwind: true, + WatchIgnore: []string{ + "node_modules", ".git", ".idea", "assets/dist", + }, + WatchFiles: []string{ + "**/*.go", "**/*.html", "**/*.css", "**/*.js", "**/*.json", "**/*.yaml", "**/*.yml", "**/*.md", + }, + } +} + +func (cfg *ProjectConfig) Enhance() *ProjectConfig { + defaultCfg := DefaultProjectConfig() + if len(cfg.WatchFiles) == 0 { + cfg.WatchFiles = defaultCfg.WatchFiles + } + if len(cfg.WatchIgnore) == 0 { + cfg.WatchIgnore = defaultCfg.WatchIgnore + } + + for i, s := range cfg.AutomaticPartialRoutingIgnore { + parts := strings.Split(s, string(os.PathSeparator)) + if len(parts) == 0 { + continue + } + if parts[0] != "partials" { + cfg.AutomaticPartialRoutingIgnore[i] = path.Join("partials", s) + } + } + + for i, s := range cfg.AutomaticPageRoutingIgnore { + parts := strings.Split(s, string(os.PathSeparator)) + if len(parts) == 0 { + continue + } + if parts[0] != "pages" { + cfg.AutomaticPageRoutingIgnore[i] = path.Join("pages", s) + } + } + + return cfg +} + +func FromConfigFile(workingDir string) *ProjectConfig { + defaultCfg := DefaultProjectConfig() + names := []string{"htmgo.yaml", "htmgo.yml", "_htmgo.yaml", "_htmgo.yml"} + for _, name := range names { + filePath := path.Join(workingDir, name) + if _, err := os.Stat(filePath); err == nil { + cfg := &ProjectConfig{} + bytes, err := os.ReadFile(filePath) + if err == nil { + err = yaml.Unmarshal(bytes, cfg) + if err != nil { + slog.Error("Error parsing config file", slog.String("file", filePath), slog.String("error", err.Error())) + os.Exit(1) + } + return cfg.Enhance() + } + } + } + return defaultCfg +} diff --git a/framework/config/project_test.go b/framework/config/project_test.go new file mode 100644 index 0000000..bb492a0 --- /dev/null +++ b/framework/config/project_test.go @@ -0,0 +1,82 @@ +package config + +import ( + "github.com/stretchr/testify/assert" + "os" + "path" + "testing" +) + +func TestDefaultProjectConfig(t *testing.T) { + t.Parallel() + cfg := DefaultProjectConfig() + assert.Equal(t, true, cfg.Tailwind) + assert.Equal(t, 4, len(cfg.WatchIgnore)) + assert.Equal(t, 8, len(cfg.WatchFiles)) +} + +func TestNoConfigFileUsesDefault(t *testing.T) { + t.Parallel() + cfg := FromConfigFile("non-existing-dir") + assert.Equal(t, true, cfg.Tailwind) + assert.Equal(t, 4, len(cfg.WatchIgnore)) + assert.Equal(t, 8, len(cfg.WatchFiles)) +} + +func TestPartialConfigMerges(t *testing.T) { + t.Parallel() + dir := writeConfigFile(t, "tailwind: false") + cfg := FromConfigFile(dir) + assert.Equal(t, false, cfg.Tailwind) + assert.Equal(t, 4, len(cfg.WatchIgnore)) + assert.Equal(t, 8, len(cfg.WatchFiles)) +} + +func TestShouldNotSetTailwindTrue(t *testing.T) { + t.Parallel() + dir := writeConfigFile(t, "someValue: true") + cfg := FromConfigFile(dir) + assert.Equal(t, false, cfg.Tailwind) + assert.Equal(t, 4, len(cfg.WatchIgnore)) + assert.Equal(t, 8, len(cfg.WatchFiles)) +} + +func TestShouldPrefixAutomaticPageRoutingIgnore(t *testing.T) { + t.Parallel() + cfg := DefaultProjectConfig() + cfg.AutomaticPageRoutingIgnore = []string{"somefile"} + cfg.Enhance() + assert.Equal(t, []string{"pages/somefile"}, cfg.AutomaticPageRoutingIgnore) +} + +func TestShouldPrefixAutomaticPageRoutingIgnore_1(t *testing.T) { + t.Parallel() + cfg := DefaultProjectConfig() + cfg.AutomaticPageRoutingIgnore = []string{"pages/somefile/*"} + cfg.Enhance() + assert.Equal(t, []string{"pages/somefile/*"}, cfg.AutomaticPageRoutingIgnore) +} + +func TestShouldPrefixAutomaticPartialRoutingIgnore(t *testing.T) { + t.Parallel() + cfg := DefaultProjectConfig() + cfg.AutomaticPartialRoutingIgnore = []string{"somefile/*"} + cfg.Enhance() + assert.Equal(t, []string{"partials/somefile/*"}, cfg.AutomaticPartialRoutingIgnore) +} + +func TestShouldPrefixAutomaticPartialRoutingIgnore_1(t *testing.T) { + t.Parallel() + cfg := DefaultProjectConfig() + cfg.AutomaticPartialRoutingIgnore = []string{"partials/somefile/*"} + cfg.Enhance() + assert.Equal(t, []string{"partials/somefile/*"}, cfg.AutomaticPartialRoutingIgnore) +} + +func writeConfigFile(t *testing.T, content string) string { + temp := os.TempDir() + os.Mkdir(temp, 0755) + err := os.WriteFile(path.Join(temp, "htmgo.yml"), []byte(content), 0644) + assert.Nil(t, err) + return temp +} diff --git a/framework/go.mod b/framework/go.mod index a6a7b64..4e9fd74 100644 --- a/framework/go.mod +++ b/framework/go.mod @@ -7,10 +7,10 @@ require ( github.com/google/uuid v1.6.0 github.com/stretchr/testify v1.9.0 golang.org/x/net v0.29.0 + gopkg.in/yaml.v3 v3.0.1 ) require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/framework/h/app.go b/framework/h/app.go index 0839672..40e62af 100644 --- a/framework/h/app.go +++ b/framework/h/app.go @@ -3,9 +3,6 @@ package h import ( "context" "fmt" - "github.com/go-chi/chi/v5" - "github.com/maddalax/htmgo/framework/hx" - "github.com/maddalax/htmgo/framework/service" "log/slog" "net/http" "os" @@ -13,6 +10,10 @@ import ( "runtime" "strings" "time" + + "github.com/go-chi/chi/v5" + "github.com/maddalax/htmgo/framework/hx" + "github.com/maddalax/htmgo/framework/service" ) type RequestContext struct { @@ -29,10 +30,85 @@ type RequestContext struct { kv map[string]interface{} } +func GetRequestContext(r *http.Request) *RequestContext { + return r.Context().Value(RequestContextKey).(*RequestContext) +} + +func (c *RequestContext) SetCookie(cookie *http.Cookie) { + http.SetCookie(c.Response, cookie) +} + +func (c *RequestContext) Redirect(path string, code int) { + if code == 0 { + code = http.StatusTemporaryRedirect + } + if code < 300 || code > 399 { + code = http.StatusTemporaryRedirect + } + c.Response.Header().Set("Location", path) + c.Response.WriteHeader(code) +} + +func (c *RequestContext) IsHttpPost() bool { + return c.Request.Method == http.MethodPost +} + +func (c *RequestContext) IsHttpGet() bool { + return c.Request.Method == http.MethodGet +} + +func (c *RequestContext) IsHttpPut() bool { + return c.Request.Method == http.MethodPut +} + +func (c *RequestContext) IsHttpDelete() bool { + return c.Request.Method == http.MethodDelete +} + +func (c *RequestContext) FormValue(key string) string { + return c.Request.FormValue(key) +} + +func (c *RequestContext) Header(key string) string { + return c.Request.Header.Get(key) +} + +func (c *RequestContext) UrlParam(key string) string { + return chi.URLParam(c.Request, key) +} + func (c *RequestContext) QueryParam(key string) string { return c.Request.URL.Query().Get(key) } +func (c *RequestContext) IsBoosted() bool { + return c.isBoosted +} + +func (c *RequestContext) IsHxRequest() bool { + return c.isHxRequest +} + +func (c *RequestContext) HxPromptResponse() string { + return c.hxPromptResponse +} + +func (c *RequestContext) HxTargetId() string { + return c.hxTargetId +} + +func (c *RequestContext) HxTriggerName() string { + return c.hxTriggerName +} + +func (c *RequestContext) HxTriggerId() string { + return c.hxTriggerId +} + +func (c *RequestContext) HxCurrentBrowserUrl() string { + return c.currentBrowserUrl +} + func (c *RequestContext) Set(key string, value interface{}) { if c.kv == nil { c.kv = make(map[string]interface{}) @@ -79,7 +155,6 @@ func Start(opts AppOpts) { const RequestContextKey = "htmgo.request.context" func populateHxFields(cc *RequestContext) { - cc.isBoosted = cc.Request.Header.Get(hx.BoostedHeader) == "true" cc.isBoosted = cc.Request.Header.Get(hx.BoostedHeader) == "true" cc.currentBrowserUrl = cc.Request.Header.Get(hx.CurrentUrlHeader) cc.hxPromptResponse = cc.Request.Header.Get(hx.PromptResponseHeader) @@ -144,10 +219,9 @@ func (app *App) start() { } port := ":3000" - slog.Info(fmt.Sprintf("Server started on port %s", port)) - err := http.ListenAndServe(port, app.Router) + slog.Info(fmt.Sprintf("Server started at localhost%s", port)) - if err != nil { + if err := http.ListenAndServe(port, app.Router); err != nil { // If we are in watch mode, just try to kill any processes holding that port // and try again if IsDevelopment() && IsWatchMode() { @@ -159,29 +233,42 @@ func (app *App) start() { cmd := exec.Command("bash", "-c", fmt.Sprintf("kill -9 $(lsof -ti%s)", port)) cmd.Run() } + time.Sleep(time.Millisecond * 50) - err = http.ListenAndServe(":3000", app.Router) - if err != nil { + + // Try to start server again + if err := http.ListenAndServe(port, app.Router); err != nil { + slog.Error("Failed to restart server", "error", err) panic(err) } - } else { - panic(err) } + panic(err) } } func writeHtml(w http.ResponseWriter, element Ren) error { + if element == nil { + return nil + } w.Header().Set("Content-Type", "text/html") - _, err := fmt.Fprint(w, Render(element)) + _, err := fmt.Fprint(w, Render(element, WithDocType())) return err } func HtmlView(w http.ResponseWriter, page *Page) error { + // if the page is nil, do nothing, this can happen if custom response is written, such as a 302 redirect + if page == nil { + return nil + } return writeHtml(w, page.Root) } func PartialViewWithHeaders(w http.ResponseWriter, headers *Headers, partial *Partial) error { + if partial == nil { + return nil + } + if partial.Headers != nil { for s, a := range *partial.Headers { w.Header().Set(s, a) @@ -198,6 +285,10 @@ func PartialViewWithHeaders(w http.ResponseWriter, headers *Headers, partial *Pa } func PartialView(w http.ResponseWriter, partial *Partial) error { + if partial == nil { + return nil + } + if partial.Headers != nil { for s, a := range *partial.Headers { w.Header().Set(s, a) diff --git a/framework/h/attribute.go b/framework/h/attribute.go index d0da2da..48efcb2 100644 --- a/framework/h/attribute.go +++ b/framework/h/attribute.go @@ -2,9 +2,10 @@ package h import ( "fmt" + "strings" + "github.com/maddalax/htmgo/framework/hx" "github.com/maddalax/htmgo/framework/internal/datastructure" - "strings" ) type AttributeMap = map[string]any @@ -89,9 +90,7 @@ func Checked() Ren { } func Id(value string) Ren { - if strings.HasPrefix(value, "#") { - value = value[1:] - } + value = strings.TrimPrefix(value, "#") return Attribute("id", value) } @@ -202,6 +201,12 @@ func Class(value ...string) *AttributeR { return Attribute("class", MergeClasses(value...)) } +// ClassF is a helper function to create a class attribute with the given format string and arguments +func ClassF(format string, args ...interface{}) *AttributeR { + atr := fmt.Sprintf(format, args...) + return Attribute("class", atr) +} + // ClassX conditionally renders a class based on a map of class names and boolean values // value is any non-conditional class name you'd like to add // m is a map of class names and boolean values diff --git a/framework/h/base.go b/framework/h/base.go index 1ec82a1..a26aa9e 100644 --- a/framework/h/base.go +++ b/framework/h/base.go @@ -1,7 +1,6 @@ package h import ( - "html" "net/http" "reflect" "runtime" @@ -24,6 +23,10 @@ func NewPage(root Ren) *Page { } } +func EmptyPage() *Page { + return NewPage(Fragment()) +} + func NewPageWithHttpMethod(httpMethod string, root *Element) *Page { return &Page{ HttpMethod: httpMethod, @@ -85,9 +88,9 @@ func SwapManyXPartial(ctx *RequestContext, swaps ...SwapArg) *Partial { } func GetPartialPath(partial PartialFunc) string { - return runtime.FuncForPC(reflect.ValueOf(partial).Pointer()).Name() + return "/" + runtime.FuncForPC(reflect.ValueOf(partial).Pointer()).Name() } func GetPartialPathWithQs(partial func(ctx *RequestContext) *Partial, qs *Qs) string { - return html.EscapeString(GetPartialPath(partial) + "?" + qs.ToString()) + return GetPartialPath(partial) + "?" + qs.ToString() } diff --git a/framework/h/cache.go b/framework/h/cache.go index 64ed5fe..9709281 100644 --- a/framework/h/cache.go +++ b/framework/h/cache.go @@ -304,7 +304,7 @@ func (c *CachedNode) ClearExpired() { c.mutex.Lock() defer c.mutex.Unlock() deletedCount := 0 - if c.isByKey == true { + if c.isByKey { if c.byKeyCache != nil && c.byKeyExpiration != nil { for key := range c.byKeyCache { expir, ok := c.byKeyExpiration[key] @@ -330,7 +330,7 @@ func (c *CachedNode) ClearExpired() { } func (c *CachedNode) Render(ctx *RenderContext) { - if c.isByKey == true { + if c.isByKey { panic("CachedPerKey should not be rendered directly") } else { c.mutex.Lock() diff --git a/framework/h/command_test.go b/framework/h/command_test.go index 3743381..0da8afb 100644 --- a/framework/h/command_test.go +++ b/framework/h/command_test.go @@ -32,17 +32,20 @@ func renderJs(t *testing.T, command Command) string { value := parsed.FirstChild.FirstChild.NextSibling.LastChild.Attr[0].Val isComplex := strings.HasPrefix(value, "__eval_") if !isComplex { - return value + value = strings.ReplaceAll(value, "var e=event;", "") + return strings.ReplaceAll(value, "var self=this;", "") } else { - id := strings.TrimSuffix(value, "(this);") + id := strings.TrimSuffix(value, "(this, event);") script := findScriptById(parsed, id) assert.NotNil(t, script) funcCall := script.LastChild.Data funcCall = strings.ReplaceAll(funcCall, "\n", "") funcCall = strings.ReplaceAll(funcCall, "\t", "") - start := fmt.Sprintf("function %s(self) {", id) + start := fmt.Sprintf("function %s(self, event) {", id) funcCall = strings.TrimPrefix(funcCall, start) funcCall = strings.TrimSuffix(funcCall, "}") + funcCall = strings.ReplaceAll(funcCall, "let e = event;", "") + funcCall = strings.ReplaceAll(funcCall, "var self=this;", "") return funcCall } } diff --git a/framework/h/lifecycle.go b/framework/h/lifecycle.go index 84d9dac..b1e4fee 100644 --- a/framework/h/lifecycle.go +++ b/framework/h/lifecycle.go @@ -30,7 +30,6 @@ func validateCommands(cmds []Command) { panic(fmt.Sprintf("element is not allowed in lifecycle events. Got: %v", t)) default: panic(fmt.Sprintf("type is not allowed in lifecycle events. Got: %v", t)) - } } } @@ -52,6 +51,7 @@ func (l *LifeCycle) OnEvent(event hx.Event, cmd ...Command) *LifeCycle { } // OnLoad executes the given commands when the element is loaded into the DOM, it also executes when the element is replaced / swapped in. +// This will work on any element because of the htmgo htmx extension to trigger it, instead of the browser. func OnLoad(cmd ...Command) *LifeCycle { return NewLifeCycle().OnEvent(hx.LoadDomEvent, cmd...) } @@ -352,12 +352,7 @@ func SetValue(value string) SimpleJsCommand { func SubmitFormOnEnter() ComplexJsCommand { // language=JavaScript return EvalJs(` - if (event.code === 'Enter') { - console.log('submitting form'); - setTimeout(() => { - self.form.dispatchEvent(new Event('submit', { cancelable: true })); - }, 250) - } + if (event.code === 'Enter') { self.form.dispatchEvent(new Event('submit', { cancelable: true })); } `) } diff --git a/framework/h/qs.go b/framework/h/qs.go index b99d7ea..11cd5bf 100644 --- a/framework/h/qs.go +++ b/framework/h/qs.go @@ -14,7 +14,7 @@ func NewQs(pairs ...string) *Qs { m: make(map[string]string), } if len(pairs)%2 != 0 { - return q + pairs = append(pairs, "") } for i := 0; i < len(pairs); i++ { q.m[pairs[i]] = pairs[i+1] @@ -38,8 +38,10 @@ func (q *Qs) ToString() string { index := 0 for k, v := range q.m { builder.WriteString(k) - builder.WriteString("=") - builder.WriteString(v) + if v != "" { + builder.WriteString("=") + builder.WriteString(v) + } if index < len(q.m)-1 { builder.WriteString("&") } diff --git a/framework/h/qs_test.go b/framework/h/qs_test.go new file mode 100644 index 0000000..31067fd --- /dev/null +++ b/framework/h/qs_test.go @@ -0,0 +1,49 @@ +package h + +import ( + "github.com/stretchr/testify/assert" + "testing" +) + +func assertHas(t *testing.T, qs *Qs, key string, value string) { + str := qs.ToString() + if value == "" { + assert.Contains(t, str, key) + assert.NotContains(t, str, key+"=") + } else { + assert.Contains(t, str, key+"="+value) + } +} + +func TestQs(t *testing.T) { + t.Parallel() + qs := NewQs("a", "b", "c") + assertHas(t, qs, "a", "b") + assertHas(t, qs, "c", "") + + qs2 := NewQs("a", "b", "c", "d") + assertHas(t, qs2, "a", "b") + assertHas(t, qs2, "c", "d") + + qs2.Add("e", "f") + assertHas(t, qs2, "a", "b") + assertHas(t, qs2, "c", "d") + assertHas(t, qs2, "e", "f") + + qs2.Remove("e") + assert.NotContains(t, qs2.ToString(), "e") +} + +func TestSetQsOnUrl(t *testing.T) { + t.Parallel() + qs := NewQs("a", "b", "c", "d") + set := SetQueryParams("https://example.com/path", qs) + assert.Equal(t, "https://example.com/path?a=b&c=d", set) +} + +func TestSetQsOnUrlWithDelete(t *testing.T) { + t.Parallel() + qs := NewQs("a", "b2", "c", "") + set := SetQueryParams("https://example.com/path?a=b&c=d", qs) + assert.Equal(t, "https://example.com/path?a=b2", set) +} diff --git a/framework/h/render.go b/framework/h/render.go index 4570708..bad3b35 100644 --- a/framework/h/render.go +++ b/framework/h/render.go @@ -8,12 +8,34 @@ type Ren interface { Render(context *RenderContext) } +type RenderOptions struct { + doctype bool +} + +type RenderOpt func(context *RenderContext, opt *RenderOptions) + +func WithDocType() RenderOpt { + return func(context *RenderContext, opt *RenderOptions) { + opt.doctype = true + } +} + // Render renders the given node recursively, and returns the resulting string. -func Render(node Ren) string { +func Render(node Ren, opts ...RenderOpt) string { builder := &strings.Builder{} context := &RenderContext{ builder: builder, } + options := &RenderOptions{} + + for _, opt := range opts { + opt(context, options) + } + + if options.doctype { + builder.WriteString("") + } + node.Render(context) return builder.String() } diff --git a/framework/h/render_test.go b/framework/h/render_test.go index 10a6662..83a4cf4 100644 --- a/framework/h/render_test.go +++ b/framework/h/render_test.go @@ -10,6 +10,14 @@ import ( "time" ) +func TestRendererShouldRenderDocType(t *testing.T) { + t.Parallel() + result := Render(Html( + Div(), + ), WithDocType()) + assert.Equal(t, `
`, result) +} + func TestSimpleRender(t *testing.T) { t.Parallel() result := Render( @@ -25,7 +33,6 @@ func TestRender(t *testing.T) { Attribute("data-attr-2", "value"), Attributes(&AttributeMap{ "data-attr-3": "value", - "data-attr-4": "value", }), HxBeforeRequest( SetText("before request"), @@ -41,12 +48,12 @@ func TestRender(t *testing.T) { div.attributes.Set("data-attr-1", "value") - expected := `
hello, world
hello, child
` + expected := `
hello, world
hello, child
` result := Render(div) assert.Equal(t, expected, - result) + strings.ReplaceAll(result, "var self=this;var e=event;", "")) } func TestRenderAttributes_1(t *testing.T) { diff --git a/framework/h/swap.go b/framework/h/swap.go index 127d05b..fa96e43 100644 --- a/framework/h/swap.go +++ b/framework/h/swap.go @@ -63,11 +63,14 @@ func SwapMany(ctx *RequestContext, elements ...*Element) *Element { return Empty() } for _, element := range elements { - element.AppendChild(outOfBandSwap("")) + if element.tag != "" { + element.AppendChild(outOfBandSwap("")) + } } - return Fragment(Map(elements, func(arg *Element) Ren { + res := Fragment(Map(elements, func(arg *Element) Ren { return arg })...) + return res } func SwapManyX(ctx *RequestContext, args ...SwapArg) *Element { diff --git a/framework/h/tag.go b/framework/h/tag.go index 23fd3b4..86d2d2a 100644 --- a/framework/h/tag.go +++ b/framework/h/tag.go @@ -38,6 +38,14 @@ func TextF(format string, args ...interface{}) *TextContent { return Text(fmt.Sprintf(format, args...)) } +func Details(children ...Ren) *Element { + return Tag("details", children...) +} + +func Summary(children ...Ren) *Element { + return Tag("summary", children...) +} + func Text(text string) *TextContent { return NewTextContent(text) } @@ -182,6 +190,10 @@ func Input(inputType string, children ...Ren) *Element { } } +func TextArea(children ...Ren) *Element { + return Tag("textarea", children...) +} + func TextInput(children ...Ren) *Element { return Input("text", children...) } @@ -264,9 +276,7 @@ func TagF(tag string, format string, args ...interface{}) *Element { case *AttributeMapOrdered: children = append(children, d) case *ChildList: - for _, child := range d.Children { - children = append(children, child) - } + children = append(children, d.Children...) case *AttributeR: children = append(children, d) default: @@ -462,6 +472,10 @@ func THead(children ...Ren) *Element { return Tag("thead", children...) } +func I(children ...Ren) *Element { + return Tag("i", children...) +} + func TFoot(children ...Ren) *Element { return Tag("tfoot", children...) } diff --git a/framework/h/xhr.go b/framework/h/xhr.go index ed3e999..3ed884c 100644 --- a/framework/h/xhr.go +++ b/framework/h/xhr.go @@ -2,7 +2,6 @@ package h import ( "github.com/maddalax/htmgo/framework/hx" - "strings" ) // Get adds two attributes to the element: hx-get and hx-trigger. @@ -27,19 +26,11 @@ func GetWithQs(path string, qs *Qs, trigger string) *AttributeMapOrdered { // PostPartial adds two attributes to the element: hx-post and hx-trigger, and uses the partial path for the hx-post attribute. func PostPartial(partial PartialFunc, triggers ...string) *AttributeMapOrdered { - path := GetPartialPath(partial) - if !strings.HasPrefix(path, "/") { - path = "/" + path - } - return Post(path, triggers...) + return Post(GetPartialPath(partial), triggers...) } // PostPartialWithQs adds two attributes to the element: hx-post and hx-trigger, and uses the partial path for the hx-post attribute. It also sets the query string parameters. func PostPartialWithQs(partial PartialFunc, qs *Qs, trigger ...string) *AttributeMapOrdered { - path := GetPartialPathWithQs(partial, qs) - if !strings.HasPrefix(path, "/") { - path = "/" + path - } return Post(GetPartialPathWithQs(partial, qs), trigger...) } diff --git a/framework/hx/htmx.go b/framework/hx/htmx.go index 9ce7393..20063e6 100644 --- a/framework/hx/htmx.go +++ b/framework/hx/htmx.go @@ -86,7 +86,6 @@ const ( HistoryCacheMissLoadEvent Event = "htmx:historyCacheMissLoad" HistoryRestoreEvent Event = "htmx:historyRestore" BeforeHistorySaveEvent Event = "htmx:beforeHistorySave" - LoadEvent Event = "htmx:load" NoSSESourceErrorEvent Event = "htmx:noSSESourceError" OnLoadErrorEvent Event = "htmx:onLoadError" OobAfterSwapEvent Event = "htmx:oobAfterSwap" @@ -131,6 +130,7 @@ const ( KeyPressEvent Event = "onkeypress" SubmitEvent Event = "onsubmit" LoadDomEvent Event = "onload" + LoadEvent Event = "onload" UnloadEvent Event = "onunload" ResizeEvent Event = "onresize" ScrollEvent Event = "onscroll" diff --git a/framework/internal/datastructure/map.go b/framework/internal/datastructure/map.go index 434c561..e4741d2 100644 --- a/framework/internal/datastructure/map.go +++ b/framework/internal/datastructure/map.go @@ -70,10 +70,10 @@ func (om *OrderedMap[K, V]) Values() []V { // Delete removes a key-value pair from the OrderedMap. func (om *OrderedMap[K, V]) Delete(key K) { if _, exists := om.values[key]; exists { - // Disconnect the key from the map + // Remove the key from the map delete(om.values, key) - // Disconnect the key from the keys slice + // Remove the key from the keys slice for i, k := range om.keys { if k == key { om.keys = append(om.keys[:i], om.keys[i+1:]...) diff --git a/framework/service/locator.go b/framework/service/locator.go index 855cae0..3cc79d4 100644 --- a/framework/service/locator.go +++ b/framework/service/locator.go @@ -10,6 +10,7 @@ type Lifecycle = string var ( Singleton Lifecycle = "singleton" + Transient Lifecycle = "transient" ) type Provider struct { @@ -35,6 +36,10 @@ func (l *Locator) setCache(key string, value any) { l.cache[key] = value } +func (l *Locator) clearCache(key string) { + delete(l.cache, key) +} + func (l *Locator) getCache(key string) any { return l.cache[key] } @@ -68,10 +73,12 @@ func Get[T any](locator *Locator) *T { func Set[T any](locator *Locator, lifecycle Lifecycle, value func() *T) { t := reflect.TypeOf(value) rt := t.Out(0) - locator.services[rt.String()] = Provider{ + key := rt.String() + locator.services[key] = Provider{ cb: func() any { return value() }, lifecycle: lifecycle, } + locator.clearCache(key) } diff --git a/htmgo-site/Taskfile.yml b/htmgo-site/Taskfile.yml index a6d5700..caf16b9 100644 --- a/htmgo-site/Taskfile.yml +++ b/htmgo-site/Taskfile.yml @@ -3,14 +3,14 @@ version: '3' tasks: run: cmds: - - go run github.com/maddalax/htmgo/cli@latest run + - htmgo run silent: true build: cmds: - - go run github.com/maddalax/htmgo/cli/htmgo@latest build + - htmgo build watch: cmds: - - go run github.com/maddalax/htmgo/cli@latest watch - silent: true \ No newline at end of file + - htmgo watch + silent: true diff --git a/htmgo-site/assets/public/apple-touch-icon.png b/htmgo-site/assets/public/apple-touch-icon.png new file mode 100644 index 0000000..d10e9fe Binary files /dev/null and b/htmgo-site/assets/public/apple-touch-icon.png differ diff --git a/htmgo-site/assets/public/auth-example.jpg b/htmgo-site/assets/public/auth-example.jpg new file mode 100644 index 0000000..9a66373 Binary files /dev/null and b/htmgo-site/assets/public/auth-example.jpg differ diff --git a/htmgo-site/assets/public/chat-example.jpg b/htmgo-site/assets/public/chat-example.jpg new file mode 100644 index 0000000..eb57c98 Binary files /dev/null and b/htmgo-site/assets/public/chat-example.jpg differ diff --git a/htmgo-site/assets/public/favicon.ico b/htmgo-site/assets/public/favicon.ico new file mode 100644 index 0000000..040cccf Binary files /dev/null and b/htmgo-site/assets/public/favicon.ico differ diff --git a/htmgo-site/assets/public/hn-example.jpg b/htmgo-site/assets/public/hn-example.jpg new file mode 100644 index 0000000..85f5f88 Binary files /dev/null and b/htmgo-site/assets/public/hn-example.jpg differ diff --git a/htmgo-site/assets/public/icon-192-maskable.png b/htmgo-site/assets/public/icon-192-maskable.png new file mode 100644 index 0000000..d4d6efb Binary files /dev/null and b/htmgo-site/assets/public/icon-192-maskable.png differ diff --git a/htmgo-site/assets/public/icon-192.png b/htmgo-site/assets/public/icon-192.png new file mode 100644 index 0000000..f533435 Binary files /dev/null and b/htmgo-site/assets/public/icon-192.png differ diff --git a/htmgo-site/assets/public/icon-512-maskable.png b/htmgo-site/assets/public/icon-512-maskable.png new file mode 100644 index 0000000..db61f3d Binary files /dev/null and b/htmgo-site/assets/public/icon-512-maskable.png differ diff --git a/htmgo-site/assets/public/icon-512.png b/htmgo-site/assets/public/icon-512.png new file mode 100644 index 0000000..ba0665d Binary files /dev/null and b/htmgo-site/assets/public/icon-512.png differ diff --git a/htmgo-site/assets/public/tailwind-intellisense.png b/htmgo-site/assets/public/tailwind-intellisense.png new file mode 100644 index 0000000..962c0b4 Binary files /dev/null and b/htmgo-site/assets/public/tailwind-intellisense.png differ diff --git a/htmgo-site/go.mod b/htmgo-site/go.mod index bf7a7f4..5083b4a 100644 --- a/htmgo-site/go.mod +++ b/htmgo-site/go.mod @@ -3,14 +3,18 @@ module htmgo-site go 1.23.0 require ( + github.com/alecthomas/chroma/v2 v2.14.0 github.com/google/uuid v1.6.0 - github.com/maddalax/htmgo/framework v0.0.0-20240930180419-e33ab7366d58 + github.com/maddalax/htmgo/framework v1.0.2-0.20241025174132-df3edccd7fb0 + github.com/maddalax/htmgo/tools/html-to-htmgo v0.0.0-20241025174132-df3edccd7fb0 github.com/yuin/goldmark v1.7.4 github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc ) require ( - github.com/alecthomas/chroma/v2 v2.2.0 // indirect - github.com/dlclark/regexp2 v1.7.0 // indirect + github.com/dlclark/regexp2 v1.11.0 // indirect github.com/go-chi/chi/v5 v5.1.0 // indirect + golang.org/x/net v0.30.0 // indirect + golang.org/x/text v0.19.0 // indirect + golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect ) diff --git a/htmgo-site/go.sum b/htmgo-site/go.sum index 9ed9583..76197bb 100644 --- a/htmgo-site/go.sum +++ b/htmgo-site/go.sum @@ -1,19 +1,28 @@ -github.com/alecthomas/chroma/v2 v2.2.0 h1:Aten8jfQwUqEdadVFFjNyjx7HTexhKP0XuqBG67mRDY= +github.com/alecthomas/assert/v2 v2.7.0 h1:QtqSACNS3tF7oasA8CU6A6sXZSBDqnm7RfpLl9bZqbE= +github.com/alecthomas/assert/v2 v2.7.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k= github.com/alecthomas/chroma/v2 v2.2.0/go.mod h1:vf4zrexSH54oEjJ7EdB65tGNHmH3pGZmVkgTP5RHvAs= -github.com/alecthomas/repr v0.0.0-20220113201626-b1b626ac65ae h1:zzGwJfFlFGD94CyyYwCJeSuD32Gj9GTaSi5y9hoVzdY= +github.com/alecthomas/chroma/v2 v2.14.0 h1:R3+wzpnUArGcQz7fCETQBzO5n9IMNi13iIs46aU4V9E= +github.com/alecthomas/chroma/v2 v2.14.0/go.mod h1:QolEbTfmUHIMVpBqxeDnNBj2uoeI4EbYP4i6n68SG4I= github.com/alecthomas/repr v0.0.0-20220113201626-b1b626ac65ae/go.mod h1:2kn6fqh/zIyPLmm3ugklbEi5hg5wS435eygvNfaDQL8= +github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc= +github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= 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/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= -github.com/dlclark/regexp2 v1.7.0 h1:7lJfhqlPssTb1WQx4yvTHN0uElPEv52sbaECrAQxjAo= github.com/dlclark/regexp2 v1.7.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= +github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI= +github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= github.com/go-chi/chi/v5 v5.1.0 h1:acVI1TYaD+hhedDJ3r54HyA6sExp3HfXq7QWEEY/xMw= github.com/go-chi/chi/v5 v5.1.0/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/maddalax/htmgo/framework v0.0.0-20240930180419-e33ab7366d58 h1:G1ZKaigLbmtKWy67XMhulKm4qXnAjRdrFiymCM+zX+U= -github.com/maddalax/htmgo/framework v0.0.0-20240930180419-e33ab7366d58/go.mod h1:HYKI49Pb6oyY2opSJdTt145B1vWgfWIDohvlolynv80= +github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= +github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= +github.com/maddalax/htmgo/framework v1.0.2-0.20241025174132-df3edccd7fb0 h1:K9Q5b7BmbpCPJFjrAHS8+wPdKDcZN9NMC3Fg51n5IaQ= +github.com/maddalax/htmgo/framework v1.0.2-0.20241025174132-df3edccd7fb0/go.mod h1:NGGzWVXWksrQJ9kV9SGa/A1F1Bjsgc08cN7ZVb98RqY= +github.com/maddalax/htmgo/tools/html-to-htmgo v0.0.0-20241025174132-df3edccd7fb0 h1:U57+3oRD+uGrc9Aapi/Ol1ZzRuCY2s0diK0pxWtIZeU= +github.com/maddalax/htmgo/tools/html-to-htmgo v0.0.0-20241025174132-df3edccd7fb0/go.mod h1:FraJsj3NRuLBQDk83ZVa+psbNRNLe+rajVtVhYMEme4= 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/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -25,8 +34,12 @@ github.com/yuin/goldmark v1.7.4 h1:BDXOHExt+A7gwPCJgPIIq7ENvceR7we7rOS9TNoLZeg= github.com/yuin/goldmark v1.7.4/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E= github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc h1:+IAOyRda+RLrxa1WC7umKOZRsGq4QrFFMYApOeHzQwQ= github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc/go.mod h1:ovIvrum6DQJA4QsJSovrkC4saKHQVs7TvcaeO8AIl5I= -golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo= -golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0= +golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4= +golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU= +golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= +golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/htmgo-site/htmgo.yml b/htmgo-site/htmgo.yml new file mode 100644 index 0000000..d60d2ff --- /dev/null +++ b/htmgo-site/htmgo.yml @@ -0,0 +1,10 @@ +# htmgo configuration + +# if tailwindcss is enabled, htmgo will automatically compile your tailwind and output it to assets/dist +tailwind: true + +# which directories to ignore when watching for changes, supports glob patterns through https://github.com/bmatcuk/doublestar +watch_ignore: [".git", "node_modules", "dist/*"] + +# files to watch for changes, supports glob patterns through https://github.com/bmatcuk/doublestar +watch_files: ["**/*.go", "**/*.css", "**/*.md"] diff --git a/htmgo-site/internal/datastructures/map.go b/htmgo-site/internal/datastructures/map.go index 0e016c3..533ca4f 100644 --- a/htmgo-site/internal/datastructures/map.go +++ b/htmgo-site/internal/datastructures/map.go @@ -64,10 +64,10 @@ func (om *OrderedMap[K, V]) Values() []V { // Delete removes a key-value pair from the OrderedMap. func (om *OrderedMap[K, V]) Delete(key K) { if _, exists := om.values[key]; exists { - // Disconnect the key from the map + // Remove the key from the map delete(om.values, key) - // Disconnect the key from the keys slice + // Remove the key from the keys slice for i, k := range om.keys { if k == key { om.keys = append(om.keys[:i], om.keys[i+1:]...) diff --git a/htmgo-site/internal/dirwalk/walk.go b/htmgo-site/internal/dirwalk/walk.go index 59df766..1c52090 100644 --- a/htmgo-site/internal/dirwalk/walk.go +++ b/htmgo-site/internal/dirwalk/walk.go @@ -4,8 +4,6 @@ import ( "github.com/maddalax/htmgo/framework/h" "io/fs" "os" - "slices" - "strconv" "strings" ) @@ -36,22 +34,5 @@ func WalkPages(dir string, system fs.FS) []*Page { return nil }) - var getRouteOrder = func(page *Page) int { - fileName := page.Parts[len(page.Parts)-1] - if len(fileName) > 1 && fileName[1] == '_' { - num, err := strconv.ParseInt(fileName[0:1], 10, 64) - if err != nil { - return 0 - } - page.Parts[len(page.Parts)-1] = fileName[2:] - return int(num) - } - return 0 - } - - slices.SortFunc(pages, func(a *Page, b *Page) int { - return getRouteOrder(a) - getRouteOrder(b) - }) - return pages } diff --git a/htmgo-site/internal/markdown/render.go b/htmgo-site/internal/markdown/render.go index 8677ac9..0c8f216 100644 --- a/htmgo-site/internal/markdown/render.go +++ b/htmgo-site/internal/markdown/render.go @@ -2,6 +2,8 @@ package markdown import ( "bytes" + "github.com/alecthomas/chroma/v2" + chromahtml "github.com/alecthomas/chroma/v2/formatters/html" "github.com/yuin/goldmark" highlighting "github.com/yuin/goldmark-highlighting/v2" "github.com/yuin/goldmark/extension" @@ -52,6 +54,12 @@ func RenderMarkdown(reader io.Reader) bytes.Buffer { ), goldmark.WithExtensions( highlighting.NewHighlighting( + highlighting.WithFormatOptions( + chromahtml.WithLineNumbers(true), + chromahtml.WithCustomCSS(map[chroma.TokenType]string{ + chroma.PreWrapper: "padding: 12px; overflow: auto; background-color: rgb(245, 245, 245) !important;", + }), + ), highlighting.WithStyle("github"), ), ), diff --git a/htmgo-site/internal/sitemap/generate.go b/htmgo-site/internal/sitemap/generate.go new file mode 100644 index 0000000..8a52fd6 --- /dev/null +++ b/htmgo-site/internal/sitemap/generate.go @@ -0,0 +1,64 @@ +package sitemap + +import ( + "bytes" + "encoding/xml" + "fmt" +) + +type URL struct { + Loc string `xml:"loc"` + ChangeFreq string `xml:"changefreq,omitempty"` + Priority float32 `xml:"priority,omitempty"` +} + +type URLSet struct { + XMLName xml.Name `xml:"urlset"` + XmlNS string `xml:"xmlns,attr"` + URLs []URL `xml:"url"` +} + +func NewSitemap(urls []URL) *URLSet { + return &URLSet{ + XmlNS: "https://www.sitemaps.org/schemas/sitemap/0.9", + URLs: urls, + } +} + +func serialize(sitemap *URLSet) ([]byte, error) { + buffer := bytes.Buffer{} + enc := xml.NewEncoder(&buffer) + enc.Indent("", " ") + if err := enc.Encode(sitemap); err != nil { + return make([]byte, 0), fmt.Errorf("could not encode sitemap: %w", err) + } + return buffer.Bytes(), nil +} + +func Generate() ([]byte, error) { + + urls := []URL{ + { + Loc: "/", + Priority: 0.5, + ChangeFreq: "weekly", + }, + { + Loc: "/docs", + Priority: 1.0, + ChangeFreq: "daily", + }, + { + Loc: "/examples", + Priority: 0.7, + ChangeFreq: "daily", + }, + { + Loc: "/html-to-go", + Priority: 0.5, + ChangeFreq: "weekly", + }, + } + sitemap := NewSitemap(urls) + return serialize(sitemap) +} diff --git a/htmgo-site/main.go b/htmgo-site/main.go index 50b0b30..2ee2dc7 100644 --- a/htmgo-site/main.go +++ b/htmgo-site/main.go @@ -6,6 +6,7 @@ import ( "htmgo-site/__htmgo" "htmgo-site/internal/cache" "htmgo-site/internal/markdown" + "htmgo-site/internal/sitemap" "io/fs" "net/http" ) @@ -35,6 +36,16 @@ func main() { http.FileServerFS(sub) + app.Router.Handle("/sitemap.xml", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + s, err := sitemap.Generate() + if err != nil { + http.Error(w, "failed to generate sitemap", http.StatusInternalServerError) + return + } + w.Header().Set("Content-Type", "application/xml") + w.Write(s) + })) + app.Router.Handle("/public/*", http.StripPrefix("/public", http.FileServerFS(sub))) __htmgo.Register(app.Router) diff --git a/htmgo-site/md/docs/1_quick-start/1_introduction.md b/htmgo-site/md/docs/1_quick-start/1_introduction.md index a1b0025..9300d43 100644 --- a/htmgo-site/md/docs/1_quick-start/1_introduction.md +++ b/htmgo-site/md/docs/1_quick-start/1_introduction.md @@ -1,30 +1,26 @@ -## **Introduction** +## Introduction htmgo is a lightweight pure go way to build interactive websites / web applications using go & htmx. We give you the utilities to build html using pure go code in a reusable way (go functions are components) while also providing htmx functions to add interactivity to your app. ```go func DocsPage(ctx *h.RequestContext) *h.Page { - assets := ctx.Get("embeddedMarkdown").(fs.FS) - pages := dirwalk.WalkPages("md/docs", assets) - - return h.NewPage(base.RootPage( + pages := dirwalk.WalkPages("md/docs") + return h.NewPage( h.Div( - h.Class("flex flex-col md:flex-row gap-4 justify-center mb-12"), - partials.DocSidebar(pages), + h.Class("flex flex-col md:flex-row gap-4"), + DocSidebar(pages), h.Div( h.Class("flex flex-col justify-center items-center mt-6"), h.List(pages, func(page *dirwalk.Page, index int) *h.Element { return h.Div( - h.Class("border-b border-b-slate-300 w-full pb-8 mb-8"), - MarkdownContent(ctx, - page.FilePath, - partials.CreateAnchor(page.Parts)), + h.Class("border-b border-b-slate-300"), + MarkdownContent(ctx, page), ) }), ), ), - )) + ) } ``` diff --git a/htmgo-site/md/docs/1_quick-start/2_installation.md b/htmgo-site/md/docs/1_quick-start/2_installation.md index a12f523..30b1a3e 100644 --- a/htmgo-site/md/docs/1_quick-start/2_installation.md +++ b/htmgo-site/md/docs/1_quick-start/2_installation.md @@ -1,4 +1,4 @@ -## **Getting Started** +## Getting Started ##### **Prerequisites:** diff --git a/htmgo-site/md/docs/2_core-concepts/1_pages.md b/htmgo-site/md/docs/2_core-concepts/1_pages.md index 78140c1..20c0338 100644 --- a/htmgo-site/md/docs/2_core-concepts/1_pages.md +++ b/htmgo-site/md/docs/2_core-concepts/1_pages.md @@ -1,4 +1,4 @@ -## Pages ## +## Pages Pages are the entry point of an htmgo application. diff --git a/htmgo-site/md/docs/2_core-concepts/2_partials.md b/htmgo-site/md/docs/2_core-concepts/2_partials.md index 51a14ef..9757880 100644 --- a/htmgo-site/md/docs/2_core-concepts/2_partials.md +++ b/htmgo-site/md/docs/2_core-concepts/2_partials.md @@ -1,4 +1,4 @@ -## Partials ## +## Partials Partials are where things get interesting. Partials allow you to start adding interactivity to your website by swapping in content, setting headers, redirecting, etc. diff --git a/htmgo-site/md/docs/2_core-concepts/3_components.md b/htmgo-site/md/docs/2_core-concepts/3_components.md index 01c4d62..9cbf2d2 100644 --- a/htmgo-site/md/docs/2_core-concepts/3_components.md +++ b/htmgo-site/md/docs/2_core-concepts/3_components.md @@ -1,4 +1,4 @@ -**Components** +## Components Components are re-usable bits of logic to render HTML. Similar to how in React components are Javascript functions, in htmgo, components are pure go functions. @@ -26,4 +26,4 @@ If you are familiar with React, then you would likely place this fetch logic ins With **htmgo**, the only way to update content on the page is to use htmx to swap out the content from loading a partial. Therefore you control exactly when this Card component is called, not the framework behind the scenes. -See [#interactivity-swapping](#interactivity-swapping) for more information \ No newline at end of file +See [#interactivity-swapping](#interactivity-swapping) for more information diff --git a/htmgo-site/md/docs/2_core-concepts/4_tags.md b/htmgo-site/md/docs/2_core-concepts/4_tags.md index 92912eb..aaeb8ba 100644 --- a/htmgo-site/md/docs/2_core-concepts/4_tags.md +++ b/htmgo-site/md/docs/2_core-concepts/4_tags.md @@ -1,4 +1,4 @@ -**HTML Tags** +## HTML Tags htmgo provides many methods to render html tags: diff --git a/htmgo-site/md/docs/2_core-concepts/5_attributes.md b/htmgo-site/md/docs/2_core-concepts/5_attributes.md index 75e0437..a0b4c73 100644 --- a/htmgo-site/md/docs/2_core-concepts/5_attributes.md +++ b/htmgo-site/md/docs/2_core-concepts/5_attributes.md @@ -1,4 +1,4 @@ -**Attributes** +## Attributes Attributes are one of the main ways we can add interactivity to the pages with [htmx](http://htmx.org). If you have not read over the htmx documentation, please do so before continuing. diff --git a/htmgo-site/md/docs/2_core-concepts/6_raw_html.md b/htmgo-site/md/docs/2_core-concepts/6_raw_html.md index 3e3e7d2..055639c 100644 --- a/htmgo-site/md/docs/2_core-concepts/6_raw_html.md +++ b/htmgo-site/md/docs/2_core-concepts/6_raw_html.md @@ -1,4 +1,4 @@ -**Rendering Raw Html** +## Rendering Raw Html In some cases, you may want to render raw HTML instead of using htmgo's functions. This can be done by using the following methods: @@ -19,4 +19,4 @@ h.UnsafeRawScript("alert('Hello World')") Important: Be careful when using these methods, these methods do not escape the HTML content and should **never** be used with user input unless you have sanitized the input. -Sanitizing input can be done using the `html.EscapeString` function or by using https://github.com/microcosm-cc/bluemonday. \ No newline at end of file +Sanitizing input can be done using the `html.EscapeString` function or by using https://github.com/microcosm-cc/bluemonday. diff --git a/htmgo-site/md/docs/3_control/1_If Else.md b/htmgo-site/md/docs/3_control/1_If Else.md index 2e441cc..3477237 100644 --- a/htmgo-site/md/docs/3_control/1_If Else.md +++ b/htmgo-site/md/docs/3_control/1_If Else.md @@ -1,4 +1,4 @@ -**If / Else Statements** +## Conditional Statements If / else statements are useful when you want to conditionally render attributes or elements / components. diff --git a/htmgo-site/md/docs/3_control/2_loops.md b/htmgo-site/md/docs/3_control/2_loops.md index e3ec62a..c42e87c 100644 --- a/htmgo-site/md/docs/3_control/2_loops.md +++ b/htmgo-site/md/docs/3_control/2_loops.md @@ -1,4 +1,4 @@ -**Loops / Dealing With Lists** +## Loops / Dealing With Lists Very commonly you will need to render a list or slice of items onto the page. Frameworks generally solve this in different ways, such as React uses regular JS .map function to solve it. diff --git a/htmgo-site/md/docs/4_interactivity/1_swapping.md b/htmgo-site/md/docs/4_interactivity/1_swapping.md index 6a9cdc6..d8ba5eb 100644 --- a/htmgo-site/md/docs/4_interactivity/1_swapping.md +++ b/htmgo-site/md/docs/4_interactivity/1_swapping.md @@ -1,4 +1,4 @@ -### Interactivity +## Interactivity / Swapping 1. Adding interactivity to your website is done through [htmx](http://htmx.org) by utilizing various attributes/headers. This should cover most use cases. htmgo offers utility methods to make this process a bit easier @@ -82,4 +82,4 @@ When the **CompleteAll** button is clicked, a **POST** will be sent to the **Com Note: These partial swap methods use https://htmx.org/attributes/hx-swap-oob/ behind the scenes, so it must match the swap target by id. -**If** you are only wanting to swap the element that made the xhr request for the partial in the first place, just use `h.NewPartial` instead, it will use the default htmx swapping, and not hx-swap-oob. \ No newline at end of file +**If** you are only wanting to swap the element that made the xhr request for the partial in the first place, just use `h.NewPartial` instead, it will use the default htmx swapping, and not hx-swap-oob. diff --git a/htmgo-site/md/docs/4_interactivity/2_events.md b/htmgo-site/md/docs/4_interactivity/2_events.md index 40664b5..ed750d8 100644 --- a/htmgo-site/md/docs/4_interactivity/2_events.md +++ b/htmgo-site/md/docs/4_interactivity/2_events.md @@ -1,4 +1,4 @@ -**Events** +## Events Handlers / Commands Sometimes you need to update elements client side without having to do a network call. For this you generally have to target an element with javascript and set an attribute, change the innerHTML, etc. @@ -42,81 +42,4 @@ OnClick(cmd ...Command) *LifeCycle HxOnAfterSwap(cmd ...Command) *LifeCycle HxOnLoad(cmd ...Command) *LifeCycle ``` -**Note:** Each command you attach to the event handler will be passed 'self' and 'event' (if applicable) as arguments. -'self' is the current element, and 'event' is the event object. - -If you use the OnEvent directly, event names may be any [HTML DOM](https://www.w3schools.com/jsref/dom_obj_event.asp) events, or any [HTMX events](https://htmx.org/events/). - -Commands: - -```go -js.AddAttribute(string, value) -js.RemoveAttribute(string) -js.AddClass(string, value) -js.SetText(string) -js.Increment(count) -js.SetInnerHtml(Ren) -js.SetOuterHtml(Ren) -js.SetDisabled(bool) -js.RemoveClass(string) -js.Alert(string) -js.EvalJs(string) // eval arbitrary js, use 'self' to get the current element as a reference -js.InjectScript(string) -js.InjectScriptIfNotExist(string) -js.GetPartial(PartialFunc) -js.GetPartialWithQs(PartialFunc, Qs) -js.PostPartial(PartialFunc) -js.PostPartialWithQs(PartialFunc, Qs) -js.GetWithQs(string, Qs) -js.PostWithQs(string, Qs) -js.ToggleClass(string) -js.ToggleClassOnElement(string, string) - -// The following methods are used to evaluate JS on nearby elements. -// Use 'element' to get the element as a reference for the EvalJs methods. -js.EvalJsOnParent(string) -js.EvalJsOnSibling(string, string) -js.EvalJsOnChildren(string, string) -js.SetClassOnParent(string) -js.RemoveClassOnParent(string) -js.SetClassOnChildren(string, string) -js.RemoveClassOnChildren(string, string) -js.SetClassOnSibling(string, string) -js.RemoveClassOnSibling(string, string) - -``` -For more usages: see https://github.com/maddalax/htmgo/blob/master/htmgo-site/pages/form.go - - -**Example:** Evaluating arbitrary JS - -```go -func MyButton() *h.Element { - return h.Button( - h.Text("Submit"), - h.OnClick( - // make sure you use 'self' instead of 'this' - // for referencing the current element - h.EvalJs(` - if(Math.random() > 0.5) { - self.innerHTML = "Success!"; - } - `, - ), - ), - ) -} -``` - -tip: If you are using Jetbrains IDE's, you can write `// language=js` as a comment above the function call (h.EvalJS) and it will automatically give you syntax highlighting on the raw JS. - -```go -// language=js -h.EvalJs(` - if(Math.random() > 0.5) { - self.innerHTML = "Success!"; - } - `, -), -``` diff --git a/htmgo-site/md/docs/4_interactivity/3_evaluating_javascript.md b/htmgo-site/md/docs/4_interactivity/3_evaluating_javascript.md new file mode 100644 index 0000000..37affdb --- /dev/null +++ b/htmgo-site/md/docs/4_interactivity/3_evaluating_javascript.md @@ -0,0 +1,85 @@ +## Evaluating Javascript In Event Handlers + +Event handlers are useful by attaching **commands** to elements to execute javascript on the client side. + +See [#interactivity-events](#interactivity-events) for more information on event handlers. + +
+ +**Note:** Each command you attach to the event handler will be passed 'self' and 'event' (if applicable) as arguments. +'self' is the current element, and 'event' is the event object. + +If you use the OnEvent directly, event names may be any [HTML DOM](https://www.w3schools.com/jsref/dom_obj_event.asp) events, or any [HTMX events](https://htmx.org/events/). + +Commands: + +```go +js.AddAttribute(string, value) +js.RemoveAttribute(string) +js.AddClass(string, value) +js.SetText(string) +js.Increment(count) +js.SetInnerHtml(Ren) +js.SetOuterHtml(Ren) +js.SetDisabled(bool) +js.RemoveClass(string) +js.Alert(string) +js.EvalJs(string) // eval arbitrary js, use 'self' to get the current element as a reference +js.InjectScript(string) +js.InjectScriptIfNotExist(string) +js.GetPartial(PartialFunc) +js.GetPartialWithQs(PartialFunc, Qs) +js.PostPartial(PartialFunc) +js.PostPartialWithQs(PartialFunc, Qs) +js.GetWithQs(string, Qs) +js.PostWithQs(string, Qs) +js.ToggleClass(string) +js.ToggleClassOnElement(string, string) + +// The following methods are used to evaluate JS on nearby elements. +// Use 'element' to get the element as a reference for the EvalJs methods. +js.EvalJsOnParent(string) +js.EvalJsOnSibling(string, string) +js.EvalJsOnChildren(string, string) +js.SetClassOnParent(string) +js.RemoveClassOnParent(string) +js.SetClassOnChildren(string, string) +js.RemoveClassOnChildren(string, string) +js.SetClassOnSibling(string, string) +js.RemoveClassOnSibling(string, string) + +``` +For more usages: see https://github.com/maddalax/htmgo/blob/master/htmgo-site/pages/form.go + + +**Example:** Evaluating arbitrary JS + +```go +func MyButton() *h.Element { + return h.Button( + h.Text("Submit"), + h.OnClick( + // make sure you use 'self' instead of 'this' + // for referencing the current element + h.EvalJs(` + if(Math.random() > 0.5) { + self.innerHTML = "Success!"; + } + `, + ), + ), + ) +} +``` + +tip: If you are using Jetbrains IDE's, you can write `// language=js` as a comment above the function call (h.EvalJS) and it will automatically give you syntax highlighting on the raw JS. + +```go +// language=js +h.EvalJs(` + if(Math.random() > 0.5) { + self.innerHTML = "Success!"; + } + `, +), +``` diff --git a/htmgo-site/md/docs/5_performance/1_caching_globally.md b/htmgo-site/md/docs/5_performance/1_caching_globally.md index 8ba3258..d8c7e9c 100644 --- a/htmgo-site/md/docs/5_performance/1_caching_globally.md +++ b/htmgo-site/md/docs/5_performance/1_caching_globally.md @@ -1,4 +1,5 @@ -**Caching Components Globally** +## Performance +### Caching Components Globally You may want to cache components to improve performance. This is especially useful for components that are expensive to render or make external requests for data. diff --git a/htmgo-site/md/docs/5_performance/1_caching_per_user.md b/htmgo-site/md/docs/5_performance/1_caching_per_user.md index 5052fef..6ba40b0 100644 --- a/htmgo-site/md/docs/5_performance/1_caching_per_user.md +++ b/htmgo-site/md/docs/5_performance/1_caching_per_user.md @@ -1,4 +1,4 @@ -**Caching Components Per User** +### Caching Components Per User If you need to cache a component per user, you can use the `CachedPerKey` functions. These functions allow you to cache a component by a specific key. This key can be any string that uniquely identifies the user. diff --git a/htmgo-site/md/docs/6_pushing_data/1_server_sent_events.md b/htmgo-site/md/docs/6_pushing_data/1_server_sent_events.md new file mode 100644 index 0000000..2a185ab --- /dev/null +++ b/htmgo-site/md/docs/6_pushing_data/1_server_sent_events.md @@ -0,0 +1,65 @@ +## Server Sent Events (SSE) + +htmgo supports server-sent events (SSE) out of the box. +This allows you to push data from the server to the client in real-time. + +Example of this can be found in the [chat-app](https://github.com/maddalax/htmgo/tree/master/examples/chat) example. +Demo: https://chat-example.htmgo.dev + +## How it works ## +1. The client sends a request to the server to establish a connection. +2. The server holds the connection open and sends data (in our case, most likely elements) to the client whenever there is new data to send. +3. The htmgo SSE extension uses https://htmx.org/attributes/hx-swap-oob/ to swap out the elements that the server sends. + + +**Note**: SSE is **unidirectional** (the server can only send data to the client). +For the client to send data to the server, normal xhr behavior should be used (form submission, triggers, etc). + +## Usage +1. Add the SSE connection attribute and the path to the handler that will handle the connection. + +```go +h.Attribute("sse-connect", fmt.Sprintf("/chat/%s", roomId)) +``` + +The following **Event Handlers** can be used to react to SSE connections. +```go +h.HxOnSseOpen +h.HxBeforeSseMessage +h.HxAfterSseMessage +h.HxOnSseError +h.HxOnSseClose +h.HxOnSseConnecting +``` + +**Example:** Adding an event listener handle SSE errors. + +```go +h.HxOnSseError( + js.EvalJs(fmt.Sprintf(` + const reason = e.detail.event.data + if(['invalid room', 'no session', 'invalid user'].includes(reason)) { + window.location.href = '/?roomId=%s'; + } else if(e.detail.event.code === 1011) { + window.location.reload() + } else if (e.detail.event.code === 1008 || e.detail.event.code === 1006) { + window.location.href = '/?roomId=%s'; + } else { + console.error('Connection closed:', e.detail.event) + } + `, roomId, roomId)), +), +``` + +**Example:** Clearing the input field after sending a message. +```go +func MessageInput() *h.Element { + return h.Input("text", + h.Id("message-input"), + h.Required(), + h.HxAfterSseMessage( + js.SetValue(""), + ), + ) +} +``` diff --git a/htmgo-site/md/docs/7_htmx_extensions/1_overview.md b/htmgo-site/md/docs/7_htmx_extensions/1_overview.md new file mode 100644 index 0000000..b47de67 --- /dev/null +++ b/htmgo-site/md/docs/7_htmx_extensions/1_overview.md @@ -0,0 +1,34 @@ +## HTMX Extensions + +htmgo provides a few extra htmx extensions to make common tasks easier. +Some of these extensions are optional, and some of these are required for htmgo to work correctly. + +The following extensions are provided by htmgo: +- [Trigger Children](#htmx-extensions-trigger-children) +- [Mutation Error](#htmx-extensions-mutation-error) +- [SSE](#pushing-data-server-sent-events) +- [Path Deps](https://github.com/bigskysoftware/htmx-extensions/blob/main/src/path-deps/README.md) + +Default extensions should be included in your project by adding the following attribute to your html tag. +```go +h.Html( + h.HxExtension(h.BaseExtensions()) +) +``` + +If you need to combine multiple extensions, you can use: + +```go +h.HxExtensions(h.BaseExtensions(), "my-extension"), +``` +or +```go +h.JoinExtensions( + h.HxExtension("sse"), + h.HxExtension("my-extension"), +), +``` + + +**Important**: h.BaseExtensions will add the the 'htmgo' extension, which is a required extension for inline scripts to work properly, please always include it in your project. + diff --git a/htmgo-site/md/docs/7_htmx_extensions/2_trigger_children.md b/htmgo-site/md/docs/7_htmx_extensions/2_trigger_children.md new file mode 100644 index 0000000..0caf026 --- /dev/null +++ b/htmgo-site/md/docs/7_htmx_extensions/2_trigger_children.md @@ -0,0 +1,13 @@ +## HTMX Extensions - Trigger Children + +The `trigger-children` extension allows you to trigger an event on all children and siblings of an element. + +This is useful for things such as: +1. Letting a child element (such as a button) inside a form know the form was submitted + +
+ +**Example:** https://github.com/maddalax/htmgo/blob/master/htmgo-site/pages/form.go#L17 + +In this example: The trigger-children extension will trigger **hx-before-request** and **hx-after-request** +on all children of the form when the form is submitted, and the button reacts to that by showing a loading state. diff --git a/htmgo-site/md/docs/7_htmx_extensions/3_mutation_error.md b/htmgo-site/md/docs/7_htmx_extensions/3_mutation_error.md new file mode 100644 index 0000000..467206a --- /dev/null +++ b/htmgo-site/md/docs/7_htmx_extensions/3_mutation_error.md @@ -0,0 +1,24 @@ +## HTMX Extensions - Mutation Error + +The `mutation-error` extension allows you to trigger an event when a request returns a >= 400 status code. + +This is useful for things such as: +1. Letting a child element (such as a button) inside a form know there was an error. + +
+ +**Example:** +```go +h.Form( + h.HxTriggerChildren(), + h.HxMutationError( + js.Alert("An error occurred"), + ), + h.Button( + h.Type("submit"), + h.Text("Submit"), + ), +) +``` + +It can also be used on children elements that do not make an xhr request, if you combine it with the `hx-trigger-children` extension. diff --git a/htmgo-site/md/docs/8_miscellaneous/1_tailwind_intellisense.md b/htmgo-site/md/docs/8_miscellaneous/1_tailwind_intellisense.md new file mode 100644 index 0000000..8c958dc --- /dev/null +++ b/htmgo-site/md/docs/8_miscellaneous/1_tailwind_intellisense.md @@ -0,0 +1,53 @@ +## Tailwind intellisense + +Tailwind's language server allows you to specify custom configuration on what it should match to start giving you tailwind intellisense. + + +![](/public/tailwind-intellisense.png) + +To make this work, you will need to update the tailwind lsp config with the config below: + +Main thing to note here is +1. "go" is added to the includeLanguages list +2. classRegex is updated to match the tailwind classes in the go code. + +### Jetbrains IDE's (GoLand) +```json +{ + "includeLanguages": { + "go": "html" + }, + "experimental": { + "configFile": null, + "classRegex": [ + ["Class\\(([^)]*)\\)", "[\"`]([^\"`]*)[\"`]"], + ["ClassX\\(([^)]*)\\)", "[\"`]([^\"`]*)[\"`]"], + ["ClassIf\\(([^)]*)\\)", "[\"`]([^\"`]*)[\"`]"], + ["Classes\\(([^)]*)\\)", "[\"`]([^\"`]*)[\"`]"] + ] + } +} +``` +To find this configuration in GoLand you can go to `Settings -> Languages & Frameworks -> Style Sheets -> Tailwind CSS` and update the configuration there. +These changes are additive, add these options to your existing tailwind lsp config, instead of replacing the entire file. + +See more: https://github.com/tailwindlabs/tailwindcss/issues/7553#issuecomment-735915659 + +
+ +### Visual Studio Code +For VSCode, you should be able to update your settings.json with the following values: + +```json +{ + "tailwindCSS.includeLanguages": { + "go": "html" + }, + "tailwindCSS.experimental.classRegex": [ + ["Class\\(([^)]*)\\)", "[\"`]([^\"`]*)[\"`]"], + ["ClassX\\(([^)]*)\\)", "[\"`]([^\"`]*)[\"`]"], + ["ClassIf\\(([^)]*)\\)", "[\"`]([^\"`]*)[\"`]"], + ["Classes\\(([^)]*)\\)", "[\"`]([^\"`]*)[\"`"] + ] +} +``` diff --git a/htmgo-site/md/docs/8_miscellaneous/2_converting_raw_html_to_go.md b/htmgo-site/md/docs/8_miscellaneous/2_converting_raw_html_to_go.md new file mode 100644 index 0000000..54fb86c --- /dev/null +++ b/htmgo-site/md/docs/8_miscellaneous/2_converting_raw_html_to_go.md @@ -0,0 +1,4 @@ +## Converting Raw HTML to Go + +In some cases, you may want to convert raw HTML to Go code. +A tool to do this automatically is available here: https://htmgo.dev/html-to-go diff --git a/htmgo-site/md/docs/8_miscellaneous/3_htmgo format.md b/htmgo-site/md/docs/8_miscellaneous/3_htmgo format.md new file mode 100644 index 0000000..18b124d --- /dev/null +++ b/htmgo-site/md/docs/8_miscellaneous/3_htmgo format.md @@ -0,0 +1,60 @@ +## Htmgo Format + +htmgo has a built-in formatter that can be used to format htmgo element blocks. + +It is available through the 'htmgo' cli tool that is installed with htmgo. + +**Note:** if you have previously installed htmgo, you will need to run `GOPROXY=direct go install github.com/maddalax/htmgo/cli/htmgo@latest` to update the cli tool. + +
+To use it, run the following command: + +```bash +// format all .go files in the current directory recursively +htmgo format . + +// format the file specified +htmgo format ./my-file.go +``` + +This will format all htmgo element blocks in your project. + +**Example:** + +```go +h.Div( + h.Class("flex gap-2"), h.Text("hello"), h.Text("world"), +) +``` + +**Output:** + +```go +h.Div( + h.Class("flex gap-2"), + h.Text("hello"), + h.Text("world"), +) +``` + +## Running htmgo format on save + +### Jetbrains IDE's + +1. Go to Settings -> Tools -> File Watchers -> + custom + +2. Set the following values: + +```yaml +Name: htmgo format +File Type: Go +Scope: Current File +Program: htmgo +Arguments: format $FilePath$ +Output paths to refresh: $FilePath$ +Working directory: $ProjectFileDir$ +``` + +3. Save the file watcher and ensure it is enabled + +4. Go to `Settings -> Tools -> Actions On Save` and ensure the `htmgo format` action is enabled diff --git a/htmgo-site/md/docs/9_configuration/htmgo_config.md b/htmgo-site/md/docs/9_configuration/htmgo_config.md new file mode 100644 index 0000000..6ba4b79 --- /dev/null +++ b/htmgo-site/md/docs/9_configuration/htmgo_config.md @@ -0,0 +1,26 @@ +## Htmgo Configuration: + +Certain aspects of htmgo can be configured via a `htmgo.yml` file in the root of your project. + +Here is an example configuration file: + +```yaml +# htmgo configuration + +# if tailwindcss is enabled, htmgo will automatically compile your tailwind and output it to assets/dist +tailwind: true + +# which directories to ignore when watching for changes, supports glob patterns through https://github.com/bmatcuk/doublestar +watch_ignore: [".git", "node_modules", "dist/*"] + +# files to watch for changes, supports glob patterns through https://github.com/bmatcuk/doublestar +watch_files: ["**/*.go", "**/*.css", "**/*.md"] + +# files or directories to ignore when automatically registering routes for pages +# supports glob patterns through https://github.com/bmatcuk/doublestar +automatic_page_routing_ignore: ["root.go"] + +# files or directories to ignore when automatically registering routes for partials +# supports glob patterns through https://github.com/bmatcuk/doublestar +automatic_partial_routing_ignore: [] +``` diff --git a/htmgo-site/md/docs/8_troubleshooting/1_common_issues.md b/htmgo-site/md/docs/9_troubleshooting/1_common_issues.md similarity index 75% rename from htmgo-site/md/docs/8_troubleshooting/1_common_issues.md rename to htmgo-site/md/docs/9_troubleshooting/1_common_issues.md index 3e64a82..e9d9792 100644 --- a/htmgo-site/md/docs/8_troubleshooting/1_common_issues.md +++ b/htmgo-site/md/docs/9_troubleshooting/1_common_issues.md @@ -1,4 +1,4 @@ -## **Troubleshooting:** +## Troubleshooting: **command not found: htmgo** -ensure you installed htmgo above and ensure GOPATH is set in your shell \ No newline at end of file +ensure you installed htmgo above and ensure GOPATH is set in your shell diff --git a/htmgo-site/md/index.md b/htmgo-site/md/index.md index b967f01..5325c88 100644 --- a/htmgo-site/md/index.md +++ b/htmgo-site/md/index.md @@ -22,5 +22,23 @@ func IndexPage(ctx *h.RequestContext) *h.Page { 2. live reload (rebuilds css, go, ent schema, and routes upon change) 3. automatic page and partial registration based on file path 4. built in tailwindcss support, no need to configure anything by default -5. plugin architecture to include optional plugins to streamline development, such as http://entgo.io -6. custom [htmx extensions](https://github.com/maddalax/htmgo/tree/b610aefa36e648b98a13823a6f8d87566120cfcc/framework/assets/js/htmxextensions) to reduce boilerplate with common tasks +5. custom [htmx extensions](https://github.com/maddalax/htmgo/tree/master/framework/assets/js/htmxextensions) to reduce boilerplate with common tasks + +------ + +**what can be built with htmgo?** + +Most web applications can be built with htmgo, including but not limited to: + +- traditional business CRUD applications +- blogs +- documentation sites +- consumer facing websites +- internal tools +- and more + +
+ +For a more detailed overview of when you should use hypermedia to build web applications, see [when-to-use-hypermedia](https://htmx.org/essays/when-to-use-hypermedia/) from htmx.org. + +Interested in some examples? Check out [examples](/examples). diff --git a/htmgo-site/pages/base/root.go b/htmgo-site/pages/base/root.go index 04b79a6..54d7a3c 100644 --- a/htmgo-site/pages/base/root.go +++ b/htmgo-site/pages/base/root.go @@ -13,16 +13,21 @@ func RootPage(ctx *h.RequestContext, children ...h.Ren) *h.Element { description := "build simple and scalable systems with go + htmx" return h.Html( - h.HxExtension(h.BaseExtensions()), + h.HxExtension( + h.BaseExtensions(), + ), h.Head( h.Meta("viewport", "width=device-width, initial-scale=1"), h.Meta("title", title), + h.Link("/public/favicon.ico", "icon"), + h.Link("/public/apple-touch-icon.png", "apple-touch-icon"), h.Meta("charset", "utf-8"), h.Meta("author", "htmgo"), h.Meta("description", description), h.Meta("og:title", title), h.Meta("og:url", "https://htmgo.dev"), h.Link("canonical", "https://htmgo.dev"), + h.Link("https://cdn.jsdelivr.net/npm/@docsearch/css@3", "stylesheet"), h.Meta("og:description", description), h.LinkWithVersion("/public/main.css", "stylesheet", Version), h.ScriptWithVersion("/public/htmgo.js", Version), @@ -33,9 +38,34 @@ func RootPage(ctx *h.RequestContext, children ...h.Ren) *h.Element { `), ), h.Body( - h.Class("bg-stone-50 min-h-screen overflow-x-hidden"), - partials.NavBar(ctx, false), + h.Class("bg-white h-screen"), h.Fragment(children...), + h.Script("https://cdn.jsdelivr.net/npm/@docsearch/js@3"), + h.UnsafeRawScript(` + docsearch({ + insights: true, + appId: "9IO2WZA8L1", + apiKey: "d8cd8b6f8f8a0c961ce971e09dbde90a", + indexName: "htmgo", + container: "#search-container", + debug: false + }); + `), + ), + ) +} + +func PageWithNav(ctx *h.RequestContext, children ...h.Ren) *h.Element { + return RootPage( + ctx, + h.Fragment( + partials.NavBar(ctx, partials.NavBarProps{ + Expanded: false, + ShowPreRelease: true, + }), + h.Div( + children..., + ), ), ) } diff --git a/htmgo-site/pages/docs.go b/htmgo-site/pages/docs.go index 6f55173..c08fa78 100644 --- a/htmgo-site/pages/docs.go +++ b/htmgo-site/pages/docs.go @@ -15,37 +15,49 @@ func DocsPage(ctx *h.RequestContext) *h.Page { return h.NewPage(base.RootPage( ctx, h.Div( - h.Class("flex flex-col md:flex-row gap-6 justify-center overflow-x-hidden"), + h.Class("flex h-full"), h.Aside( - h.Class("md:h-screen md:sticky md:top-0 md:w-42"), // Applied sticky positioning here + h.Class("hidden md:block md:min-w-60 text-white overflow-y-auto"), partials.DocSidebar(pages), ), - h.Main( - h.Class("md:flex gap-4 justify-center mb-6"), - h.Div( - h.Class("flex flex-col"), + h.Div( + h.Class("flex flex-col flex-1 overflow-hidden"), + partials.NavBar(ctx, partials.NavBarProps{ + Expanded: false, + ShowPreRelease: false, + }), + h.Main( h.Div( - h.Class("flex flex-col justify-center items-center md:mt-6 "), - h.List(pages, func(page *dirwalk.Page, index int) *h.Element { - anchor := partials.CreateAnchor(page.Parts) - return h.Div( - h.Class("border-b border-b-slate-300 w-full pb-8 p-4 md:px-0 -mb-2"), - MarkdownContent(ctx, page.FilePath, anchor), - h.Div( - h.Class("ml-4 pl-1 mt-2 bg-rose-200"), - h.If(anchor == "core-concepts-partials", - h.GetPartial(partials.CurrentTimePartial, "load, every 1s"), - ), - ), - ) - }), + h.Class("w-full md:hidden bg-neutral-50 overflow-y-auto"), + partials.DocSidebar(pages), ), + h.Class("overflow-y-auto justify-center overflow-x-hidden pb-6 items-center w-full"), h.Div( - h.Class("flex justify-center items-center mt-6"), - h.A( - h.Text("Back to Top"), - h.Class("py-2 px-3 bg-slate-800 rounded text-white"), - h.Href("#"), + h.Class("flex flex-col mx-auto"), + h.Div( + h.Class("flex flex-col justify-center items-center md:mt-6 mx-auto"), + h.List(pages, func(page *dirwalk.Page, index int) *h.Element { + anchor := partials.CreateAnchor(page.Parts) + return h.Div( + h.Class("border-b border-b-slate-300 w-full pb-8 p-4 md:px-0 -mb-2"), + MarkdownContent(ctx, page.FilePath, anchor), + h.Div( + h.Class("ml-4 pl-1 mt-2 bg-rose-200"), + h.If( + anchor == "core-concepts-partials", + h.GetPartial(partials.CurrentTimePartial, "load, every 1s"), + ), + ), + ) + }), + ), + h.Div( + h.Class("flex justify-center items-center mt-6"), + h.A( + h.Text("Back to Top"), + h.Class("py-2 px-3 bg-slate-800 rounded text-white"), + h.Href("#quick-start-introduction"), + ), ), ), ), diff --git a/htmgo-site/pages/examples.go b/htmgo-site/pages/examples.go index 7a8d6c2..ceefac9 100644 --- a/htmgo-site/pages/examples.go +++ b/htmgo-site/pages/examples.go @@ -14,6 +14,27 @@ type Example struct { } var examples = []Example{ + { + Title: "User Authentication Example", + Github: "https://github.com/maddalax/htmgo/tree/master/examples/simple-auth", + Description: "An example showing basic user registration and login with htmgo", + Demo: "https://auth-example.htmgo.dev", + Image: "public/auth-example.jpg", + }, + { + Title: "Hacker News Clone", + Github: "https://github.com/maddalax/htmgo/tree/master/examples/hackernews", + Description: "A hacker news reader clone built with htmgo", + Demo: "https://hn.htmgo.dev", + Image: "public/hn-example.jpg", + }, + { + Title: "Chat App Example", + Github: "https://github.com/maddalax/htmgo/tree/master/examples/chat", + Description: "A simple chat application built with htmgo using SSE for real-time updates", + Demo: "https://chat-example.htmgo.dev", + Image: "public/chat-example.jpg", + }, { Title: "Todo List MVC", Github: "https://github.com/maddalax/htmgo/tree/master/examples/todo-list", @@ -36,69 +57,80 @@ var examples = []Example{ func ExamplesPage(ctx *h.RequestContext) *h.Page { return h.NewPage( - base.RootPage(ctx, h.Div( - h.Class("flex items-center justify-center"), + base.PageWithNav( + ctx, h.Div( - h.Class("w-full px-4 flex flex-col prose max-w-[95vw] md:max-w-3xl mt-6"), + h.Class("flex items-center justify-center"), h.Div( - h.Class("flex flex-col mb-6 md:mb-0 md:flex-row justify-between items-center"), + h.Class("w-full px-4 flex flex-col prose max-w-[95vw] md:max-w-3xl mt-6"), h.Div( - h.H1( - h.Class("text-center md:text-left"), - h.Text("htmgo examples"), + h.Class("flex flex-col mb-6 md:mb-0 md:flex-row justify-between items-center"), + h.Div( + h.H1( + h.Class("text-center md:text-left"), + h.Text("htmgo examples"), + ), + h.H3( + h.Class("-mt-4"), + h.TextF("example projects built with htmgo"), + ), ), - h.H3( - h.Class("-mt-4"), - h.TextF("example projects built with htmgo"), + ), + h.Div( + h.Class("border-b border-b-slate-200 h-1"), + h.Div( + h.Class("mt-4"), + ExampleCards(), ), ), ), - h.Div( - h.Class("border-b border-b-slate-200 h-1"), - h.Div( - h.Class("mt-4"), - ExampleCards(), - ), - ), - )), + ), ), ) } func ExampleCards() *h.Element { return h.Div( - h.Class("prose-h2:my-1 prose-img:my-1 flex flex-col md:flex-row gap-6 md:gap-2 text-center pb-8"), // Left-aligns and allows multiple cards in a row + h.Class("prose-h2:my-1 prose-img:my-1 grid grid-cols-1 gap-6 text-center pb-8"), h.List(examples, func(example Example, index int) *h.Element { return h.Div( - h.Class("border border-gray-200 shadow-sm rounded-md px-4 pb-4 w-full md:w-1/2 bg-neutral-100"), // Reduces padding + h.Class("border border-gray-200 shadow-sm rounded-md px-4 pb-4 bg-neutral-100"), h.Div( h.Class("flex flex-col gap-1 mt-4"), h.H2( - h.Class("text-lg text-center mb-1"), // Reduced margin at the bottom of the title + h.Class("text-lg text-center mb-1"), h.Text(example.Title), ), - h.If(example.Image != "", h.Div( - h.A( - h.Href(example.Demo), - h.Class("not-prose"), - h.Img( - h.Src(example.Image), - h.Class("md:w-full rounded-md mx-auto"), + h.If( + example.Image != "", + h.Div( + h.A( + h.Href(example.Demo), + h.Class("not-prose"), + h.Img( + h.Src(example.Image), + h.Class("w-[75%] rounded-md mx-auto"), + ), ), - ), // Ensures image is centered within the card - )), - h.If(example.Description != "", h.Pf(example.Description)), + ), + ), + h.If( + example.Description != "", + h.Div( + h.Pf(example.Description), + ), + ), h.Div( h.Div( - h.Class("flex gap-2 justify-center mt-2"), // Slight margin-top for spacing from the image + h.Class("flex gap-2 justify-center mt-2"), h.A( h.Href(example.Github), - h.Class("not-prose p-2 bg-slate-900 text-white rounded-md"), // Reduced padding for the buttons + h.Class("not-prose p-2 bg-slate-900 text-white rounded-md"), h.Text("Github"), ), h.A( h.Href(example.Demo), - h.Class("not-prose p-2 bg-slate-900 text-white rounded-md"), // Reduced padding for the buttons + h.Class("not-prose p-2 bg-slate-900 text-white rounded-md"), h.Text("Demo"), ), ), diff --git a/htmgo-site/pages/form.go b/htmgo-site/pages/form.go index 9415075..fdd9d24 100644 --- a/htmgo-site/pages/form.go +++ b/htmgo-site/pages/form.go @@ -9,21 +9,29 @@ import ( ) func Form(ctx *h.RequestContext) *h.Page { - return h.NewPage(base.RootPage(ctx, + return h.NewPage(base.RootPage( + ctx, h.Div( h.Class("flex flex-col items-center justify-center p-4 gap-6"), - h.H2F("Form submission with loading state example", h.Class("text-2xl font-bold")), + h.H2F( + "Form submission with loading state example", + h.Class("text-2xl font-bold"), + ), h.Form( h.TriggerChildren(), h.PostPartial(partials.SubmitForm), h.Class("flex flex-col gap-2"), h.LabelFor("name", "Your Name"), - h.Input("text", + h.Input( + "text", h.Required(), h.Class("p-4 rounded-md border border-slate-200"), h.Name("name"), h.Placeholder("Name"), - h.OnEvent(hx.KeyDownEvent, js.SubmitFormOnEnter()), + h.OnEvent( + hx.KeyDownEvent, + js.SubmitFormOnEnter(), + ), ), SubmitButton(), ), diff --git a/htmgo-site/pages/html-to-go.go b/htmgo-site/pages/html-to-go.go new file mode 100644 index 0000000..face609 --- /dev/null +++ b/htmgo-site/pages/html-to-go.go @@ -0,0 +1,27 @@ +package pages + +import ( + "github.com/maddalax/htmgo/framework/h" + "htmgo-site/pages/base" + "htmgo-site/partials" +) + +func HtmlToGoPage(ctx *h.RequestContext) *h.Page { + return h.NewPage( + base.PageWithNav( + ctx, + h.Div( + h.Class("flex flex-col h-screen items-center justify-center w-full pt-6"), + h.H3( + h.Text("Convert raw html to htmgo code"), + h.Class("text-2xl font-bold"), + ), + h.Div( + h.Class("h-full w-full flex gap-4 p-8"), + partials.HtmlInput(), + partials.GoOutput(""), + ), + ), + ), + ) +} diff --git a/htmgo-site/pages/index.go b/htmgo-site/pages/index.go index 91cde87..1b08002 100644 --- a/htmgo-site/pages/index.go +++ b/htmgo-site/pages/index.go @@ -7,37 +7,43 @@ import ( func IndexPage(ctx *h.RequestContext) *h.Page { return h.NewPage( - base.RootPage(ctx, h.Div( - h.Class("flex items-center justify-center"), + base.PageWithNav( + ctx, h.Div( - h.Class("w-full px-4 flex flex-col prose md:max-w-3xl mt-6 mx-auto"), + h.Class("flex items-center justify-center"), h.Div( - h.Class("flex flex-col mb-6 md:mb-0 md:flex-row justify-between items-center"), + h.Class("w-full px-4 flex flex-col prose md:max-w-3xl mt-6 mx-auto"), h.Div( - h.H1F("htmgo", h.Class("text-center md:text-left")), - h.H3F( - "build simple and scalable systems with %s", - "go + htmx", - h.Class("-mt-4"), + h.Class("flex flex-col mb-6 md:mb-0 md:flex-row justify-between items-center"), + h.Div( + h.H1F( + "htmgo", + h.Class("text-center md:text-left"), + ), + h.H3F( + "build simple and scalable systems with %s", + "go + htmx", + h.Class("-mt-4"), + ), + ), + h.Div( + h.Class("mt-2"), + h.A( + h.Href("/docs"), + h.Class("not-prose p-3 bg-slate-900 text-white rounded-md"), + h.Text("Get Started"), + ), ), ), h.Div( - h.Class("mt-2"), - h.A( - h.Href("/docs"), - h.Class("not-prose p-3 bg-slate-900 text-white rounded-md"), - h.Text("Get Started"), + h.Class("border-b border-b-slate-200 h-1"), + h.Div( + h.Class("mt-4"), + MarkdownPage(ctx, "md/index.md", ""), ), ), ), - h.Div( - h.Class("border-b border-b-slate-200 h-1"), - h.Div( - h.Class("mt-4"), - MarkdownPage(ctx, "md/index.md", ""), - ), - ), - )), + ), ), ) } diff --git a/htmgo-site/pages/markdown.go b/htmgo-site/pages/markdown.go index 7239048..8f2fcbb 100644 --- a/htmgo-site/pages/markdown.go +++ b/htmgo-site/pages/markdown.go @@ -20,9 +20,12 @@ func MarkdownContent(ctx *h.RequestContext, path string, id string) *h.Element { embeddedMd := ctx.Get("embeddedMarkdown").(fs.FS) renderer := service.Get[markdown.Renderer](ctx.ServiceLocator()) return h.Div( - h.If(id != "", h.Id(id)), + h.If( + id != "", + h.Id(id), + ), h.Div( - h.Class("w-full flex flex-col prose max-w-[95vw] md:max-w-3xl prose-code:text-black prose-p:my-1 prose:p-0 prose-li:m-0 prose-ul:m-0 prose-ol:m-0"), + h.Class("w-full flex flex-col prose max-w-md md:max-w-xl lg:max-w-4xl prose-code:text-black prose-p:my-1 prose:p-0 prose-li:m-0 prose-ul:m-0 prose-ol:m-0"), h.UnsafeRaw(renderer.RenderFile(path, embeddedMd)), ), ) diff --git a/htmgo-site/pages/test.go b/htmgo-site/pages/test.go new file mode 100644 index 0000000..e868461 --- /dev/null +++ b/htmgo-site/pages/test.go @@ -0,0 +1,59 @@ +package pages + +import ( + "fmt" + "github.com/maddalax/htmgo/framework/h" + "htmgo-site/pages/base" +) + +func TestFormatPage(ctx *h.RequestContext) *h.Page { + return h.NewPage( + base.RootPage( + ctx, + h.Div( + h.P( + h.Class("hello"), + h.Details( + h.Summary( + h.Text("Summary"), + ), + h.Text("Details"), + ), + h.Id("hi"), + ), + ), + ), + ) +} + +func notPage() int { + test := 1 + fmt.Printf("test: %d\n", test) + return test +} + +func TestOtherPage(ctx *h.RequestContext) *h.Page { + + return h.NewPage( + base.RootPage( + ctx, + h.Div( + h.Id("test"), + h.Details( + h.Summary( + h.Text("Summary"), + ), + h.Text("Details"), + ), + h.Class("flex flex-col gap-2 bg-white h-full"), + h.Id("test"), + h.Details( + h.Summary( + h.Text("Summary"), + ), + h.Text("Details"), + ), + ), + ), + ) +} diff --git a/htmgo-site/partials/form.go b/htmgo-site/partials/form.go index 4934822..8ef0b75 100644 --- a/htmgo-site/partials/form.go +++ b/htmgo-site/partials/form.go @@ -8,6 +8,8 @@ import ( func SubmitForm(ctx *h.RequestContext) *h.Partial { time.Sleep(time.Second * 3) return h.NewPartial( - h.Div(h.Text("Form submitted")), + h.Div( + h.Text("Form submitted"), + ), ) } diff --git a/htmgo-site/partials/html-to-go.go b/htmgo-site/partials/html-to-go.go new file mode 100644 index 0000000..a43f70c --- /dev/null +++ b/htmgo-site/partials/html-to-go.go @@ -0,0 +1,68 @@ +package partials + +import ( + "github.com/maddalax/htmgo/framework/h" + "github.com/maddalax/htmgo/framework/js" + "github.com/maddalax/htmgo/tools/html-to-htmgo/htmltogo" + "htmgo-site/ui" +) + +func ConvertHtmlToGo(ctx *h.RequestContext) *h.Partial { + value := ctx.FormValue("html-input") + parsed := htmltogo.Parse([]byte(value)) + + formatted := ui.FormatCode(string(parsed), "height: 100%;") + + return h.SwapPartial(ctx, GoOutput(formatted)) +} + +func HtmlInput() *h.Element { + return h.Div( + h.Class("h-full w-1/2 min-w-1/2"), + h.TextArea( + h.Name("html-input"), + h.MaxLength(500*1000), + h.PostPartial(ConvertHtmlToGo, "keyup delay:300ms"), + h.Class("h-[90%] w-full p-4 rounded border border-slate-200"), + h.Placeholder("Paste your HTML here"), + h.Rows(10), + ), + ) +} + +func GoOutput(content string) *h.Element { + return h.Div( + h.Class("h-full w-1/2 min-w-1/2"), + h.Id("go-output"), + h.Div( + h.Class("h-[90%] w-full rounded border border-slate-200 relative"), + h.Div( + h.Class("h-full"), + h.Id("go-output-content"), + h.UnsafeRaw(content), + ), + h.If( + content != "", + h.Div( + h.Class("absolute top-0 right-0 p-2 bg-slate-800 text-white rounded-bl-md cursor-pointer"), + h.Text("Copy"), + h.OnClick( + // language=JavaScript + js.EvalJs(` + if(!navigator.clipboard) { + alert("Clipboard API not supported"); + return; + } + let text = self.parentElement.querySelector("#go-output-content").innerText; + navigator.clipboard.writeText(text); + self.innerText = "Copied!"; + setTimeout(() => { + self.innerText = "Copy"; + }, 1000); + `), + ), + ), + ), + ), + ) +} diff --git a/htmgo-site/partials/navbar.go b/htmgo-site/partials/navbar.go index c384b88..9c7daa7 100644 --- a/htmgo-site/partials/navbar.go +++ b/htmgo-site/partials/navbar.go @@ -15,6 +15,12 @@ type NavItem struct { var navItems = []NavItem{ {Name: "Docs", Url: "/docs"}, {Name: "Examples", Url: "/examples"}, + {Name: "Convert HTML", Url: "/html-to-go"}, +} + +type NavBarProps struct { + Expanded bool + ShowPreRelease bool } func ToggleNavbar(ctx *h.RequestContext) *h.Partial { @@ -28,6 +34,31 @@ var CachedStar = h.CachedT(time.Minute*15, func(t *h.RequestContext) *h.Element return Star(t) }) +func Logo() *h.Element { + return h.Svg( + h.Attribute("viewBox", "0 0 370 80.8775381264543"), + h.Class("h-full w-full"), + h.Tag( + "G", + h.Attribute("transform", "matrix(1.276616840702525,0,0,1.276616840702525,-4.447757222875277,-26.431067200135733)"), + h.Attribute("fill", "#111111"), + h.Path( + h.Attribute("xmlns", "http://www.w3.org/2000/svg"), + h.Attribute("fill", "#111111"), + h.Attribute("d", "M48.34863,25.46777c-0.23438,0.48438-0.47461,0.99414-0.72461,1.55859 c-3.42871,7.72266-11.42285,25.09375-13.74707,30.13672c-1.5293-1.76367-3.08398-3.55469-4.55859-5.24609L48.34863,25.46777z M5.83398,68.5127l10.04492-21.2168c0.31445-0.83008,1.5293-3.20117,2.93848-3.20117c0.02832,0,0.05957,0.01172,0.08887,0.01367 c-0.67969,0.76563-1.42188,2.04102-2.00195,4.17578c-1.03125,3.79492-4.48535,16.94922-5.34473,20.22852H5.83398z M13.62695,68.5127 c1.03516-3.94531,4.22461-16.08984,5.20703-19.70313c0.63086-2.32227,1.39551-3.26367,1.83691-3.63477 c0.00684,0.00781,0.01367,0.01172,0.02051,0.01953C23.94629,48.68066,36.72852,63.5127,41.0293,68.5127H13.62695z M43.66699,68.5127 c-1.00293-1.16602-4.45117-5.17773-8.35352-9.68945c1.65039-3.58008,10.4834-22.75195,14.13867-30.98633 c2.32031-5.22852,3.6582-6.17773,4.04297-6.34961c0.82715,0.41797,1.73926,1.29102,2.66992,2.61719 c3.94141,5.61719,23.45703,37.07422,28.00098,44.4082H43.66699z"), + ), + ), + h.Tag( + "G", + h.Attribute("transform", "matrix(4.097970099125154,0,0,4.097970099125154,114.03704346489575,-20.491491909735604)"), + h.Attribute("fill", "#111111"), + h.Path( + h.Attribute("d", "M7.5293 9.766 c2.2461 0 3.5938 1.25 3.5938 3.7598 l0 6.4746 l-2.8223 0 l0 -6.0156 c0 -1.4746 -0.82031 -1.9629 -1.8262 -1.9629 c-1.0449 0 -2.1875 0.51758 -2.207 2.4414 l0 5.5371 l-2.8125 0 l0 -15 l2.8125 0 l0 6.1621 c0.71289 -0.86914 1.8359 -1.3965 3.2617 -1.3965 z M17.568346875 20 c-1.9531 0 -3.0664 -1.1328 -3.0664 -3.1348 l0 -4.7461 l-1.9727 0 l0 -2.1582 l0.63477 0 c1.0645 0 1.6504 -0.41016 1.6504 -1.9141 l0 -1.3281 l2.5391 0 l0 3.2422 l2.0703 0 l0 2.1582 l-2.0703 0 l0 4.4434 c0 0.89844 0.43945 1.2988 1.1621 1.2988 l0.9082 0 l0 2.1387 l-1.8555 0 z M33.496484375 9.766 c2.1484 0 3.5352 1.0938 3.5352 3.1543 l0 7.0801 l-2.8125 0 l0 -6.2793 c0 -1.1816 -0.74219 -1.6992 -1.582 -1.6992 c-1.0059 0 -1.8945 0.57617 -1.8945 2.3145 l0 5.6641 l-2.8418 0 l0 -6.25 c0 -1.2012 -0.72266 -1.7285 -1.6113 -1.7285 c-0.97656 0 -1.8848 0.57617 -1.8848 2.4609 l0 5.5176 l-2.8027 0 l0 -10.039 l2.8027 0 l0 1.1816 c0.66406 -0.88867 1.6797 -1.377 2.9102 -1.377 c1.4551 0 2.5488 0.52734 3.0762 1.5039 c0.70313 -1.0059 1.7773 -1.5039 3.1055 -1.5039 z M46.679646875 9.961 l2.6758 0 l0 9.2871 c0 3.9063 -2.1191 5.4883 -5.3223 5.4883 c-2.8809 0 -4.4434 -1.2109 -5.1758 -3.1152 l2.334 -0.99609 c0.56641 1.2988 1.3867 1.9238 2.7344 1.9238 c1.7773 0 2.6074 -1.1133 2.6074 -3.0957 l0 -1.1719 c-0.58594 0.80078 -1.7383 1.3672 -3.0469 1.3672 c-2.4902 0 -4.5801 -1.9629 -4.5801 -4.9609 c0 -3.0078 2.0996 -4.9219 4.5996 -4.9219 c1.4063 0 2.5586 0.625 3.1055 1.5234 z M44.208946875 17.373 c1.4648 0 2.5977 -1.1914 2.5977 -2.6855 c0 -1.5039 -1.1133 -2.6953 -2.5977 -2.6953 c-1.4746 0 -2.5879 1.1426 -2.5879 2.6953 c0 1.5332 1.1328 2.6855 2.5879 2.6855 z M56.9531125 20.19531 c-3.1934 0 -5.498 -1.9434 -5.498 -5.2246 c0 -3.2617 2.2852 -5.2051 5.498 -5.2051 c3.2324 0 5.5078 1.9434 5.5078 5.2051 c0 3.2813 -2.2852 5.2246 -5.5078 5.2246 z M56.9238125 17.959 c1.6309 0 2.7441 -1.1914 2.7441 -2.9883 s-1.1133 -2.9883 -2.7441 -2.9883 c-1.5723 0 -2.6758 1.1914 -2.6758 2.9883 s1.1035 2.9883 2.6758 2.9883 z"), + ), + ), + ) +} + func Star(ctx *h.RequestContext) *h.Element { type Repo struct { @@ -52,42 +83,49 @@ func Star(ctx *h.RequestContext) *h.Element { h.Class("w-4 h-4 -mt-0.5 mr-0.5 stroke-current text-white"), h.Attribute("xmlns", "http://www.w3.org/2000/svg"), h.Attribute("viewBox", "0 0 24 24"), - h.Attribute("fill", "none"), // No fill - h.Attribute("stroke", "currentColor"), // Apply stroke - h.Attribute("stroke-width", "2"), // Stroke width + h.Attribute("fill", "none"), + h.Attribute("stroke", "currentColor"), + h.Attribute("stroke-width", "2"), h.Path( h.D("M12 17.27l5.18 3.05-1.64-5.68 4.46-3.87-5.88-.5L12 3.5l-2.12 6.77-5.88.5 4.46 3.87-1.64 5.68L12 17.27z"), ), ), h.Text("Star"), ), - h.If(count > 0, h.Div( - h.Class("flex items-center px-3 py-1 bg-black text-white text-sm font-semibold"), - h.Pf("%d", count), - )), + h.If( + count > 0, + h.Div( + h.Class("flex items-center px-3 py-1 bg-black text-white text-sm font-semibold"), + h.Pf("%d", count), + ), + ), ) } -func NavBar(ctx *h.RequestContext, expanded bool) *h.Element { - prelease := h.A(h.Class("bg-yellow-200 text-yellow-800 text-center p-2 flex items-center justify-center"), - h.Href("https://github.com/maddalax/htmgo/issues"), - h.Attribute("target", "_blank"), - h.Text("htmgo is in alpha release. Please report any issues on GitHub."), +func NavBar(ctx *h.RequestContext, props NavBarProps) *h.Element { + banner := h.If( + true, + h.A( + h.Class("bg-blue-200 text-slate-700 text-center p-2 flex items-center justify-center"), + h.Href("https://github.com/maddalax/htmgo/releases/tag/framework%2Fv1.0.1"), + h.Attribute("target", "_blank"), + h.Text("htmgo v1.0.1 is released and it includes a new automatic formatter, view release notes"), + ), ) - desktopNav := h.Nav( - h.Class("hidden sm:block bg-neutral-100 border border-b-slate-300 p-4 md:p-3"), + h.Class("hidden sm:block bg-neutral-100 border border-b-slate-300 p-4 md:p-3 max-h-[100vh - 9rem] overflow-y-auto"), h.Div( h.Class("max-w-[95%] md:max-w-3xl px-4 mx-auto"), h.Div( h.Class("flex justify-between items-center"), + h.A( + h.Href("/"), + h.Class("mt-1 max-w-[125px]"), + Logo(), + ), h.Div( - h.Class("flex items-center"), - h.A( - h.Class("text-2xl"), - h.Href("/"), - h.Text("htmgo"), - )), + h.Id("search-container"), + ), h.Div( h.Class("flex gap-4 items-center"), h.List(navItems, func(item NavItem, index int) *h.Element { @@ -108,8 +146,8 @@ func NavBar(ctx *h.RequestContext, expanded bool) *h.Element { return h.Div( h.Id("navbar"), - prelease, - MobileNav(ctx, expanded), + banner, + MobileNav(ctx, props.Expanded), desktopNav, ) } @@ -125,47 +163,57 @@ func MobileNav(ctx *h.RequestContext, expanded bool) *h.Element { h.Div( h.Class("flex items-center"), h.A( - h.Boost(), - h.Class("text-2xl"), h.Href("/"), - h.Text("htmgo"), - )), + h.Class("mt-1 max-w-[125px]"), + Logo(), + ), + ), h.Div( h.Class("flex items-center gap-3"), - h.Div(h.Class("mt-1"), CachedStar(ctx)), + h.Div( + h.Class("mt-1"), + CachedStar(ctx), + ), h.Button( h.Boost(), - h.GetPartialWithQs( ToggleNavbar, - h.NewQs("expanded", h.Ternary(expanded, "false", "true"), "test", "true"), + h.NewQs( + "expanded", + h.Ternary(expanded, "false", "true"), + "test", + "true", + ), "click", ), - h.AttributePairs( - "class", "text-2xl", - "aria-expanded", h.Ternary(expanded, "true", "false"), + "class", + "text-2xl", + "aria-expanded", + h.Ternary(expanded, "true", "false"), ), - h.Class("text-2xl"), h.UnsafeRaw("☰"), ), ), ), ), - h.If(expanded, h.Div( - h.Class("mt-2 ml-2 flex flex-col gap-2"), - h.List(navItems, func(item NavItem, index int) *h.Element { - return h.Div( - h.Class("flex items-center"), - h.A( - h.Boost(), - h.Class(""), - h.Href(item.Url), - h.Text(item.Name), - ), - ) - }), - )), + h.If( + expanded, + h.Div( + h.Class("mt-2 ml-2 flex flex-col gap-2"), + h.List(navItems, func(item NavItem, index int) *h.Element { + return h.Div( + h.Class("flex items-center"), + h.A( + h.Boost(), + h.Class(""), + h.Href(item.Url), + h.Text(item.Name), + ), + ) + }), + ), + ), ) } diff --git a/htmgo-site/partials/sidebar.go b/htmgo-site/partials/sidebar.go index 8a38d63..a3447c6 100644 --- a/htmgo-site/partials/sidebar.go +++ b/htmgo-site/partials/sidebar.go @@ -57,14 +57,24 @@ func DocSidebar(pages []*dirwalk.Page) *h.Element { grouped := groupByFirstPart(pages) return h.Div( - h.Class("px-3 py-2 pr-6 md:min-h-[(calc(100%))] md:min-h-screen bg-neutral-50 border-r border-r-slate-300"), + h.Class("px-3 py-2 pr-6 min-h-screen bg-neutral-50 border-r border-r-slate-300 overflow-y-auto"), h.Div( - h.H4(h.Text("Contents"), h.Class("mt-4 text-slate-900 font-bold mb-3")), + h.Div( + h.Class("mb-3"), + h.A( + h.Href("#quick-start-introduction"), + h.Text("Documentation"), + h.Class("md:mt-4 text-xl text-slate-900 font-bold"), + ), + ), h.Div( h.Class("flex flex-col gap-4"), h.List(grouped.Entries(), func(entry datastructures.Entry[string, []*dirwalk.Page], index int) *h.Element { return h.Div( - h.P(h.Text(formatPart(entry.Key)), h.Class("text-slate-800 font-bold")), + h.P( + h.Text(formatPart(entry.Key)), + h.Class("text-slate-800 font-bold"), + ), h.Div( h.Class("pl-4 flex flex-col"), h.List(entry.Value, func(page *dirwalk.Page, index int) *h.Element { diff --git a/htmgo-site/ui/snippet.go b/htmgo-site/ui/snippet.go new file mode 100644 index 0000000..68c9c1c --- /dev/null +++ b/htmgo-site/ui/snippet.go @@ -0,0 +1,34 @@ +package ui + +import ( + "bytes" + "fmt" + "github.com/alecthomas/chroma/v2" + "github.com/alecthomas/chroma/v2/formatters/html" + "github.com/alecthomas/chroma/v2/lexers" + "github.com/alecthomas/chroma/v2/styles" + "github.com/maddalax/htmgo/framework/h" + "strings" +) + +func FormatCode(code string, customStyles ...string) string { + var buf bytes.Buffer + lexer := lexers.Get("go") + style := styles.Get("github") + formatter := html.New( + html.WithCustomCSS(map[chroma.TokenType]string{ + chroma.PreWrapper: fmt.Sprintf("padding: 12px; overflow: auto; %s", strings.Join(customStyles, ";")), + })) + iterator, err := lexer.Tokenise(nil, code) + if err != nil { + return "" + } + err = formatter.Format(&buf, style, iterator) + return buf.String() +} + +func CodeSnippet(code string) *h.Element { + return h.Div( + h.UnsafeRaw(FormatCode(code)), + ) +} diff --git a/tailwind-lsp-config.json b/tailwind-lsp-config.json index f6afb22..ae70df6 100644 --- a/tailwind-lsp-config.json +++ b/tailwind-lsp-config.json @@ -38,9 +38,11 @@ }, "experimental": { "configFile": null, - "classRegex": [[ - "Class|h.Class\\(([^)]*)\\)", - "[\"'`]([^\"'`]*).*?[\"'`]" - ]] + "classRegex": [ + ["Class\\(([^)]*)\\)", "[\"`]([^\"`]*)[\"`]"], + ["ClassX\\(([^)]*)\\)", "[\"`]([^\"`]*)[\"`]"], + ["ClassIf\\(([^)]*)\\)", "[\"`]([^\"`]*)[\"`]"], + ["Classes\\(([^)]*)\\)", "[\"`]([^\"`]*)[\"`]"] + ] } -} \ No newline at end of file +} diff --git a/templates/starter/Taskfile.yml b/templates/starter/Taskfile.yml index 695006f..28f1902 100644 --- a/templates/starter/Taskfile.yml +++ b/templates/starter/Taskfile.yml @@ -3,12 +3,12 @@ version: '3' tasks: run: cmds: - - go run github.com/maddalax/htmgo/cli/htmgo@latest run + - htmgo run silent: true build: cmds: - - go run github.com/maddalax/htmgo/cli/htmgo@latest build + - htmgo build docker: cmds: @@ -16,5 +16,5 @@ tasks: watch: cmds: - - go run github.com/maddalax/htmgo/cli/htmgo@latest watch - silent: true \ No newline at end of file + - htmgo watch + silent: true diff --git a/templates/starter/assets.go b/templates/starter/assets.go new file mode 100644 index 0000000..1114223 --- /dev/null +++ b/templates/starter/assets.go @@ -0,0 +1,13 @@ +//go:build !prod +// +build !prod + +package main + +import ( + "io/fs" + "starter-template/internal/embedded" +) + +func GetStaticAssets() fs.FS { + return embedded.NewOsFs() +} diff --git a/templates/starter/assets/public/apple-touch-icon.png b/templates/starter/assets/public/apple-touch-icon.png new file mode 100644 index 0000000..d10e9fe Binary files /dev/null and b/templates/starter/assets/public/apple-touch-icon.png differ diff --git a/templates/starter/assets/public/favicon.ico b/templates/starter/assets/public/favicon.ico new file mode 100644 index 0000000..040cccf Binary files /dev/null and b/templates/starter/assets/public/favicon.ico differ diff --git a/templates/starter/assets/public/icon-192-maskable.png b/templates/starter/assets/public/icon-192-maskable.png new file mode 100644 index 0000000..d4d6efb Binary files /dev/null and b/templates/starter/assets/public/icon-192-maskable.png differ diff --git a/templates/starter/assets/public/icon-192.png b/templates/starter/assets/public/icon-192.png new file mode 100644 index 0000000..f533435 Binary files /dev/null and b/templates/starter/assets/public/icon-192.png differ diff --git a/templates/starter/assets/public/icon-512-maskable.png b/templates/starter/assets/public/icon-512-maskable.png new file mode 100644 index 0000000..db61f3d Binary files /dev/null and b/templates/starter/assets/public/icon-512-maskable.png differ diff --git a/templates/starter/assets/public/icon-512.png b/templates/starter/assets/public/icon-512.png new file mode 100644 index 0000000..ba0665d Binary files /dev/null and b/templates/starter/assets/public/icon-512.png differ diff --git a/templates/starter/assets_prod.go b/templates/starter/assets_prod.go new file mode 100644 index 0000000..f0598e1 --- /dev/null +++ b/templates/starter/assets_prod.go @@ -0,0 +1,16 @@ +//go:build prod +// +build prod + +package main + +import ( + "embed" + "io/fs" +) + +//go:embed assets/dist/* +var staticAssets embed.FS + +func GetStaticAssets() fs.FS { + return staticAssets +} diff --git a/templates/starter/go.mod b/templates/starter/go.mod index 5fce281..437362f 100644 --- a/templates/starter/go.mod +++ b/templates/starter/go.mod @@ -2,7 +2,7 @@ module starter-template go 1.23.0 -require github.com/maddalax/htmgo/framework v0.0.0-20240930180419-e33ab7366d58 +require github.com/maddalax/htmgo/framework v1.0.2-0.20241025174132-df3edccd7fb0 require ( github.com/go-chi/chi/v5 v5.1.0 // indirect diff --git a/templates/starter/go.sum b/templates/starter/go.sum index cce2d15..f050d04 100644 --- a/templates/starter/go.sum +++ b/templates/starter/go.sum @@ -4,8 +4,8 @@ github.com/go-chi/chi/v5 v5.1.0 h1:acVI1TYaD+hhedDJ3r54HyA6sExp3HfXq7QWEEY/xMw= github.com/go-chi/chi/v5 v5.1.0/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/maddalax/htmgo/framework v0.0.0-20240930180419-e33ab7366d58 h1:G1ZKaigLbmtKWy67XMhulKm4qXnAjRdrFiymCM+zX+U= -github.com/maddalax/htmgo/framework v0.0.0-20240930180419-e33ab7366d58/go.mod h1:HYKI49Pb6oyY2opSJdTt145B1vWgfWIDohvlolynv80= +github.com/maddalax/htmgo/framework v1.0.2-0.20241025174132-df3edccd7fb0 h1:K9Q5b7BmbpCPJFjrAHS8+wPdKDcZN9NMC3Fg51n5IaQ= +github.com/maddalax/htmgo/framework v1.0.2-0.20241025174132-df3edccd7fb0/go.mod h1:NGGzWVXWksrQJ9kV9SGa/A1F1Bjsgc08cN7ZVb98RqY= 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/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= diff --git a/templates/starter/htmgo.yml b/templates/starter/htmgo.yml new file mode 100644 index 0000000..7647094 --- /dev/null +++ b/templates/starter/htmgo.yml @@ -0,0 +1,18 @@ +# htmgo configuration + +# if tailwindcss is enabled, htmgo will automatically compile your tailwind and output it to assets/dist +tailwind: true + +# which directories to ignore when watching for changes, supports glob patterns through https://github.com/bmatcuk/doublestar +watch_ignore: [".git", "node_modules", "dist/*"] + +# files to watch for changes, supports glob patterns through https://github.com/bmatcuk/doublestar +watch_files: ["**/*.go", "**/*.css", "**/*.md"] + +# files or directories to ignore when automatically registering routes for pages +# supports glob patterns through https://github.com/bmatcuk/doublestar +automatic_page_routing_ignore: ["root.go"] + +# files or directories to ignore when automatically registering routes for partials +# supports glob patterns through https://github.com/bmatcuk/doublestar +automatic_partial_routing_ignore: [] diff --git a/templates/starter/internal/embedded/os.go b/templates/starter/internal/embedded/os.go new file mode 100644 index 0000000..ddfd55f --- /dev/null +++ b/templates/starter/internal/embedded/os.go @@ -0,0 +1,17 @@ +package embedded + +import ( + "io/fs" + "os" +) + +type OsFs struct { +} + +func (receiver OsFs) Open(name string) (fs.File, error) { + return os.Open(name) +} + +func NewOsFs() OsFs { + return OsFs{} +} diff --git a/templates/starter/main.go b/templates/starter/main.go index bd79fac..bab347c 100644 --- a/templates/starter/main.go +++ b/templates/starter/main.go @@ -1,7 +1,6 @@ package main import ( - "embed" "github.com/maddalax/htmgo/framework/h" "github.com/maddalax/htmgo/framework/service" "io/fs" @@ -9,9 +8,6 @@ import ( "starter-template/__htmgo" ) -//go:embed assets/dist/* -var StaticAssets embed.FS - func main() { locator := service.NewLocator() @@ -19,7 +15,7 @@ func main() { ServiceLocator: locator, LiveReload: true, Register: func(app *h.App) { - sub, err := fs.Sub(StaticAssets, "assets/dist") + sub, err := fs.Sub(GetStaticAssets(), "assets/dist") if err != nil { panic(err) diff --git a/templates/starter/pages/index.go b/templates/starter/pages/index.go index ac1c004..7c81c8a 100644 --- a/templates/starter/pages/index.go +++ b/templates/starter/pages/index.go @@ -6,15 +6,17 @@ import ( ) func IndexPage(ctx *h.RequestContext) *h.Page { - return h.NewPage( - RootPage( + return RootPage( + h.Div( + h.Class("flex flex-col gap-4 items-center pt-24 min-h-screen bg-neutral-100"), + h.H3( + h.Id("intro-text"), + h.Text("hello htmgo"), + h.Class("text-5xl"), + ), h.Div( - h.Class("flex flex-col gap-4 items-center pt-24 min-h-screen bg-neutral-100"), - h.H3(h.Id("intro-text"), h.Text("hello htmgo"), h.Class("text-5xl")), - h.Div( - h.Class("mt-3"), - partials.CounterForm(0), - ), + h.Class("mt-3"), + partials.CounterForm(0), ), ), ) diff --git a/templates/starter/pages/root.go b/templates/starter/pages/root.go index 63374f1..323f436 100644 --- a/templates/starter/pages/root.go +++ b/templates/starter/pages/root.go @@ -4,17 +4,32 @@ import ( "github.com/maddalax/htmgo/framework/h" ) -func RootPage(children ...h.Ren) h.Ren { - return h.Html( - h.HxExtension(h.BaseExtensions()), - h.Head( - h.Link("/public/main.css", "stylesheet"), - h.Script("/public/htmgo.js"), - ), - h.Body( - h.Div( - h.Class("flex flex-col gap-2 bg-white h-full"), - h.Fragment(children...), +func RootPage(children ...h.Ren) *h.Page { + return h.NewPage( + h.Html( + h.HxExtensions( + h.BaseExtensions(), + ), + h.Head( + h.Meta("viewport", "width=device-width, initial-scale=1"), + h.Link("/public/favicon.ico", "icon"), + h.Link("/public/apple-touch-icon.png", "apple-touch-icon"), + h.Meta("title", "htmgo template"), + h.Meta("charset", "utf-8"), + h.Meta("author", "htmgo"), + h.Meta("description", "this is a template"), + h.Meta("og:title", "htmgo template"), + h.Meta("og:url", "https://htmgo.dev"), + h.Link("canonical", "https://htmgo.dev"), + h.Meta("og:description", "this is a template"), + h.Link("/public/main.css", "stylesheet"), + h.Script("/public/htmgo.js"), + ), + h.Body( + h.Div( + h.Class("flex flex-col gap-2 bg-white h-full"), + h.Fragment(children...), + ), ), ), ) diff --git a/templates/starter/partials/index.go b/templates/starter/partials/index.go index f5b47e8..bdedba9 100644 --- a/templates/starter/partials/index.go +++ b/templates/starter/partials/index.go @@ -26,7 +26,8 @@ func CounterForm(count int) *h.Element { h.Class("flex flex-col gap-3 items-center"), h.Id("counter-form"), h.PostPartial(CounterPartial), - h.Input("text", + h.Input( + "text", h.Class("hidden"), h.Value(count), h.Name("count"), diff --git a/tools/html-to-htmgo/go.mod b/tools/html-to-htmgo/go.mod new file mode 100644 index 0000000..e60bec4 --- /dev/null +++ b/tools/html-to-htmgo/go.mod @@ -0,0 +1,16 @@ +module github.com/maddalax/htmgo/tools/html-to-htmgo + +go 1.23.0 + +require ( + github.com/stretchr/testify v1.9.0 + golang.org/x/net v0.30.0 + golang.org/x/text v0.19.0 + golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d +) + +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/tools/html-to-htmgo/go.sum b/tools/html-to-htmgo/go.sum new file mode 100644 index 0000000..ef925bc --- /dev/null +++ b/tools/html-to-htmgo/go.sum @@ -0,0 +1,16 @@ +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/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4= +golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU= +golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= +golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +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/tools/html-to-htmgo/htmltogo/indent.go b/tools/html-to-htmgo/htmltogo/indent.go new file mode 100644 index 0000000..da7146c --- /dev/null +++ b/tools/html-to-htmgo/htmltogo/indent.go @@ -0,0 +1,148 @@ +package htmltogo + +import ( + "bytes" + "fmt" + "go/ast" + "go/format" + "go/parser" + "go/printer" + "go/token" + "golang.org/x/tools/go/ast/astutil" + "slices" + "strings" +) + +func Indent(input string) string { + fset := token.NewFileSet() + // Parse the code string into an AST + f, err := parser.ParseFile(fset, "", input, parser.ParseComments) + + if err != nil { + return input + } + + htmgoComponentTypes := []string{ + "h.Element", + "h.Page", + "h.Partial", + "h.Ren", + } + + for _, decl := range f.Decls { + switch c := decl.(type) { + case *ast.FuncDecl: + + if c.Type.Results == nil || len(c.Type.Results.List) == 0 { + continue + } + + returnType := c.Type.Results.List[0].Type + + isHtmgoComponent := false + if v, ok := returnType.(*ast.StarExpr); ok { + if x, ok := v.X.(*ast.SelectorExpr); ok { + name := x.X.(*ast.Ident).Name + str := name + "." + x.Sel.Name + isHtmgoComponent = slices.Contains(htmgoComponentTypes, str) + } + } + + // support non-pointer return types + if v, ok := returnType.(*ast.SelectorExpr); ok { + if x, ok := v.X.(*ast.Ident); ok { + name := x.Name + str := name + "." + v.Sel.Name + isHtmgoComponent = slices.Contains(htmgoComponentTypes, str) + } + } + + if !isHtmgoComponent { + continue + } + + var isHTag = func(n ast.Expr) bool { + switch argc := n.(type) { + // If the first argument is another node, add an indent + case *ast.CallExpr: + if v, ok := argc.Fun.(*ast.SelectorExpr); ok { + if v2, ok := v.X.(*ast.Ident); ok { + if v2.Name == "h" || v2.Name == "js" { + return true + } + } + } + } + return false + } + + var indent = func(children []ast.Expr) []ast.Expr { + children = append(children, ast.NewIdent("INDENTME")) + return children + } + + astutil.Apply(c.Body, nil, func(cursor *astutil.Cursor) bool { + switch n := cursor.Node().(type) { + case *ast.CallExpr: + newChildren := make([]ast.Expr, 0) + + hasAnyHElements := false + + for _, arg := range n.Args { + if isHTag(arg) { + hasAnyHElements = true + break + } + } + + for i, arg := range n.Args { + + if len(n.Args) == 1 && isHTag(arg) { + newChildren = indent(newChildren) + newChildren = append(newChildren, arg) + newChildren = indent(newChildren) + continue + } + + if !hasAnyHElements { + newChildren = append(newChildren, arg) + continue + } + + if len(n.Args) > 1 { + if i == 0 { + newChildren = indent(newChildren) + } + } + newChildren = append(newChildren, arg) + if len(n.Args) > 1 { + newChildren = indent(newChildren) + } + } + n.Args = newChildren + return true + } + return true + }) + } + } + + // Convert the AST node to a string + var buf bytes.Buffer + if err := printer.Fprint(&buf, fset, f); err != nil { + fmt.Println("Error printing AST:", err) + return input + } + + // Output the formatted code + indented := strings.ReplaceAll(buf.String(), "INDENTME,", "\n\t\t") + indented = strings.ReplaceAll(indented, ", INDENTME", ", \n\t\t") + + formatted, err := format.Source([]byte(indented)) + + if err != nil { + return input + } + + return string(formatted) +} diff --git a/tools/html-to-htmgo/htmltogo/indent_test.go b/tools/html-to-htmgo/htmltogo/indent_test.go new file mode 100644 index 0000000..b29679f --- /dev/null +++ b/tools/html-to-htmgo/htmltogo/indent_test.go @@ -0,0 +1,38 @@ +package htmltogo + +import ( + "github.com/stretchr/testify/assert" + "testing" +) + +func TestIdentHRen(t *testing.T) { + input := ` + package main + import ( + "github.com/maddalax/htmgo/framework/h" + ) + func Button(props ButtonProps) h.Ren { + return h.Div( + h.Div(h.Div(),h.P(),h.P(), + ), + ) + } + ` + indented := Indent(input) + assert.Equal(t, `package main + +import ( + "github.com/maddalax/htmgo/framework/h" +) + +func Button(props ButtonProps) h.Ren { + return h.Div( + h.Div( + h.Div(), + h.P(), + h.P(), + ), + ) +} +`, indented) +} diff --git a/tools/html-to-htmgo/htmltogo/main.go b/tools/html-to-htmgo/htmltogo/main.go new file mode 100644 index 0000000..d2806fd --- /dev/null +++ b/tools/html-to-htmgo/htmltogo/main.go @@ -0,0 +1,22 @@ +// Forked from https://github.com/PiotrKowalski/html-to-gomponents + +package htmltogo + +import ( + serviceformatter "github.com/maddalax/htmgo/tools/html-to-htmgo/internal/adapters/services/formatter" + serviceparser "github.com/maddalax/htmgo/tools/html-to-htmgo/internal/adapters/services/parser" +) + +func Parse(input []byte) []byte { + parser := serviceparser.New() + formatter := serviceformatter.New() + parsed, err := parser.FromBytes( + input, + ) + + if err != nil { + return nil + } + + return []byte(Indent(formatter.Format(parsed))) +} diff --git a/tools/html-to-htmgo/internal/adapters/services/formatter/formatter.go b/tools/html-to-htmgo/internal/adapters/services/formatter/formatter.go new file mode 100644 index 0000000..6bfb3bd --- /dev/null +++ b/tools/html-to-htmgo/internal/adapters/services/formatter/formatter.go @@ -0,0 +1,28 @@ +package formatter + +import ( + "github.com/maddalax/htmgo/tools/html-to-htmgo/internal/domain" + "go/format" +) + +type Formatter struct { +} + +func (f Formatter) Format(node *domain.CustomNode) string { + b := []byte(`package main +import ( + "github.com/maddalax/htmgo/framework/h" +) +func MyComponent() *h.Element { + return ` + node.String() + ` +}`) + dist, err := format.Source(b) + if err != nil { + return string(b) + } + return string(dist) +} + +func New() Formatter { + return Formatter{} +} diff --git a/tools/html-to-htmgo/internal/adapters/services/parser/parser.go b/tools/html-to-htmgo/internal/adapters/services/parser/parser.go new file mode 100644 index 0000000..0fe45e3 --- /dev/null +++ b/tools/html-to-htmgo/internal/adapters/services/parser/parser.go @@ -0,0 +1,69 @@ +package parser + +import ( + "bytes" + "errors" + "fmt" + "github.com/maddalax/htmgo/tools/html-to-htmgo/internal/domain" + "golang.org/x/net/html" + "strings" +) + +type Parser struct { +} + +var ParseErr = errors.New("parse error") + +func (p Parser) FromBytes(in []byte) (*domain.CustomNode, error) { + hNode, err := html.Parse(bytes.NewReader(in)) + if err != nil { + return nil, fmt.Errorf("%w: %v", ParseErr, err) + } + var findBody func(n *html.Node) *html.Node + findBody = func(n *html.Node) *html.Node { + if n.Data == "body" { + return n + } + var e *html.Node + for c := n.FirstChild; c != nil; c = c.NextSibling { + e = findBody(c) + } + return e + } + + body := findBody(hNode) + if body == nil { + return nil, fmt.Errorf("%w", ParseErr) + } + + var f func(*html.Node, *domain.CustomNode) *domain.CustomNode + f = func(n *html.Node, cNode *domain.CustomNode) *domain.CustomNode { + if n.Type == html.ElementNode { + cNode.SetType(n.Data) + + for _, attr := range n.Attr { + cNode.AddAttr(attr.Key, attr.Val) + } + } + + if n.Type == html.TextNode && len(strings.TrimSpace(n.Data)) > 0 { + cNode.ParentNode.AddAttr("h.Text", strings.TrimSpace(n.Data)) + } + + var i uint + for c := n.FirstChild; c != nil; c = c.NextSibling { + cNode.Nodes = append(cNode.Nodes, &domain.CustomNode{ParentNode: cNode, Level: cNode.Level + 1}) + cNode.Nodes[i] = f(c, cNode.Nodes[i]) + i++ + } + return cNode + } + output := &domain.CustomNode{} + out := f(body, output) + + return out, nil +} + +func New() Parser { + return Parser{} +} diff --git a/tools/html-to-htmgo/internal/adapters/services/parser/parser_test.go b/tools/html-to-htmgo/internal/adapters/services/parser/parser_test.go new file mode 100644 index 0000000..2554172 --- /dev/null +++ b/tools/html-to-htmgo/internal/adapters/services/parser/parser_test.go @@ -0,0 +1,33 @@ +package parser + +import ( + "errors" + "testing" +) + +func FuzzFromBytes(f *testing.F) { + serviceParser := New() + f.Add([]byte("Hello World")) + f.Add([]byte("TestSample")) + f.Add([]byte("
Some random text
")) + f.Add([]byte("Invalid HTML")) + f.Add([]byte("

")) + f.Add([]byte(" 10000 { // (10KB) + t.Skip() + } + _, err := serviceParser.FromBytes(data) + if err != nil { + return + } + if !isExpectedError(err) { + t.Errorf("Unexpected error: %v", err) + } + }) +} + +func isExpectedError(err error) bool { + return err != nil && errors.Is(err, ParseErr) +} diff --git a/tools/html-to-htmgo/internal/domain/formatter.go b/tools/html-to-htmgo/internal/domain/formatter.go new file mode 100644 index 0000000..35c4709 --- /dev/null +++ b/tools/html-to-htmgo/internal/domain/formatter.go @@ -0,0 +1,5 @@ +package domain + +type Formatter interface { + Format(node *CustomNode) (string, error) +} diff --git a/tools/html-to-htmgo/internal/domain/node.go b/tools/html-to-htmgo/internal/domain/node.go new file mode 100644 index 0000000..1c1c079 --- /dev/null +++ b/tools/html-to-htmgo/internal/domain/node.go @@ -0,0 +1,175 @@ +package domain + +import ( + "fmt" + "slices" + "strings" + + "golang.org/x/text/cases" + "golang.org/x/text/language" +) + +type CustomNode struct { + ParentNode *CustomNode + Level uint + customType bool + Type string + Attrs []Attr + Nodes []*CustomNode +} + +func (n *CustomNode) SetType(in string) { + switch in { + case "textarea": + n.Type = "h.TextArea" + case "head": + n.Type = "h.Head" + case "thead": + n.Type = "h.THead" + case "tbody": + n.Type = "h.TBody" + case "id": + n.Type = "h.Id" + case "circle": + n.Type = "circle" + n.customType = true + case "rect": + n.Type = "rect" + n.customType = true + case "line": + n.Type = "line" + n.customType = true + case "polyline": + n.Type = "line" + n.customType = true + case "svg": + n.Type = "h.Svg" + default: + n.Type = fmt.Sprintf("h.%s", cases.Title(language.English).String(in)) + } +} + +func (n *CustomNode) AddAttr(key, value string) { + if slices.Contains([]string{"xmlns", "fill", "viewBox", "stroke", "stroke-width", "fill-rule", "d", "stroke-linecap", "stroke-linejoin", "cx", "cy", "r", "x", "y", "rx", "ry", "x1", "x2", "y1", "y2", "points"}, key) { + n.Attrs = append(n.Attrs, Attr{ + custom: true, + key: key, + value: value, + }) + return + } + + switch { + case key == "autocomplete": + n.Attrs = append(n.Attrs, Attr{key: "h.AutoComplete", value: value}) + case key == "id": + n.Attrs = append(n.Attrs, Attr{key: "h.Id", value: value}) + case key == "tabindex": + n.Attrs = append(n.Attrs, Attr{key: "h.TabIndex", value: value}) + case key == "h.Text": + n.Attrs = append(n.Attrs, Attr{key: key, value: value}) + case strings.ContainsRune(key, '-'): + n.Attrs = append(n.Attrs, Attr{ + custom: true, + key: key, + value: value, + }) + fmt.Printf("key: %s, value: %s\n", key, value) + default: + n.Attrs = append(n.Attrs, Attr{key: "h." + cases.Title(language.English).String(key), value: value}) + } +} + +func (n *CustomNode) String() string { + str := "" + + if n.customType { + str += "h.Tag(\"" + n.Type + "\"," + } else { + str += n.Type + "(" + } + + if str == "h.Input(" { + if len(n.Attrs) > 0 { + for i, attr := range n.Attrs { + if attr.key == "h.Type" { + str = str + fmt.Sprintf(`"%s"`, attr.value) + "," + n.Attrs = append(n.Attrs[:i], n.Attrs[i+1:]...) + } + } + } + } + + if str == "h.Script(" { + if len(n.Attrs) > 0 { + for _, attr := range n.Attrs { + if attr.key == "h.Src" { + str = str + fmt.Sprintf(`"%s"`, attr.value) + "," + n.Attrs = make([]Attr, 0) + } + } + } + } + + booleanAttributes := []string{ + "h.AllowFullscreen", + "h.Async", + "h.Autofocus", + "h.Autoplay", + "h.Checked", + "h.Controls", + "h.Default", + "h.Defer", + "h.Disabled", + "h.FormNoValidate", + "h.Hidden", + "h.IsMap", + "h.Loop", + "h.Multiple", + "h.Muted", + "h.NoModule", + "h.NoValidate", + "h.Open", + "h.ReadOnly", + "h.Required", + "h.Reversed", + "h.Selected", + } + + if len(n.Attrs) > 0 { + for _, v := range n.Attrs { + switch { + case v.custom: + str = fmt.Sprintf("%sh.Attribute(\"%s\",\"%s\"),", str, v.key, v.value) + case v.hyphenated: + str = fmt.Sprintf("%s%s(\"%s\", \"%s\"),", str, v.key, v.arg, v.value) + case len(v.value) > 0: + if strings.Contains(v.value, "\n") { + str = fmt.Sprintf("%s%s(`%s`),", str, v.key, v.value) + } else { + str = fmt.Sprintf("%s%s(\"%s\"),", str, v.key, v.value) + } + case v.value == "" && !slices.Contains(booleanAttributes, v.key): + str = fmt.Sprintf("%s%s(\"\"),", str, v.key) + default: + str = fmt.Sprintf("%s%s(),", str, v.key) + } + } + } + + if len(n.Nodes) > 0 { + for _, v := range n.Nodes { + if v.Type != "" { + str = fmt.Sprintf("%s\n%s%s,", str, strings.Repeat(" ", int(n.Level)), v) + } + } + } + + str = fmt.Sprintf("%s\n%s)", str, strings.Repeat(" ", int(n.Level))) + return str +} + +type Attr struct { + custom, hyphenated bool + key, value, arg string +} diff --git a/tools/html-to-htmgo/internal/domain/parser.go b/tools/html-to-htmgo/internal/domain/parser.go new file mode 100644 index 0000000..e7c1597 --- /dev/null +++ b/tools/html-to-htmgo/internal/domain/parser.go @@ -0,0 +1,5 @@ +package domain + +type HTMLParser interface { + FromBytes(bytes []byte) (*CustomNode, error) +} diff --git a/tools/update-htmgo-dep.go b/tools/update-htmgo-dep.go index bd96b80..2737f0d 100644 --- a/tools/update-htmgo-dep.go +++ b/tools/update-htmgo-dep.go @@ -13,6 +13,13 @@ import ( ) const frameworkRepo = "github.com/maddalax/htmgo/framework" +const htmlToHtmgoRepo = "github.com/maddalax/htmgo/tools/html-to-htmgo" + +var depsToUpdate = []string{ + frameworkRepo, + htmlToHtmgoRepo, +} + const githubAPIURL = "https://api.github.com/repos/maddalax/htmgo/commits" // Commit represents the structure of a commit object returned by the GitHub API. @@ -52,17 +59,14 @@ func main() { // Check if the directory contains a go.mod file. if info.IsDir() && fileExists(filepath.Join(path, "go.mod")) { - // Check if the go.mod contains 'github.com/maddalax/htmgo/framework'. - if containsFrameworkDependency(filepath.Join(path, "go.mod")) { - wg.Add(1) - go func() { - defer wg.Done() - // Run go get github.com/maddalax/htmgo/framework@. - fmt.Printf("Running 'go get' with latest commit hash in %s\n", path) - RunCommand(path, "go", "get", fmt.Sprintf("%s@%s", frameworkRepo, latestCommitHash)) - RunCommand(path, "go", "mod", "tidy") - }() - } + goModPath := filepath.Join(path, "go.mod") + wg.Add(1) + go func() { + defer wg.Done() + for _, s := range depsToUpdate { + updateDepToLatestVersion(s, goModPath, latestCommitHash) + } + }() } return nil @@ -82,8 +86,18 @@ func fileExists(path string) bool { return !os.IsNotExist(err) } -// containsFrameworkDependency checks if 'github.com/maddalax/htmgo/framework' is in the go.mod file. -func containsFrameworkDependency(goModPath string) bool { +func updateDepToLatestVersion(dep string, goModPath string, latestCommitHash string) { + if containsDep(dep, goModPath) { + dir := filepath.Dir(goModPath) + // Run go get github.com/maddalax/htmgo/framework@. + fmt.Printf("Running 'go get' with latest commit hash in %s\n", dep) + RunCommand(dir, "go", "get", fmt.Sprintf("%s@%s", dep, latestCommitHash)) + RunCommand(dir, "go", "mod", "tidy") + } +} + +// containsDep checks if 'github.com/maddalax/htmgo/framework' is in the go.mod file. +func containsDep(dep string, goModPath string) bool { file, err := os.Open(goModPath) if err != nil { fmt.Println("Error opening go.mod file:", err) @@ -93,7 +107,7 @@ func containsFrameworkDependency(goModPath string) bool { scanner := bufio.NewScanner(file) for scanner.Scan() { - if strings.Contains(scanner.Text(), frameworkRepo) { + if strings.Contains(scanner.Text(), dep) { return true } }