[
  {
    "path": ".gitignore",
    "content": "# The following command works for downloading when using Git for Windows:\n# curl -LOf http://gist.githubusercontent.com/kmorcinek/2710267/raw/.gitignore\n#\n# Download this file using PowerShell v3 under Windows with the following comand:\n# Invoke-WebRequest https://gist.githubusercontent.com/kmorcinek/2710267/raw/ -OutFile .gitignore\n#\n# or wget:\n# wget --no-check-certificate http://gist.githubusercontent.com/kmorcinek/2710267/raw/.gitignore\n\n# User-specific files\n*.suo\n*.user\n*.sln.docstates\n\n# Build results\n[Dd]ebug/\n[Rr]elease/\nx64/\n[Bb]in/\n[Oo]bj/\n# build folder is nowadays used for build scripts and should not be ignored\n#build/\n\n# NuGet Packages\n*.nupkg\n# The packages folder can be ignored because of Package Restore\n**/packages/*\n# except build/, which is used as an MSBuild target.\n!**/packages/build/\n# Uncomment if necessary however generally it will be regenerated when needed\n#!**/packages/repositories.config\n\n# MSTest test Results\n[Tt]est[Rr]esult*/\n[Bb]uild[Ll]og.*\n\n*_i.c\n*_p.c\n*.ilk\n*.meta\n*.obj\n*.pch\n*.pdb\n*.pgc\n*.pgd\n*.rsp\n*.sbr\n*.tlb\n*.tli\n*.tlh\n*.tmp\n*.tmp_proj\n*.log\n*.vspscc\n*.vssscc\n.builds\n*.pidb\n*.scc\n\n# OS generated files #\n.DS_Store*\nIcon?\n\n# Visual C++ cache files\nipch/\n*.aps\n*.ncb\n*.opensdf\n*.sdf\n*.cachefile\n\n# Visual Studio profiler\n*.psess\n*.vsp\n*.vspx\n\n# Guidance Automation Toolkit\n*.gpState\n\n# ReSharper is a .NET coding add-in\n_ReSharper*/\n*.[Rr]e[Ss]harper\n\n# TeamCity is a build add-in\n_TeamCity*\n\n# DotCover is a Code Coverage Tool\n*.dotCover\n\n# NCrunch\n*.ncrunch*\n.*crunch*.local.xml\n\n# Installshield output folder\n[Ee]xpress/\n\n# DocProject is a documentation generator add-in\nDocProject/buildhelp/\nDocProject/Help/*.HxT\nDocProject/Help/*.HxC\nDocProject/Help/*.hhc\nDocProject/Help/*.hhk\nDocProject/Help/*.hhp\nDocProject/Help/Html2\nDocProject/Help/html\n\n# Click-Once directory\npublish/\n\n# Publish Web Output\n*.Publish.xml\n\n# Windows Azure Build Output\ncsx\n*.build.csdef\n\n# Windows Store app package directory\nAppPackages/\n\n# Others\n*.Cache\nClientBin/\n[Ss]tyle[Cc]op.*\n~$*\n*~\n*.dbmdl\n*.[Pp]ublish.xml\n*.pfx\n*.publishsettings\nmodulesbin/\ntempbin/\n\n# EPiServer Site file (VPP)\nAppData/\n\n# RIA/Silverlight projects\nGenerated_Code/\n\n# Backup & report files from converting an old project file to a newer\n# Visual Studio version. Backup files are not needed, because we have git ;-)\n_UpgradeReport_Files/\nBackup*/\nUpgradeLog*.XML\nUpgradeLog*.htm\n\n# vim\n*.txt~\n*.swp\n*.swo\n\n# Temp files when opening LibreOffice on ubuntu\n.~lock.*\n \n# svn\n.svn\n\n# CVS - Source Control\n**/CVS/\n\n# Remainings from resolving conflicts in Source Control\n*.orig\n\n# SQL Server files\n**/App_Data/*.mdf\n**/App_Data/*.ldf\n**/App_Data/*.sdf\n\n\n#LightSwitch generated files\nGeneratedArtifacts/\n_Pvt_Extensions/\nModelManifest.xml\n\n# =========================\n# Windows detritus\n# =========================\n\n# Windows image file caches\nThumbs.db\nehthumbs.db\n\n# Folder config file\nDesktop.ini\n\n# Recycle Bin used on file shares\n$RECYCLE.BIN/\n\n# Mac desktop service store files\n.DS_Store\n\n# SASS Compiler cache\n.sass-cache\n\n# Visual Studio 2014 CTP\n**/*.sln.ide\n\n# Visual Studio temp something\n.vs/\n\n# dotnet stuff\nproject.lock.json\n\n# VS 2015+\n*.vc.vc.opendb\n*.vc.db\n\n# Rider\n.idea/\n\n# Visual Studio Code\n.vscode/\n\n# Output folder used by Webpack or other FE stuff\n**/node_modules/*\n**/wwwroot/*\n\n# SpecFlow specific\n*.feature.cs\n*.feature.xlsx.*\n*.Specs_*.html\n\n#####\n# End of core ignore list, below put you custom 'per project' settings (patterns or path)\n#####\n/src/LaraUI/LaraUI.js\n/src/.axoCover/runs/run_2019-05-14_19-16-46/coverageReport.xml\n/src/LaraDocumentation/Help\n/src/LaraClient/build\n*.map\n*.js\n/src/environment.txt\n/src/Tests/results.xml\n/codecov\n/src/LaraClient/dist/assets/images\nIntegrative.Lara.xml\n"
  },
  {
    "path": ".travis.yml",
    "content": "language: csharp\nsolution: src/LaraUI.sln\nmono: none\ndotnet: 5.0.100\n \nbefore_install:\n - sudo apt-get update\n - sudo apt-get install nuget dotnet-sdk-2.1=2.1.300-1\n - curl -s -o $HOME/.nvm/nvm.sh https://raw.githubusercontent.com/creationix/nvm/v0.31.0/nvm.sh\n - source $HOME/.nvm/nvm.sh\n - nvm install stable\n - npm -v\n - node -v\n - dotnet tool install coveralls.net --tool-path tools\n\ninstall:\n  - nuget restore src/LaraUI.sln\n\nscript:\n - cd src\n - echo \"Debug\" > environment.txt\n - cd LaraClient\n - npm install .\n - npm run-script build\n - cd ..\n - cd ..\n - dotnet build --configuration Debug src/LaraUI/LaraUI.csproj\n - dotnet build --configuration Debug --framework net5.0 src/Tests/Tests.csproj\n - dotnet test --configuration Debug --framework net5.0 src/Tests/Tests.csproj /p:CollectCoverage=true /p:CoverletOutputFormat=opencover /p:CoverletOutput=results.xml\n \nafter_script:\n  - REPO_COMMIT_AUTHOR=$(git show -s --pretty=format:\"%cn\")\n  - REPO_COMMIT_AUTHOR_EMAIL=$(git show -s --pretty=format:\"%ce\")\n  - REPO_COMMIT_MESSAGE=$(git show -s --pretty=format:\"%s\")\n  - echo $TRAVIS_COMMIT\n  - echo $TRAVIS_BRANCH\n  - echo $REPO_COMMIT_AUTHOR\n  - echo $REPO_COMMIT_AUTHOR_EMAIL\n  - echo $REPO_COMMIT_MESSAGE\n  - echo $TRAVIS_JOB_ID\n  - ./tools/csmacnz.Coveralls --opencover -i src/Tests/results.xml --repoToken $COVERALLS_TOKEN --commitId $TRAVIS_COMMIT --commitBranch $TRAVIS_BRANCH --commitAuthor \"$REPO_COMMIT_AUTHOR\" --commitEmail \"$REPO_COMMIT_AUTHOR_EMAIL\" --commitMessage \"$REPO_COMMIT_MESSAGE\" --jobId $TRAVIS_JOB_ID  --serviceName travis-ci  --useRelativePaths \n"
  },
  {
    "path": "LICENSE",
    "content": "                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright 2019-2020 Integrative Software LLC\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "README.md",
    "content": "## Lara Web Engine\n\n[![License: Apache 2.0](https://img.shields.io/badge/License-Apache--2.0-blue)](https://github.com/integrativesoft/lara/blob/master/LICENSE) [![NuGet version](http://img.shields.io/nuget/v/Integrative.Lara.svg?nocache=1)](https://www.nuget.org/packages/Integrative.Lara/)  [![Download count](https://img.shields.io/nuget/dt/Integrative.Lara.svg)](https://www.nuget.org/packages/Integrative.Lara/)  [![Build Status](https://api.travis-ci.com/integrativesoft/lara.svg?branch=master)](https://travis-ci.org/integrativesoft/lara)  [![Coverage Status](https://coveralls.io/repos/github/integrativesoft/lara/badge.svg?branch=master&lala=3)](https://coveralls.io/github/integrativesoft/lara?branch=master)\n==================\n\n\n**Lara** is a server-side rendering framework for developing **web user interfaces** using C#.\n\n>*\"It is similar to server-side Blazor, but is much more lightweight and easier to install. For example, while any type of Blazor requires a whole SDK, Lara is just a NuGet package.\"* [ScientificProgrammer.net](https://scientificprogrammer.net/2019/08/18/pros-and-cons-of-blazor-for-web-development/?pagename=pros-and-cons-of-blazor)\n\n## Sample application\n\n```csharp\nusing Integrative.Lara;\nusing System;\nusing System.Threading.Tasks;\n\nnamespace SampleApp\n{\n    public static class Program\n    {\n        public static async Task Main()\n        {\n            // create and start application\n            const int port = 8182;\n            using var app = new Application();\n            app.PublishPage(\"/\", () => new MyCounterComponent { Value = 5 });\n            await app.Start(new StartServerOptions { Port = port });\n\n            // print address on console\n            var address = $\"http://localhost:{port}\";\n            Console.WriteLine($\"Listening on {address}/\");\n\n            // helper function to launch browser (comment out as needed)\n            LaraUI.LaunchBrowser(address);\n\n            // wait for ASP.NET Core shutdown\n            await app.WaitForShutdown();\n        }\n    }\n\n    internal class MyCounterComponent : WebComponent\n    {\n        private int _value; // triggers PropertyChanged event\n        public int Value { get => _value; set => SetProperty(ref _value, value); }\n\n        public MyCounterComponent()\n        {\n            ShadowRoot.Children = new Node[]\n            {\n                new HtmlDivElement() // on PropertyChanged, assigns InnerText\n                    .Bind(this, x => x.InnerText = Value.ToString()),\n                new HtmlButtonElement\n                    { InnerText = \"Increase\" }\n                    .Event(\"click\", () => Value++)\n            };\n        }\n    }\n}\n```\n\n## Adding Lara to an existing web server application\n\nTo add Lara to an existing ASP.NET Core server, add to the Startup class or equivalent:\n\n```csharp\nprivate readonly Application _laraApp = new Application();\n\npublic void Configure(IApplicationBuilder app)  \n{  \n    app.UseLara(_laraApp, new LaraOptions\n    {\n        // configuration options\n    });\n} \n```\n\n## Creating Desktop applications\n\nTo create a desktop container for your web app, here's a few options:\n\n- [electron.js](https://www.electronjs.org/) combined with [electron-cgi](https://github.com/ruidfigueiredo/electron-cgi#readme) library\n- [Chromely](https://github.com/chromelyapps/Chromely)\n- [neutralinojs](https://github.com/neutralinojs/neutralinojs)\n\n## Getting started\n\nThere's no need to download this repository to use Lara, instead, there's a [NuGet package](https://www.nuget.org/packages/Integrative.Lara/).\n\n**Check out the [wiki documentation](https://github.com/integrativesoft/lara/wiki)**\n\n## How does Lara work?\n\nWhenever the browser triggers a registered event (e.g. click on a button), it sends to the server a message saying that the button was clicked. The server executes the code associated with the event, manipulating the server's copy of the page, and replies a JSON message with the delta between server and client.\n\n## How to contribute\n\n**Please send feedback!** Issues, questions, suggestions, requests for features, and success stories. Please let me know by either opening an issue. Thank you!\n\n**If you like Lara, please give it a star - it helps!**\n\n## Credits\n\nThanks to [JetBrains](https://www.jetbrains.com/?from=LaraWebEngine) for the licenses of Rider and DotCover.\n\n[![JetBrains](support/jetbrains.svg)](https://www.jetbrains.com/?from=LaraWebEngine)\n"
  },
  {
    "path": "src/Boilerplate/Program.cs",
    "content": "﻿using Integrative.Lara;\nusing System;\nusing System.Threading.Tasks;\n\nnamespace SampleApp\n{\n    public static class Program\n    {\n        public static async Task Main()\n        {\n            // create and start application\n            const int port = 8182;\n            using var app = new Application();\n            app.PublishPage(\"/\", () => new HttpContextExample());\n            await app.Start(new StartServerOptions { Port = port });\n\n            // print address on console (set project's output type to WinExe to avoid console)\n            var address = $\"http://localhost:{port}\";                   \n            Console.WriteLine($\"Listening on {address}/\");\n\n            // helper function to launch browser (comment out as needed)\n            LaraUI.LaunchBrowser(address);\n\n            // wait for ASP.NET Core shutdown\n            await app.WaitForShutdown();\n        }\n    }\n\n    internal class MyCounterComponent : WebComponent\n    {\n        private int _value; // triggers PropertyChanged event\n        public int Value { get => _value; set => SetProperty(ref _value, value); }\n\n        public MyCounterComponent()\n        {\n            ShadowRoot.Children = new Node[]\n            {\n                new HtmlDivElement() // on PropertyChanged, assigns InnerText\n                    .Bind(this, x => x.InnerText = Value.ToString()),\n                new HtmlButtonElement\n                    { InnerText = \"Increase\" }\n                    .Event(\"click\", () => Value++)\n            };\n        }\n    }\n}\n"
  },
  {
    "path": "src/Boilerplate/Wiki/ComponentPropertyExample.cs",
    "content": "﻿using Integrative.Lara;\n\ninternal class ComponentPropertyExample : WebComponent\n{\n    public ComponentPropertyExample()\n    {\n        var myLabel = new HtmlSpanElement\n        {\n            InnerText = \"Hello!\"\n        };\n        ShadowRoot.Children = new Element[]\n        {\n            new MyLabelComponent\n            {\n                Label = myLabel\n            },\n        };\n    }\n}\n\ninternal class MyLabelComponent : WebComponent\n{\n    private Element _label;\n    public Element Label { get => _label; set => SetProperty(ref _label, value); }\n\n    public MyLabelComponent()\n    {\n        ShadowRoot.Children = new Element[]\n        {\n            new RenderIf(this, () => _label != null, () => _label)\n        };\n    }\n}\n"
  },
  {
    "path": "src/Boilerplate/Wiki/ComposingComponent.cs",
    "content": "﻿using Integrative.Lara;\n\ninternal class ComposingComponent : WebComponent\n{\n    public ComposingComponent()  // parent component\n    {\n        ShadowRoot.Children = new Element[]\n        {\n            new ItemComponent { Name = \"Sara\" },\n            new ItemComponent { Name = \"Mike\" },\n            new ItemComponent { Name = \"Tom\" },\n        };\n    }\n}\n\ninternal class ItemComponent : WebComponent  // child component\n{\n    private string _name;\n    public string Name { get => _name; set => SetProperty(ref _name, value); }\n\n    public ItemComponent()\n    {\n        ShadowRoot.Children = new Element[]\n        {\n            new HtmlDivElement\n            {\n                Children = new Element[]\n                {\n                    new HtmlTableCellElement()\n                        .Bind(this, x => x.InnerText = Name),\n                }\n            }\n        };\n    }\n}"
  },
  {
    "path": "src/Boilerplate/Wiki/ConditionalRenderComponent.cs",
    "content": "﻿using Integrative.Lara;\n\ninternal class ConditionalRenderComponent : WebComponent\n{\n    private bool _showText;\n    public bool ShowText { get => _showText; set => SetProperty(ref _showText, value); }\n\n    public ConditionalRenderComponent()\n    {\n        ShadowRoot.Children = new Node[]\n        {\n            new HtmlDivElement\n            {\n                InnerText = \"Hello!\",\n            }\n            .Bind(this, x => x.Render = ShowText) // Render property here\n        };\n    }\n}\n"
  },
  {
    "path": "src/Boilerplate/Wiki/DocumentContextExample.cs",
    "content": "﻿using Integrative.Lara;\n\ninternal class DocumentContextExample : WebComponent\n{\n    const string IconId = \"MyIconElement\";\n\n    public DocumentContextExample()\n    {\n        ShadowRoot.Children = new Element[]\n        {\n            new HtmlDivElement\n            {\n                InnerText = \"Check the title and icon of this webpage\"\n            }\n        };\n    }\n\n    protected override void OnConnect() // our component is placed on Document\n    {\n        base.OnConnect();\n        var icon = Document.GetElementById(IconId);\n        if (icon != null) return;\n        Document.Head.AppendChild(new HtmlLinkElement\n        {\n            Id = IconId,\n            Rel = \"icon\",\n            HRef = \"https://stackoverflow.com/favicon.ico\",\n        });\n        Document.Head.AppendChild(new HtmlTitleElement\n        {\n            InnerText = \"Hello title\",\n        });\n    }\n}\n"
  },
  {
    "path": "src/Boilerplate/Wiki/HttpContextExample.cs",
    "content": "﻿using Integrative.Lara;\n\ninternal class HttpContextExample : WebComponent\n{\n    private string _message;\n    public string Message { get => _message; set => SetProperty(ref _message, value); }\n\n    public HttpContextExample()\n    {\n        ShadowRoot.Children = new Element[]\n        {\n            new HtmlDivElement()\n                .Bind(this, x => x.InnerText = Message)            \n        };\n    }\n\n    protected override void OnConnect()\n    {\n        base.OnConnect();\n        Message = $\"Your IP is {LaraUI.Context.Http.Connection.RemoteIpAddress}\";\n    }\n}\n"
  },
  {
    "path": "src/Boilerplate/Wiki/LoopComponent.cs",
    "content": "﻿using Integrative.Lara;\nusing System.Collections.ObjectModel;\n\ninternal class MyList : WebComponent\n{\n    private readonly ObservableCollection<string> _names = new ObservableCollection<string>();\n\n    public MyList()\n    {\n        ShadowRoot.Children = new Node[]\n        {\n            Fragment.ForEach(_names, (string name) => new HtmlDivElement { InnerText = name }),\n        };\n        _names.Add(\"Sarah\");\n        _names.Add(\"John\");\n    }\n}\n"
  },
  {
    "path": "src/Boilerplate/Wiki/RedBoxExample.cs",
    "content": "﻿using Integrative.Lara;\n\ninternal class RedBoxExample : WebComponent\n{\n    public RedBoxExample()\n    {\n        ShadowRoot.Children = new Element[]\n        {\n            new RedBoxComponent\n            {\n                Children = new Element[]\n                {\n                    new HtmlDivElement\n                    {\n                        InnerText = \"Hello!\",\n                    }\n                }\n            }\n        };\n    }\n}\n\ninternal class RedBoxComponent : WebComponent\n{\n    public RedBoxComponent()\n    {\n        ShadowRoot.Children = new Element[]\n        {\n            new HtmlDivElement\n            {\n                Style = \"border: solid 3px red\",\n                Children = new Element[]\n                {\n                    new Slot(),\n                }\n            }\n        };\n    }\n}\n"
  },
  {
    "path": "src/Boilerplate/Wiki/SimpleComponent.cs",
    "content": "﻿using Integrative.Lara;\n\ninternal class SimpleComponent : WebComponent\n{\n    private string _message;\n    private string Message { get => _message; set => SetProperty(ref _message, value); }\n\n    public SimpleComponent()\n    {\n        Message = \"My Lara app\";\n        ShadowRoot.Children = new Node[]\n        {\n                new HtmlDivElement\n                {\n                    Children = new Node[]\n                    {\n                        new HtmlSpanElement()\n                            .Bind(this, x => x.InnerText = Message)\n                    }\n                }\n        };\n    }\n}"
  },
  {
    "path": "src/Boilerplate/Wiki/UserInputComponent.cs",
    "content": "﻿using Integrative.Lara;\n\ninternal class UserInputComponent : WebComponent\n{\n    int _counter;\n    public int Counter { get => _counter; set => SetProperty(ref _counter, value); }\n\n    public UserInputComponent()\n    {\n        ShadowRoot.Children = new Element[]\n        {\n            new HtmlDivElement()\n                .Bind(this, x => x.InnerText = Counter.ToString()),\n            new HtmlButtonElement\n                { InnerText = \"Increase\" }\n                .Event(\"click\", () => Counter++)\n        };\n    }\n}\n"
  },
  {
    "path": "src/Boilerplate/Wiki/UserTextComponent.cs",
    "content": "﻿using Integrative.Lara;\n\ninternal class UserTextComponent : WebComponent\n{\n    private string _name;\n    public string Name { get => _name; set => SetProperty(ref _name, value); }\n\n    public UserTextComponent()\n    {\n        Name = \"Taylor\";\n        ShadowRoot.Children = new Element[]\n        {\n            new HtmlDivElement\n                { InnerText = \"Please enter your name: \" },\n            new HtmlInputElement()\n                .Bind(this, x => x.Value = Name)  // if property changes, update element\n                .BindBack(x => Name = x.Value),   // if element changes, update property\n            new HtmlButtonElement\n                { InnerText = \"Read\" }\n                .Event(\"click\", () => { }),\n            new HtmlDivElement()\n                .Bind(this, x => x.InnerText = $\"Your name is {Name}\"),\n        };\n    }\n}\n"
  },
  {
    "path": "src/Boilerplate/WikiExamples.csproj",
    "content": "<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <OutputType>Exe</OutputType>\n    <TargetFramework>net5.0</TargetFramework>\n    <AssemblyName>WikiExamples</AssemblyName>\n    <RootNamespace>SampleApp</RootNamespace>\n  </PropertyGroup>\n\n  <ItemGroup>\n    <ProjectReference Include=\"..\\LaraUI\\LaraUI.csproj\" />\n  </ItemGroup>\n\n</Project>\n"
  },
  {
    "path": "src/LaraClient/.eslintignore",
    "content": "node_modules\ndist\n.js\n"
  },
  {
    "path": "src/LaraClient/.eslintrc",
    "content": "{\n    \"root\": true,\n    \"parser\": \"@typescript-eslint/parser\",\n    \"plugins\": [\n      \"@typescript-eslint\",\n      \"prettier\"\n    ],\n    \"extends\": [\n      \"eslint:recommended\",\n      \"plugin:@typescript-eslint/eslint-recommended\",\n      \"plugin:@typescript-eslint/recommended\",\n      \"prettier\"\n    ],\n    \"rules\": {\n      \"no-console\": 1,\n      \"prettier/prettier\": 2,\n      \"no-unused-vars\": [2, {\"args\": \"all\", \"argsIgnorePattern\": \"^_\", \"varsIgnorePattern\": \"^_\" }]\n    }\n  }\n  "
  },
  {
    "path": "src/LaraClient/.prettierrc",
    "content": "{\n  \"semi\": false,\n  \"trailingComma\": \"none\",\n  \"singleQuote\": false,\n  \"printWidth\": 80\n}\n"
  },
  {
    "path": "src/LaraClient/LaraClient.njsproj",
    "content": "﻿<Project DefaultTargets=\"Build\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\" ToolsVersion=\"4.0\">\n  <PropertyGroup>\n    <VisualStudioVersion Condition=\"'$(VisualStudioVersion)' == ''\">14.0</VisualStudioVersion>\n    <VSToolsPath Condition=\"'$(VSToolsPath)' == ''\">$(MSBuildExtensionsPath32)\\Microsoft\\VisualStudio\\v$(VisualStudioVersion)</VSToolsPath>\n    <Name>LaraClient</Name>\n    <RootNamespace>LaraClient</RootNamespace>\n  </PropertyGroup>\n  <Import Project=\"$(MSBuildExtensionsPath)\\$(MSBuildToolsVersion)\\Microsoft.Common.props\" Condition=\"Exists('$(MSBuildExtensionsPath)\\$(MSBuildToolsVersion)\\Microsoft.Common.props')\" />\n  <PropertyGroup>\n    <Configuration Condition=\" '$(Configuration)' == '' \">Debug</Configuration>\n    <SchemaVersion>2.0</SchemaVersion>\n    <ProjectGuid>6f0bee5a-5c72-4dcb-9173-bd17ba95a76f</ProjectGuid>\n    <ProjectHome>.</ProjectHome>\n    <StartupFile>\n    </StartupFile>\n    <StartWebBrowser>False</StartWebBrowser>\n    <SearchPath>\n    </SearchPath>\n    <WorkingDirectory>.</WorkingDirectory>\n    <OutputPath>.</OutputPath>\n    <TargetFrameworkVersion>v4.0</TargetFrameworkVersion>\n    <ProjectTypeGuids>{3AF33F2E-1136-4D97-BBB7-1795711AC8B8};{9092AA53-FB77-4645-B42D-1CCCA6BD08BD}</ProjectTypeGuids>\n    <EnableTypeScript>true</EnableTypeScript>\n    <StartWebBrowser>false</StartWebBrowser>\n  </PropertyGroup>\n  <PropertyGroup Condition=\" '$(Configuration)' == 'Debug' \">\n    <DebugSymbols>true</DebugSymbols>\n  </PropertyGroup>\n  <PropertyGroup Condition=\" '$(Configuration)' == 'Release' \">\n    <DebugSymbols>true</DebugSymbols>\n  </PropertyGroup>\n  <ItemGroup>\n    <Content Include=\"package.json\" />\n    <Content Include=\"README.md\" />\n    <Content Include=\"src\\blockUI.js\" />\n    <Content Include=\"tsconfig.json\" />\n    <Content Include=\"webpack.config.js\" />\n  </ItemGroup>\n  <ItemGroup>\n    <Folder Include=\"src\\\" />\n  </ItemGroup>\n  <ItemGroup>\n    <TypeScriptCompile Include=\"src\\Autocomplete.ts\">\n      <SubType>Code</SubType>\n    </TypeScriptCompile>\n    <TypeScriptCompile Include=\"src\\Blocker.ts\" />\n    <TypeScriptCompile Include=\"src\\ContentInterfaces.ts\" />\n    <TypeScriptCompile Include=\"src\\custom.d.ts\" />\n    <TypeScriptCompile Include=\"src\\DeltaInterfaces.ts\" />\n    <TypeScriptCompile Include=\"src\\Initializer.ts\" />\n    <TypeScriptCompile Include=\"src\\InputCollector.ts\" />\n    <TypeScriptCompile Include=\"src\\index.ts\" />\n    <TypeScriptCompile Include=\"src\\RegisteredEvents.ts\">\n      <SubType>Code</SubType>\n    </TypeScriptCompile>\n    <TypeScriptCompile Include=\"src\\Sequencer.ts\">\n      <SubType>Code</SubType>\n    </TypeScriptCompile>\n    <TypeScriptCompile Include=\"src\\SocketEvents.ts\">\n      <SubType>Code</SubType>\n    </TypeScriptCompile>\n    <TypeScriptCompile Include=\"src\\Worker.ts\" />\n  </ItemGroup>\n  <Import Project=\"$(VSToolsPath)\\Node.js Tools\\Microsoft.NodejsToolsV2.targets\" />\n  <ProjectExtensions>\n    <VisualStudio>\n      <FlavorProperties GUID=\"{349c5851-65df-11da-9384-00065b846f21}\">\n        <WebProjectProperties>\n          <UseIIS>False</UseIIS>\n          <AutoAssignPort>True</AutoAssignPort>\n          <DevelopmentServerPort>0</DevelopmentServerPort>\n          <DevelopmentServerVPath>/</DevelopmentServerVPath>\n          <IISUrl>http://localhost:48022/</IISUrl>\n          <NTLMAuthentication>False</NTLMAuthentication>\n          <UseCustomServer>True</UseCustomServer>\n          <CustomServerUrl>http://localhost:1337</CustomServerUrl>\n          <SaveServerSettingsInUserFile>False</SaveServerSettingsInUserFile>\n        </WebProjectProperties>\n      </FlavorProperties>\n      <FlavorProperties GUID=\"{349c5851-65df-11da-9384-00065b846f21}\" User=\"\">\n        <WebProjectProperties>\n          <StartPageUrl>\n          </StartPageUrl>\n          <StartAction>CurrentPage</StartAction>\n          <AspNetDebugging>True</AspNetDebugging>\n          <SilverlightDebugging>False</SilverlightDebugging>\n          <NativeDebugging>False</NativeDebugging>\n          <SQLDebugging>False</SQLDebugging>\n          <ExternalProgram>\n          </ExternalProgram>\n          <StartExternalURL>\n          </StartExternalURL>\n          <StartCmdLineArguments>\n          </StartCmdLineArguments>\n          <StartWorkingDirectory>\n          </StartWorkingDirectory>\n          <EnableENC>False</EnableENC>\n          <AlwaysStartWebServerOnDebug>False</AlwaysStartWebServerOnDebug>\n        </WebProjectProperties>\n      </FlavorProperties>\n    </VisualStudio>\n  </ProjectExtensions>\n</Project>"
  },
  {
    "path": "src/LaraClient/README.md",
    "content": "﻿# LaraClient\n\n\n"
  },
  {
    "path": "src/LaraClient/dist/lara-client.js.LICENSE.txt",
    "content": "/*!\n * Lara Web Engine\n * Copyright (c) 2019-2020 Integrative Software LLC.\n * License: Apache-2.0\n */\n\n/*!\n * Sizzle CSS Selector Engine v2.3.5\n * https://sizzlejs.com/\n *\n * Copyright JS Foundation and other contributors\n * Released under the MIT license\n * https://js.foundation/\n *\n * Date: 2020-03-14\n */\n\n/*!\n * jQuery UI Autocomplete 1.12.1\n * http://jqueryui.com\n *\n * Copyright jQuery Foundation and other contributors\n * Released under the MIT license.\n * http://jquery.org/license\n */\n\n/*!\n * jQuery UI Keycode 1.12.1\n * http://jqueryui.com\n *\n * Copyright jQuery Foundation and other contributors\n * Released under the MIT license.\n * http://jquery.org/license\n */\n\n/*!\n * jQuery UI Menu 1.12.1\n * http://jqueryui.com\n *\n * Copyright jQuery Foundation and other contributors\n * Released under the MIT license.\n * http://jquery.org/license\n */\n\n/*!\n * jQuery UI Position 1.12.1\n * http://jqueryui.com\n *\n * Copyright jQuery Foundation and other contributors\n * Released under the MIT license.\n * http://jquery.org/license\n *\n * http://api.jqueryui.com/position/\n */\n\n/*!\n * jQuery UI Unique ID 1.12.1\n * http://jqueryui.com\n *\n * Copyright jQuery Foundation and other contributors\n * Released under the MIT license.\n * http://jquery.org/license\n */\n\n/*!\n * jQuery UI Widget 1.12.1\n * http://jqueryui.com\n *\n * Copyright jQuery Foundation and other contributors\n * Released under the MIT license.\n * http://jquery.org/license\n */\n"
  },
  {
    "path": "src/LaraClient/package.json",
    "content": "{\n  \"name\": \"lara-client\",\n  \"version\": \"0.5.8\",\n  \"description\": \"LaraClient\",\n  \"module\": \"src/index.ts\",\n  \"private\": true,\n  \"author\": {\n    \"name\": \"Integrative Software LLC\"\n  },\n  \"scripts\": {\n    \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\",\n    \"build\": \"webpack --env debug=1\",\n    \"release\": \"webpack --env release=1\",\n    \"clean\": \"echo \\\"Webpack clean not needed\\\" && exit 0\",\n    \"lint\": \"eslint . --ext .ts\",\n    \"prettier-format\": \"prettier --config .prettierrc 'src/*.ts' --write\"\n  },\n  \"devDependencies\": {\n    \"@types/node\": \"^16.11.8\",\n    \"@typescript-eslint/eslint-plugin\": \"^5.4.0\",\n    \"@typescript-eslint/parser\": \"^5.4.0\",\n    \"clean-webpack-plugin\": \"^4.0.0\",\n    \"eslint\": \"^8.2.0\",\n    \"eslint-config-prettier\": \"^8.3.0\",\n    \"eslint-plugin-prettier\": \"^4.0.0\",\n    \"npm-check-updates\": \"^16.0.5\",\n    \"prettier\": \"^2.4.1\",\n    \"terser-webpack-plugin\": \"5.2.5\",\n    \"ts-loader\": \"^9.2.6\",\n    \"typescript\": \"^4.5.2\",\n    \"webpack\": \"^5.76.0\",\n    \"webpack-cli\": \"^4.9.1\"\n  },\n  \"dependencies\": {\n    \"@types/debounce\": \"^1.2.1\",\n    \"@types/jquery\": \"^3.5.8\",\n    \"@types/jquery.blockui\": \"0.0.29\",\n    \"@types/jqueryui\": \"^1.12.16\",\n    \"debounce\": \"^1.2.1\",\n    \"webpack-jquery-ui\": \"^2.0.1\"\n  }\n}\n"
  },
  {
    "path": "src/LaraClient/src/Autocomplete.ts",
    "content": "/*\nCopyright (c) 2019-2020 Integrative Software LLC\nCreated: 11/2019\nAuthor: Pablo Carbonell\n*/\n\nrequire(\"webpack-jquery-ui/autocomplete\")\nrequire(\"webpack-jquery-ui/css\")\n\nimport { getDocumentId } from \"./index\"\n\nexport interface AutocompleteCommand {\n  ElementId: string\n  AutoFocus: boolean\n  MinLength: number\n  Strict: boolean\n}\n\ninterface AutocompleteEntry {\n  html?: string\n  label: string\n  subtitle?: string\n  code: string\n}\n\ninterface SourceRequest {\n  term: string\n}\n\ninterface AutocompleteRequest {\n  Key: string\n  Term: string\n}\n\ntype AutocompleteCallback = (_list: AutocompleteEntry[]) => void\n\ninterface SuccessData {\n  Suggestions: AutocompleteEntry[]\n}\n\nexport function autocompleteStart(json: string): void {\n  const step = JSON.parse(json) as AutocompleteCommand\n  const input = document.getElementById(step.ElementId) as HTMLInputElement\n  $(input).autocomplete({\n    autoFocus: step.AutoFocus,\n    minLength: step.MinLength,\n    source: function (\n      request: SourceRequest,\n      updater: AutocompleteCallback\n    ): void {\n      $.ajax({\n        url: \"/lara_autocomplete\",\n        dataType: \"json\",\n        data: buildData(input, request.term),\n        success: function (data: SuccessData) {\n          updater(data.Suggestions)\n        },\n        method: \"POST\"\n      })\n    },\n    select: function (_event, ui: JQueryUI.AutocompleteUIParams): void {\n      const entry = ui.item as AutocompleteEntry\n      setValue(input, entry.code, entry.label)\n    },\n    change: function (_event, ui: JQueryUI.AutocompleteUIParams): void {\n      if (step.Strict && !ui.item) {\n        setValue(input, \"\", \"\")\n      }\n    }\n  })\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  const instance = $(input).autocomplete(\"instance\") as any\n  instance._renderItem = render\n}\n\nexport function autocompleteStop(id: string): void {\n  const input = document.getElementById(id)\n  $(input).autocomplete(\"destroy\")\n}\n\nfunction setValue(input: HTMLInputElement, value: string, text: string): void {\n  if (!value) {\n    input.removeAttribute(\"data-lara-value\")\n  } else {\n    input.setAttribute(\"data-lara-value\", value)\n  }\n  input.value = text\n}\n\nfunction render(ul: Element, entry: AutocompleteEntry): JQuery<HTMLElement> {\n  let html: string\n  if (entry.html) {\n    html = entry.html\n  } else {\n    html = buildLabel(entry)\n  }\n  return $(\"<li>\").append(html).appendTo(ul)\n}\n\nfunction buildLabel(entry: AutocompleteEntry): string {\n  const div = $(\"<div>\", { class: \"autocompleteEntry\" })\n  const title = $(\"<span>\", { class: \"autocompleteTitle\" })\n  title.text(entry.label)\n  div.append(title)\n  if (entry.subtitle) {\n    const subtitle = $('<span style=\"font-size:small;\">', {\n      class: \"autocompleteSubtitle\"\n    })\n    subtitle.text(entry.subtitle)\n    div.append(\"<br>\")\n    div.append(subtitle)\n  }\n  return div.clone().wrap(\"<p>\").parent().html()\n}\n\nfunction buildData(input: HTMLInputElement, term: string): string {\n  return JSON.stringify(buildRequest(input, term))\n}\n\nfunction buildRequest(\n  input: HTMLInputElement,\n  term: string\n): AutocompleteRequest {\n  const documentId = getDocumentId()\n  const key = input.id + \" \" + documentId\n  return {\n    Key: key,\n    Term: term\n  }\n}\n"
  },
  {
    "path": "src/LaraClient/src/Blocker.ts",
    "content": "﻿/*\nCopyright (c) 2019-2020 Integrative Software LLC\nCreated: 6/2019\nAuthor: Pablo Carbonell\n*/\n\nimport { PlugOptions } from \"./index\"\nimport \"./blockUI.js\"\n\nexport function block(plug: PlugOptions): void {\n  if (plug.Block) {\n    const target = resolveTarget(plug)\n    const params = buildParameters(plug)\n    if (target) {\n      $(target).block(params)\n    } else {\n      $.blockUI(params)\n    }\n  }\n}\n\nexport function unblock(plug: PlugOptions): void {\n  if (plug.Block) {\n    const target = resolveTarget(plug)\n    if (target) {\n      $(target).unblock()\n    } else {\n      $.unblockUI()\n    }\n  }\n}\n\nfunction buildParameters(plug: PlugOptions): JQBlockUIOptions {\n  const result: JQBlockUIOptions = {}\n  const shownId = plug.BlockShownId\n  if (shownId) {\n    setElementCSS(result)\n  } else {\n    setDefaultCSS(result)\n  }\n  if (shownId) {\n    result.message = $(\"#\" + shownId)\n  } else if (plug.BlockHTML) {\n    result.message = plug.BlockHTML\n  } else {\n    result.message = null\n  }\n  result.baseZ = 2000\n  return result\n}\n\nfunction resolveTarget(plug: PlugOptions): Element {\n  if (plug.BlockElementId) {\n    const el = document.getElementById(plug.BlockElementId)\n    if (el) {\n      return el\n    }\n  }\n  return null\n}\n\nfunction setElementCSS(options: JQBlockUIOptions): void {\n  options.css = {\n    position: \"absolute\",\n    top: \"50%\",\n    left: \"50%\",\n    transform: \"translate(-50%, -50%)\",\n    padding: \"unset\",\n    margin: \"unset\",\n    border: \"unset\",\n    width: \"unset\",\n    \"text-align\": \"unset\",\n    color: \"unset\",\n    \"background-color\": \"unset\"\n  }\n}\n\nfunction setDefaultCSS(options: JQBlockUIOptions): void {\n  options.css = {\n    border: \"none\",\n    padding: \"15px\",\n    backgroundColor: \"#000\",\n    \"-webkit-border-radius\": \"10px\",\n    \"-moz-border-radius\": \"10px\",\n    \"border-radius\": \"10px\",\n    opacity: \".5\",\n    color: \"#fff\",\n    fontSize: \"18px\",\n    fontFamily: \"Verdana,Arial\",\n    fontWeight: 200\n  }\n}\n"
  },
  {
    "path": "src/LaraClient/src/ContentInterfaces.ts",
    "content": "﻿/*\nCopyright (c) 2019-2020 Integrative Software LLC\nCreated: 5/2019\nAuthor: Pablo Carbonell\n*/\n\nexport enum ContentNodeType {\n  // eslint-disable-next-line no-unused-vars\n  Element = 1,\n  // eslint-disable-next-line no-unused-vars\n  Text = 2,\n  // eslint-disable-next-line no-unused-vars\n  Array = 3,\n  // eslint-disable-next-line no-unused-vars\n  Placeholder = 4\n}\n\nexport interface ContentNode {\n  Type: ContentNodeType\n}\n\nexport interface ContentTextNode extends ContentNode {\n  Data: string\n}\n\nexport interface ContentAttribute {\n  Attribute: string\n  Value: string\n}\n\nexport interface ContentElementNode extends ContentNode {\n  TagName: string\n  NS: string\n  Attributes: ContentAttribute[]\n  Children: ContentNode[]\n}\n\nexport interface ContentArrayNode extends ContentNode {\n  Nodes: ContentNode[]\n}\n\nexport interface ContentPlaceholder extends ContentNode {\n  ElementId: string\n}\n"
  },
  {
    "path": "src/LaraClient/src/DeltaInterfaces.ts",
    "content": "﻿/* eslint-disable no-unused-vars */\n/*\nCopyright (c) 2019-2020 Integrative Software LLC\nCreated: 5/2019\nAuthor: Pablo Carbonell\n*/\n\nimport { ContentNode } from \"./ContentInterfaces\"\nimport { PlugOptions } from \"./index\"\n\nexport enum EventResultType {\n  Success = 0,\n  NoSession = 1,\n  NoElement = 2,\n  OutOfSequence = 3\n}\n\nexport enum DeltaType {\n  Append = 1,\n  Insert = 2,\n  TextModified = 3,\n  Remove = 4,\n  EditAttribute = 5,\n  RemoveAttribute = 6,\n  Focus = 7,\n  SetId = 8,\n  SetValue = 9,\n  SubmitJS = 10,\n  SetChecked = 11,\n  ClearChildren = 12,\n  Replace = 13,\n  ServerEvents = 14,\n  SwapChildren = 15,\n  Subscribe = 16,\n  Unsubscribe = 17,\n  RemoveElementId = 18,\n  Render = 19,\n  UnRender = 20\n}\n\nexport interface BaseDelta {\n  Type: DeltaType\n}\n\nexport interface EventResult {\n  ResultType: EventResultType\n  List: BaseDelta[]\n}\n\nexport interface NodeAddedDelta extends BaseDelta {\n  ParentId: string\n  Node: ContentNode\n}\n\nexport interface NodeInsertedDelta extends BaseDelta {\n  ParentElementId: string\n  Index: number\n  ContentNode: ContentNode\n}\n\nexport interface TextModifiedDelta extends BaseDelta {\n  ParentElementId: string\n  ChildNodeIndex: number\n  Text: string\n}\n\nexport interface NodeRemovedDelta extends BaseDelta {\n  ParentId: string\n  ChildIndex: number\n}\n\nexport interface AttributeEditedDelta extends BaseDelta {\n  ElementId: string\n  Attribute: string\n  Value: string\n}\n\nexport interface AttributeRemovedDelta extends BaseDelta {\n  ElementId: string\n  Attribute: string\n}\n\nexport interface NodeLocator {\n  StartingId: string\n  ChildIndex?: number\n}\n\nexport interface FocusDelta extends BaseDelta {\n  ElementId: string\n}\n\nexport interface SetIdDelta extends BaseDelta {\n  OldId: string\n  NewId: string\n}\n\nexport interface SetValueDelta extends BaseDelta {\n  ElementId: string\n  Value: string\n}\n\nexport interface SubmitJsDelta extends BaseDelta {\n  Code: string\n  Payload?: string\n}\n\nexport interface SetCheckedDelta extends BaseDelta {\n  ElementId: string\n  Checked: boolean\n}\n\nexport interface ClearChildrenDelta extends BaseDelta {\n  ElementId: string\n}\n\nexport interface ReplaceDelta extends BaseDelta {\n  Location: string\n}\n\nexport interface SwapChildrenDelta extends BaseDelta {\n  ParentId: string\n  Index1: number\n  Index2: number\n}\n\nexport interface SubscribeDelta extends BaseDelta {\n  ElementId: string\n  Settings: PlugOptions\n  DebounceInterval?: number\n  EvalFilter?: string\n}\n\nexport interface UnsubscribeDelta extends BaseDelta {\n  ElementId: string\n  EventName: string\n}\n\nexport interface RemoveElement extends BaseDelta {\n  ElementId: string\n}\n\nexport interface RenderDelta extends BaseDelta {\n  Locator: NodeLocator\n  Node: ContentNode\n}\n\nexport interface UnRenderDelta extends BaseDelta {\n  Locator: NodeLocator\n}\n"
  },
  {
    "path": "src/LaraClient/src/Initializer.ts",
    "content": "﻿/*\nCopyright (c) 2019-2020 Integrative Software LLC\nCreated: 6/2019\nAuthor: Pablo Carbonell\n*/\n\nexport function clean(node: Node): void {\n  for (let n = 0; n < node.childNodes.length; n++) {\n    const child = node.childNodes[n]\n    if (\n      child.nodeType === 8 ||\n      (child.nodeType === 3 && !/\\S/.test(child.nodeValue))\n    ) {\n      node.removeChild(child)\n      n--\n    } else if (child.nodeType === 1) {\n      clean(child)\n    }\n  }\n}\n"
  },
  {
    "path": "src/LaraClient/src/InputCollector.ts",
    "content": "﻿/*\nCopyright (c) 2019-2020 Integrative Software LLC\nCreated: 5/2019\nAuthor: Pablo Carbonell\n*/\n\nimport { PlugOptions } from \"./index\"\n\nexport class ElementEventValue {\n  ElementId: string\n  Value: string\n  Checked: boolean\n}\n\nexport class ClientEventMessage {\n  Values: ElementEventValue[]\n  ExtraData: string\n  isEmpty(): boolean {\n    return this.Values.length == 0 && !this.ExtraData\n  }\n}\n\nexport function collectValues(plug: PlugOptions): FormData {\n  const data = new FormData()\n  const message = collectMessage(plug)\n  const fileCount = collectFiles(plug, data)\n  if (message.isEmpty() && fileCount == 0) {\n    return undefined\n  } else {\n    data.append(\"_message\", JSON.stringify(message))\n    return data\n  }\n}\n\nexport function collectMessage(plug: PlugOptions): ClientEventMessage {\n  const message = new ClientEventMessage()\n  message.Values = []\n  message.ExtraData = plug.ExtraData\n  collectType(\"input\", message, collectInput)\n  collectType(\"textarea\", message, collectSimpleValue)\n  collectType(\"button\", message, collectSimpleValue)\n  collectType(\"select\", message, collectSimpleValue)\n  collectType(\"option\", message, collectOption)\n  return message\n}\n\nfunction collectType(\n  tagName: string,\n  message: ClientEventMessage,\n  processor: (_el: Element, _m: ElementEventValue) => void\n) {\n  const list = document.getElementsByTagName(tagName)\n  for (let index = 0; index < list.length; index++) {\n    const el = list[index]\n    if (el.id) {\n      const entry = new ElementEventValue()\n      entry.ElementId = el.id\n      processor(el, entry)\n      message.Values.push(entry)\n    }\n  }\n}\n\nfunction collectInput(el: Element, entry: ElementEventValue): void {\n  const input = el as HTMLInputElement\n  entry.Value = getValue(input)\n  entry.Checked = input.checked\n}\n\nfunction collectSimpleValue(el: Element, entry: ElementEventValue): void {\n  entry.Value = getValue(el)\n}\n\nfunction collectOption(el: Element, entry: ElementEventValue): void {\n  const option = el as HTMLOptionElement\n  entry.Checked = option.selected\n}\n\nfunction getValue(el: Element): string {\n  if (el.hasAttribute(\"data-lara-value\")) {\n    return el.getAttribute(\"data-lara-value\")\n  } else if (\"value\" in el) {\n    // @ts-ignore\n    return el[\"value\"]\n  } else {\n    return \"\"\n  }\n}\n\nfunction collectFiles(plug: PlugOptions, data: FormData): number {\n  if (!plug.UploadFiles) {\n    return 0\n  }\n  let count = 0\n  const list = document.getElementsByTagName(\"input\")\n  for (let index = 0; index < list.length; index++) {\n    const input = list[index] as HTMLInputElement\n    count += collectFilesInput(input, data)\n  }\n  return count\n}\n\nfunction collectFilesInput(input: HTMLInputElement, data: FormData): number {\n  if (!input.id || input.type != \"file\") {\n    return 0\n  }\n  const files = input.files\n  const key = \"file/\" + input.id\n  for (let index = 0; index < files.length; index++) {\n    const file = files[index]\n    data.append(key, file, file.name)\n  }\n  return files.length\n}\n"
  },
  {
    "path": "src/LaraClient/src/RegisteredEvents.ts",
    "content": "/*\nCopyright (c) 2019-2020 Integrative Software LLC\nCreated: 10/2019\nAuthor: Pablo Carbonell\n*/\n\nimport { SubscribeDelta, UnsubscribeDelta } from \"./DeltaInterfaces\"\nimport { plug, plugEvent, getTargetId } from \"./index\"\nimport { debounce } from \"debounce\"\nconst registered = new Map<string, EventListener>()\n\nfunction addElementEvent(\n  element: EventTarget,\n  eventName: string,\n  handler: EventListener\n): void {\n  const id = getTargetId(element)\n  const key = getKey(id, eventName)\n  registered.set(key, handler)\n  element.addEventListener(eventName, handler)\n}\n\nfunction removeElementEvent(element: EventTarget, eventName: string): void {\n  const id = getTargetId(element)\n  const key = getKey(id, eventName)\n  const handler = registered.get(key)\n  registered.delete(key)\n  element.removeEventListener(eventName, handler)\n}\n\nfunction getKey(id: string, eventName: string) {\n  return id + \" \" + eventName\n}\n\nexport function subscribe(step: SubscribeDelta): void {\n  const element = getEventTarget(step.ElementId)\n  let handler = buildHandler(element, step)\n  if (step.EvalFilter) {\n    handler = addEvalFilter(handler, step.EvalFilter)\n  }\n  addElementEvent(element, step.Settings.EventName, handler)\n}\n\nfunction getEventTarget(id: string): EventTarget {\n  if (id) {\n    return document.getElementById(id)\n  } else {\n    return document\n  }\n}\n\nfunction buildHandler(\n  element: EventTarget,\n  step: SubscribeDelta\n): EventListener {\n  if (step.DebounceInterval) {\n    return buildDebouncedHandler(element, step)\n  } else {\n    return buildRegularHandler(element, step)\n  }\n}\n\nfunction buildDebouncedHandler(\n  element: EventTarget,\n  step: SubscribeDelta\n): EventListener {\n  // eslint-disable-next-line @typescript-eslint/no-unused-vars\n  const handler = function (_ev: Event): void {\n    plug(element, step.Settings)\n  }\n  return debounce(handler, step.DebounceInterval)\n}\n\nfunction buildRegularHandler(\n  element: EventTarget,\n  step: SubscribeDelta\n): EventListener {\n  return function (ev: Event): void {\n    plugEvent(element, ev, step.Settings)\n  }\n}\n\nfunction addEvalFilter(handler: EventListener, filter: string): EventListener {\n  return function (event: Event): void {\n    let run = false\n    try {\n      const result = eval(filter)\n      if (result) {\n        run = true\n      }\n      // eslint-disable-next-line no-empty\n    } catch {}\n    if (run) {\n      handler(event)\n    }\n  }\n}\n\nexport function unsubscribe(step: UnsubscribeDelta): void {\n  const element = getEventTarget(step.ElementId)\n  removeElementEvent(element, step.EventName)\n}\n"
  },
  {
    "path": "src/LaraClient/src/Sequencer.ts",
    "content": "/*\nCopyright (c) 2019-2020 Integrative Software LLC\nCreated: 10/2019\nAuthor: Pablo Carbonell\n*/\n\ntype resolver = (_value?: boolean | PromiseLike<boolean>) => void\n\nexport class Sequencer {\n  private next: number\n  private pending: Map<number, resolver>\n\n  constructor() {\n    this.next = 1\n    this.pending = new Map<number, resolver>()\n  }\n\n  async waitForTurn(turn: number): Promise<boolean> {\n    if (turn == 0) {\n      return true\n    } else if (turn == this.next) {\n      this.next++\n      this.flushPending()\n      return true\n    } else if (turn > this.next) {\n      let resolver: resolver\n      // eslint-disable-next-line @typescript-eslint/no-unused-vars\n      const task = new Promise<boolean>((resolve, _reject) => {\n        resolver = resolve\n      })\n      this.pending.set(turn, resolver)\n      return task\n    } else {\n      return false\n    }\n  }\n\n  private flushPending(): void {\n    while (this.pending.has(this.next)) {\n      const resolver = this.pending.get(this.next)\n      this.pending.delete(this.next)\n      resolver()\n      this.next++\n    }\n  }\n}\n"
  },
  {
    "path": "src/LaraClient/src/SocketEvents.ts",
    "content": "/*\nCopyright (c) 2019-2020 Integrative Software LLC\nCreated: 12/2019\nAuthor: Pablo Carbonell\n*/\n\nimport { PlugOptions } from \"./index\"\nimport { ClientEventMessage } from \"./InputCollector\"\n\nexport class EventParameters {\n  DocumentId: string\n  ElementId: string\n  EventName: string\n  EventNumber: number\n  Message: ClientEventMessage\n}\n\nexport class SocketEventParameters extends EventParameters {\n  SocketFiles: FormFileCollection\n}\n\nclass FormFileCollection {\n  InnerList: FormFile[]\n\n  constructor() {\n    this.InnerList = []\n  }\n}\n\nclass FormFile {\n  ContentType: string\n  ContentDisposition: string\n  Name: string\n  FileName: string\n  Content: string\n  Length: number\n}\n\nexport async function loadFiles(\n  plug: PlugOptions\n): Promise<FormFileCollection> {\n  const result = new FormFileCollection()\n  if (!plug.UploadFiles) {\n    return result\n  }\n  const list = document.getElementsByTagName(\"input\")\n  for (let index = 0; index < list.length; index++) {\n    const input = list[index] as HTMLInputElement\n    await collectFilesInput(input, result)\n  }\n  return result\n}\n\nasync function collectFilesInput(\n  input: HTMLInputElement,\n  data: FormFileCollection\n): Promise<void> {\n  if (!input.id || input.type != \"file\") {\n    return\n  }\n  const files = input.files\n  const key = \"file/\" + input.id\n  for (let index = 0; index < files.length; index++) {\n    const file = files[index]\n    const copy = await copyFile(file, key)\n    copy.Name = key\n    data.InnerList.push(copy)\n  }\n}\n\nasync function copyFile(file: File, name: string): Promise<FormFile> {\n  const bytes = await readFile(file)\n  const copy = new FormFile()\n  copy.ContentType = file.type\n  copy.Content = bufferToBase64(bytes)\n  copy.Name = name\n  copy.FileName = file.name\n  copy.Length = bytes.byteLength\n  return copy\n}\n\nfunction bufferToBase64(buffer: ArrayBuffer): string {\n  let binary = \"\"\n  const bytes = new Uint8Array(buffer)\n  const len = bytes.byteLength\n  for (let index = 0; index < len; index++) {\n    binary += String.fromCharCode(bytes[index])\n  }\n  return window.btoa(binary)\n}\n\nasync function readFile(file: File): Promise<ArrayBuffer> {\n  const reader = new FileReader()\n  reader.readAsArrayBuffer(file)\n  return new Promise<ArrayBuffer>((resolve, reject) => {\n    reader.onerror = () => {\n      reject(\"Error reading input file.\")\n    }\n    reader.onload = function () {\n      resolve(reader.result as ArrayBuffer)\n    }\n  })\n}\n"
  },
  {
    "path": "src/LaraClient/src/Worker.ts",
    "content": "﻿/*\nCopyright (c) 2019-2020 Integrative Software LLC\nCreated: 5/2019\nAuthor: Pablo Carbonell\n*/\n\nimport {\n  ContentArrayNode,\n  ContentElementNode,\n  ContentNode,\n  ContentNodeType,\n  ContentPlaceholder,\n  ContentTextNode\n} from \"./ContentInterfaces\"\nimport * as Delta from \"./DeltaInterfaces\"\nimport { listenServerEvents } from \"./index\"\nimport { subscribe, unsubscribe } from \"./RegisteredEvents\"\n\nexport function processResult(steps: Delta.BaseDelta[]): void {\n  for (const step of steps) {\n    if (!processStepCatch(step)) {\n      return\n    }\n  }\n}\n\nfunction processStepCatch(step: Delta.BaseDelta): boolean {\n  try {\n    processStep(step)\n    return true\n  } catch (Error) {\n    // eslint-disable-next-line no-console\n    console.log(\"Error processing step:\")\n    // eslint-disable-next-line no-console\n    console.log(Error)\n    // eslint-disable-next-line no-console\n    console.log(step)\n    return false\n  }\n}\n\nfunction processStep(step: Delta.BaseDelta): void {\n  switch (step.Type) {\n    case Delta.DeltaType.Append:\n      append(step as Delta.NodeAddedDelta)\n      break\n    case Delta.DeltaType.Insert:\n      insert(step as Delta.NodeInsertedDelta)\n      break\n    case Delta.DeltaType.TextModified:\n      textModified(step as Delta.TextModifiedDelta)\n      break\n    case Delta.DeltaType.Remove:\n      remove(step as Delta.NodeRemovedDelta)\n      break\n    case Delta.DeltaType.EditAttribute:\n      editAttribute(step as Delta.AttributeEditedDelta)\n      break\n    case Delta.DeltaType.RemoveAttribute:\n      removeAttribute(step as Delta.AttributeRemovedDelta)\n      break\n    case Delta.DeltaType.Focus:\n      focus(step as Delta.FocusDelta)\n      break\n    case Delta.DeltaType.SetId:\n      setId(step as Delta.SetIdDelta)\n      break\n    case Delta.DeltaType.SetValue:\n      setValue(step as Delta.SetValueDelta)\n      break\n    case Delta.DeltaType.SubmitJS:\n      submitJS(step as Delta.SubmitJsDelta)\n      break\n    case Delta.DeltaType.SetChecked:\n      setChecked(step as Delta.SetCheckedDelta)\n      break\n    case Delta.DeltaType.ClearChildren:\n      clearChildren(step as Delta.ClearChildrenDelta)\n      break\n    case Delta.DeltaType.Replace:\n      replaceLocation(step as Delta.ReplaceDelta)\n      break\n    case Delta.DeltaType.ServerEvents:\n      listenServerEvents()\n      break\n    case Delta.DeltaType.SwapChildren:\n      swapChildren(step as Delta.SwapChildrenDelta)\n      break\n    case Delta.DeltaType.Subscribe:\n      subscribe(step as Delta.SubscribeDelta)\n      break\n    case Delta.DeltaType.Unsubscribe:\n      unsubscribe(step as Delta.UnsubscribeDelta)\n      break\n    case Delta.DeltaType.RemoveElementId:\n      removeElementId(step as Delta.RemoveElement)\n      break\n    case Delta.DeltaType.Render:\n      render(step as Delta.RenderDelta)\n      break\n    case Delta.DeltaType.UnRender:\n      unRender(step as Delta.UnRenderDelta)\n      break\n    default:\n      // eslint-disable-next-line no-console\n      console.log(\n        \"Error processing event response. Unknown step type: \" + step.Type\n      )\n  }\n}\n\nfunction append(delta: Delta.NodeAddedDelta): void {\n  const el = document.getElementById(delta.ParentId)\n  const children = createNodes(delta.Node)\n  appendChildren(el, children)\n}\n\nfunction appendChildren(el: Element, children: Node[]): void {\n  for (const child of children) {\n    el.appendChild(child)\n  }\n}\n\nfunction insert(delta: Delta.NodeInsertedDelta): void {\n  const el = document.getElementById(delta.ParentElementId)\n  const children = createNodes(delta.ContentNode)\n  if (delta.Index < el.childNodes.length) {\n    const before = el.childNodes[delta.Index]\n    insertBeforeChildren(el, before, children)\n  } else {\n    appendChildren(el, children)\n  }\n}\n\nfunction render(delta: Delta.RenderDelta): void {\n  const stub = locateNode(delta.Locator)\n  const elements = createNodes(delta.Node)\n  if (elements.length == 0) {\n    stub.remove()\n    return\n  }\n  let last = elements.pop()\n  const parent = stub.parentElement\n  parent.replaceChild(last, stub)\n  while (elements.length) {\n    const pop = elements.pop()\n    parent.insertBefore(pop, last)\n    last = pop\n  }\n}\n\nfunction insertBeforeChildren(\n  el: Element,\n  before: ChildNode,\n  children: Node[]\n): void {\n  for (const child of children) {\n    el.insertBefore(child, before)\n    before = child.nextSibling\n  }\n}\n\nfunction createNodes(node: ContentNode): Node[] {\n  const list: Node[] = []\n  pushNodes(node, list)\n  return list\n}\n\nfunction pushNodes(node: ContentNode, list: Node[]): void {\n  if (node.Type == ContentNodeType.Text) {\n    list.push(createTextNode(node as ContentTextNode))\n  } else if (node.Type == ContentNodeType.Element) {\n    list.push(createElementNode(node as ContentElementNode))\n  } else if (node.Type == ContentNodeType.Array) {\n    pushArrayNodes(node as ContentArrayNode, list)\n  } else if (node.Type == ContentNodeType.Placeholder) {\n    list.push(createPlaceholder(node as ContentPlaceholder))\n  } else {\n    // eslint-disable-next-line no-console\n    console.log(\n      \"Error processing event response. Unknown content type: \" + node.Type\n    )\n    document.createTextNode(\"\")\n  }\n}\n\nfunction pushArrayNodes(node: ContentArrayNode, list: Node[]): void {\n  for (let index = 0; index < node.Nodes.length; index++) {\n    const item = node.Nodes[index]\n    pushNodes(item, list)\n  }\n}\n\nfunction createTextNode(node: ContentTextNode): Node {\n  const div = document.createElement(\"div\")\n  div.innerHTML = node.Data\n  return document.createTextNode(div.innerText)\n}\n\nfunction createElementNode(node: ContentElementNode): Element {\n  const child = createRootNode(node)\n  for (const attribute of node.Attributes) {\n    setAttribute(child, attribute.Attribute, attribute.Value)\n  }\n  for (const branch of node.Children) {\n    const nodes = createNodes(branch)\n    for (const node of nodes) {\n      child.appendChild(node)\n    }\n  }\n  return child\n}\n\nfunction createPlaceholder(node: ContentPlaceholder): Element {\n  const stub = document.createElement(\"script\")\n  stub.id = node.ElementId\n  stub.type = \"placeholder/lara\"\n  return stub\n}\n\nfunction setAttribute(child: Element, attribute: string, value: string): void {\n  if (!value) {\n    value = \"\"\n  }\n  if (\n    attribute == \"value\" &&\n    (child instanceof HTMLInputElement ||\n      child instanceof HTMLSelectElement ||\n      child instanceof HTMLTextAreaElement)\n  ) {\n    child.value = value\n    return\n  }\n  if (attribute == \"checked\" && child instanceof HTMLInputElement) {\n    child.checked = true\n    return\n  }\n  child.setAttribute(attribute, value)\n}\n\nfunction createRootNode(node: ContentElementNode): Element {\n  if (node.NS) {\n    return document.createElementNS(node.NS, node.TagName)\n  } else {\n    return document.createElement(node.TagName)\n  }\n}\n\nfunction textModified(delta: Delta.TextModifiedDelta): void {\n  const el = document.getElementById(delta.ParentElementId)\n  const child = el.childNodes[delta.ChildNodeIndex]\n  child.textContent = delta.Text\n}\n\nfunction remove(delta: Delta.NodeRemovedDelta): void {\n  const parent = document.getElementById(delta.ParentId)\n  const child = parent.childNodes[delta.ChildIndex]\n  child.remove()\n}\n\nfunction editAttribute(delta: Delta.AttributeEditedDelta): void {\n  const el = document.getElementById(delta.ElementId)\n  if (el.tagName == \"OPTION\" && delta.Attribute == \"selected\") {\n    const option = el as HTMLOptionElement\n    option.selected = true\n  } else {\n    el.setAttribute(delta.Attribute, delta.Value)\n  }\n}\n\nfunction removeAttribute(delta: Delta.AttributeRemovedDelta): void {\n  const el = document.getElementById(delta.ElementId)\n  if (el.tagName == \"OPTION\" && delta.Attribute == \"selected\") {\n    const option = el as HTMLOptionElement\n    option.selected = false\n  } else {\n    el.removeAttribute(delta.Attribute)\n  }\n}\n\nfunction focus(delta: Delta.FocusDelta): void {\n  const el = document.getElementById(delta.ElementId)\n  el.focus()\n}\n\nfunction setId(delta: Delta.SetIdDelta): void {\n  const el = document.getElementById(delta.OldId)\n  el.id = delta.NewId\n}\n\nfunction setValue(delta: Delta.SetValueDelta): void {\n  const input = document.getElementById(delta.ElementId) as HTMLInputElement\n  input.value = delta.Value\n}\n\nfunction submitJS(context: Delta.SubmitJsDelta): void {\n  try {\n    eval(context.Code)\n  } catch (e) {\n    // eslint-disable-next-line no-console\n    console.log((<Error>e).message)\n  }\n}\n\nfunction setChecked(delta: Delta.SetCheckedDelta): void {\n  const input = document.getElementById(delta.ElementId) as HTMLInputElement\n  input.checked = delta.Checked\n}\n\nfunction clearChildren(delta: Delta.ClearChildrenDelta): void {\n  const parent = document.getElementById(delta.ElementId)\n  while (parent.lastChild) {\n    parent.removeChild(parent.lastChild)\n  }\n}\n\nfunction replaceLocation(delta: Delta.ReplaceDelta): void {\n  location.replace(delta.Location)\n}\n\nfunction swapChildren(step: Delta.SwapChildrenDelta): void {\n  const el = document.getElementById(step.ParentId)\n  const node1 = el.childNodes[step.Index1]\n  const node2 = el.childNodes[step.Index2]\n  swapDom(node1, node2)\n}\n\nfunction swapDom(obj1: Node, obj2: Node): void {\n  const temp = document.createElement(\"div\")\n  obj1.parentNode.insertBefore(temp, obj1)\n  obj2.parentNode.insertBefore(obj1, obj2)\n  temp.parentNode.insertBefore(obj2, temp)\n  temp.parentNode.removeChild(temp)\n}\n\nfunction removeElementId(delta: Delta.RemoveElement): void {\n  const element = document.getElementById(delta.ElementId)\n  element.remove()\n}\n\nfunction unRender(delta: Delta.UnRenderDelta): void {\n  const node = locateNode(delta.Locator)\n  const stub = document.createElement(\"script\")\n  if (node instanceof Element) {\n    stub.id = node.id\n  }\n  stub.type = \"placeholder/lara\"\n  node.parentElement.replaceChild(stub, node)\n}\n\nfunction locateNode(locator: Delta.NodeLocator): ChildNode {\n  const element = document.getElementById(locator.StartingId)\n  const index = locator.ChildIndex\n  if (index == 0 || index > 0) {\n    return element.childNodes[index]\n  }\n  return element\n}\n"
  },
  {
    "path": "src/LaraClient/src/custom.d.ts",
    "content": "declare module \"*.svg\" {\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  const content: any\n  export default content\n}\n"
  },
  {
    "path": "src/LaraClient/src/index.ts",
    "content": "﻿/*\nCopyright (c) 2019-2020 Integrative Software LLC\nCreated: 5/2019\nAuthor: Pablo Carbonell\n*/\n\n/*\nLara is a server-side DOM rendering library for C#.\nThis file is the client runtime for Lara.\nhttps://laraui.com\n*/\n\n//#region Framework\n\nimport { autocompleteStart, autocompleteStop } from \"./Autocomplete\"\nimport { block, unblock } from \"./Blocker\"\nimport { EventResult, EventResultType } from \"./DeltaInterfaces\"\nimport { clean } from \"./Initializer\"\nimport { collectValues, collectMessage } from \"./InputCollector\"\nimport { Sequencer } from \"./Sequencer\"\nimport { processResult } from \"./Worker\"\nimport { SocketEventParameters, loadFiles } from \"./SocketEvents\"\n\nlet documentId: string\nlet lastEventNumber: number\nlet sequencer: Sequencer\n\nexport function initialize(id: string, keepAliveInterval: number): void {\n  sequencer = new Sequencer()\n  documentId = id\n  lastEventNumber = 0\n  window.addEventListener(\"unload\", terminate, false)\n  clean(document)\n  const json = document.head.getAttribute(\"data-lara-initialdelta\")\n  if (json) {\n    const result = JSON.parse(json)\n    processEventResult(result)\n  }\n  if (keepAliveInterval) {\n    window.setInterval(sendKeepAlive, keepAliveInterval)\n  }\n}\n\nexport function getDocumentId(): string {\n  return documentId\n}\n\nfunction terminate(): void {\n  const url = \"/_discard?doc=\" + documentId\n  navigator.sendBeacon(url)\n}\n\nfunction sendKeepAlive() {\n  const id = getDocumentId()\n  const url = \"/_keepAlive?doc=\" + id\n  navigator.sendBeacon(url)\n}\n\nexport enum PropagationType {\n  // eslint-disable-next-line no-unused-vars\n  Default = 0,\n  // eslint-disable-next-line no-unused-vars\n  StopPropagation = 1,\n  // eslint-disable-next-line no-unused-vars\n  StopImmediatePropagation = 2\n}\n\nexport interface PlugOptions {\n  EventName: string\n  Block?: boolean\n  BlockElementId?: string\n  BlockHTML?: string\n  BlockShownId?: string\n  ExtraData?: string\n  LongRunning?: boolean\n  IgnoreSequence?: boolean\n  Propagation?: PropagationType\n  PreventDefault?: boolean\n  UploadFiles?: boolean\n}\n\nexport function plugEvent(\n  el: EventTarget,\n  ev: Event,\n  options: PlugOptions\n): void {\n  stopPropagation(ev, options)\n  plug(el, options)\n}\n\nfunction stopPropagation(ev: Event, options: PlugOptions): void {\n  if (options.PreventDefault) {\n    ev.preventDefault()\n  }\n  if (options.Propagation == PropagationType.StopImmediatePropagation) {\n    ev.stopImmediatePropagation()\n  } else if (options.Propagation == PropagationType.StopPropagation) {\n    ev.stopPropagation()\n  }\n}\n\nexport function plug(el: EventTarget, options: PlugOptions): void {\n  if (options.LongRunning) {\n    plugWebSocket(el, options)\n  } else {\n    plugAjax(el, options)\n  }\n}\n\nfunction plugWebSocket(el: EventTarget, plug: PlugOptions): void {\n  block(plug)\n  const promise = buildSocketParameters(el, plug)\n  promise.then(\n    (params) => {\n      plugWebSocketStart(plug, params)\n    },\n    (reason) => {\n      // eslint-disable-next-line no-console\n      console.log(reason)\n      location.reload()\n    }\n  )\n}\n\nfunction plugWebSocketStart(\n  plug: PlugOptions,\n  params: SocketEventParameters\n): void {\n  const url = getSocketUrl(\"/_event\")\n  const socket = new WebSocket(url)\n  // eslint-disable-next-line @typescript-eslint/no-unused-vars\n  socket.onopen = function (_event) {\n    socket.onmessage = async function (e1) {\n      await onSocketMessage(e1.data, params.EventNumber)\n    }\n    // eslint-disable-next-line @typescript-eslint/no-unused-vars\n    socket.onclose = function (_e2) {\n      unblock(plug)\n    }\n    const json = JSON.stringify(params)\n    socket.send(json)\n  }\n  // eslint-disable-next-line @typescript-eslint/no-unused-vars\n  socket.onerror = function (_event) {\n    // eslint-disable-next-line no-console\n    console.log(\"Error on websocket communication. Reloading.\")\n    location.reload()\n  }\n}\n\nfunction getSocketUrl(name: string): string {\n  let url: string\n  if (location.protocol == \"https:\") {\n    url = \"wss://\"\n  } else {\n    url = \"ws://\"\n  }\n  return url + window.location.host + name\n}\n\nasync function buildSocketParameters(\n  el: EventTarget,\n  plug: PlugOptions\n): Promise<SocketEventParameters> {\n  const params = createSocketParameters(el, plug)\n  params.SocketFiles = await loadFiles(plug)\n  return params\n}\n\nfunction createSocketParameters(\n  el: EventTarget,\n  plug: PlugOptions\n): SocketEventParameters {\n  const params = new SocketEventParameters()\n  if (plug.IgnoreSequence) {\n    params.EventNumber = 0\n  } else {\n    params.EventNumber = getEventNumber()\n  }\n  params.DocumentId = documentId\n  params.ElementId = getTargetId(el)\n  params.EventName = plug.EventName\n  params.Message = collectMessage(plug)\n  return params\n}\n\nfunction getEventNumber(): number {\n  lastEventNumber++\n  return lastEventNumber\n}\n\nasync function onSocketMessage(\n  json: string,\n  eventNumber: number\n): Promise<void> {\n  await sequencer.waitForTurn(eventNumber)\n  const result = JSON.parse(json) as EventResult\n  processEventResult(result)\n}\n\nexport function getTargetId(target: EventTarget): string {\n  if (target instanceof Element) {\n    return target.id\n  } else {\n    return \"\"\n  }\n}\n\nfunction plugAjax(el: EventTarget, plug: PlugOptions): void {\n  block(plug)\n  const eventNumber = getEventNumber()\n  const url = getEventUrl(el, plug.EventName, eventNumber)\n  const ajax = new XMLHttpRequest()\n  ajax.onreadystatechange = async function () {\n    if (this.readyState == 4) {\n      await processAjax(this, eventNumber)\n      unblock(plug)\n    }\n  }\n  const data = collectValues(plug)\n  ajax.open(\"POST\", url, true)\n  if (data) {\n    ajax.send(data)\n  } else {\n    ajax.send()\n  }\n}\n\nexport interface MessageOptions {\n  key: string\n  data?: string\n  block?: boolean\n  blockElementId?: string\n  blockHtml?: string\n  blockShowElementId?: string\n  longRunning?: boolean\n}\n\nexport function sendMessage(options: MessageOptions): void {\n  const params: PlugOptions = {\n    EventName: \"_\" + options.key,\n    Block: options.block,\n    BlockElementId: options.blockHtml,\n    BlockHTML: options.blockHtml,\n    BlockShownId: options.blockShowElementId,\n    ExtraData: options.data,\n    LongRunning: options.longRunning\n  }\n  plug(document.head, params)\n}\n\nasync function processAjax(\n  ajax: XMLHttpRequest,\n  eventNumber: number\n): Promise<void> {\n  if (ajax.status == 200) {\n    await processAjaxResult(ajax, eventNumber)\n  } else {\n    processAjaxError(ajax)\n  }\n}\n\nfunction getEventUrl(\n  el: EventTarget,\n  eventName: string,\n  eventNumber: number\n): string {\n  return (\n    \"/_event?doc=\" +\n    documentId +\n    \"&el=\" +\n    getTargetId(el) +\n    \"&ev=\" +\n    eventName +\n    \"&seq=\" +\n    eventNumber.toString()\n  )\n}\n\nasync function processAjaxResult(\n  ajax: XMLHttpRequest,\n  eventNumber: number\n): Promise<void> {\n  await sequencer.waitForTurn(eventNumber)\n  const result = JSON.parse(ajax.responseText) as EventResult\n  processEventResult(result)\n}\n\nfunction processEventResult(result: EventResult): void {\n  if (result.ResultType == EventResultType.Success) {\n    if (result.List) {\n      processResult(result.List)\n    }\n  } else if (result.ResultType == EventResultType.NoSession) {\n    location.reload()\n  }\n}\n\nfunction processAjaxError(ajax: XMLHttpRequest): void {\n  if (ajax.responseText) {\n    document.write(ajax.responseText)\n  } else {\n    // eslint-disable-next-line no-console\n    console.log(\n      \"Internal Server Error on AJAX call. Detailed exception information on the client is turned off.\"\n    )\n  }\n}\n\nexport function listenServerEvents(): void {\n  plugWebSocket(document.head, {\n    EventName: \"_server_event\",\n    Block: false,\n    ExtraData: \"\",\n    LongRunning: true,\n    IgnoreSequence: true\n  })\n}\n\n//#endregion\n\n//#region Built-in web components\n\nexport function autocompleteApply(payload: string): void {\n  autocompleteStart(payload)\n}\n\nexport function autocompleteDestroy(id: string): void {\n  autocompleteStop(id)\n}\n\n//#endregion\n"
  },
  {
    "path": "src/LaraClient/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"outDir\": \"./dist/\",\n    \"noImplicitAny\": true,\n    \"module\": \"es6\",\n    \"target\": \"es5\",\n    \"lib\": [ \"es6\", \"dom\" ],\n    \"sourceMap\": true,\n    \"allowJs\": true\n  }\n}\n"
  },
  {
    "path": "src/LaraClient.sln",
    "content": "﻿\nMicrosoft Visual Studio Solution File, Format Version 12.00\n# Visual Studio Version 16\nVisualStudioVersion = 16.0.29519.181\nMinimumVisualStudioVersion = 10.0.40219.1\nProject(\"{9092AA53-FB77-4645-B42D-1CCCA6BD08BD}\") = \"LaraClient\", \"LaraClient\\LaraClient.njsproj\", \"{6F0BEE5A-5C72-4DCB-9173-BD17BA95A76F}\"\nEndProject\nGlobal\n\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\n\t\tDebug|Any CPU = Debug|Any CPU\n\t\tRelease|Any CPU = Release|Any CPU\n\tEndGlobalSection\n\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\n\t\t{6F0BEE5A-5C72-4DCB-9173-BD17BA95A76F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{6F0BEE5A-5C72-4DCB-9173-BD17BA95A76F}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{6F0BEE5A-5C72-4DCB-9173-BD17BA95A76F}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{6F0BEE5A-5C72-4DCB-9173-BD17BA95A76F}.Release|Any CPU.Build.0 = Release|Any CPU\n\tEndGlobalSection\n\tGlobalSection(SolutionProperties) = preSolution\n\t\tHideSolutionNode = FALSE\n\tEndGlobalSection\n\tGlobalSection(ExtensibilityGlobals) = postSolution\n\t\tSolutionGuid = {3770CCA5-4282-46C3-9C5D-65FC0534FF93}\n\tEndGlobalSection\nEndGlobal\n"
  },
  {
    "path": "src/LaraDocumentation/Content/Welcome.aml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<topic id=\"306fbdba-5d74-46ab-a362-aabe1e0a4051\" revisionNumber=\"1\">\n\t<developerConceptualDocument xmlns=\"http://ddue.schemas.microsoft.com/authoring/2003/5\" xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n\t\t<introduction>\n\t\t\t<para>Use the menu on the left to navigate.</para>\n\t\t</introduction>\n\n\t\t<relatedTopics>\n\t\t</relatedTopics>\n\t</developerConceptualDocument>\n</topic>\n"
  },
  {
    "path": "src/LaraDocumentation/ContentLayout.content",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Topics>\n  <Topic id=\"306fbdba-5d74-46ab-a362-aabe1e0a4051\" visible=\"True\" isDefault=\"true\" isSelected=\"true\" title=\"Welcome to Lara Web Engine\">\n    <HelpKeywords>\n      <HelpKeyword index=\"K\" term=\"Welcome\" />\n    </HelpKeywords>\n  </Topic>\n</Topics>"
  },
  {
    "path": "src/LaraDocumentation/LaraDocumentation.shfbproj",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project ToolsVersion=\"14.0\" DefaultTargets=\"Build\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n  <PropertyGroup>\n    <!-- The configuration and platform will be used to determine which assemblies to include from solution and\n\t\t\t\t project documentation sources -->\n    <Configuration Condition=\" '$(Configuration)' == '' \">Debug</Configuration>\n    <Platform Condition=\" '$(Platform)' == '' \">AnyCPU</Platform>\n    <SchemaVersion>2.0</SchemaVersion>\n    <ProjectGuid>0019560b-ed95-4921-9050-8ad37bf9d7ef</ProjectGuid>\n    <SHFBSchemaVersion>2017.9.26.0</SHFBSchemaVersion>\n    <!-- AssemblyName, Name, and RootNamespace are not used by SHFB but Visual Studio adds them anyway -->\n    <AssemblyName>LaraDocumentation</AssemblyName>\n    <RootNamespace>LaraDocumentation</RootNamespace>\n    <Name>LaraDocumentation</Name>\n    <!-- SHFB properties -->\n    <FrameworkVersion>.NET Framework 4.5</FrameworkVersion>\n    <OutputPath>.\\Help\\</OutputPath>\n    <HtmlHelpName>LaraDocumentation</HtmlHelpName>\n    <Language>en-US</Language>\n    <TransformComponentArguments>\n      <Argument Key=\"logoFile\" Value=\"Help.png\" xmlns=\"\" />\n      <Argument Key=\"logoHeight\" Value=\"\" xmlns=\"\" />\n      <Argument Key=\"logoWidth\" Value=\"\" xmlns=\"\" />\n      <Argument Key=\"logoAltText\" Value=\"\" xmlns=\"\" />\n      <Argument Key=\"logoPlacement\" Value=\"left\" xmlns=\"\" />\n      <Argument Key=\"logoAlignment\" Value=\"left\" xmlns=\"\" />\n      <Argument Key=\"maxVersionParts\" Value=\"\" xmlns=\"\" />\n    </TransformComponentArguments>\n    <HelpFileFormat>Website</HelpFileFormat>\n    <SyntaxFilters>C#, F#</SyntaxFilters>\n    <PresentationStyle>VS2013</PresentationStyle>\n    <CleanIntermediates>True</CleanIntermediates>\n    <KeepLogFile>True</KeepLogFile>\n    <DisableCodeBlockComponent>False</DisableCodeBlockComponent>\n    <IndentHtml>False</IndentHtml>\n    <BuildAssemblerVerbosity>OnlyWarningsAndErrors</BuildAssemblerVerbosity>\n    <SaveComponentCacheCapacity>100</SaveComponentCacheCapacity>\n    <HelpTitle>Lara Documentation</HelpTitle>\n    <HelpFileVersion>1.0.0.0</HelpFileVersion>\n    <RootNamespaceContainer>False</RootNamespaceContainer>\n    <NamespaceGrouping>False</NamespaceGrouping>\n    <MaximumGroupParts>2</MaximumGroupParts>\n    <Preliminary>False</Preliminary>\n    <SdkLinkTarget>Blank</SdkLinkTarget>\n    <VisibleItems>ProtectedInternalAsProtected, NonBrowsable</VisibleItems>\n    <DocumentationSources>\n      <DocumentationSource sourceFile=\"..\\LaraUI\\bin\\Release\\netstandard2.1\\Integrative.Lara.xml\" />\n      <DocumentationSource sourceFile=\"..\\LaraUI\\bin\\Release\\netstandard2.1\\Integrative.Lara.dll\" />\n    </DocumentationSources>\n    <CopyrightText>Copyright %28c%29 2020 Integrative Software LLC</CopyrightText>\n    <NamespaceSummaries>\n      <NamespaceSummaryItem name=\"(global)\" isDocumented=\"True\">Lara's namespace</NamespaceSummaryItem>\n      <NamespaceSummaryItem name=\"Integrative.Lara\" isDocumented=\"True\">Integrative.Lara namespace</NamespaceSummaryItem>\n    </NamespaceSummaries>\n    <PlugInConfigurations>\n      <PlugInConfig id=\"Assembly Binding Redirection\" enabled=\"True\" xmlns=\"\">\n        <configuration useGAC=\"false\">\n          <assemblyBinding />\n          <ignoreIfUnresolved>\n            <assemblyIdentity name=\"BusinessObjects.Licensing.KeycodeDecoder\" />\n            <assemblyIdentity name=\"Microsoft.VisualStudio.TestTools.UITest.Playback\" />\n            <assemblyIdentity name=\"netstandard\" />\n          </ignoreIfUnresolved>\n        </configuration>\n      </PlugInConfig>\n    </PlugInConfigurations>\n  </PropertyGroup>\n  <!-- There are no properties for these groups.  AnyCPU needs to appear in order for Visual Studio to perform\n\t\t\t the build.  The others are optional common platform types that may appear. -->\n  <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' \">\n  </PropertyGroup>\n  <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' \">\n  </PropertyGroup>\n  <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'Debug|x86' \">\n  </PropertyGroup>\n  <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'Release|x86' \">\n  </PropertyGroup>\n  <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'Debug|x64' \">\n  </PropertyGroup>\n  <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'Release|x64' \">\n  </PropertyGroup>\n  <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'Debug|Win32' \">\n  </PropertyGroup>\n  <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'Release|Win32' \">\n  </PropertyGroup>\n  <ItemGroup>\n    <Folder Include=\"Content\" />\n    <Folder Include=\"icons\" />\n    <Folder Include=\"media\" />\n  </ItemGroup>\n  <ItemGroup>\n    <None Include=\"Content\\Welcome.aml\" />\n  </ItemGroup>\n  <ItemGroup>\n    <ContentLayout Include=\"ContentLayout.content\" />\n  </ItemGroup>\n  <ItemGroup>\n    <Content Include=\"icons\\Help.png\" />\n  </ItemGroup>\n  <ItemGroup>\n    <ProjectReference Include=\"..\\LaraUI\\LaraUI.csproj\">\n      <Name>LaraUI</Name>\n      <Project>{1cf006b1-8b28-45bc-b9c8-1e548acd115d}</Project>\n      <Private>True</Private>\n    </ProjectReference>\n  </ItemGroup>\n  <!-- Import the SHFB build targets -->\n  <Import Project=\"$(SHFBROOT)\\SandcastleHelpFileBuilder.targets\" />\n  <!-- The pre-build and post-build event properties must appear *after* the targets file import in order to be\n\t\t\t evaluated correctly. -->\n  <PropertyGroup>\n    <PreBuildEvent>\n    </PreBuildEvent>\n    <PostBuildEvent>\n    </PostBuildEvent>\n    <RunPostBuildEvent>OnBuildSuccess</RunPostBuildEvent>\n  </PropertyGroup>\n</Project>"
  },
  {
    "path": "src/LaraUI/.gitignore",
    "content": "###############\n#    folder   #\n###############\n/**/DROP/\n/**/TEMP/\n/**/packages/\n/**/bin/\n/**/obj/\n_site\n"
  },
  {
    "path": "src/LaraUI/Autocomplete/AutocompleteElement.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 11/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System;\n\nnamespace Integrative.Lara\n{\n    /// <summary>\n    /// Autocomplete web component\n    /// </summary>\n    public class AutocompleteElement : WebComponent\n    {\n        /// <summary>\n        /// Autocomplete's custom HTML tag\n        /// </summary>\n        public const string CustomTag = \"lara-autocomplete\";\n\n        /// <summary>\n        /// Returns the inner input element\n        /// </summary>\n        public HtmlInputElement InnerInput { get; } = new HtmlInputElement();\n\n        /// <summary>\n        /// Constructor\n        /// </summary>\n        public AutocompleteElement() : base(CustomTag)\n        {\n            InnerInput.Autocomplete = \"off\";\n            ShadowRoot.AppendChild(InnerInput);\n        }\n\n        /// <summary>\n        /// Input element's class\n        /// </summary>\n        public override string? Class\n        {\n            get => InnerInput.Class;\n            set => InnerInput.Class = value;\n        }\n\n        private bool _pending, _applied;\n\n        private AutocompleteOptions? _options;\n\n        internal AutocompleteOptions? GetOptions() => _options;\n\n        /// <summary>\n        /// Enables the autocomplete functionality\n        /// </summary>\n        /// <param name=\"options\">Autocomplete options</param>\n        public void Start(AutocompleteOptions options)\n        {\n            _options = options ?? throw new ArgumentNullException(nameof(options));\n            if (Document == null)\n            {\n                _pending = true;\n            }\n            else\n            {\n                DestroyAutocomplete();\n                SubmitAutocomplete(Document, options);\n            }\n        }\n\n        /// <summary>\n        /// Stops the autocomplete functionality\n        /// </summary>\n        public void Stop()\n        {\n            DestroyAutocomplete();\n        }\n\n        /// <summary>\n        /// Establishes autocomplete if pending\n        /// </summary>\n        protected override void OnConnect()\n        {\n            if (!_pending) return;\n            _pending = false;\n            DestroyAutocomplete();\n            if (Document != null && _options != null)\n            {\n                SubmitAutocomplete(Document, _options);\n            }\n        }\n\n        /// <summary>\n        /// Disposes autocomplete references on browser\n        /// </summary>\n        protected override void OnDisconnect()\n        {\n            DestroyAutocomplete();\n        }\n\n        internal string AutocompleteId { get; private set; } = string.Empty;\n\n        private void SubmitAutocomplete(Document document, AutocompleteOptions options)\n        {\n            AutocompleteId = GetAutocompleteKey(document);\n            AutocompleteService.Register(AutocompleteId, this);\n            _applied = true;\n            _pending = false;\n            var payload = new AutocompletePayload\n            {\n                AutoFocus = options.AutoFocus,\n                ElementId = InnerInput.Id,\n                MinLength = options.MinLength,\n                Strict = options.Strict\n            };\n            var json = LaraUI.JSON.Stringify(payload);\n            var code = $\"LaraUI.autocompleteApply(context.Payload);\";\n            LaraUI.Page.JSBridge.Submit(code, json);\n        }\n\n        private string GetAutocompleteKey(Document document)\n        {\n            return InnerInput.Id + \" \" + document.VirtualIdString;\n        }\n\n        private void DestroyAutocomplete()\n        {\n            if (!_applied) return;\n            _applied = false;\n            AutocompleteService.Unregister(AutocompleteId);\n            var code = $\"LaraUI.autocompleteDestroy('{InnerInput.Id}');\";\n            LaraUI.Page.JSBridge.Submit(code);\n        }\n\n        /// <summary>\n        /// Value property\n        /// </summary>\n        public string? Value\n        {\n            get => InnerInput.Value;\n            set => InnerInput.Value = value;\n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Autocomplete/AutocompleteEntry.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 11/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System.Runtime.Serialization;\n\nnamespace Integrative.Lara\n{\n    /// <summary>\n    /// Default implementation for IAutocompleteEntry\n    /// </summary>\n    [DataContract]\n    public class AutocompleteEntry\n    {\n        /// <summary>\n        /// Optional custom HTML for the autocomplete row shown\n        /// </summary>\n        /// <returns></returns>\n        [DataMember(Name = \"html\")]\n        public string? Html { get; set; }\n\n        /// <summary>\n        /// Text to fill the input control when selected\n        /// </summary>\n        [DataMember(Name = \"label\")]\n        public string? Label { get; set; }\n\n        /// <summary>\n        /// Value property associated with the entry\n        /// </summary>\n        [DataMember(Name = \"code\")]\n        public string? Code { get; set; }\n\n        /// <summary>\n        /// Subtitle to show when using default HTML\n        /// </summary>\n        [DataMember(Name = \"subtitle\")]\n        public string? Subtitle { get; set; }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Autocomplete/AutocompleteOptions.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 11/2019\nAuthor: Pablo Carbonell\n*/\n\nnamespace Integrative.Lara\n{\n    /// <summary>\n    /// Autocomplete options\n    /// </summary>\n    public class AutocompleteOptions\n    {\n        /// <summary>\n        /// Minimum number of characters required to trigger autocomplete suggestions\n        /// </summary>\n        public int MinLength { get; set; }\n\n        /// <summary>\n        /// Automatically focus on selection list\n        /// </summary>\n        public bool AutoFocus { get; set; } = true;\n\n        /// <summary>\n        /// When true, only the suggested values can be selected\n        /// </summary>\n        public bool Strict { get; set; }\n\n        /// <summary>\n        /// Autocomplete suggestions provider\n        /// </summary>\n        public IAutocompleteProvider? Provider { get; set; }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Autocomplete/AutocompletePayload.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 11/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System.Runtime.Serialization;\n\nnamespace Integrative.Lara\n{\n    [DataContract]\n    internal class AutocompletePayload\n    {\n        [DataMember]\n        public string ElementId { get; set; } = string.Empty;\n\n        [DataMember]\n        public bool AutoFocus { get; set; }\n\n        [DataMember]\n        public int MinLength { get; set; }\n\n        [DataMember]\n        public bool Strict { get; set; }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Autocomplete/AutocompleteRegistry.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 11/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System.Collections.Generic;\nusing System.Diagnostics.CodeAnalysis;\n\nnamespace Integrative.Lara\n{\n    internal class AutocompleteRegistry\n    {\n        private readonly SessionLocal<Dictionary<string, AutocompleteElement>> _map;\n        private readonly object _mapLock = new object();\n\n        public AutocompleteRegistry()\n        {\n            _map = new SessionLocal<Dictionary<string, AutocompleteElement>>();\n        }\n\n        public bool TryGet(string key, [NotNullWhen(true)] out AutocompleteElement? element)\n        {\n            element = default;\n            lock (_mapLock)\n            {\n                var map = _map.Value;\n                return map != null\n                    && map.TryGetValue(key, out element);\n            }\n        }\n\n        public void Set(string key, AutocompleteElement element)\n        {\n            lock (_mapLock)\n            {\n                var map = _map.Value;\n                if (map == null)\n                {\n                    map = new Dictionary<string, AutocompleteElement>();\n                    _map.Value = map;\n                    map.Add(key, element);\n                }\n                else\n                {\n                    map.Remove(key);\n                    map.Add(key, element);\n                }\n            }\n        }\n\n        public void Remove(string key)\n        {\n            lock (_mapLock)\n            {\n                _map.Value?.Remove(key);\n            }\n        }\n\n        public int Count => GetCount();\n\n        private int GetCount()\n        {\n            var value = GetValue();\n            return value?.Count ?? 0;\n        }\n\n        private Dictionary<string, AutocompleteElement>? GetValue()\n        {\n            lock (_mapLock)\n            {\n                return _map.Value;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Autocomplete/AutocompleteResponse.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 11/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System.Collections.Generic;\nusing System.Runtime.Serialization;\n\nnamespace Integrative.Lara\n{\n    /// <summary>\n    /// Autocomplete results\n    /// </summary>\n    [DataContract]\n    public class AutocompleteResponse\n    {\n        /// <summary>\n        /// List of autocomplete entries\n        /// </summary>\n        [DataMember]\n        public List<AutocompleteEntry>? Suggestions { get; set; }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Autocomplete/AutocompleteService.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 11/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System.Runtime.Serialization;\nusing System.Threading.Tasks;\n\nnamespace Integrative.Lara\n{\n    [DataContract]\n    internal class AutocompleteRequest\n    {\n        [DataMember]\n        public string Key { get; set; } = string.Empty;\n\n        [DataMember]\n        public string Term { get; set; } = string.Empty;\n    }\n\n    internal class AutocompleteService : IWebService\n    {\n        public const string Address = \"/lara_autocomplete\";\n\n        private static readonly AutocompleteRegistry _Map = new AutocompleteRegistry();\n\n        public Task<string> Execute()\n        {\n            return Execute(LaraUI.Service.RequestBody);\n        }\n\n        internal static async Task<string> Execute(string json)\n        {\n            var request = LaraUI.JSON.Parse<AutocompleteRequest>(json);\n            if (!_Map.TryGet(request.Key, out var element))\n            {\n                return string.Empty;\n            }\n            var options = element.GetOptions();\n            if (options?.Provider == null)\n            {\n                return string.Empty;\n            }\n\n            var response = await options.Provider.GetAutocompleteList(request.Term);\n            return LaraUI.JSON.Stringify(response);\n        }\n\n        public static void Register(string key, AutocompleteElement element)\n        {\n            _Map.Set(key, element);\n        }\n\n        public static void Unregister(string key)\n        {\n            _Map.Remove(key);\n        }\n\n        public static int RegisteredCount => _Map.Count;\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Autocomplete/IAutocompleteProvider.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 11/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System.Threading.Tasks;\n\nnamespace Integrative.Lara\n{\n    /// <summary>\n    /// Interface for a class that provides autocomplete suggestions\n    /// </summary>\n    public interface IAutocompleteProvider\n    {\n        /// <summary>\n        /// Method that provides autocomplete suggestions\n        /// </summary>\n        /// <param name=\"term\">Search term typed</param>\n        /// <returns>Autocomplete response</returns>\n        Task<AutocompleteResponse> GetAutocompleteList(string term);\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Components/ComponentRegistry.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 8/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System;\nusing System.Collections.Generic;\n\nnamespace Integrative.Lara\n{\n    internal sealed class ComponentRegistry\n    {\n        private readonly Dictionary<string, Type> _components;\n\n        public ComponentRegistry()\n        {\n            _components = new Dictionary<string, Type>();\n        }\n\n        public void Register(string name, Type type)\n        {\n            name = name ?? throw new ArgumentNullException(nameof(name));\n            type = type ?? throw new ArgumentNullException(nameof(type));\n            if (!IsValidTagName(name))\n            {\n                throw new ArgumentException(Resources.DashRequired);\n            }\n\n            if (!type.IsSubclassOf(typeof(WebComponent)))\n            {\n                throw new InvalidOperationException(Resources.MustInherit);\n            }\n            if (_components.TryGetValue(name, out var previous))\n            {\n                var message = $\"Duplicate entries for tag '{name}'. The class '{previous.FullName}' already registers the tag name.\";\n                throw new InvalidOperationException(message);\n            }\n            _components.Add(name, type);\n        }\n\n        public void Unregister(string tagName)\n        {\n            _components.Remove(tagName);\n        }\n\n        private static bool IsValidTagName(string tagName)\n        {\n            return !string.IsNullOrEmpty(tagName)\n                && !tagName.Contains(\" \", StringComparison.InvariantCulture)\n                && tagName.Contains(\"-\", StringComparison.InvariantCulture);\n        }\n\n        public bool TryGetComponent(string name, out Type type)\n        {\n            return _components.TryGetValue(name, out type);\n        }\n\n        public void Clear()\n        {\n            _components.Clear();\n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Components/Fragment.cs",
    "content": "﻿/*\nCopyright (c) 2021 Integrative Software LLC\nCreated: 1/2021\nAuthor: Pablo Carbonell\n*/\n\nusing System;\nusing System.Collections.ObjectModel;\n\nnamespace Integrative.Lara\n{\n    /// <summary>\n    /// Fragment component to group element in a single parent\n    /// </summary>\n    public class Fragment : WebComponent\n    {\n        /// <summary>\n        /// Constructor\n        /// </summary>\n        public Fragment()\n        {\n            ShadowRoot.AppendChild(new Slot());\n        }\n\n        /// <summary>\n        /// Creates a Fragment with a list of children that follows an observable collection\n        /// </summary>\n        /// <typeparam name=\"TValue\"></typeparam>\n        /// <param name=\"source\"></param>\n        /// <param name=\"factory\"></param>\n        /// <returns></returns>\n        public static Fragment ForEach<TValue>(ObservableCollection<TValue> source, Func<TValue, Element> factory)\n        {\n            var fragment = new Fragment();\n            fragment.BindChildren(source, factory);\n            return fragment;\n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Components/LaraWebComponent.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 8/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System;\n\nnamespace Integrative.Lara\n{\n    /// <summary>\n    /// Classes marked as LaraWebComponent will registered when executing LaraUI.PublishAssemblies()\n    /// </summary>\n    [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]\n    public sealed class LaraWebComponentAttribute : Attribute\n    {\n        /// <summary>\n        /// Custom tag name for the component. Must contain the '-' character.\n        /// </summary>\n        public string ComponentTagName { get; }\n\n        /// <summary>\n        /// Constructor with custom tag name\n        /// </summary>\n        /// <param name=\"customTagName\">Tag name of the web component</param>\n        public LaraWebComponentAttribute(string customTagName)\n        {\n            ComponentTagName = customTagName;\n        }\n\n        /// <summary>\n        /// Constructor\n        /// </summary>\n        public LaraWebComponentAttribute() : this(\"\")\n        {\n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Components/RenderIf.cs",
    "content": "﻿using System;\nusing System.ComponentModel;\n\nnamespace Integrative.Lara\n{\n    /// <summary>\n    /// Conditional render placeholder\n    /// </summary>\n    public class RenderIf : WebComponent\n    {\n        private readonly Func<bool> _criteria;\n        private readonly Func<Element> _factory;\n        private bool _rendered;\n\n        /// <summary>\n        /// Constructor\n        /// </summary>\n        /// <param name=\"source\"></param>\n        /// <param name=\"criteria\"></param>\n        /// <param name=\"factory\"></param>\n        public RenderIf(INotifyPropertyChanged source, Func<bool> criteria, Func<Element> factory)\n        {\n            _criteria = criteria;\n            _factory = factory;\n            source.PropertyChanged += (_, _) => Update();\n        }\n\n        private void Update()\n        {\n            var rendered = _criteria();\n            if (rendered == _rendered) return;\n            _rendered = rendered;\n            if (rendered)\n            {\n                ShadowRoot.AppendChild(_factory());\n            }\n            else\n            {\n                ShadowRoot.ClearChildren();\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Components/Shadow.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 8/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System.Collections.Generic;\nusing System.Linq;\n\nnamespace Integrative.Lara\n{\n    internal sealed class Shadow : Element\n    {\n        private const string ShadowTagName = \"__shadow\";\n\n        public Shadow(WebComponent parent) : base(ShadowTagName)\n        {\n            ParentComponent = parent;\n        }\n\n        public WebComponent ParentComponent { get; }\n\n        internal override IEnumerable<Node> GetLightSlotted()\n        {\n            return Enumerable.Empty<Node>();\n        }\n\n        internal override bool IsPrintable => false;\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Components/Slot.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 8/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System.Collections.Generic;\nusing System.Diagnostics.CodeAnalysis;\nusing System.Linq;\n\nnamespace Integrative.Lara\n{\n    /// <summary>\n    /// A slot element is a placeholder inside a web component that you can fill with your own element\n    /// </summary>\n    public sealed class Slot : Element\n    {\n        /// <summary>\n        /// The slot's name\n        /// </summary>\n        public string? Name\n        {\n            get => GetAttribute(\"name\");\n            set => SetAttributeLower(\"name\", value);\n        }\n\n        /// <summary>\n        /// Constructor\n        /// </summary>\n        public Slot() : base(\"slot\")\n        {\n        }\n\n        internal bool MatchesName(string? slotName)\n        {\n            var name = Name;\n            if (string.IsNullOrEmpty(name))\n            {\n                return string.IsNullOrEmpty(slotName);\n            }\n\n            return name == slotName;\n        }\n\n        internal override IEnumerable<Node> GetLightSlotted()\n        {\n            return TryFindParentComponent(this, out var component) ? component.GetSlottedElements(Name) : Enumerable.Repeat(this, 1);\n        }\n\n        private static bool TryFindParentComponent(Node element, [NotNullWhen(true)] out WebComponent? component)\n        {\n            var parent = element.ParentElement;\n            if (parent is null)\n            {\n                component = default;\n                return false;\n            }\n\n            if (parent is not Shadow shadow) return TryFindParentComponent(parent, out component);\n            component = shadow.ParentComponent;\n            return true;\n            // ReSharper disable once TailRecursiveCall\n        }\n\n        internal override bool IsPrintable => false;\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Components/SlottedCalculator.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 8/2019\nAuthor: Pablo Carbonell\n*/\n\nnamespace Integrative.Lara\n{\n    internal static class SlottedCalculator\n    {\n        public static void UpdateSlotted(Node node)\n        {\n            node.IsSlotted = IsParentSlotting(node);\n            if (node is WebComponent component)\n            {\n                var shadow = component.GetShadow();\n                UpdateSlotted(shadow);\n            }\n            if (node is Element element)\n            {\n                UpdateChildren(element);\n            }\n        }\n\n        internal static bool IsParentSlotting(Node node)\n        {\n            var parent = node.ParentElement;\n            if (parent == null || parent is Slot)\n            {\n                return false;\n            }\n            if (parent is Shadow shadow)\n            {\n                return shadow.ParentComponent.IsSlotted;\n            }\n            if (parent is WebComponent component)\n            {\n                return node is Element element\n                       && component.IsSlotActive(element.GetAttributeLower(\"slot\"));\n            }\n            return parent.IsSlotted;\n        }\n\n        private static void UpdateChildren(Element element)\n        {\n            foreach (var node in element.Children)\n            {\n                UpdateSlotted(node);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Components/WebComponent.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 8/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System;\nusing System.Collections.Generic;\nusing System.ComponentModel;\nusing System.Linq;\n\nnamespace Integrative.Lara\n{\n    /// <summary>\n    /// Base class for web components\n    /// </summary>\n    public abstract class WebComponent : Element\n    {\n        /// <summary>\n        /// The 'shadow root' is the element that contains the shadow DOM tree\n        /// </summary>\n        protected Element ShadowRoot => _shadow;\n\n        private readonly Shadow _shadow;\n\n        internal Shadow GetShadow() => _shadow;\n\n        private HashSet<string>? _observedAttributes;\n\n        /// <summary>\n        /// Constructor\n        /// </summary>\n        /// <param name=\"tagName\">Component's custom tag name</param>\n        protected WebComponent(string tagName) : base(tagName ?? throw new ArgumentNullException(nameof(tagName)))\n        {\n            VerifyTypeThrow(tagName, GetType());\n            _shadow = new Shadow(this);\n        }\n\n        /// <summary>\n        /// Constructor\n        /// </summary>\n        protected WebComponent()\n        {\n            VerifyTypeThrow(TagName, GetType());\n            _shadow = new Shadow(this);\n        }\n\n        private void InitializeObservedAttributes()\n        {\n            if (_observedAttributes == null)\n            {\n                _observedAttributes = new HashSet<string>(GetObservedAttributes());\n            }\n        }\n\n        private static void VerifyTypeThrow(string tagName, Type componentType)\n        {\n            if (!VerifyType(tagName, componentType, out var error))\n            {\n                throw new InvalidOperationException(error);\n            }\n        }\n\n        internal static bool VerifyType(string tagName, Type componentType, out string error)\n        {\n            // register component if not previous;y registered\n            var app = LaraUI.Context.Application;\n            if (!app.TryGetComponent(tagName, out var type))\n            {\n                app.PublishComponent(new WebComponentOptions\n                {\n                    ComponentTagName = tagName,\n                    ComponentType = componentType\n                });\n                error = \"\";\n                return true;\n            }\n\n            // error if already registered for different type\n            if (type != componentType)\n            {\n                error = $\"The tag '{tagName}' is registered with the type '{type.FullName}' and not '{componentType.FullName}'.\";\n                return false;\n            }\n\n            // already registered with matching type\n            error = string.Empty;\n            return true;\n        }\n\n        /// <summary>\n        /// Obsolete\n        /// </summary>\n        [Obsolete(\"Not needed anymore, Shadow root is automatically created\")]\n        [EditorBrowsable(EditorBrowsableState.Never)]\n        protected void AttachShadow()\n        {\n        }\n\n        /// <summary>\n        /// Override to declare a list of attributes that will trigger the OnAttributeChanged event\n        /// </summary>\n        /// <returns></returns>\n        protected virtual IEnumerable<string> GetObservedAttributes()\n        {\n            return Enumerable.Empty<string>();\n        }\n\n        internal override IEnumerable<Node> GetLightSlotted()\n        {\n            foreach (var child in ShadowRoot.Children)\n            {\n                if (child is Element childElement)\n                {\n                    foreach (var light in childElement.GetLightSlotted())\n                    {\n                        yield return light;\n                    }\n                }\n                else\n                {\n                    yield return child;\n                }\n            }\n        }\n\n        internal override IEnumerable<Node> GetAllDescendants()\n        {\n            yield return ShadowRoot;\n            foreach (var child in Children)\n            {\n                yield return child;\n            }\n        }\n\n        /// <summary>\n        /// Returns the elements that are slotted with the given slot name\n        /// </summary>\n        /// <param name=\"slotName\">Slot name</param>\n        /// <returns>IEnumerable of nodes</returns>\n        public IEnumerable<Node> GetSlottedElements(string? slotName)\n        {\n            foreach (var node in Children)\n            {\n                if (NodeMatchesSlot(node, slotName))\n                {\n                    yield return node;\n                }\n            }\n        }\n\n        private static bool NodeMatchesSlot(Node node, string? slotName)\n        {\n            return node is Element element\n                && ElementMatchesSlot(element, slotName);\n        }\n\n        private static bool ElementMatchesSlot(Element element, string? slotName)\n        {\n            var slot = element.GetAttributeLower(\"slot\");\n            if (string.IsNullOrEmpty(slotName))\n            {\n                return string.IsNullOrEmpty(slot);\n            }\n\n            return slot == slotName;\n        }\n\n        internal override void AttributeChanged(string attribute, string? value)\n        {\n            BeginUpdate();\n            base.AttributeChanged(attribute, value);\n            InitializeObservedAttributes();\n            if (_observedAttributes != null && _observedAttributes.Contains(attribute))\n            {\n                OnAttributeChanged(attribute);\n            }\n            EndUpdate();\n        }\n\n        /// <summary>\n        /// Invoked each time an attribute defined in GetObservedAttributes is modified.\n        /// </summary>\n        /// <param name=\"attribute\"></param>\n        protected virtual void OnAttributeChanged(string attribute)\n        {\n        }\n\n        internal override IEnumerable<Element> GetNotifyList()\n        {\n            foreach (var child in base.GetNotifyList())\n            {\n                yield return child;\n            }\n            yield return ShadowRoot;\n        }\n\n        internal override bool IsPrintable => false;\n\n        internal bool IsSlotActive(string? slotName)\n        {\n            return IsSlotActive(ShadowRoot, slotName);\n        }\n\n        private static bool IsSlotActive(Element parent, string? slotName)\n        {\n            foreach (var child in parent.Children)\n            {\n                if (IsSlotChildActive(child, slotName))\n                {\n                    return true;\n                }\n            }\n            return false;\n        }\n\n        private static bool IsSlotChildActive(Node child, string? slotName)\n        {\n            return (child is Slot slot && slot.MatchesName(slotName))\n                || (child is Element element && IsSlotActive(element, slotName));\n        }\n\n        /// <summary>\n        /// Triggers a custom event\n        /// </summary>\n        /// <param name=\"eventName\">Event's name</param>\n        public void TriggerEvent(string eventName) => NotifyEvent(eventName);\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Components/WebComponentOptions.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 8/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System;\n\nnamespace Integrative.Lara\n{\n    /// <summary>\n    /// Options for publishing web components\n    /// </summary>\n    public sealed class WebComponentOptions\n    {\n        /// <summary>\n        /// Custom tag name for the component. Needs to include the '-' character.\n        /// </summary>\n        public string ComponentTagName { get; set; } = string.Empty;\n\n        /// <summary>\n        /// Type of the component. Needs to inherit from WebComponent. Example: 'typeof(MyComponent)'\n        /// </summary>\n        public Type? ComponentType { get; set; }\n\n        internal Type GetComponentType()\n        {\n            return ComponentType ?? throw new MissingMemberException(nameof(WebComponentOptions), nameof(ComponentType));\n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/DOM/Attributes.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 5/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System;\nusing System.Collections;\nusing System.Collections.Generic;\n\nnamespace Integrative.Lara\n{\n    internal sealed class Attributes : IEnumerable<KeyValuePair<string, string>>\n    {\n        private readonly Element _element;\n        private readonly Dictionary<string, string?> _values;\n\n        public Attributes(Element element)\n        {\n            _element = element;\n            _values = new Dictionary<string, string?>();\n            SetAttributeLower(\"id\", element.Id);\n        }\n\n        public bool HasAttribute(string name) => HasAttributeLower(name.ToLowerInvariant());\n\n        public string? GetAttribute(string name) => GetAttributeLower(name.ToLowerInvariant());\n\n        internal bool HasAttributeLower(string nameLower)\n            => _values.ContainsKey(nameLower);\n\n        internal void SetAttributeLower(string nameLower, string? value)\n        {\n            if (nameLower == \"slot\" && _element.ParentElement != null)\n            {\n                throw new InvalidOperationException(Resources.SlotOnlyParent);\n            }\n            if (_values.TryGetValue(nameLower, out var previous))\n            {\n                if (previous == value)\n                {\n                    return;\n                }\n                _values.Remove(nameLower);\n            }\n            _values.Add(nameLower, value);\n            if (nameLower == \"value\")\n            {\n                SetValueDelta.Enqueue(_element, value);\n            }\n            else if (nameLower == \"checked\")\n            {\n                SetCheckedDelta.Enqueue(_element, true);\n            }\n            else if (nameLower == \"id\")\n            {\n                SetIdDelta.Enqueue(_element, value ?? \"\");\n            }\n            else\n            {\n                AttributeEditedDelta.Enqueue(_element, nameLower, value);\n            }\n            _element.AttributeChanged(nameLower, value);\n            if (nameLower == \"slot\")\n            {\n                _element.UpdateSlotted();\n            }\n        }\n\n        internal void RemoveAttributeLower(string nameLower)\n        {\n            if (!_values.ContainsKey(nameLower))\n            {\n                return;\n            }\n            _values.Remove(nameLower);\n            if (nameLower == \"checked\")\n            {\n                SetCheckedDelta.Enqueue(_element, false);\n            }\n            else\n            {\n                AttributeRemovedDelta.Enqueue(_element, nameLower);\n            }\n            _element.AttributeChanged(nameLower, null);\n        }\n\n        internal void SetFlagAttributeLower(string nameLower, bool value)\n        {\n            var current = _values.ContainsKey(nameLower);\n            if (value == current) return;\n            if (value)\n            {\n                SetAttributeLower(nameLower, \"\");\n            }\n            else\n            {\n                RemoveAttributeLower(nameLower);\n            }\n        }\n\n        internal void NotifyValue(string value)\n        {\n            const string valueAttribute = \"value\";\n            if (_values.TryGetValue(valueAttribute, out var previous))\n            {\n                if (previous == value)\n                {\n                    return;\n                }\n                _values.Remove(valueAttribute);\n            }\n            _values.Add(valueAttribute, value);\n            _element.AttributeChanged(valueAttribute, value);\n        }\n\n        internal void NotifyChecked(bool isChecked)\n        {\n            NotifyFlag(\"checked\", isChecked);\n        }\n\n        internal void NotifySelected(bool selected)\n        {\n            NotifyFlag(\"selected\", selected);\n        }\n\n        private void NotifyFlag(string nameLower, bool value)\n        {\n            var current = _values.ContainsKey(nameLower);\n            if (current == value)\n            {\n                return;\n            }\n            if (value)\n            {\n                _values.Add(nameLower, null);\n            }\n            else\n            {\n                _values.Remove(nameLower);\n            }\n            _element.AttributeChanged(nameLower, string.Empty);\n        }\n\n        internal string? GetAttributeLower(string nameLower)\n        {\n            return _values.TryGetValue(nameLower, out var result) ? result : string.Empty;\n        }\n\n        public IEnumerator<KeyValuePair<string, string>> GetEnumerator()\n        {\n            return _values.GetEnumerator();\n        }\n\n        IEnumerator IEnumerable.GetEnumerator()\n        {\n            return _values.GetEnumerator();\n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/DOM/BlockOptions.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 6/2019\nAuthor: Pablo Carbonell\n*/\n\nnamespace Integrative.Lara\n{\n    /// <summary>\n    /// Defines options for blocking the UI while executing an event\n    /// </summary>\n    public class BlockOptions\n    {\n        /// <summary>\n        /// Gets or sets the ID of element to block. Leave empty to block the whole page.\n        /// </summary>\n        /// <value>\n        /// The ID of the element to block. If left blank, block the entire page.\n        /// </value>\n        public string? BlockedElementId { get; set; }\n\n        /// <summary>\n        /// Gets or sets the ID of the element to show. If set, the element specified will be shown instead of the default block dialog.\n        /// </summary>\n        /// <value>\n        /// The ID of the element to show instead of the default block dialog.\n        /// </value>\n        public string? ShowElementId { get; set; }\n\n        /// <summary>\n        /// Gets or sets an HTML message to show while blocking the user interface. If set, the specified HTML will be shown instead of the default block dialog.\n        /// </summary>\n        /// <value>\n        /// The HTML message to show instead of the default block dialog.\n        /// </value>\n        public string? ShowHtmlMessage { get; set; }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/DOM/ChildrenBindingSubscription.cs",
    "content": "﻿/*\nCopyright (c) 2020-2021 Integrative Software LLC\nCreated: 12/2020\nAuthor: Pablo Carbonell\n*/\n\nusing System.Collections.Specialized;\n\nnamespace Integrative.Lara\n{\n    internal class ChildrenBindingSubscription\n    {\n        public NotifyCollectionChangedEventHandler Handler { get; }\n        public INotifyCollectionChanged Source { get; }\n\n        public ChildrenBindingSubscription(\n            NotifyCollectionChangedEventHandler handler,\n            INotifyCollectionChanged source)\n        {\n            Handler = handler;\n            Source = source;\n            source.CollectionChanged += handler;\n        }\n\n        public void Unsubscribe()\n        {\n            Source.CollectionChanged -= Handler;\n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/DOM/Document.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 5/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System;\nusing System.Collections.Generic;\nusing System.Globalization;\nusing System.Net.WebSockets;\nusing System.Threading;\nusing System.Threading.Tasks;\n\nnamespace Integrative.Lara\n{\n    /// <summary>\n    /// Status options for server-side events\n    /// </summary>\n    public enum ServerEventsStatus\n    {\n        /// <summary>\n        /// Server-side events have not been enabled\n        /// </summary>\n        Disabled,\n\n        /// <summary>\n        /// The server is waiting for the client to listen to server-side events\n        /// </summary>\n        Connecting,\n\n        /// <summary>\n        /// Server-side events are enabled\n        /// </summary>\n        Enabled\n    }\n\n    /// <summary>\n    /// An HTML5 document.\n    /// </summary>\n    public class Document\n    {\n        internal IPage Page { get; }\n\n        /// <summary>\n        /// Global unique identifier for the document\n        /// </summary>\n        public Guid VirtualId { get; }\n\n        private readonly DocumentIdMap _map;\n        private readonly Queue<BaseDelta> _queue;\n        internal SemaphoreSlim Semaphore { get; }\n\n        private readonly ServerEventsController _serverEvents;\n        private readonly MessageRegistry _messageRegistry;\n        private readonly Sequencer _sequencer = new();\n\n        private Dictionary<string, EventSettings> Events { get; } = new();\n\n        /// <summary>\n        /// Occurs when the document is unloaded\n        /// </summary>\n        public event EventHandler? OnUnload;\n\n        /// <summary>\n        /// Asynchronous unload event\n        /// </summary>\n        public AsyncEvent OnUnloadAsync { get; } = new();\n\n        internal event EventHandler? UnloadComplete;\n\n        /// <summary>\n        /// Gets or sets the language. See 'lang' property for HTML5 documents.\n        /// </summary>\n        /// <value>\n        /// The language.\n        /// </value>\n        public string? Lang { get; set; }\n\n        /// <summary>\n        /// The document's Head element.\n        /// </summary>\n        /// <value>\n        /// The head.\n        /// </value>\n        public HtmlHeadElement Head { get; }\n\n        /// <summary>\n        /// The document's Body element.\n        /// </summary>\n        /// <value>\n        /// The body.\n        /// </value>\n        public HtmlBodyElement Body { get; }\n\n        internal DateTime LastUtc { get; private set; }\n\n        internal Queue<BaseDelta> GetQueue() => _queue;\n\n        internal Document(IPage page, double keepAliveInterval)\n            : this(page, Connections.CreateCryptographicallySecureGuid(), keepAliveInterval)\n        {\n        }\n\n        internal Document(IPage page, Guid virtualId, double keepAliveInterval)\n        {\n            VirtualId = virtualId;\n            Page = page;\n            _map = new DocumentIdMap();\n            _queue = new Queue<BaseDelta>();\n            Semaphore = new SemaphoreSlim(1);\n            Head = new HtmlHeadElement\n            {\n                Document = this,\n                IsSlotted = true\n            };\n            OnElementAdded(Head);\n            Body = new HtmlBodyElement\n            {\n                Document = this,\n                IsSlotted = true\n            };\n            UpdateTimestamp();\n            OnElementAdded(Body);\n            TemplateBuilder.Build(this, keepAliveInterval);\n            _serverEvents = new ServerEventsController(this);\n            _messageRegistry = new MessageRegistry(this);\n        }\n\n        internal string VirtualIdString =>\n            VirtualId.ToString(GlobalConstants.GuidFormat, CultureInfo.InvariantCulture);\n\n        /// <summary>\n        /// Creates an HTML element.\n        /// </summary>\n        /// <param name=\"tagName\">Name of the tag.</param>\n        /// <returns>Element created</returns>\n        public static Element CreateElement(string tagName) => Element.Create(tagName);\n\n        /// <summary>\n        /// Creates a text node.\n        /// </summary>\n        /// <param name=\"data\">The node's data.</param>\n        /// <returns>Text node created</returns>\n        public static TextNode CreateTextNode(string data) => new TextNode(data);\n\n        internal void UpdateTimestamp()\n        {\n            LastUtc = DateTime.UtcNow;\n        }\n\n        internal void ModifyLastUtcForTesting(DateTime value)\n        {\n            LastUtc = value;\n        }\n\n        internal void OnElementAdded(Element element)\n            => _map.NotifyAdded(element);\n\n        internal void OnElementRemoved(Element element)\n            => _map.NotifyRemoved(element);\n\n        internal void NotifyChangeId(Element element, string before, string after)\n            => _map.NotifyChangeId(element, before, after);\n\n        internal bool QueueingEvents { get; private set; }\n\n        internal void OpenEventQueue()\n        {\n            if (_queue.Count > 0)\n            {\n                var json = FlushQueue();\n                Head.SetAttribute(\"data-lara-initialdelta\", json);\n            }\n            QueueingEvents = true;\n        }\n\n        internal string FlushQueue()\n        {\n            var list = new List<BaseDelta>();\n            while (_queue.Count > 0)\n            {\n                var step = _queue.Dequeue();\n                list.Add(step);\n            }\n            var result = new EventResult(list);\n            return result.ToJSON();\n        }\n\n        internal void Enqueue(BaseDelta delta)\n        {\n            _queue.Enqueue(delta);\n        }\n\n        /// <summary>\n        /// Retrieves the element with the given ID.\n        /// </summary>\n        /// <param name=\"id\">Element ID</param>\n        /// <param name=\"element\">The element.</param>\n        /// <returns>True when the element was found.</returns>\n        public bool TryGetElementById(string id, out Element element)\n            => _map.TryGetElementById(id, out element);\n\n        /// <summary>\n        /// Retrieves the element with the given ID.\n        /// </summary>\n        /// <param name=\"id\">Element ID</param>\n        /// <returns>The element</returns>\n        public Element GetElementById(string id)\n        {\n            _map.TryGetElementById(id, out var element);\n            return element;\n        }\n\n        /// <summary>\n        /// Returns true when there are UI changes pending to be flushed to the client\n        /// </summary>\n        public bool HasPendingChanges => _queue.Count > 0;\n\n        [Obsolete(\"Support old methods\")]\n        internal void OnMessage(string key, Func<Task> handler)\n        {\n            Head.On(\"_\" + key, handler);\n        }\n\n        internal void AddMessageListener(string messageId, Func<MessageEventArgs, Task> handler)\n        {\n            _messageRegistry.Add(messageId, handler);\n        }\n\n        internal void RemoveMessageListener(string messageId, Func<MessageEventArgs, Task> handler)\n        {\n            _messageRegistry.Remove(messageId, handler);\n        }\n\n        internal async Task NotifyUnload()\n        {\n            await _serverEvents.NotifyUnload();\n            var args = new EventArgs();\n            OnUnload?.Invoke(this, args);\n            await OnUnloadAsync.InvokeAsync(this, args);\n            UnloadComplete?.Invoke(this, new EventArgs());\n            _sequencer.AbortAll();\n        }\n\n        /// <summary>\n        /// Returns the current status of server-side events\n        /// </summary>\n        public ServerEventsStatus ServerEventsStatus\n            => _serverEvents.ServerEventsStatus;\n\n        /// <summary>\n        /// Starts a server event. Call with 'using' and dispose the class returned.\n        /// </summary>\n        /// <returns>Disposable token</returns>\n        public ServerEvent StartServerEvent()\n            => _serverEvents.StartServerEvent();\n\n        internal void ServerEventsOn()\n        {\n            NotifyHasEvent();\n            _serverEvents.ServerEventsOn();\n        }\n\n        internal Task ServerEventsOff()\n            => _serverEvents.ServerEventsOff();\n\n        internal bool SocketRemainsOpen(string eventName)\n            => _serverEvents.SocketRemainsOpen(eventName);\n\n        internal virtual Task<TaskCompletionSource<bool>> GetSocketCompletion(WebSocket socket)\n            => _serverEvents.GetSocketCompletion(socket);\n\n        internal Task ServerEventFlush()\n            => _serverEvents.ServerEventFlush();\n\n        internal ServerEventsController GetServerEventsController()\n            => _serverEvents;\n\n        internal Task<bool> WaitForTurn(long turn) => _sequencer.WaitForTurn(turn);\n\n        internal bool CanDiscard { get; private set; } = true;\n\n        internal void NotifyHasEvent()\n        {\n            CanDiscard = false;\n        }\n\n        internal Task NotifyEvent(string eventName)\n        {\n            if (Events.TryGetValue(eventName, out var settings)\n                && settings.Handler != null)\n            {\n                return settings.Handler();\n            }\n            return Task.CompletedTask;\n        }\n\n        /// <summary>\n        /// Registers an event and associates code to execute.\n        /// </summary>\n        /// <param name=\"settings\">The event's settings.</param>\n        public void On(EventSettings settings)\n        {\n            settings = settings ?? throw new ArgumentNullException(nameof(settings));\n            settings.Verify();\n            RemoveEvent(settings.EventName);\n            Events.Add(settings.EventName, settings);\n            SubscribeDelta.Enqueue(this, settings);\n        }\n\n        /// <summary>\n        /// Registers an event and associates code to execute.\n        /// </summary>\n        /// <param name=\"eventName\">Name of the event.</param>\n        /// <param name=\"handler\">The handler to execute.</param>\n        public void On(string eventName, Func<Task>? handler)\n        {\n            eventName = eventName ?? throw new ArgumentNullException(nameof(eventName));\n            if (handler == null)\n            {\n                RemoveEvent(eventName);\n            }\n            else\n            {\n                On(new EventSettings\n                {\n                    EventName = eventName,\n                    Handler = handler\n                });\n            }\n        }\n\n        private void RemoveEvent(string eventName)\n        {\n            if (!Events.ContainsKey(eventName)) return;\n            Events.Remove(eventName);\n            Enqueue(new UnsubscribeDelta\n            {\n                ElementId = string.Empty,\n                EventName = eventName\n            });\n        }\n\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/DOM/DocumentIdMap.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 5/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System.Collections.Generic;\n\nnamespace Integrative.Lara\n{\n    internal sealed class DocumentIdMap\n    {\n        private readonly Dictionary<string, Element> _map;\n\n        public DocumentIdMap()\n        {\n            _map = new Dictionary<string, Element>();\n        }\n\n        public bool TryGetElementById(string id, out Element element)\n        {\n            return _map.TryGetValue(id, out element);\n        }\n\n        public void NotifyChangeId(Element element, string before, string after)\n        {\n            if (before == after) return;\n            RemovePrevious(before);\n            AddAfter(element, after);\n        }\n\n        private void RemovePrevious(string before)\n        {\n            _map.Remove(before);\n        }\n\n        private void AddAfter(Element element, string after)\n        {\n            if (_map.ContainsKey(after))\n            {\n                throw DuplicateElementIdException.Create(after);\n            }\n            _map.Add(after, element);\n        }\n\n        public void NotifyRemoved(Element element)\n        {\n            RemovePrevious(element.Id);\n        }\n\n        public void NotifyAdded(Element element)\n        {\n            AddAfter(element, element.Id);\n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/DOM/DocumentWriter.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 5/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System;\nusing System.Globalization;\nusing System.Text;\nusing System.Web;\n\nnamespace Integrative.Lara\n{\n    internal sealed class DocumentWriter\n    {\n        public const int MaxLevelDeep = 500;\n\n        private readonly Document? _document;\n        private readonly StringBuilder _builder;\n\n        public DocumentWriter(Document document)\n            : this()\n        {\n            _document = document;\n        }\n\n        public DocumentWriter()\n        {\n            _builder = new StringBuilder();\n        }\n\n        public void Print()\n        {\n            if (_document == null)\n            {\n                return;\n            }\n            _builder.AppendLine(\"<!doctype html>\");\n            _builder.AppendLine(\"<!-- Rendered with Lara Web Engine. https://laraui.com -->\");\n            _builder.Append(\"<html\");\n            if (!string.IsNullOrEmpty(_document.Lang))\n            {\n                var lang = HttpUtility.HtmlAttributeEncode(_document.Lang);\n                _builder.Append($\" lang=\\\"{lang}\\\"\");\n            }\n            _builder.AppendLine(\">\");\n            PrintElement(_document.Head, 1);\n            PrintElement(_document.Body, 1);\n            _builder.AppendLine(\"</html>\");\n        }\n\n        public void PrintElement(Element element, int indent)\n        {\n            VerifyNestedLevel(indent);\n            if (!element.Render)\n            {\n                PrintStubElement(element);\n            }\n            else if (IsInlineElement(element))\n            {\n                Indent(indent);\n                PrintInlineElement(element);\n                _builder.AppendLine();\n            }\n            else\n            {\n                PrintRegularElement(element, indent);\n            }\n        }\n\n        internal static void VerifyNestedLevel(int indent)\n        {\n            if (indent > MaxLevelDeep)\n            {\n                throw new InvalidOperationException($\"Document exceeded maximum nesting level of {MaxLevelDeep.ToString(CultureInfo.CurrentCulture)}.\");\n            }\n        }\n\n        private static bool IsInlineElement(Element element)\n        {\n            var hasChildren = false;\n            foreach (var child in element.GetLightChildren())\n            {\n                hasChildren = true;\n                if (child is TextNode)\n                {\n                    return true;\n                }\n            }\n            return !hasChildren;\n        }\n\n        private void PrintInlineElement(Element element)\n        {\n            PrintOpeningTag(element);\n            if (HtmlReference.IsSelfClosingTag(element.TagName)) return;\n            PrintInlineChildNodes(element);\n            PrintClosingTag(element, 0);\n        }\n\n        private void PrintStubElement(Element element)\n        {\n            _builder.Append(\"<script id=\\\"\");\n            _builder.Append(HttpUtility.HtmlAttributeEncode(element.Id));\n            _builder.Append(\"\\\" type=\\\"placeholder/lara\\\"></script>\");\n        }\n\n        private void Indent(int indent)\n        {\n            for (var index = 0; index < indent; index++)\n            {\n                _builder.Append('\\t');\n            }\n        }\n\n        private void PrintOpeningTag(Element element)\n        {\n            _builder.Append('<');\n            _builder.Append(element.TagName);\n            PrintAttributes(element);\n            _builder.Append('>');\n        }\n\n        private void PrintAttributes(Element element)\n        {\n            foreach (var pair in element.Attributes)\n            {\n                _builder.Append(' ');\n                _builder.Append(pair.Key);\n                var value = pair.Value;\n                if (value == null) continue;\n                _builder.Append('=');\n                _builder.Append('\"');\n                _builder.Append(HttpUtility.HtmlAttributeEncode(value));\n                _builder.Append('\"');\n            }\n        }\n\n        private void PrintInlineChildNodes(Element element)\n        {\n            foreach (var child in element.GetLightChildren())\n            {\n                PrintInlineNode(child);\n            }\n        }\n\n        private void PrintInlineNode(Node node)\n        {\n            if (node is TextNode text)\n            {\n                _builder.Append(text.Data);\n            }\n            else if (node is Element element)\n            {\n                PrintInlineElement(element);\n            }\n        }\n\n        private void PrintRegularElement(Element element, int indent)\n        {\n            Indent(indent);\n            PrintOpeningTag(element);\n            _builder.AppendLine();\n            if (!HtmlReference.IsSelfClosingTag(element.TagName))\n            {\n                PrintChildNodes(element, indent + 1);\n                PrintClosingTag(element, indent);\n            }\n            _builder.AppendLine();\n        }\n\n        private void PrintChildNodes(Element element, int indent)\n        {\n            foreach (var child in element.GetLightChildren())\n            {\n                PrintElement((Element)child, indent);\n            }\n        }\n\n        private void PrintClosingTag(Element element, int indent)\n        {\n            Indent(indent);\n            _builder.Append('<');\n            _builder.Append('/');\n            _builder.Append(element.TagName);\n            _builder.Append('>');\n        }\n\n        public override string ToString()\n        {\n            return _builder.ToString();\n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/DOM/DomSurgeon.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 5/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System;\nusing System.Collections.Generic;\nusing System.Diagnostics.CodeAnalysis;\n\nnamespace Integrative.Lara\n{\n    internal sealed class DomSurgeon\n    {\n        private readonly Element _parent;\n\n        public DomSurgeon(Element parent)\n        {\n            _parent = parent;\n        }\n\n        #region Public methods\n\n        public void Append(Node child)\n        {\n            var previous = BeforeOperation(child);\n            AppendInternal(child);\n            AfterOperation(child, previous);\n        }\n\n        public void InsertChildBefore(Node reference, Node child)\n        {\n            var previous = BeforeOperation(child);\n            InsertChild(reference, 0, child);\n            AfterOperation(child, previous);\n        }\n\n        public void InsertChildAfter(Node reference, Node child)\n        {\n            var previous = BeforeOperation(child);\n            InsertChild(reference, 1, child);\n            AfterOperation(child, previous);\n        }\n\n        public void InsertChildAt(int index, Node child)\n        {\n            var previous = BeforeOperation(child);\n            InsertChild(index, child);\n            AfterOperation(child, previous);\n        }\n\n        public void Remove(Node child)\n        {\n            RemoveInternal(child);\n            AfterOperation(child, _parent);\n        }\n\n        public void RemoveAt(int index)\n        {\n            var child = _parent.GetChildAt(index);\n            RemoveInternal(index);\n            AfterOperation(child, _parent);\n        }\n\n        public void ClearChildren()\n        {\n            var list = new List<Node>(_parent.Children);\n            ClearChildrenInternal();\n            foreach (var node in list)\n            {\n                AfterOperation(node, _parent);\n            }\n        }\n\n        #endregion\n\n        #region Connect and disconnect\n\n        private static Element? BeforeOperation(Node child)\n        {\n            return child.ParentElement;\n        }\n\n        private static void AfterOperation(Node node, Node? previousParent)\n        {\n            if (node is Element child)\n            {\n                AfterOperationInternal(child, previousParent);\n            }\n        }\n\n        private static void AfterOperationInternal(Element child, Node? previousParent)\n        {\n            var previousDocument = GetPreviousDocument(previousParent);\n            if (previousDocument == child.Document)\n            {\n                if (previousDocument != null)\n                {\n                    child.NotifyMove();\n                }\n            }\n            else if (child.Document == null)\n            {\n                child.NotifyDisconnect();\n            }\n            else if (previousDocument == null)\n            {\n                child.NotifyConnect();\n            }\n            else\n            {\n                child.NotifyAdopted();\n            }            \n        }\n\n        private static Document? GetPreviousDocument(Node? previousParent)\n        {\n            return previousParent?.Document;\n        }\n\n        #endregion\n\n        #region Operations\n\n        private void AppendInternal(Node child)\n        {\n            PreventCycles(child);\n            UpdateDocumentMappings(child);\n            UpdateChildParentLinks(child);\n        }\n\n        private void InsertChild(Node reference, int offset, Node child)\n        {\n            VerifyParentContainsChild(reference);\n            PreventCycles(child);\n            UpdateDocumentMappings(child);\n            UpdateChildParentLinks(reference, offset, child);\n        }\n\n        private void InsertChild(int index, Node child)\n        {\n            PreventCycles(child);\n            UpdateDocumentMappings(child);\n            UpdateChildParentLinks(index, child);\n        }\n\n        private void VerifyParentContainsChild(Node reference)\n        {\n            if (!_parent.ContainsChild(reference))\n            {\n                throw new InvalidOperationException(Resources.ReferenceNodeNotFound);\n            }\n        }\n\n        private void RemoveInternal(Node child)\n        {\n            if (child.ParentElement != _parent)\n            {\n                throw new InvalidOperationException(Resources.NodeNotFoundInsideParent);\n            }\n            if (child is Element childElement)\n            {\n                RemoveElementDelta.Enqueue(childElement);\n                RemoveInternalCommon(child);\n            }\n            else\n            {\n                var index = _parent.GetChildNodePosition(child);\n                RemoveInternalCommon(child);\n                NodeRemovedDelta.Enqueue(_parent, index);\n            }\n        }\n\n        private void RemoveInternal(int index)\n        {\n            var child = _parent.GetChildAt(index);\n            RemoveInternalCommon(child);\n            NodeRemovedDelta.Enqueue(_parent, index);\n        }\n\n        private void ClearChildrenInternal()\n        {\n            while (_parent.ChildCount > 0)\n            {\n                var child = _parent.GetChildAt(_parent.ChildCount - 1);\n                RemoveInternalCommon(child);\n            }\n            ClearChildrenDelta.Enqueue(_parent);\n        }\n\n        private void RemoveInternalCommon(Node child)\n        {\n            if (child is Element && _parent.Document != null)\n            {\n                var list = CollectNodes(child);\n                RemoveFromPreviousDocument(list, _parent.Document);\n            }\n            _parent.OnChildRemoved(child);\n            child.ParentElement = null;\n            child.UpdateSlotted();\n        }\n\n        #endregion\n\n        #region Prevent cycles in DOM tree\n\n        private void PreventCycles(Node child)\n        {\n            if (child is Element element && _parent.DescendsFrom(element))\n            {\n                throw new InvalidOperationException(Resources.CannotAddInsideItself);\n            }\n        }\n\n        #endregion\n\n        #region Update parent document and ID maps\n\n        private void UpdateDocumentMappings(Node child)\n        {\n            var newToDocument = NewToDocument(child, out var newDocument);\n            var leavingPrevious = LeavingPrevious(child, out var previous);\n            if (!newToDocument && !leavingPrevious) return;\n            var list = CollectNodes(child);\n            if (leavingPrevious)\n            {\n                RemoveFromPreviousDocument(list, previous!);\n            }\n\n            if (!newToDocument) return;\n            PreventDuplicateIds(list);\n            AddToDocument(list, newDocument!);\n        }\n\n        private bool NewToDocument(Node child, [NotNullWhen(true)] out Document? newDocument)\n        {\n            newDocument = _parent.Document;\n            return newDocument != null\n                   && child.Document != newDocument;\n        }\n\n        private bool LeavingPrevious(Node child, [NotNullWhen(true)] out Document? previousDocument)\n        {\n            previousDocument = child.Document;\n            return previousDocument != null\n                   && previousDocument != _parent.Document;\n        }\n\n        private static List<Node> CollectNodes(Node child)\n        {\n            var list = new List<Node>();\n            CollectElements(list, child);\n            return list;\n        }\n\n        private static void CollectElements(ICollection<Node> list, Node node)\n        {\n            list.Add(node);\n            if (node is not Element element) return;\n            foreach (var child in element.GetAllDescendants())\n            {\n                CollectElements(list, child);\n            }\n        }\n\n        private void PreventDuplicateIds(IEnumerable<Node> list)\n        {\n            var document = _parent.Document;\n            var hash = new HashSet<string>();\n            foreach (var node in list)\n            {\n                if (node is not Element element || string.IsNullOrEmpty(element.Id)) continue;\n                var id = element.Id;\n                if (hash.Contains(id) || DuplicateIdInDocument(document, id))\n                {\n                    throw DuplicateElementIdException.Create(id);\n                }\n                hash.Add(id);\n            }\n        }\n\n        private static bool DuplicateIdInDocument(Document? document, string id)\n        {\n            return document != null\n                && document.TryGetElementById(id, out _);\n        }\n\n        private static void AddToDocument(IEnumerable<Node> list, Document document)\n        {\n            foreach (var node in list)\n            {\n                node.Document = document;\n                if (node is Element element)\n                {\n                    document.OnElementAdded(element);\n                }\n            }\n        }\n\n        private static void RemoveFromPreviousDocument(IEnumerable<Node> list, Document document)\n        {\n            foreach (var node in list)\n            {\n                if (node is Element element)\n                {\n                    document.OnElementRemoved(element);\n                }\n                node.Document = null;\n            }\n        }\n\n        #endregion\n\n        #region Update Children collections and ParentElement reference\n\n        private void UpdateChildParentLinks(Node child)\n        {\n            child.ParentElement?.OnChildRemoved(child);\n            _parent.OnChildAppend(child);\n            child.ParentElement = _parent;\n            child.UpdateSlotted();\n            NodeAddedDelta.Enqueue(child);\n        }\n\n        private void UpdateChildParentLinks(Node reference, int offset, Node child)\n        {\n            var index = _parent.GetChildNodePosition(reference) + offset;\n            UpdateChildParentLinks(index, child);\n        }\n\n        private void UpdateChildParentLinks(int index, Node child)\n        {\n            child.ParentElement?.OnChildRemoved(child);\n            _parent.OnChildInsert(index, child);\n            child.ParentElement = _parent;\n            child.UpdateSlotted();\n            NodeInsertedDelta.Enqueue(child, index);\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/DOM/DuplicateElementIdException.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 5/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System;\n\nnamespace Integrative.Lara\n{\n    /// <summary>\n    /// Exception thrown when adding a duplicate element ID into a document\n    /// </summary>\n    /// <seealso cref=\"System.InvalidOperationException\" />\n    public class DuplicateElementIdException : InvalidOperationException\n    {\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"DuplicateElementIdException\"/> class.\n        /// </summary>\n        // ReSharper disable once UnusedMember.Global\n        public DuplicateElementIdException()\n        {\n        }\n\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"DuplicateElementIdException\"/> class.\n        /// </summary>\n        /// <param name=\"message\">The message that describes the error.</param>\n        // ReSharper disable once MemberCanBePrivate.Global\n        public DuplicateElementIdException(string message)\n            : base(message)\n        {\n        }\n\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"DuplicateElementIdException\"/> class.\n        /// </summary>\n        /// <param name=\"message\">The message.</param>\n        /// <param name=\"inner\">The inner.</param>\n        public DuplicateElementIdException(string message, Exception inner)\n            : base(message, inner)\n        {\n        }\n\n        /// <summary>\n        /// Creates a DuplicateElementId exception\n        /// </summary>\n        /// <param name=\"id\">The identifier that is duplicate.</param>\n        /// <returns>Exception created</returns>\n        public static DuplicateElementIdException Create(string id)\n        {\n            var message = $\"Duplicate element Id: {id}\";\n            return new DuplicateElementIdException(message);\n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/DOM/Element.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 5/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System;\nusing System.Collections.Generic;\nusing System.Collections.Specialized;\nusing System.ComponentModel;\nusing System.Diagnostics.CodeAnalysis;\nusing System.Globalization;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace Integrative.Lara\n{\n    /// <summary>\n    /// An Element node inside an HTML5 document\n    /// </summary>\n    /// <seealso cref=\"Node\" />\n    public abstract class Element : Node\n    {\n        private readonly Attributes _attributes;\n        private readonly List<Node> _children;\n\n        internal Dictionary<string, EventSettings> Events { get; }\n\n        private string _id;\n\n        /// <summary>\n        /// Element's tag name\n        /// </summary>\n        /// <value>\n        /// The element's tag name\n        /// </value>\n        public string TagName { get; }\n\n        /// <summary>\n        /// Creates an element\n        /// </summary>\n        /// <param name=\"tagName\">Element's tag name.</param>\n        /// <returns>Element created</returns>\n        public static Element Create(string tagName) => ElementFactory.CreateElement(tagName);\n\n        /// <summary>\n        /// Creates an element\n        /// </summary>\n        /// <param name=\"tagName\">Element's tag name.</param>\n        /// <param name=\"id\">The identifier.</param>\n        /// <returns>Element created</returns>\n        public static Element Create(string tagName, string id) => ElementFactory.CreateElement(tagName, id);\n\n        /// <summary>\n        /// Creates a namespace-specific HTML5 element (e.g. SVG elements)\n        /// </summary>\n        /// <param name=\"ns\">The namespace of the element</param>\n        /// <param name=\"tagName\">Element's tag name.</param>\n        /// <returns>Element created</returns>\n        // ReSharper disable once InconsistentNaming\n        public static Element CreateNS(string ns, string tagName) => ElementFactory.CreateElementNs(ns, tagName);\n\n        /// <summary>\n        /// Constructor\n        /// </summary>\n        /// <param name=\"tagName\">element tag</param>\n        protected Element(string tagName)\n        {\n            if (string.IsNullOrWhiteSpace(tagName))\n            {\n                tagName = GetDefaultTagName(GetType());\n            }\n            TagName = tagName.ToLowerInvariant();\n            _id = GlobalSerializer.GenerateElementId();\n            _attributes = new Attributes(this);\n            _children = new List<Node>();\n            Events = new Dictionary<string, EventSettings>();\n        }\n\n        /// <summary>\n        /// Constructor\n        /// </summary>\n        protected Element() : this(\"\")\n        {\n        }\n\n        /// <summary>\n        /// Returns a default/suggested tag name for a type\n        /// </summary>\n        /// <param name=\"type\">object type</param>\n        /// <returns>default/suggested tag name</returns>\n        public static string GetDefaultTagName(Type type)\n        {\n            var name = type.FullName ?? throw new ArgumentException(\"Invalid type name\");\n            var tail = name.Replace('.', '-').ToLowerInvariant();\n            return $\"x-{tail}\";\n        }\n\n        /// <summary>\n        /// Gets the type of the node.\n        /// </summary>\n        /// <value>\n        /// The type of the node.\n        /// </value>\n        public sealed override NodeType NodeType => NodeType.Element;\n\n        /// <summary>\n        /// Converts to string.\n        /// </summary>\n        /// <returns>\n        /// A <see cref=\"string\" /> that represents this instance.\n        /// </returns>\n        public override string ToString()\n        {\n            var suffix = Class;\n            if (string.IsNullOrEmpty(suffix))\n            {\n                suffix = string.Empty;\n            }\n            else\n            {\n                suffix = \" \" + suffix;\n            }\n            return string.IsNullOrEmpty(_id) ? TagName : $\"{TagName} #{_id}{suffix}\";\n        }\n\n        /// <summary>\n        /// Returns the ID by assigning one if needed\n        /// </summary>\n        /// <returns></returns>\n        [Obsolete(\"Not needed anymore\")]\n        [EditorBrowsable(EditorBrowsableState.Never)]\n        public string EnsureElementId() => Id;\n\n        #region Attributes\n\n        /// <summary>\n        /// Gets or sets the identifier.\n        /// </summary>\n        /// <value>\n        /// The identifier.\n        /// </value>\n        public string Id\n        {\n            get => _id;\n            set\n            {\n                if (value == _id)\n                {\n                    return;\n                }\n                if (string.IsNullOrWhiteSpace(value))\n                {\n                    throw new InvalidOperationException(\"Element IDs cannot be empty\");\n                }\n                Document?.NotifyChangeId(this, _id, value);\n                _attributes.SetAttributeLower(\"id\", value);\n                _id = value;\n            }\n        }\n\n        /// <summary>\n        /// Sets an attribute and its value.\n        /// </summary>\n        /// <param name=\"attributeName\">The name of the attribute.</param>\n        /// <param name=\"attributeValue\">The value of the attribute.</param>\n        public void SetAttribute(string attributeName, string? attributeValue)\n        {\n            attributeName = attributeName ?? throw new ArgumentNullException(nameof(attributeName));\n            SetAttributeLower(attributeName.ToLowerInvariant(), attributeValue);\n        }\n\n        internal void SetAttributeLower(string nameLower, string? value)\n        {\n            if (nameLower == \"id\")\n            {\n                if (string.IsNullOrWhiteSpace(value))\n                {\n                    throw new ArgumentException(\"Element IDs cannot be empty\");\n                }\n                Id = value;\n            }\n            else\n            {\n                _attributes.SetAttributeLower(nameLower, value);\n            }\n        }\n\n        internal void ToggleAttributeLower(string nameLower, bool value)\n        {\n            if (value)\n            {\n                SetAttributeLower(nameLower, \"\");\n            }\n            else\n            {\n                RemoveAttribute(nameLower);\n            }\n        }\n\n        /// <summary>\n        /// Determines whether the element has the given attribute\n        /// </summary>\n        /// <param name=\"attributeName\">The name of the attribute.</param>\n        /// <returns>\n        ///   <c>true</c> if the element has the specified attribute; otherwise, <c>false</c>.\n        /// </returns>\n        public bool HasAttribute(string attributeName)\n        {\n            attributeName = attributeName ?? throw new ArgumentNullException(nameof(attributeName));\n            return _attributes.HasAttribute(attributeName);\n        }\n\n        internal bool HasAttributeLower(string nameLower)\n            => _attributes.HasAttributeLower(nameLower);\n\n        /// <summary>\n        /// Gets the value of an attribute.\n        /// </summary>\n        /// <param name=\"attributeName\">The name of the attribute</param>\n        /// <returns>Value of the attribute</returns>\n        public string? GetAttribute(string attributeName)\n        {\n            attributeName = attributeName ?? throw new ArgumentNullException(nameof(attributeName));\n            return _attributes.GetAttribute(attributeName);\n        }\n\n        internal string? GetAttributeLower(string nameLower)\n            => _attributes.GetAttributeLower(nameLower);\n\n        /// <summary>\n        /// Adds or removes a flag attribute\n        /// </summary>\n        /// <param name=\"attributeName\">Attribute's name</param>\n        /// <param name=\"value\">true to add, false to remove</param>\n        public void SetFlagAttribute(string attributeName, bool value)\n        {\n            attributeName = attributeName ?? throw new ArgumentNullException(nameof(attributeName));\n            _attributes.SetFlagAttributeLower(attributeName.ToLowerInvariant(), value);\n        }\n\n        internal void SetFlagAttributeLower(string nameLower, bool value)\n            => _attributes.SetFlagAttributeLower(nameLower, value);\n\n        /// <summary>\n        /// Removes an attribute.\n        /// </summary>\n        /// <param name=\"attributeName\">The name of the attribute.</param>\n        public void RemoveAttribute(string attributeName)\n        {\n            attributeName = attributeName ?? throw new ArgumentNullException(nameof(attributeName));\n            attributeName = attributeName.ToLowerInvariant();\n            if (attributeName == \"id\")\n            {\n                throw new InvalidOperationException(\"Cannot remove element ID attribute\");\n            }\n\n            _attributes.RemoveAttributeLower(attributeName);\n        }\n\n        internal int? GetIntAttribute(string nameLower)\n        {\n            if (int.TryParse(GetAttributeLower(nameLower), out var value))\n            {\n                return value;\n            }\n\n            return null;\n        }\n\n        internal void SetIntAttribute(string nameLower, int? value)\n        {\n            if (value == null)\n            {\n                _attributes.RemoveAttributeLower(nameLower);\n            }\n            else\n            {\n                var text = ((int)value).ToString(CultureInfo.InvariantCulture);\n                SetAttributeLower(nameLower, text);\n            }\n        }\n\n        /// <summary>\n        /// Gets the attributes.\n        /// </summary>\n        /// <value>\n        /// The attributes.\n        /// </value>\n        public IEnumerable<KeyValuePair<string, string>> Attributes => _attributes;\n\n        /// <summary>\n        /// Determines whether the 'class' attribute contains the specified class.\n        /// </summary>\n        /// <param name=\"className\">Name of the class.</param>\n        /// <returns>\n        ///   <c>true</c> if the specified class is found; otherwise, <c>false</c>.\n        /// </returns>\n        public bool HasClass(string className) => ClassEditor.HasClass(Class, className);\n\n        /// <summary>\n        /// Adds the given class name to the 'class' attribute.\n        /// </summary>\n        /// <param name=\"className\">Name of the class.</param>\n        public void AddClass(string className) => Class = ClassEditor.AddClass(Class, className);\n\n        /// <summary>\n        /// Removes the given class name from the 'class' attribute.\n        /// </summary>\n        /// <param name=\"className\">Name of the class.</param>\n        public void RemoveClass(string className) => Class = ClassEditor.RemoveClass(Class, className);\n\n        /// <summary>\n        /// Adds or removes the given class name from the 'class' attribute.\n        /// </summary>\n        /// <param name=\"className\">Name of the class.</param>\n        /// <param name=\"value\">true to add the class, false to remove.</param>\n        public void ToggleClass(string className, bool value)\n            => Class = ClassEditor.ToggleClass(Class, className, value);\n\n        /// <summary>\n        /// Toggles (adds or removes) the class passed in parameters\n        /// </summary>\n        /// <param name=\"className\">Class name to toggle</param>\n        public void ToggleClass(string className) => Class = ClassEditor.ToggleClass(Class, className);\n\n        internal void NotifyValue(string value) => _attributes.NotifyValue(value);\n\n        internal void NotifyChecked(bool value) => _attributes.NotifyChecked(value);\n\n        internal void NotifySelected(bool value) => _attributes.NotifySelected(value);\n\n        #endregion\n\n        #region Global attributes\n\n        /// <summary>\n        /// The 'accesskey' HTML5 attribute.\n        /// </summary>\n        /// <value>\n        /// The access key.\n        /// </value>\n        public virtual string? AccessKey\n        {\n            get => GetAttributeLower(\"accesskey\");\n            set => SetAttributeLower(\"accesskey\", value);\n        }\n\n        /// <summary>\n        /// The 'autocapitalize' HTML5 attribute.\n        /// </summary>\n        /// <value>\n        /// The automatic capitalize.\n        /// </value>\n        // ReSharper disable once UnusedMember.Global\n        public virtual string? AutoCapitalize\n        {\n            get => GetAttributeLower(\"autocapitalize\");\n            set => SetAttributeLower(\"autocapitalize\", value);\n        }\n\n        /// <summary>\n        /// The 'class' HTML5 attribute.\n        /// </summary>\n        /// <value>\n        /// The class.\n        /// </value>\n        public virtual string? Class\n        {\n            get => GetAttributeLower(\"class\");\n            set => SetAttributeLower(\"class\", value);\n        }\n\n        /// <summary>\n        /// The 'contenteditable' HTML5 attribute.\n        /// </summary>\n        /// <value>\n        /// The content editable.\n        /// </value>\n        public virtual string? ContentEditable\n        {\n            get => GetAttributeLower(\"contenteditable\");\n            set => SetAttributeLower(\"contenteditable\", value);\n        }\n\n        /// <summary>\n        /// The 'contextmenu' HTML5 attribute.\n        /// </summary>\n        /// <value>\n        /// The context menu.\n        /// </value>\n        // ReSharper disable once UnusedMember.Global\n        public virtual string? ContextMenu\n        {\n            get => GetAttributeLower(\"contextmenu\");\n            set => SetAttributeLower(\"contextmenu\", value);\n        }\n\n        /// <summary>\n        /// The 'dir' HTML5 attribute.\n        /// </summary>\n        /// <value>\n        /// The dir.\n        /// </value>\n        public virtual string? Dir\n        {\n            get => GetAttributeLower(\"dir\");\n            set => SetAttributeLower(\"dir\", value);\n        }\n\n        /// <summary>\n        /// The 'draggable' HTML5 attribute.\n        /// </summary>\n        /// <value>\n        /// The draggable.\n        /// </value>\n        public virtual string? Draggable\n        {\n            get => GetAttributeLower(\"draggable\");\n            set => SetAttributeLower(\"draggable\", value);\n        }\n\n        /// <summary>\n        /// The 'dropzone' HTML5 attribute.\n        /// </summary>\n        /// <value>\n        /// The drop zone.\n        /// </value>\n        public virtual string? DropZone\n        {\n            get => GetAttributeLower(\"dropzone\");\n            set => SetAttributeLower(\"dropzone\", value);\n        }\n\n        /// <summary>\n        /// The 'hidden' HTML5 attribute.\n        /// </summary>\n        /// <value>\n        ///   <c>true</c> if hidden; otherwise, <c>false</c>.\n        /// </value>\n        public virtual bool Hidden\n        {\n            get => HasAttributeLower(\"hidden\");\n            set => SetFlagAttributeLower(\"hidden\", value);\n        }\n\n        /// <summary>\n        /// The 'inputmode' HTML5 attribute.\n        /// </summary>\n        /// <value>\n        ///   <c>true</c> if [input mode]; otherwise, <c>false</c>.\n        /// </value>\n        // ReSharper disable once UnusedMember.Global\n        public virtual bool InputMode\n        {\n            get => HasAttributeLower(\"inputmode\");\n            set => SetFlagAttributeLower(\"inputmode\", value);\n        }\n\n        /// <summary>\n        /// The 'lang' HTML5 attribute\n        /// </summary>\n        /// <value>\n        /// The language.\n        /// </value>\n        public virtual string? Lang\n        {\n            get => GetAttributeLower(\"lang\");\n            set => SetAttributeLower(\"lang\", value);\n        }\n\n        /// <summary>\n        /// The 'spellcheck' HTML5 attribute.\n        /// </summary>\n        /// <value>\n        /// The spellcheck.\n        /// </value>\n        public virtual string? Spellcheck\n        {\n            get => GetAttributeLower(\"spellcheck\");\n            set => SetAttributeLower(\"spellcheck\", value);\n        }\n\n        /// <summary>\n        /// The 'style' HTML5 attribute.\n        /// </summary>\n        /// <value>\n        /// The style.\n        /// </value>\n        public virtual string? Style\n        {\n            get => GetAttributeLower(\"style\");\n            set => SetAttributeLower(\"style\", value);\n        }\n\n        /// <summary>\n        /// The 'tabindex' HTML5 attribute.\n        /// </summary>\n        /// <value>\n        /// The index of the tab.\n        /// </value>\n        public virtual string? TabIndex\n        {\n            get => GetAttributeLower(\"tabindex\");\n            set => SetAttributeLower(\"tabindex\", value);\n        }\n\n        /// <summary>\n        /// The 'title' HTML5 attribute.\n        /// </summary>\n        /// <value>\n        /// The title.\n        /// </value>\n        public virtual string? Title\n        {\n            get => GetAttributeLower(\"title\");\n            set => SetAttributeLower(\"title\", value);\n        }\n\n        /// <summary>\n        /// The 'translate' HTML5 attribute.\n        /// </summary>\n        /// <value>\n        /// The translate.\n        /// </value>\n        public virtual string? Translate\n        {\n            get => GetAttributeLower(\"translate\");\n            set => SetAttributeLower(\"translate\", value);\n        }\n\n        #endregion\n\n        #region DOM tree queries\n\n        /// <summary>\n        /// Element's child nodes\n        /// </summary>\n        /// <value>\n        /// The children.\n        /// </value>\n        public IEnumerable<Node> Children\n        {\n            get => _children;\n            set\n            {\n                if (_children.Equals(value)) return;\n                var list = value.ToArray();\n                BeginUpdate();\n                ClearChildren();\n                AppendChild(list);\n                EndUpdate();\n            }\n        }\n\n        /// <summary>\n        /// Returns the number of child nodes.\n        /// </summary>\n        /// <value>\n        /// The child count.\n        /// </value>\n        public int ChildCount => _children.Count;\n\n        /// <summary>\n        /// Gets the child at the given index.\n        /// </summary>\n        /// <param name=\"index\">The index of the child.</param>\n        /// <returns>Child node at the given index</returns>\n        public Node GetChildAt(int index) => _children[index];\n\n        /// <summary>\n        /// Searches for a direct child node and returns its index in the list of child nodes.\n        /// </summary>\n        /// <param name=\"node\">The node to search for.</param>\n        /// <returns>The 0-based child index, or -1 if not found.</returns>\n        public int GetChildNodePosition(Node node)\n        {\n            var index = 0;\n            foreach (var child in _children)\n            {\n                if (child == node)\n                {\n                    return index;\n                }\n                index++;\n            }\n            return -1;\n        }\n\n        /// <summary>\n        /// Searches for a direct child element and returns its index in the list of child elements.\n        /// </summary>\n        /// <param name=\"element\">The element to search for.</param>\n        /// <returns>The 0-based child index, or -1 if not found.</returns>\n        public int GetChildElementPosition(Element element)\n        {\n            var index = 0;\n            foreach (var child in _children)\n            {\n                if (child == element)\n                {\n                    return index;\n                }\n\n                if (child is Element)\n                {\n                    index++;\n                }\n            }\n            return -1;\n        }\n\n        /// <summary>\n        /// Verifies whether an element descends from another.\n        /// </summary>\n        /// <param name=\"element\">The element that may be a parent.</param>\n        /// <returns>True if the current element descends from the given element.</returns>\n        public bool DescendsFrom(Element? element)\n        {\n            if (this == element)\n            {\n                return true;\n            }\n\n            if (element == null || ParentElement == null)\n            {\n                return false;\n            }\n            return ParentElement == element || ParentElement.DescendsFrom(element);\n        }\n\n        /// <summary>\n        /// Determines whether the node is a direct child.\n        /// </summary>\n        /// <param name=\"node\">The node.</param>\n        /// <returns>\n        ///   <c>true</c> if the specified node is a child; otherwise, <c>false</c>.\n        /// </returns>\n        public bool ContainsChild(Node node)\n        {\n            return _children.Contains(node);\n        }\n\n        #endregion\n\n        #region DOM operations\n\n        internal void OnChildRemoved(Node child)\n        {\n            _children.Remove(child);\n        }\n\n        internal void OnChildAppend(Node child)\n        {\n            _children.Add(child);\n        }\n\n        internal void OnChildInsert(int index, Node child)\n        {\n            _children.Insert(index, child);\n        }\n\n        /// <summary>\n        /// Appends a child node.\n        /// </summary>\n        /// <param name=\"nodes\">The node to append.</param>\n        public void AppendChild(params Node[] nodes)\n        {\n            BeginUpdate();\n            var append = new DomSurgeon(this);\n            foreach (var node in nodes)\n            {\n                append.Append(node);\n                OnChildAdded(node);\n            }\n            EndUpdate();\n        }\n\n        /// <summary>\n        /// Appends text inside an element.\n        /// When the element's last child is a text node, the text is appended to that node.\n        /// Otherwise, a new child text node is added to the element.\n        /// </summary>\n        /// <param name=\"text\">Text of the node</param>\n        public void AppendText(string text)\n        {\n            AppendEncode(text, true);\n        }\n\n        /// <summary>\n        /// Appends raw HTML inside an element. The HTML won't be verified or parsed by Lara.\n        /// </summary>\n        /// <param name=\"data\">raw HTML</param>\n        public void AppendData(string data)\n        {\n            AppendEncode(data, false);\n        }\n\n        internal void AppendEncode(string? data, bool encode)\n        {\n            var count = _children.Count;\n            if (count > 0 && _children[count - 1] is TextNode node)\n            {\n                node.AppendEncode(data, encode);\n            }\n            else\n            {\n                AppendChild(new TextNode(data, encode));\n            }\n        }\n\n        /// <summary>\n        /// Inserts a child node, right before the specified node.\n        /// </summary>\n        /// <param name=\"before\">The node that is before.</param>\n        /// <param name=\"node\">The node to insert.</param>\n        public void InsertChildBefore(Node before, Node node)\n        {\n            var append = new DomSurgeon(this);\n            append.InsertChildBefore(before, node);\n            OnChildAdded(node);\n        }\n\n        /// <summary>\n        /// Inserts a child node, right after the specified node.\n        /// </summary>\n        /// <param name=\"after\">The node that is after.</param>\n        /// <param name=\"node\">The node to insert.</param>\n        public void InsertChildAfter(Node after, Node node)\n        {\n            var append = new DomSurgeon(this);\n            append.InsertChildAfter(after, node);\n            OnChildAdded(node);\n        }\n\n        /// <summary>\n        /// Inserts a node at a given index position\n        /// </summary>\n        /// <param name=\"index\">0-based index position</param>\n        /// <param name=\"node\">Node to insert</param>\n        public void InsertChildAt(int index, Node node)\n        {\n            var append = new DomSurgeon(this);\n            append.InsertChildAt(index, node);\n            OnChildAdded(node);\n        }\n\n        /// <summary>\n        /// Removes a child.\n        /// </summary>\n        /// <param name=\"child\">The child.</param>\n        public void RemoveChild(Node child)\n        {\n            var remover = new DomSurgeon(this);\n            remover.Remove(child);\n        }\n\n        /// <summary>\n        /// Removes the child at the given index position\n        /// </summary>\n        /// <param name=\"index\">Index position</param>\n        public void RemoveAt(int index)\n        {\n            var remover = new DomSurgeon(this);\n            remover.RemoveAt(index);\n        }\n\n        /// <summary>\n        /// Removes all child nodes.\n        /// </summary>\n        public void ClearChildren()\n        {\n            var remover = new DomSurgeon(this);\n            remover.ClearChildren();\n            OnPropertyChanged(nameof(Children));\n        }\n\n        /// <summary>\n        /// Removes this node from its parent.\n        /// </summary>\n        /// <exception cref=\"InvalidOperationException\">Cannot remove from parent, the node has no parent element already</exception>\n        public void Remove()\n        {\n            if (ParentElement == null)\n            {\n                throw new InvalidOperationException(Resources.CannotRemoveNoParent);\n            }\n            ParentElement.RemoveChild(this);\n        }\n\n        /// <summary>\n        /// Swaps two child nodes within the element\n        /// </summary>\n        /// <param name=\"index1\">Index of 1st node</param>\n        /// <param name=\"index2\">Index of 2nd node</param>\n        public void SwapChildren(int index1, int index2)\n        {\n            if (index1 == index2)\n            {\n                return;\n            }\n            var temp = _children[index1];\n            _children[index1] = _children[index2];\n            _children[index2] = temp;\n            SwapChildrenDelta.Enqueue(this, index1, index2);\n        }\n\n        internal virtual void NotifyValue(ElementEventValue entry)\n        {\n        }\n\n        private protected virtual void OnChildAdded(Node child)\n        {\n            OnPropertyChanged(nameof(Children));\n        }\n\n        /// <summary>\n        /// Occurs when the element is connected to the document's DOM.\n        /// </summary>\n        protected virtual void OnConnect()\n        {\n        }\n\n        /// <summary>\n        /// Occurs when the element is disconnected from the document's DOM.\n        /// </summary>\n        protected virtual void OnDisconnect()\n        {\n        }\n\n        /// <summary>\n        /// Occurs when the element is moved from one document to another.\n        /// </summary>\n        protected virtual void OnAdopted()\n        {\n        }\n\n        /// <summary>\n        /// Occurs when the element or one of its containing elements is moved within the same document's DOM.\n        /// </summary>\n        protected virtual void OnMove()\n        {\n        }\n\n        internal void NotifyConnect()\n        {\n            FlushEvents();\n            OnConnect();\n            foreach (var child in GetNotifyList())\n            {\n                child.NotifyConnect();\n            }\n        }\n\n        internal void NotifyDisconnect()\n        {\n            OnDisconnect();\n            foreach (var child in GetNotifyList())\n            {\n                child.NotifyDisconnect();\n            }\n        }\n\n        internal void NotifyAdopted()\n        {\n            OnAdopted();\n            foreach (var child in GetNotifyList())\n            {\n                child.NotifyAdopted();\n            }\n        }\n\n        internal void NotifyMove()\n        {\n            OnMove();\n            foreach (var child in GetNotifyList())\n            {\n                child.NotifyMove();\n            }\n        }\n\n        internal virtual IEnumerable<Element> GetNotifyList()\n        {\n            foreach (var node in _children)\n            {\n                if (node is Element child)\n                {\n                    yield return child;\n                }\n            }\n        }\n\n        #endregion\n\n        #region Generate Delta content\n\n        internal override ContentNode GetContentNode()\n        {\n            if (!Render)\n            {\n                return GetUnrenderedContent();\n            }\n            return GetRenderedContent();\n        }\n\n        private ContentNode GetUnrenderedContent()\n        {\n            if (IsPrintable && IsSlotted)\n            {\n                return new ContentPlaceholder(Id);\n            }\n            else\n            {\n                return new ContentArrayNode { Nodes = new List<ContentNode>() };\n            }\n        }\n\n        private ContentNode GetRenderedContent()\n        {\n            var list = new List<Node>(GetLightSlotted());\n            if (list.Count == 1 && list[0] == this)\n            {\n                return GetElementContent();\n            }\n            return GetArrayContent(list);\n        }\n\n        private ContentNode GetArrayContent(List<Node> list)\n        {\n            var array = new ContentArrayNode\n            {\n                Nodes = new List<ContentNode>()\n            };\n            foreach (var node in list)\n            {\n                array.Nodes.Add(node.GetContentNode());\n            }\n            return array;\n        }\n\n        private ContentElementNode GetElementContent()\n        {\n            return new ContentElementNode\n            {\n                TagName = TagName,\n                NS = GetAttributeLower(\"xlmns\"),\n                Attributes = CopyAttributes(),\n                Children = CopyLightChildren()\n            };\n        }\n\n        private List<ContentAttribute> CopyAttributes()\n        {\n            var list = new List<ContentAttribute>();\n            foreach (var pair in _attributes)\n            {\n                list.Add(new ContentAttribute\n                {\n                    Attribute = pair.Key,\n                    Value = pair.Value\n                });\n            }\n            return list;\n        }\n\n        private List<ContentNode> CopyLightChildren()\n        {\n            var list = new List<ContentNode>();\n            foreach (var child in GetLightChildren())\n            {\n                list.Add(child.GetContentNode());\n            }\n            return list;\n        }\n\n        #endregion\n\n        #region Subscribe to events\n\n        internal Task NotifyEvent(string eventName)\n        {\n            if (Events.TryGetValue(eventName, out var settings)\n                && settings.Handler != null)\n            {\n                return settings.Handler();\n            }\n            return Task.CompletedTask;\n        }\n\n        /// <summary>\n        /// Registers an event and associates code to execute.\n        /// </summary>\n        /// <param name=\"settings\">The event's settings.</param>\n        public void On(EventSettings settings)\n        {\n            settings = settings ?? throw new ArgumentNullException(nameof(settings));\n            settings.Verify();\n            RemoveEvent(settings.EventName);\n            Events.Add(settings.EventName, settings);\n            if (Document != null)\n            {\n                SubscribeDelta.Enqueue(this, settings);\n            }\n        }\n\n        /// <summary>\n        /// Registers an event and associates code to execute.\n        /// </summary>\n        /// <param name=\"eventName\">Name of the event.</param>\n        /// <param name=\"handler\">The handler to execute.</param>\n        public void On(string eventName, Func<Task>? handler)\n        {\n            eventName = eventName ?? throw new ArgumentNullException(nameof(eventName));\n            if (handler == null)\n            {\n                RemoveEvent(eventName);\n            }\n            else\n            {\n                On(new EventSettings\n                {\n                    EventName = eventName,\n                    Handler = handler\n                });\n            }\n        }\n\n        /// <summary>\n        /// Registers an event and associates code to execute.\n        /// </summary>\n        /// <param name=\"eventName\">Name of the event.</param>\n        /// <param name=\"handler\">The handler to execute.</param>\n        public void On(string eventName, Action? handler)\n        {\n            eventName = eventName ?? throw new ArgumentNullException(nameof(eventName));\n            if (handler == null)\n            {\n                RemoveEvent(eventName);\n            }\n            else\n            {\n                On(new EventSettings\n                {\n                    EventName = eventName,\n                    Handler = () =>\n                    {\n                        handler();\n                        return Task.CompletedTask;\n                    }\n                });\n            }\n        }\n\n        private void RemoveEvent(string eventName)\n        {\n            if (!Events.ContainsKey(eventName)) return;\n            Events.Remove(eventName);\n            Document?.Enqueue(new UnsubscribeDelta\n            {\n                ElementId = Id,\n                EventName = eventName\n            });\n        }\n\n        private void FlushEvents()\n        {\n            foreach (var settings in Events.Values)\n            {\n                SubscribeDelta.Enqueue(this, settings);\n            }\n        }\n\n        #endregion\n\n        #region Binding\n\n        private HashSet<BindingSubscription>? _subscriptions;\n\n        private ChildrenBindingSubscription? _childrenBinding;\n\n        private int _applyingBinding;\n        private const int MaxApplyLevel = 5;\n\n        internal void AddSubscription(INotifyPropertyChanged source, Action action)\n        {\n            _subscriptions ??= new HashSet<BindingSubscription>();\n            action();\n            _subscriptions.Add(new BindingSubscription(source, (_, _) =>\n            {\n                if (_applyingBinding > MaxApplyLevel)\n                {\n                    throw new InvalidOperationException(\"Cycle detected when applying updates on bindings\");\n                }\n                _applyingBinding++;\n                action();\n                _applyingBinding--;\n            }));\n        }\n\n        internal void SubscribeChildren(\n            INotifyCollectionChanged source,\n            NotifyCollectionChangedEventHandler handler)\n        {\n            _childrenBinding?.Unsubscribe();\n            _childrenBinding = new ChildrenBindingSubscription(handler, source);\n        }\n\n        private void ClearSubscriptions()\n        {\n            _childrenBinding?.Unsubscribe();\n            if (_subscriptions == null) return;\n            foreach (var item in _subscriptions)\n            {\n                item.Unsubscribe();\n            }\n            _subscriptions.Clear();\n        }\n\n        /// <summary>\n        /// Removes all bindings for the element\n        /// </summary>\n        public void UnbindAll()\n        {\n            ClearSubscriptions();\n        }\n\n        /// <summary>\n        /// Clears all child nodes and replaces them with raw HTML code. The HTML won't be parsed by Lara.\n        /// </summary>\n        /// <param name=\"data\">raw HTML</param>\n        public void SetInnerData(string data)\n        {\n            SetInnerEncode(data, false);\n        }\n\n        internal void SetInnerEncode(string? value, bool encode)\n        {\n            if (_children.Count == 1 && _children[0] is TextNode node)\n            {\n                if (encode)\n                {\n                    node.SetEncodedText(value);\n                }\n                else\n                {\n                    node.Data = value;\n                }\n            }\n            else\n            {\n                ClearChildren();\n                AppendEncode(value, encode);\n            }\n        }\n\n        internal override string? GetNodeInnerText()\n        {\n            if (ChildCount == 0)\n            {\n                return string.Empty;\n            }\n\n            if (ChildCount == 1)\n            {\n                return GetChildAt(0).InnerText;\n            }\n            var builder = new StringBuilder();\n            AppendNodeInnerText(builder);\n            return builder.ToString();\n        }\n\n        internal override void AppendNodeInnerText(StringBuilder builder)\n        {\n            foreach (var child in Children)\n            {\n                child.AppendNodeInnerText(builder);\n            }\n        }\n\n        internal override void SetNodeInnerText(string? value)\n        {\n            SetInnerEncode(value, true);\n        }\n\n        #endregion\n\n        #region Component-related\n\n        private bool _render = true;\n\n        /// <summary>\n        /// Set to false to prevent the element from rendering on the client's document\n        /// </summary>\n        public bool Render\n        {\n            get => _render;\n            set\n            {\n                if (value == _render) return;\n                SetProperty(ref _render, value);\n                if (Document == null || !IsSlotted) return;\n                var light = GetLightSlotted();\n                if (_render)\n                {\n                    RenderDelta.Enqueue(Document, light);\n                }\n                else\n                {\n                    UnRenderDelta.Enqueue(Document, light);\n                }\n            }\n        }\n\n        internal virtual IEnumerable<Node> GetLightSlotted()\n        {\n            yield return this;\n        }\n\n        internal IEnumerable<Node> GetLightChildren()\n        {\n            foreach (var node in Children)\n            {\n                if (node is Element childElement)\n                {\n                    foreach (var light in childElement.GetLightSlotted())\n                    {\n                        yield return light;\n                    }\n                }\n                else\n                {\n                    yield return node;\n                }\n            }\n        }\n\n        internal virtual IEnumerable<Node> GetAllDescendants()\n        {\n            return Children;\n        }\n\n        internal virtual void AttributeChanged(string attribute, string? value)\n        {\n            OnPropertyChanged(attribute);\n        }\n\n        internal bool TryGetQueue([NotNullWhen(true)] out Document? document)\n        {\n            return TryGetEvents(out document)\n                && document != null\n                && document.QueueingEvents;\n        }\n\n        internal bool TryGetEvents([NotNullWhen(true)] out Document? document)\n        {\n            document = Document;\n            return document != null\n                   && IsSlotted\n                   && IsPrintable\n                   && Render;\n        }\n\n        #endregion\n\n        #region Other methods\n\n        /// <summary>\n        /// Focuses this element.\n        /// </summary>\n        // ReSharper disable once VirtualMemberNeverOverridden.Global\n        public virtual void Focus()\n        {\n            if (Document == null)\n            {\n                throw new InvalidOperationException(Resources.FocusDisconnected);\n            }\n            FocusDelta.Enqueue(this);\n        }\n\n        /// <summary>\n        /// Calculates and returns the HTML code of the element\n        /// </summary>\n        /// <returns>HTML code</returns>\n        public string GetHtml()\n        {\n            var writer = new DocumentWriter();\n            writer.PrintElement(this, 0);\n            return writer.ToString();\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/DOM/ElementFactory.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 5/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System;\nusing System.Collections.Generic;\n\nnamespace Integrative.Lara\n{\n    internal static class ElementFactory\n    {\n        private readonly static Dictionary<string, Func<Element>> _Creators\n            = new Dictionary<string, Func<Element>>\n            {\n                { \"a\", () => new HtmlAnchorElement() },\n                { \"body\", () => new HtmlBodyElement() },\n                { \"button\", () => new HtmlButtonElement() },\n                { \"colgroup\", () => new HtmlColGroupElement() },\n                { \"div\", () => new HtmlDivElement() },\n                { \"h1\", () => new HtmlHeadingElement(1) },\n                { \"h2\", () => new HtmlHeadingElement(2) },\n                { \"h3\", () => new HtmlHeadingElement(3) },\n                { \"h4\", () => new HtmlHeadingElement(4) },\n                { \"h5\", () => new HtmlHeadingElement(5) },\n                { \"h6\", () => new HtmlHeadingElement(6) },\n                { \"head\", () => new HtmlHeadElement() },\n                { \"img\", () => new HtmlImageElement() },\n                { \"input\", () => new HtmlInputElement() },\n                { \"label\", () => new HtmlLabelElement() },\n                { \"link\", () => new HtmlLinkElement() },\n                { \"li\", () => new HtmlLiElement() },\n                { \"meta\", () => new HtmlMetaElement() },\n                { \"meter\", () => new HtmlMeterElement() },\n                { \"option\", () => new HtmlOptionElement() },\n                { \"optgroup\", () => new HtmlOptionGroupElement() },\n                { \"ol\", () => new HtmlOlElement() },\n                { \"p\", () => new HtmlParagraphElement() },\n                { \"script\", () => new HtmlScriptElement() },\n                { \"select\", () => new HtmlSelectElement() },\n                { \"slot\", () => new Slot() },\n                { \"span\", () => new HtmlSpanElement() },\n                { \"table\", () => new HtmlTableElement() },\n                { \"tbody\", () => new HtmlTableSectionElement(HtmlTableSectionType.Body) },\n                { \"thead\", () => new HtmlTableSectionElement(HtmlTableSectionType.Head) },\n                { \"tfoot\", () => new HtmlTableSectionElement(HtmlTableSectionType.Foot) },\n                { \"td\", () => new HtmlTableCellElement() },\n                { \"th\", () => new HtmlTableHeaderElement() },\n            };\n\n        public static Element CreateElement(string tagName)\n        {\n            tagName = VerifyTagName(tagName);\n\n            if (_Creators.TryGetValue(tagName, out var creator))\n            {\n                return creator();\n            }\n            if (LaraUI.Context.Application.TryGetComponent(tagName, out var type))\n            {\n                return (Element)Activator.CreateInstance(type);\n            }\n\n            return new GenericElement(tagName);\n        }\n\n        private static string VerifyTagName(string tagName)\n        {\n            if (string.IsNullOrEmpty(tagName))\n            {\n                throw new ArgumentException(Resources.InvalidTagName);\n            }\n\n            if (tagName.Contains(' ', StringComparison.InvariantCulture))\n            {\n                throw new ArgumentException(Resources.TagNameSpaces);\n            }\n\n            return tagName.ToLowerInvariant();\n        }\n\n        public static Element CreateElement(string tagName, string id)\n        {\n            var element = CreateElement(tagName);\n            element.Id = id;\n            return element;\n        }\n\n        public static Element CreateElementNs(string ns, string tagName)\n        {\n            var element = CreateElement(tagName);\n            element.SetAttribute(\"xlmns\", ns);\n            return element;\n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/DOM/EventSettings.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 6/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System;\nusing System.Threading.Tasks;\n\nnamespace Integrative.Lara\n{\n    /// <summary>\n    /// Propagation options for client events\n    /// </summary>\n    public enum PropagationType\n    {\n        /// <summary>\n        /// Uses the 'StopPropagation' option\n        /// </summary>\n        Default = 0,\n\n        /// <summary>\n        /// Prevents the event to bubble up to parent elements\n        /// </summary>\n        StopPropagation = 1,\n\n        /// <summary>\n        /// Prevents the event to bubble up to parent elements or any other handler\n        /// </summary>\n        StopImmediatePropagation = 2,\n\n        /// <summary>\n        /// Allows event propagation\n        /// </summary>\n        AllowAll = 3\n    }\n\n    /// <summary>\n    /// Specifies settings to declare an event and associate execution code to it.\n    /// </summary>\n    public sealed class EventSettings\n    {\n        /// <summary>\n        /// Name of the HTML5 event (e.g. 'click')\n        /// </summary>\n        /// <value>\n        /// The name of the event.\n        /// </value>\n        public string EventName { get; set; } = string.Empty;\n\n        /// <summary>\n        /// Specifies an interval in millisenconds to postpone and hold an event while it gets triggered repeatedly.\n        /// The resulting 'debounced' event will execute only after it has not been triggered for the interval specified.\n        /// </summary>\n        public int DebounceInterval { get; set; }\n\n        /// <summary>\n        /// Block UI input while the event is executing? Default is true\n        /// </summary>\n        /// <value>\n        ///   <c>true</c> if block; otherwise, <c>false</c>.\n        /// </value>\n        public bool Block { get; set; } = true;\n\n        /// <summary>\n        /// Gets or sets the options for blocking the UI.\n        /// </summary>\n        /// <value>\n        /// The block options.\n        /// </value>\n        public BlockOptions? BlockOptions { get; set; }\n\n        /// <summary>\n        /// Gets or sets the code to execute for the event.\n        /// </summary>\n        /// <value>\n        /// The handler.\n        /// </value>\n        public Func<Task>? Handler { get; set; }\n\n        /// <summary>\n        /// Long-running events use websockets and can flush partial progress to the client.\n        /// </summary>\n        /// <value>\n        ///   <c>true</c> if [long running]; otherwise, <c>false</c>.\n        /// </value>\n        public bool LongRunning { get; set; }\n\n        /// <summary>\n        /// When set to true, the client will send the files from input elements of type 'file'\n        /// </summary>\n        public bool UploadFiles { get; set; }\n\n        /// <summary>\n        /// Defines JavaScript code to execute in order to determine if the event should trigger.\n        /// When this property is set, the event is trigger only if the expression evaluates to true.\n        /// Example: \"event.keyCode === 13\"\n        /// </summary>\n        public string? EvalFilter { get; set; }\n\n        /// <summary>\n        /// Specifies propagation options for client events\n        /// </summary>\n        public PropagationType Propagation { get; set; }\n\n        /// <summary>\n        /// Execute the event.PreventDefault() method to disable default behavior\n        /// </summary>\n        public bool PreventDefault { get; set; }\n\n        internal void Verify()\n        {\n            if (string.IsNullOrEmpty(EventName))\n            {\n                throw new ArgumentException(Resources.EventNameNull);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/DOM/GlobalSerializer.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 6/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System.Globalization;\nusing System.Threading;\n\nnamespace Integrative.Lara\n{\n    internal static class GlobalSerializer\n    {\n        private static long _counter;\n\n        public static string GenerateElementId()\n        {\n            Interlocked.Increment(ref _counter);\n            return \"_g\" + _counter.ToString(\"X\", CultureInfo.InvariantCulture);\n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/DOM/HtmlReference.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 5/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System.Collections.Generic;\n\nnamespace Integrative.Lara\n{\n    internal static class HtmlReference\n    {\n        private static readonly HashSet<string> _SelfClosingTags;\n        private static readonly HashSet<string> _DoesRequireId;\n\n        static HtmlReference()\n        {\n            _SelfClosingTags = new HashSet<string>\n            {\n                \"area\",\n                \"base\",\n                \"br\",\n                \"col\",\n                \"command\",\n                \"embed\",\n                \"hr\",\n                \"img\",\n                \"input\",\n                \"keygen\",\n                \"link\",\n                \"meta\",\n                \"param\",\n                \"source\",\n                \"track\",\n                \"wbr\"\n            };\n            _DoesRequireId = new HashSet<string>\n            {\n                \"input\", \"textarea\", \"select\", \"button\", \"option\"\n            };\n        }\n\n        public static bool IsSelfClosingTag(string tagNameLower)\n            => _SelfClosingTags.Contains(tagNameLower);\n\n        public static bool RequiresId(string tagNameLower)\n            => _DoesRequireId.Contains(tagNameLower);\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/DOM/MessageRegistry.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 6/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System;\nusing System.Collections.Generic;\nusing System.Threading.Tasks;\n\nnamespace Integrative.Lara\n{\n    /// <summary>\n    /// Parameters for client messages\n    /// </summary>\n    public class MessageEventArgs : EventArgs\n    {\n        /// <summary>\n        /// Body of message sent from JavaScript\n        /// </summary>\n        public string? Body { get; }\n\n        internal MessageEventArgs(string? body)\n        {\n            Body = body;\n        }\n    }\n\n    internal class MessageTypeRegistry\n    {\n        private readonly HashSet<Func<MessageEventArgs, Task>> _registry = new HashSet<Func<MessageEventArgs, Task>>();\n\n        public void Add(Func<MessageEventArgs, Task> handler)\n        {\n            _registry.Add(handler);\n        }\n\n        public void Remove(Func<MessageEventArgs, Task> handler)\n        {\n            _registry.Remove(handler);\n        }\n\n        public async Task RunAll(MessageEventArgs args)\n        {\n            var list = new List<Func<MessageEventArgs, Task>>(_registry);\n            foreach (var handler in list)\n            {\n                await handler(args);\n            }\n        }\n    }\n\n    internal class MessageRegistry\n    {\n        private readonly Document _parent;\n        private readonly Dictionary<string, MessageTypeRegistry> _map;\n\n        public MessageRegistry(Document parent)\n        {\n            _parent = parent;\n            _map = new Dictionary<string, MessageTypeRegistry>();\n        }\n\n        public void Add(string messageId, Func<MessageEventArgs, Task> handler)\n        {\n            var registry = GetRegistry(messageId);\n            registry.Add(handler);\n        }\n\n        private MessageTypeRegistry GetRegistry(string messageId)\n        {\n            if (_map.TryGetValue(messageId, out var registry)) return registry;\n            registry = new MessageTypeRegistry();\n            _map.Add(messageId, registry);\n            _parent.Head.On(\"_\" + messageId, () =>\n            {\n                var body = LaraUI.Page.JSBridge.EventData;\n                var args = new MessageEventArgs(body);\n                return RunAll(messageId, args);\n            });\n\n            return registry;\n        }\n\n        public void Remove(string messageId, Func<MessageEventArgs, Task> handler)\n        {\n            if (_map.TryGetValue(messageId, out var registry))\n            {\n                registry.Remove(handler);\n            }\n        }\n\n        public async Task RunAll(string messageId, MessageEventArgs args)\n        {\n            if (_map.TryGetValue(messageId, out var registry))\n            {\n                await registry.RunAll(args);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/DOM/Node.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 5/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System.Text;\n\nnamespace Integrative.Lara\n{\n    /// <summary>\n    /// Defines the types of document nodes\n    /// </summary>\n    public enum NodeType\n    {\n        /// <summary>\n        /// Element node\n        /// </summary>\n        Element,\n\n        /// <summary>\n        /// Text node\n        /// </summary>\n        Text\n    }\n\n    /// <summary>\n    /// An abstract class that represents a node in an HTML5 document\n    /// </summary>\n    public abstract class Node : BindableBase\n    {\n        /// <summary>\n        /// Creates a node\n        /// </summary>\n        protected internal Node()\n        {\n        }\n\n        /// <summary>\n        /// The node's parent element.\n        /// </summary>\n        /// <value>\n        /// The parent element.\n        /// </value>\n        public Element? ParentElement { get; internal set; }\n\n        /// <summary>\n        /// The node's document. This property returns null when the node is not attached to a document.\n        /// </summary>\n        /// <value>\n        /// The document.\n        /// </value>\n        public Document? Document { get; internal set; }\n\n        /// <summary>\n        /// Gets the type of the node.\n        /// </summary>\n        /// <value>\n        /// The type of the node.\n        /// </value>\n        public abstract NodeType NodeType { get; }\n\n        internal abstract ContentNode GetContentNode();\n\n        /// <summary>\n        /// True when the node is currently rendered in a parent document.\n        /// </summary>\n        public bool IsSlotted { get; internal set; }\n\n        internal void UpdateSlotted()\n        {\n            SlottedCalculator.UpdateSlotted(this);\n        }\n\n        internal virtual bool IsPrintable => true;\n\n        /// <summary>\n        /// The InnerText property represents the text content of a node and all of its descendants.\n        /// When setting the property, all descendants are replaced with a text node and the given text content.\n        /// When getting the property, it retrieves the text of all descendants.\n        /// </summary>\n        public string? InnerText\n        {\n            get => GetNodeInnerText();\n            set => SetNodeInnerText(value);\n        }\n\n        internal abstract string? GetNodeInnerText();\n\n        internal abstract void SetNodeInnerText(string? value);\n\n        internal abstract void AppendNodeInnerText(StringBuilder builder);\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/DOM/NodeExtensions.cs",
    "content": "﻿/*\nCopyright (c) 2020-2021 Integrative Software LLC\nCreated: 12/2020\nAuthor: Pablo Carbonell\n*/\n\nusing System;\nusing System.Diagnostics.CodeAnalysis;\nusing System.Threading.Tasks;\n\nnamespace Integrative.Lara\n{\n    /// <summary>\n    /// Extensions for Node class\n    /// </summary>\n    public static class NodeExtensions\n    {\n        #region Generic wrapping of methods\n\n        /// <summary>\n        /// Executes an action on an object and returns the object itself\n        /// </summary>\n        /// <typeparam name=\"TNode\"></typeparam>\n        /// <param name=\"node\"></param>\n        /// <param name=\"actions\"></param>\n        /// <returns>object invoked</returns>\n        public static TNode Wrap<TNode>(this TNode node, params Action<TNode>[] actions)\n        {\n            foreach (var action in actions)\n            {\n                action(node);\n            }\n            return node;\n        }\n\n        /// <summary>\n        /// Returns the object itself\n        /// </summary>\n        /// <typeparam name=\"TNode\">type of node</typeparam>\n        /// <param name=\"node\">node</param>\n        /// <param name=\"result\">node itself</param>\n        /// <returns>node itself</returns>\n        public static TNode Extract<TNode>(this TNode node, [NotNull] out TNode result)\n        {\n            node = node ?? throw new ArgumentNullException(nameof(node));\n            result = node;\n            return node;\n        }\n\n        #endregion\n\n        #region add children\n\n        /// <summary>\n        /// Appends multiple children and returns the element passed as parameter\n        /// </summary>\n        /// <typeparam name=\"T\">Type of parent</typeparam>\n        /// <param name=\"element\">parent</param>\n        /// <param name=\"elements\">child nodes</param>\n        /// <returns></returns>\n        public static T Child<T>(this T element, params Node[] elements)\n            where T : Element\n        {\n            element.AppendChild(elements);\n            return element;\n        }\n\n        #endregion\n\n        #region add events\n\n        /// <summary>\n        /// Registers an event and associates code to execute\n        /// </summary>\n        /// <typeparam name=\"T\">Element type</typeparam>\n        /// <param name=\"element\">Element</param>\n        /// <param name=\"eventName\">Event name (e.g. \"click\")</param>\n        /// <param name=\"handler\">Code to execute</param>\n        /// <returns>Element instance</returns>\n        public static T Event<T>(this T element, string eventName, Action handler)\n            where T : Element\n        {\n            element.On(eventName, handler);\n            return element;\n        }\n\n        /// <summary>\n        /// Registers an event and associates code to execute\n        /// </summary>\n        /// <typeparam name=\"T\">Element type</typeparam>\n        /// <param name=\"element\">Element</param>\n        /// <param name=\"eventName\">Event name (e.g. \"click\")</param>\n        /// <param name=\"handler\">Code to execute</param>\n        /// <returns>Element instance</returns>\n        public static T Event<T>(this T element, string eventName, Func<Task> handler)\n            where T : Element\n        {\n            element.On(eventName, handler);\n            return element;\n        }\n\n        /// <summary>\n        /// Registers an event and associates code to execute\n        /// </summary>\n        /// <typeparam name=\"T\">Element type</typeparam>\n        /// <param name=\"element\">Element</param>\n        /// <param name=\"options\">Event options</param>\n        /// <returns>Element instance</returns>\n        public static T Event<T>(this T element, EventSettings options)\n            where T : Element\n        {\n            element.On(options);\n            return element;\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/DOM/TextNode.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 5/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System.Text;\nusing System.Web;\n\nnamespace Integrative.Lara\n{\n    /// <summary>\n    /// A text node.\n    /// </summary>\n    /// <seealso cref=\"Node\" />\n    public sealed class TextNode : Node\n    {\n        /// <summary>\n        /// Gets the type of the node.\n        /// </summary>\n        /// <value>\n        /// The type of the node.\n        /// </value>\n        public override NodeType NodeType => NodeType.Text;\n\n        private string? _data;\n\n        /// <summary>\n        /// Gets or sets the data of the node. This property gets and sets the raw (unencoded) HTML text for the node.\n        /// </summary>\n        /// <value>\n        /// The data.\n        /// </value>\n        public string? Data\n        {\n            get => _data;\n            set\n            {\n                if (_data == value) return;\n                _data = value;\n                TextModifiedDelta.Enqueue(this);\n            }\n        }\n\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"TextNode\"/> class.\n        /// </summary>\n        public TextNode()\n        {\n        }\n\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"TextNode\"/> class.\n        /// </summary>\n        /// <param name=\"data\">The data.</param>\n        /// <param name=\"encode\">if set to <c>true</c> encode.</param>\n        public TextNode(string? data, bool encode = true)\n        {\n            if (encode)\n            {\n                SetEncodedText(data);\n            }\n            else\n            {\n                _data = data;\n            }\n        }\n\n        /// <summary>\n        /// Appends text to the node\n        /// </summary>\n        /// <param name=\"text\">Text to append</param>\n        public void AppendText(string text)\n        {\n            AppendEncode(text, true);\n        }\n\n        /// <summary>\n        /// Appends raw HTML to the node\n        /// </summary>\n        /// <param name=\"data\">raw HTML</param>\n        public void AppendData(string data)\n        {\n            AppendEncode(data, false);\n        }\n\n        internal void AppendEncode(string? text, bool encode)\n        {\n            if (encode)\n            {\n                text = HttpUtility.HtmlEncode(text);\n            }\n            if (string.IsNullOrEmpty(_data))\n            {\n                _data = text;\n            }\n            else\n            {\n                _data += text;\n            }\n        }\n\n        internal override ContentNode GetContentNode()\n        {\n            return new ContentTextNode\n            {\n                Data = _data,\n            };\n        }\n\n        /// <summary>\n        /// Sets text for the node.\n        /// </summary>\n        /// <param name=\"unencoded\">The unencoded text to encode.</param>\n        public void SetEncodedText(string? unencoded)\n        {\n            Data = HttpUtility.HtmlEncode(unencoded);\n        }\n\n        internal override string GetNodeInnerText()\n        {\n            return Data == null ? string.Empty : HttpUtility.HtmlDecode(Data);\n        }\n\n        internal override void SetNodeInnerText(string? value)\n        {\n            SetEncodedText(value);\n        }\n\n        internal override void AppendNodeInnerText(StringBuilder builder)\n        {\n            builder.Append(GetNodeInnerText());\n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Delta/AttributeEditedDelta.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 5/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System.Runtime.Serialization;\n\nnamespace Integrative.Lara\n{\n    [DataContract]\n    internal sealed class AttributeEditedDelta : BaseDelta\n    {\n        [DataMember]\n        public string ElementId { get; set; } = string.Empty;\n\n        [DataMember]\n        public string Attribute { get; set; } = string.Empty;\n\n        [DataMember]\n        public string Value { get; set; } = string.Empty;\n\n        public AttributeEditedDelta() : base(DeltaType.EditAttribute)\n        {\n        }\n\n        public static void Enqueue(Element element, string attribute, string? value)\n        {\n            if (element.TryGetQueue(out var document))\n            {\n                document.Enqueue(new AttributeEditedDelta\n                {\n                    Attribute = attribute,\n                    ElementId = element.Id,\n                    Value = value ?? \"\"\n                });\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Delta/AttributeRemovedDelta.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 5/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System.Runtime.Serialization;\n\nnamespace Integrative.Lara\n{\n    [DataContract]\n    internal sealed class AttributeRemovedDelta : BaseDelta\n    {\n        [DataMember]\n        public string ElementId { get; set; } = string.Empty;\n\n        [DataMember]\n        public string Attribute { get; set; } = string.Empty;\n\n        public AttributeRemovedDelta() : base(DeltaType.RemoveAttribute)\n        {\n        }\n\n        public static void Enqueue(Element element, string attribute)\n        {\n            if (element.TryGetQueue(out var document))\n            {\n                document.Enqueue(new AttributeRemovedDelta\n                {\n                    ElementId = element.Id,\n                    Attribute = attribute\n                });\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Delta/BaseDelta.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 5/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System.Runtime.Serialization;\n\nnamespace Integrative.Lara\n{\n    internal enum DeltaType\n    {\n        Append = 1,\n        Insert = 2,\n        TextModified = 3,\n        Remove = 4,\n        EditAttribute = 5,\n        RemoveAttribute = 6,\n        Focus = 7,\n        SetId = 8,\n        SetValue = 9,\n        SubmitJs = 10,\n        SetChecked = 11,\n        ClearChildren = 12,\n        Replace = 13,\n        ServerEvents = 14,\n        SwapChildren = 15,\n        Subscribe = 16,\n        Unsubscribe = 17,\n        RemoveElement = 18,\n        Render = 19,\n        UnRender = 20\n    }\n\n    [DataContract]\n    [KnownType(typeof(NodeAddedDelta))]\n    [KnownType(typeof(NodeInsertedDelta))]\n    [KnownType(typeof(TextModifiedDelta))]\n    [KnownType(typeof(NodeRemovedDelta))]\n    [KnownType(typeof(AttributeEditedDelta))]\n    [KnownType(typeof(AttributeRemovedDelta))]\n    [KnownType(typeof(FocusDelta))]\n    [KnownType(typeof(SetIdDelta))]\n    [KnownType(typeof(SetValueDelta))]\n    [KnownType(typeof(SubmitJsDelta))]\n    [KnownType(typeof(SetCheckedDelta))]\n    [KnownType(typeof(ClearChildrenDelta))]\n    [KnownType(typeof(ReplaceDelta))]\n    [KnownType(typeof(ServerEventsDelta))]\n    [KnownType(typeof(SwapChildrenDelta))]\n    [KnownType(typeof(SubscribeDelta))]\n    [KnownType(typeof(UnsubscribeDelta))]\n    [KnownType(typeof(RemoveElementDelta))]\n    [KnownType(typeof(RenderDelta))]\n    [KnownType(typeof(UnRenderDelta))]\n    internal abstract class BaseDelta\n    {\n        [DataMember]\n        public DeltaType Type { get; set; }\n\n        protected BaseDelta(DeltaType type)\n        {\n            Type = type;\n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Delta/ClearChildrenDelta.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 6/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System.Runtime.Serialization;\n\nnamespace Integrative.Lara\n{\n    [DataContract]\n    internal sealed class ClearChildrenDelta : BaseDelta\n    {\n        [DataMember]\n        public string ElementId { get; set; } = string.Empty;\n\n        public ClearChildrenDelta() : base(DeltaType.ClearChildren)\n        {\n        }\n\n        public static void Enqueue(Element element)\n        {\n            if (element.TryGetQueue(out var document))\n            {\n                document.Enqueue(new ClearChildrenDelta\n                {\n                    ElementId = element.Id,\n                });\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Delta/ContentArrayNode.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 8/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System.Collections.Generic;\nusing System.Runtime.Serialization;\n\nnamespace Integrative.Lara\n{\n    [DataContract]\n    internal sealed class ContentArrayNode : ContentNode\n    {\n        [DataMember]\n        public List<ContentNode>? Nodes { get; set; }\n\n        public ContentArrayNode() : base(ContentNodeType.Array)\n        {\n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Delta/ContentAttribute.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 5/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System.Runtime.Serialization;\n\nnamespace Integrative.Lara\n{\n    [DataContract]\n    internal sealed class ContentAttribute\n    {\n        [DataMember]\n        public string Attribute { get; set; } = string.Empty;\n\n        [DataMember]\n        public string Value { get; set; } = string.Empty;\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Delta/ContentElementNode.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 5/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System.Collections.Generic;\nusing System.Runtime.Serialization;\n\nnamespace Integrative.Lara\n{\n    [DataContract]\n    internal sealed class ContentElementNode : ContentNode\n    {\n        [DataMember]\n        public string TagName { get; set; } = string.Empty;\n\n        [DataMember]\n        // ReSharper disable once InconsistentNaming\n        public string? NS { get; set; }\n\n        [DataMember]\n        public List<ContentAttribute>? Attributes { get; set; }\n\n        [DataMember]\n        public List<ContentNode>? Children { get; set; }\n\n        public ContentElementNode() : base(ContentNodeType.Element)\n        {            \n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Delta/ContentNode.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 5/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System.Runtime.Serialization;\n\nnamespace Integrative.Lara\n{\n    internal enum ContentNodeType\n    {\n        Element = 1,\n        Text = 2,\n        Array = 3,\n        Placeholder = 4\n    }\n\n    [DataContract]\n    [KnownType(typeof(ContentTextNode))]\n    [KnownType(typeof(ContentElementNode))]\n    [KnownType(typeof(ContentArrayNode))]\n    [KnownType(typeof(ContentPlaceholder))]\n    internal abstract class ContentNode\n    {\n        [DataMember]\n        public ContentNodeType Type { get; set; }\n\n        protected ContentNode(ContentNodeType type)\n        {\n            Type = type;\n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Delta/ContentPlaceholder.cs",
    "content": "﻿/*\nCopyright (c) 2021 Integrative Software LLC\nCreated: 1/2021\nAuthor: Pablo Carbonell\n*/\n\nusing System.Runtime.Serialization;\n\nnamespace Integrative.Lara\n{\n    [DataContract]\n    internal class ContentPlaceholder : ContentNode\n    {\n        [DataMember]\n        public string ElementId { get; set; } = string.Empty;\n\n        public ContentPlaceholder() : base(ContentNodeType.Placeholder)\n        {\n        }\n\n        public ContentPlaceholder(string id) : this()\n        {\n            ElementId = id;\n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Delta/ContentTextNode.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 5/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System.Runtime.Serialization;\n\nnamespace Integrative.Lara\n{\n    [DataContract]\n    internal sealed class ContentTextNode : ContentNode\n    {\n        [DataMember]\n        public string? Data { get; set; }\n\n        public ContentTextNode() : base(ContentNodeType.Text)\n        {            \n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Delta/ElementValue.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 5/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System.Runtime.Serialization;\n\nnamespace Integrative.Lara\n{\n    [DataContract]\n    internal sealed class ElementEventValue\n    {\n        [DataMember]\n        public string ElementId { get; set; } = string.Empty;\n\n        [DataMember]\n        public string Value { get; set; } = string.Empty;\n\n        [DataMember]\n        public bool Checked { get; set; }\n\n        public override string ToString()\n        {\n            return $\"#{ElementId}='{Value}'{GetCheckedSuffix()}\";\n        }\n\n        private string GetCheckedSuffix()\n        {\n            return Checked ? \" checked\" : string.Empty;\n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Delta/EventResult.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 5/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System.Collections.Generic;\nusing System.Runtime.Serialization;\n\nnamespace Integrative.Lara\n{\n    internal enum EventResultType\n    {\n        Success = 0,\n        NoSession = 1,\n        NoElement = 2,\n        OutOfSequence = 3\n    }\n\n    [DataContract]\n    internal sealed class EventResult\n    {\n        [DataMember]\n        public EventResultType ResultType { get; set; }\n\n        [DataMember]\n        public List<BaseDelta>? List { get; set; }\n\n        public EventResult()\n        {\n        }\n\n        public EventResult(List<BaseDelta> list) : this()\n        {\n            List = list;\n        }\n\n        // ReSharper disable once InconsistentNaming\n        public string ToJSON()\n        {\n            return LaraTools.Serialize(this);\n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Delta/FocusDelta.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 5/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System.Runtime.Serialization;\n\nnamespace Integrative.Lara\n{\n    [DataContract]\n    internal sealed class FocusDelta : BaseDelta\n    {\n        [DataMember]\n        public string ElementId { get; set; } = string.Empty;\n\n        public FocusDelta() : base(DeltaType.Focus)\n        {\n        }\n\n        public static void Enqueue(Element element)\n        {\n            element.Document?.Enqueue(new FocusDelta\n            {\n                ElementId = element.Id\n            });\n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Delta/NodeAddedDelta.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 5/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System.Runtime.Serialization;\n\nnamespace Integrative.Lara\n{\n    [DataContract]\n    internal sealed class NodeAddedDelta : BaseDelta\n    {\n        [DataMember]\n        public string ParentId { get; set; } = string.Empty;\n\n        [DataMember]\n        public ContentNode? Node { get; set; }\n\n        public NodeAddedDelta() : base(DeltaType.Append)\n        {\n        }\n\n        public static void Enqueue(Node node)\n        {\n            var parent = node.ParentElement;\n            if (parent == null || !parent.TryGetQueue(out var document)) return;\n            var parentId = parent.Id;\n            var content = node.GetContentNode();\n            document.Enqueue(new NodeAddedDelta\n            {\n                ParentId = parentId,\n                Node = content,\n            });\n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Delta/NodeInsertedDelta.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 5/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System.Runtime.Serialization;\n\nnamespace Integrative.Lara\n{\n    [DataContract]\n    internal sealed class NodeInsertedDelta : BaseDelta\n    {\n        [DataMember]\n        public string ParentElementId { get; set; } = string.Empty;\n\n        [DataMember]\n        public int Index { get; set; }\n\n        [DataMember]\n        public ContentNode? Node { get; set; }\n\n        public NodeInsertedDelta() : base(DeltaType.Insert)\n        {\n        }\n\n        public static void Enqueue(Node node, int index)\n        {\n            var parent = node.ParentElement;\n            if (parent != null && parent.TryGetQueue(out var document))\n            {\n                document.Enqueue(new NodeInsertedDelta\n                {\n                    ParentElementId = parent.Id,\n                    Index = index,\n                    Node = node.GetContentNode()\n                });\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Delta/NodeLocator.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 5/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System;\nusing System.Runtime.Serialization;\n\nnamespace Integrative.Lara\n{\n    [DataContract]\n    internal sealed class NodeLocator\n    {\n        [DataMember]\n        public string StartingId { get; set; } = string.Empty;\n\n        [DataMember]\n        public int? ChildIndex { get; set; }\n\n        public static NodeLocator FromNode(Node node)\n        {\n            if (node is Element element)\n            {\n                return new NodeLocator\n                {\n                    StartingId = element.Id\n                };\n            }\n            var parent = node.ParentElement;\n            if (parent == null)\n            {\n                throw new ArgumentException(\"NodeLocator from orphan non-element node\");\n            }\n            return new NodeLocator\n            {\n                StartingId = parent.Id,\n                ChildIndex = parent.GetChildNodePosition(node)\n            };\n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Delta/NodeRemovedDelta.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 5/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System.Runtime.Serialization;\n\nnamespace Integrative.Lara\n{\n    [DataContract]\n    internal sealed class NodeRemovedDelta : BaseDelta\n    {\n        [DataMember]\n        public string ParentId { get; set; } = string.Empty;\n\n        [DataMember]\n        public int ChildIndex { get; set; }\n\n        public NodeRemovedDelta() : base(DeltaType.Remove)\n        {\n        }\n\n        public static void Enqueue(Element parent, int index)\n        {\n            if (parent.TryGetQueue(out var document))\n            {\n                document.Enqueue(new NodeRemovedDelta\n                {\n                    ParentId = parent.Id,\n                    ChildIndex = index,\n                });\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Delta/PlugOptions.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 6/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System.Runtime.Serialization;\n\nnamespace Integrative.Lara\n{\n    [DataContract]\n    internal sealed class PlugOptions\n    {\n        [DataMember(IsRequired = false, EmitDefaultValue = false)]\n        public bool Block { get; set; }\n\n        [DataMember(IsRequired = false, EmitDefaultValue = false)]\n        public string? BlockElementId { get; set; }\n\n        [DataMember(IsRequired = false, EmitDefaultValue = false)]\n        // ReSharper disable once InconsistentNaming\n        public string? BlockHTML { get; set; }\n\n        [DataMember(IsRequired = false, EmitDefaultValue = false)]\n        public string? BlockShownId { get; set; }\n\n        [DataMember(IsRequired = false, EmitDefaultValue = false)]\n        public bool LongRunning { get; set; }\n\n        [DataMember(IsRequired = false, EmitDefaultValue = false)]\n        public bool UploadFiles { get; set; }\n\n        public PlugOptions()\n        {\n        }\n\n        public PlugOptions(EventSettings settings)\n        {\n            Block = settings.Block;\n            if (settings.BlockOptions != null)\n            {\n                BlockElementId = settings.BlockOptions.BlockedElementId;\n                BlockHTML = settings.BlockOptions.ShowHtmlMessage;\n                BlockShownId = settings.BlockOptions.ShowElementId;\n            }\n            LongRunning = settings.LongRunning;\n            UploadFiles = settings.UploadFiles;\n        }\n\n        // ReSharper disable once InconsistentNaming\n        public string ToJSON() => LaraTools.Serialize(this);\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Delta/RemoveElementDelta.cs",
    "content": "﻿/*\nCopyright (c) 2021 Integrative Software LLC\nCreated: 1/2021\nAuthor: Pablo Carbonell\n*/\n\nusing System.Runtime.Serialization;\n\nnamespace Integrative.Lara\n{\n    [DataContract]\n    internal sealed class RemoveElementDelta : BaseDelta\n    {\n        [DataMember]\n        public string ElementId { get; set; } = string.Empty;\n\n        public RemoveElementDelta() : base(DeltaType.RemoveElement)\n        {\n        }\n\n        public static void Enqueue(Element element)\n        {\n            if (element.TryGetQueue(out var document))\n            {\n                document.Enqueue(new RemoveElementDelta\n                {\n                    ElementId = element.Id\n                });\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Delta/RenderDelta.cs",
    "content": "﻿/*\nCopyright (c) 2021 Integrative Software LLC\nCreated: 1/2021\nAuthor: Pablo Carbonell\n*/\n\nusing System.Collections.Generic;\nusing System.Runtime.Serialization;\n\nnamespace Integrative.Lara\n{\n    [DataContract]\n    internal class RenderDelta : BaseDelta\n    {\n        [DataMember]\n        public NodeLocator? Locator { get; set; }\n\n        [DataMember]\n        public ContentNode? Node { get; set; }\n\n        public RenderDelta() : base(DeltaType.Render)\n        {\n        }\n\n        public static void Enqueue(Document document, IEnumerable<Node> nodes)\n        {\n            foreach (var node in nodes)\n            {\n                document.Enqueue(new RenderDelta\n                {\n                    Locator = NodeLocator.FromNode(node),\n                    Node = node.GetContentNode()\n                });\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Delta/ReplaceDelta.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 6/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System.Runtime.Serialization;\n\nnamespace Integrative.Lara\n{\n    [DataContract]\n    internal sealed class ReplaceDelta : BaseDelta\n    {\n        [DataMember]\n        public string? Location { get; set; }\n\n        public ReplaceDelta() : base(DeltaType.Replace)\n        {\n        }\n\n        public static void Enqueue(Document document, string location)\n        {\n            document.Enqueue(new ReplaceDelta\n            {\n                Location = location\n            });\n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Delta/ServerEventsDelta.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 8/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System.Runtime.Serialization;\n\nnamespace Integrative.Lara\n{\n    [DataContract]\n    internal class ServerEventsDelta : BaseDelta\n    {\n        public ServerEventsDelta() : base(DeltaType.ServerEvents)\n        {\n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Delta/SetCheckedDelta.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 5/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System.Runtime.Serialization;\n\nnamespace Integrative.Lara\n{\n    [DataContract]\n    internal sealed class SetCheckedDelta : BaseDelta\n    {\n        [DataMember]\n        public string ElementId { get; set; } = string.Empty;\n\n        [DataMember]\n        public bool Checked { get; set; }\n\n        public SetCheckedDelta() : base(DeltaType.SetChecked)\n        {\n        }\n\n        public static void Enqueue(Element element, bool value)\n        {\n            if (element.TryGetQueue(out var document))\n            {\n                document.Enqueue(new SetCheckedDelta\n                {\n                    ElementId = element.Id,\n                    Checked = value\n                });\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Delta/SetIdDelta.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 5/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System.Runtime.Serialization;\n\nnamespace Integrative.Lara\n{\n    [DataContract]\n    internal sealed class SetIdDelta : BaseDelta\n    {\n        [DataMember]\n        public string OldId { get; set; } = string.Empty;\n\n        [DataMember]\n        public string NewId { get; set; } = string.Empty;\n\n        public SetIdDelta() : base(DeltaType.SetId)\n        {\n        }\n\n        public static void Enqueue(Element element, string newValue)\n        {\n            if (element.TryGetQueue(out var document))\n            {\n                document.Enqueue(new SetIdDelta\n                {\n                    OldId = element.Id,\n                    NewId = newValue\n                });\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Delta/SetValueDelta.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 5/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System.Runtime.Serialization;\n\nnamespace Integrative.Lara\n{\n    [DataContract]\n    internal sealed class SetValueDelta : BaseDelta\n    {\n        [DataMember]\n        public string ElementId { get; set; } = string.Empty;\n\n        [DataMember]\n        public string? Value { get; set; }\n\n        public SetValueDelta() : base(DeltaType.SetValue)\n        {\n        }\n\n        public static void Enqueue(Element element, string? value)\n        {\n            if (element.TryGetQueue(out var document))\n            {\n                document.Enqueue(new SetValueDelta\n                {\n                    ElementId = element.Id,\n                    Value = value\n                });\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Delta/SubmitJsDelta.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 5/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System.Runtime.Serialization;\n\nnamespace Integrative.Lara\n{\n    [DataContract]\n    internal sealed class SubmitJsDelta : BaseDelta\n    {\n        [DataMember]\n        public string Code { get; set; } = string.Empty;\n\n        [DataMember(EmitDefaultValue = false)]\n        public string? Payload { get; set; }\n\n        public SubmitJsDelta() : base(DeltaType.SubmitJs)\n        {\n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Delta/SubscribeDelta.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 10/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System.Runtime.Serialization;\n\nnamespace Integrative.Lara\n{\n    [DataContract]\n    internal class SubscribeDelta : BaseDelta\n    {\n        [DataMember]\n        public string ElementId { get; set; } = string.Empty;\n\n        [DataMember]\n        public ClientEventSettings? Settings { get; set; }\n\n        [DataMember(EmitDefaultValue = false)]\n        public int DebounceInterval { get; set; }\n\n        [DataMember(EmitDefaultValue = false)]\n        public string? EvalFilter { get; set; }\n\n        public SubscribeDelta() : base(DeltaType.Subscribe)\n        {\n        }\n\n        public static void Enqueue(Element element, EventSettings settings)\n        {\n            if (!element.TryGetEvents(out var document)) return;\n            document.NotifyHasEvent();\n            document.Enqueue(CreateDelta(element.Id, settings));\n        }\n\n        public static void Enqueue(Document document, EventSettings settings)\n        {\n            document.NotifyHasEvent();\n            document.Enqueue(CreateDelta(string.Empty, settings));\n        }\n\n        private static SubscribeDelta CreateDelta(string id, EventSettings settings)\n        {\n            return new SubscribeDelta\n            {\n                ElementId = id,\n                Settings = ClientEventSettings.CreateFrom(settings),\n                DebounceInterval = settings.DebounceInterval,\n                EvalFilter = settings.EvalFilter\n            };\n        }\n    }\n\n    [DataContract]\n    internal class ClientEventSettings\n    {\n        [DataMember]\n        public string EventName { get; set; } = string.Empty;\n\n        [DataMember(EmitDefaultValue = false)]\n        public bool Block { get; set; }\n\n        [DataMember(EmitDefaultValue = false)]\n        public string? BlockElementId { get; set; }\n\n        [DataMember(EmitDefaultValue = false)]\n        // ReSharper disable once InconsistentNaming\n        public string? BlockHTML { get; set; }\n\n        [DataMember(EmitDefaultValue = false)]\n        public string? BlockShownId { get; set; }\n\n        [DataMember(EmitDefaultValue = false)]\n        public string? ExtraData { get; set; }\n\n        [DataMember(EmitDefaultValue = false)]\n        public bool LongRunning { get; set; }\n\n        [DataMember(EmitDefaultValue = false)]\n        public PropagationType Propagation { get; set; }\n\n        [DataMember(EmitDefaultValue = false)]\n        public bool PreventDefault { get; set; }\n\n        [DataMember(EmitDefaultValue = false)]\n        public bool UploadFiles { get; set; }\n\n        public static ClientEventSettings CreateFrom(EventSettings settings)\n        {\n            var client = new ClientEventSettings\n            {\n                Block = settings.Block,\n                EventName = settings.EventName,\n                LongRunning = settings.LongRunning,\n                Propagation = settings.Propagation,\n                PreventDefault = settings.PreventDefault,\n                UploadFiles = settings.UploadFiles\n            };\n            if (settings.BlockOptions == null) return client;\n            client.BlockElementId = settings.BlockOptions.BlockedElementId;\n            client.BlockHTML = settings.BlockOptions.ShowHtmlMessage;\n            client.BlockShownId = settings.BlockOptions.ShowElementId;\n            return client;\n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Delta/SwapChildrenDelta.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 8/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System.Runtime.Serialization;\n\nnamespace Integrative.Lara\n{\n    [DataContract]\n    internal sealed class SwapChildrenDelta : BaseDelta\n    {\n        public SwapChildrenDelta() : base(DeltaType.SwapChildren)\n        {            \n        }\n\n        [DataMember]\n        public string ParentId { get; set; } = string.Empty;\n\n        [DataMember]\n        public int Index1 { get; set; }\n        \n        [DataMember]\n        public int Index2 { get; set; }\n\n        public static void Enqueue(Element parent, int index1, int index2)\n        {\n            if (parent.TryGetQueue(out var document))\n            {\n                document.Enqueue(new SwapChildrenDelta\n                {\n                    ParentId = parent.Id,\n                    Index1 = index1,\n                    Index2 = index2\n                });\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Delta/TextModifiedDelta.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 5/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System.Runtime.Serialization;\n\nnamespace Integrative.Lara\n{\n    [DataContract]\n    internal sealed class TextModifiedDelta : BaseDelta\n    {\n        [DataMember]\n        public string ParentElementId { get; set; } = string.Empty;\n\n        [DataMember]\n        public int ChildNodeIndex { get; set; }\n\n        [DataMember]\n        public string? Text { get; set; }\n\n        public TextModifiedDelta() : base(DeltaType.TextModified)\n        {\n        }\n\n        public static void Enqueue(TextNode node)\n        {\n            var parent = node.ParentElement;\n            if (parent == null || !parent.TryGetQueue(out var document)) return;\n            var index = parent.GetChildNodePosition(node);\n            document.Enqueue(new TextModifiedDelta\n            {\n                ParentElementId = parent.Id,\n                ChildNodeIndex = index,\n                Text = node.Data\n            });\n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Delta/UnRenderDelta.cs",
    "content": "﻿/*\nCopyright (c) 2021 Integrative Software LLC\nCreated: 1/2021\nAuthor: Pablo Carbonell\n*/\n\nusing System.Collections.Generic;\nusing System.Runtime.Serialization;\n\nnamespace Integrative.Lara\n{\n    [DataContract]\n    internal class UnRenderDelta : BaseDelta\n    {\n        [DataMember]\n        public NodeLocator? Locator { get; set; }\n\n        public UnRenderDelta() : base(DeltaType.UnRender)\n        {\n        }\n\n        public static void Enqueue(Document document, IEnumerable<Node> nodes)\n        {\n            foreach (var node in nodes)\n            {\n                document.Enqueue(new UnRenderDelta\n                {\n                    Locator = NodeLocator.FromNode(node)\n                });\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Delta/UnsubscribeDelta.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 10/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System.Runtime.Serialization;\n\nnamespace Integrative.Lara\n{\n    [DataContract]\n    internal class UnsubscribeDelta : BaseDelta\n    {\n        [DataMember]\n        public string ElementId { get; set; } = string.Empty;\n\n        [DataMember]\n        public string EventName { get; set; } = string.Empty;\n\n        public UnsubscribeDelta() : base(DeltaType.Unsubscribe)\n        {\n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Elements/GenericElement.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 5/2019\nAuthor: Pablo Carbonell\n*/\n\nnamespace Integrative.Lara\n{\n    /// <summary>\n    /// A generic element class for all elements that are not handled by specialized classes.\n    /// </summary>\n    /// <seealso cref=\"Element\" />\n    public sealed class GenericElement : Element\n    {\n        internal GenericElement(string tagName) : base(tagName)\n        {\n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Elements/HtmlAnchorElement.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 5/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System;\nusing System.ComponentModel;\n\nnamespace Integrative.Lara\n{\n    /// <summary>\n    /// Anchor element\n    /// </summary>\n    [Obsolete(\"Use HtmlAnchorElement instead\")]\n    [EditorBrowsable(EditorBrowsableState.Never)]\n    public class Anchor : HtmlAnchorElement\n    {\n    }\n\n    /// <summary>\n    /// The HTML5 'a' element\n    /// </summary>\n    /// <seealso cref=\"Element\" />\n    public class HtmlAnchorElement : Element\n    {\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"HtmlAnchorElement\"/> class.\n        /// </summary>\n        public HtmlAnchorElement() : base(\"a\")\n        {\n        }\n\n        /// <summary>\n        /// The 'download' HTML5 attribute.\n        /// </summary>\n        public bool Download\n        {\n            get => HasAttribute(\"download\");\n            set => SetFlagAttributeLower(\"download\", value);\n        }\n\n        /// <summary>\n        /// The 'href' HTML5 attribute.\n        /// </summary>\n        public string? HRef\n        {\n            get => GetAttribute(\"href\");\n            set => SetAttributeLower(\"href\", value);\n        }\n\n        /// <summary>\n        /// The 'hreflang' HTML5 attribute.\n        /// </summary>\n        public string? HRefLang\n        {\n            get => GetAttribute(\"hreflang\");\n            set => SetAttributeLower(\"hreflang\", value);\n        }\n\n        /// <summary>\n        /// The 'media' HTML5 attribute.\n        /// </summary>\n        public string? Media\n        {\n            get => GetAttribute(\"media\");\n            set => SetAttributeLower(\"media\", value);\n        }\n\n        /// <summary>\n        /// The 'ping' HTML5 attribute.\n        /// </summary>\n        public string? Ping\n        {\n            get => GetAttribute(\"ping\");\n            set => SetAttributeLower(\"ping\", value);\n        }\n\n        /// <summary>\n        /// The 'referrerpolicy' HTML5 attribute.\n        /// </summary>\n        public string? ReferrerPolicy\n        {\n            get => GetAttribute(\"referrerpolicy\");\n            set => SetAttributeLower(\"referrerpolicy\", value);\n        }\n\n        /// <summary>\n        /// The 'rel' HTML5 attribute.\n        /// </summary>\n        public string? Rel\n        {\n            get => GetAttribute(\"rel\");\n            set => SetAttributeLower(\"rel\", value);\n        }\n\n        /// <summary>\n        /// The 'target' HTML5 attribute.\n        /// </summary>\n        public string? Target\n        {\n            get => GetAttributeLower(\"target\");\n            set => SetAttributeLower(\"target\", value);\n        }\n\n        /// <summary>\n        /// The 'type' HTML5 attribute.\n        /// </summary>\n        public string? Type\n        {\n            get => GetAttributeLower(\"type\");\n            set => SetAttributeLower(\"type\", value);\n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Elements/HtmlBodyElement.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 12/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System;\nusing System.ComponentModel;\n\nnamespace Integrative.Lara\n{\n    /// <summary>\n    /// Body element\n    /// </summary>\n    [Obsolete(\"Use HtmlBodyElement\")]\n    [EditorBrowsable(EditorBrowsableState.Never)]\n    public class BodyElement : HtmlBodyElement\n    {\n    }\n\n    /// <summary>\n    /// HTML 'body' element\n    /// </summary>\n    public class HtmlBodyElement : Element\n    {\n        /// <summary>\n        /// Constructor\n        /// </summary>\n        public HtmlBodyElement() : base(\"body\")\n        {\n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Elements/HtmlButtonElement.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 5/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System;\nusing System.ComponentModel;\n\nnamespace Integrative.Lara\n{\n    /// <summary>\n    /// Button element\n    /// </summary>\n    [Obsolete(\"Use HtmlButtonElement instead\")]\n    [EditorBrowsable(EditorBrowsableState.Never)]\n    public class Button : HtmlButtonElement\n    {\n    }\n\n    /// <summary>\n    /// The 'button' HTML5 element\n    /// </summary>\n    /// <seealso cref=\"Element\" />\n    public class HtmlButtonElement : Element\n    {\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"HtmlButtonElement\"/> class.\n        /// </summary>\n        public HtmlButtonElement() : base(\"button\")\n        {\n            Type = \"button\";\n        }\n\n        internal override void NotifyValue(ElementEventValue entry)\n        {\n            base.NotifyValue(entry);\n            NotifyValue(entry.Value);\n        }\n\n        /// <summary>\n        /// Gets or sets the 'autofocus' HTML5 attribute.\n        /// </summary>\n        public bool AutoFocus\n        {\n            get => HasAttributeLower(\"autofocus\");\n            set => SetFlagAttributeLower(\"autofocus\", value);\n        }\n\n        /// <summary>\n        /// Gets or sets the 'disabled' HTML5 attribute.\n        /// </summary>\n        public bool Disabled\n        {\n            get => HasAttribute(\"disabled\");\n            set => SetFlagAttributeLower(\"disabled\", value);\n        }\n\n        /// <summary>\n        /// Gets or sets the 'name' HTML5 attribute.\n        /// </summary>\n        public string? Name\n        {\n            get => GetAttributeLower(\"name\");\n            set => SetAttributeLower(\"name\", value);\n        }\n\n        /// <summary>\n        /// Gets or sets the 'type' HTML5 attribute.\n        /// </summary>\n        public string? Type\n        {\n            get => GetAttributeLower(\"type\");\n            set => SetAttributeLower(\"type\", value);\n        }\n\n        /// <summary>\n        /// Gets or sets the 'value' property.\n        /// </summary>\n        public string? Value\n        {\n            get => GetAttributeLower(\"value\");\n            set => SetAttributeLower(\"value\", value);\n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Elements/HtmlColGroupElement.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 5/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System;\nusing System.ComponentModel;\n\nnamespace Integrative.Lara\n{\n    /// <summary>\n    /// ColGroup element\n    /// </summary>\n    [Obsolete(\"Use HtmlColGroupElement instead\")]\n    [EditorBrowsable(EditorBrowsableState.Never)]\n    public class ColGroup : HtmlColGroupElement\n    {\n    }\n\n    /// <summary>\n    /// The 'colgroup' HTML5 element\n    /// </summary>\n    /// <seealso cref=\"Element\" />\n    public class HtmlColGroupElement : Element\n    {\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"HtmlColGroupElement\"/> class.\n        /// </summary>\n        public HtmlColGroupElement() : base(\"colgroup\")\n        {\n        }\n\n        /// <summary>\n        /// Gets or sets the 'span' HTML5 attribute.\n        /// </summary>\n        public int? Span\n        {\n            get => GetIntAttribute(\"span\");\n            set => SetIntAttribute(\"span\", value);\n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Elements/HtmlDivElement.cs",
    "content": "﻿/*\nCopyright (c) 2020-2021 Integrative Software LLC\nCreated: 12/2020\nAuthor: Pablo Carbonell\n*/\n\nnamespace Integrative.Lara\n{\n    /// <summary>\n    /// HTML div element\n    /// </summary>\n    public class HtmlDivElement : Element\n    {\n        /// <summary>\n        /// Constructor\n        /// </summary>\n        public HtmlDivElement() : base(\"div\")\n        {\n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Elements/HtmlHeadElement.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 12/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System;\nusing System.ComponentModel;\n\nnamespace Integrative.Lara\n{\n    /// <summary>\n    /// Head element\n    /// </summary>\n    [Obsolete(\"Use HtmlHeadElement instead\")]\n    [EditorBrowsable(EditorBrowsableState.Never)]\n    public class HeadElement : HtmlHeadElement\n    {\n    }\n\n    /// <summary>\n    /// HTML 'head' element\n    /// </summary>\n    /// <seealso cref=\"Element\" />\n    public class HtmlHeadElement : Element\n    {\n        /// <summary>\n        /// Constructor\n        /// </summary>\n        public HtmlHeadElement() : base(\"head\")\n        {\n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Elements/HtmlHeadingElement.cs",
    "content": "﻿/*\nCopyright (c) 2020-2021 Integrative Software LLC\nCreated: 12/2020\nAuthor: Pablo Carbonell\n*/\n\nusing System;\n\nnamespace Integrative.Lara\n{\n    /// <summary>\n    /// Represents the h1..h6 tags\n    /// </summary>\n    public class HtmlHeadingElement : Element\n    {\n        /// <summary>\n        /// Constructor\n        /// </summary>\n        /// <param name=\"level\"></param>\n        public HtmlHeadingElement(int level) : base(GetLevelTag(level))\n        {\n        }\n\n        /// <summary>\n        /// Returns h1..h6 for levels 1..6\n        /// </summary>\n        /// <param name=\"level\"></param>\n        /// <returns></returns>\n        public static string GetLevelTag(int level)\n        {\n            if (level < 1 || level > 6)\n            {\n                throw new ArgumentException(\"Invalid heading level\");\n            }\n            return $\"h{level}\";\n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Elements/HtmlImageElement.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 5/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System;\nusing System.ComponentModel;\n\nnamespace Integrative.Lara\n{\n    /// <summary>\n    /// Image element\n    /// </summary>\n    [Obsolete(\"Use HtmlImageElement instead\")]\n    [EditorBrowsable(EditorBrowsableState.Never)]\n    public class Image : HtmlImageElement\n    {\n    }\n\n    /// <summary>\n    /// The 'img' HTML5 element.\n    /// </summary>\n    /// <seealso cref=\"Element\" />\n    public class HtmlImageElement : Element\n    {\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"HtmlImageElement\"/> class.\n        /// </summary>\n        public HtmlImageElement() : base(\"img\")\n        {\n        }\n\n        /// <summary>\n        /// Gets or sets the 'alt' HTML5 attribute.\n        /// </summary>\n        public string? Alt\n        {\n            get => GetAttributeLower(\"alt\");\n            set => SetAttributeLower(\"alt\", value);\n        }\n\n        /// <summary>\n        /// Gets or sets the 'crossorigin' HTML5 attribute.\n        /// </summary>\n        public string? CrossOrigin\n        {\n            get => GetAttributeLower(\"crossorigin\");\n            set => SetAttributeLower(\"crossorigin\", value);\n        }\n\n        /// <summary>\n        /// Gets or sets the 'height' HTML5 attribute.\n        /// </summary>\n        public string? Height\n        {\n            get => GetAttribute(\"height\");\n            set => SetAttribute(\"height\", value);\n        }\n\n        /// <summary>\n        /// Gets or sets the 'ismap' HTML5 attribute.\n        /// </summary>\n        public bool IsMap\n        {\n            get => HasAttribute(\"ismap\");\n            set => SetFlagAttributeLower(\"ismap\", value);\n        }\n\n        /// <summary>\n        /// Gets or sets the 'longdesc' HTML5 attribute.\n        /// </summary>\n        public string? LongDesc\n        {\n            get => GetAttributeLower(\"longdesc\");\n            set => SetAttributeLower(\"longdesc\", value);\n        }\n\n        /// <summary>\n        /// Gets or sets the 'src' HTML5 attribute.\n        /// </summary>\n        public string? Src\n        {\n            get => GetAttributeLower(\"src\");\n            set => SetAttributeLower(\"src\", value);\n        }\n\n        /// <summary>\n        /// Gets or sets the 'srcset' HTML5 attribute.\n        /// </summary>\n        public string? SrcSet\n        {\n            get => GetAttributeLower(\"srcset\");\n            set => SetAttributeLower(\"srcset\", value);\n        }\n\n        /// <summary>\n        /// Gets or sets the 'usemap' HTML5 attribute.\n        /// </summary>\n        public string? UseMap\n        {\n            get => GetAttributeLower(\"usemap\");\n            set => SetAttributeLower(\"usemap\", value);\n        }\n\n        /// <summary>\n        /// Gets or sets the 'width' HTML5 attribute.\n        /// </summary>\n        public string? Width\n        {\n            get => GetAttribute(\"width\");\n            set => SetAttribute(\"width\", value);\n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Elements/HtmlInputElement.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 5/2019\nAuthor: Pablo Carbonell\n*/\n\nusing Microsoft.AspNetCore.Http;\nusing System;\nusing System.Collections.Generic;\nusing System.ComponentModel;\n\nnamespace Integrative.Lara\n{\n    /// <summary>\n    /// Input element\n    /// </summary>\n    [Obsolete(\"Use HtmlInputElement instead\")]\n    [EditorBrowsable(EditorBrowsableState.Never)]\n    public class InputElement : HtmlInputElement\n    {\n    }\n\n    /// <summary>\n    /// The 'input' HTML5 element\n    /// </summary>\n    /// <seealso cref=\"Element\" />\n    public class HtmlInputElement : Element\n    {\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"HtmlInputElement\"/> class.\n        /// </summary>\n        public HtmlInputElement() : base(\"input\")\n        {\n        }\n\n        internal override void NotifyValue(ElementEventValue entry)\n        {\n            BeginUpdate();\n            base.NotifyValue(entry);\n            NotifyValue(entry.Value);\n            NotifyChecked(entry.Checked);\n            EndUpdate();\n            ClearFiles();\n        }\n\n        /// <summary>\n        /// Gets or sets the 'accept' HTML5 attribute.\n        /// </summary>\n        public string? Accept\n        {\n            get => GetAttributeLower(\"accept\");\n            set => SetAttributeLower(\"accept\", value);\n        }\n\n        /// <summary>\n        /// Gets or sets the 'alt' HTML5 attribute.\n        /// </summary>\n        public string? Alt\n        {\n            get => GetAttributeLower(\"alt\");\n            set => SetAttributeLower(\"alt\", value);\n        }\n\n        /// <summary>\n        /// Gets or sets the 'autocomplete' HTML5 attribute.\n        /// </summary>\n        public string? Autocomplete\n        {\n            get => GetAttributeLower(\"autocomplete\");\n            set => SetAttributeLower(\"autocomplete\", value);\n        }\n\n        /// <summary>\n        /// Gets or sets the 'autofocus' HTML5 attribute.\n        /// </summary>\n        public bool Autofocus\n        {\n            get => HasAttributeLower(\"autofocus\");\n            set => SetFlagAttributeLower(\"autofocus\", value);\n        }\n\n        /// <summary>\n        /// Gets or sets the 'checked' HTML5 attribute.\n        /// </summary>\n        public bool Checked\n        {\n            get => HasAttributeLower(\"checked\");\n            set => SetFlagAttributeLower(\"checked\", value);\n        }\n\n        /// <summary>\n        /// Gets or sets the 'dirname' HTML5 attribute.\n        /// </summary>\n        public string? Dirname\n        {\n            get => GetAttributeLower(\"dirname\");\n            set => SetAttributeLower(\"dirname\", value);\n        }\n\n        /// <summary>\n        /// Gets or sets the 'disabled' HTML5 attribute.\n        /// </summary>\n        public bool Disabled\n        {\n            get => HasAttributeLower(\"disabled\");\n            set => SetFlagAttributeLower(\"disabled\", value);\n        }\n\n        /// <summary>\n        /// Gets or sets the 'height' HTML5 attribute.\n        /// </summary>\n        public int? Height\n        {\n            get => GetIntAttribute(\"height\");\n            set => SetIntAttribute(\"height\", value);\n        }\n\n        /// <summary>\n        /// Gets or sets the 'list' HTML5 attribute.\n        /// </summary>\n        public string? List\n        {\n            get => GetAttributeLower(\"list\");\n            set => SetAttributeLower(\"list\", value);\n        }\n\n        /// <summary>\n        /// Gets or sets the 'max' HTML5 attribute.\n        /// </summary>\n        public string? Max\n        {\n            get => GetAttributeLower(\"max\");\n            set => SetAttributeLower(\"max\", value);\n        }\n\n        /// <summary>\n        /// Gets or sets the 'maxlength' HTML5 attribute.\n        /// </summary>\n        public int? MaxLength\n        {\n            get => GetIntAttribute(\"maxlength\");\n            set => SetIntAttribute(\"maxlength\", value);\n        }\n\n        /// <summary>\n        /// Gets or sets the 'min' HTML5 attribute.\n        /// </summary>\n        public string? Min\n        {\n            get => GetAttributeLower(\"min\");\n            set => SetAttributeLower(\"min\", value);\n        }\n\n        /// <summary>\n        /// Gets or sets the 'multiple' HTML5 attribute.\n        /// </summary>\n        public bool Multiple\n        {\n            get => HasAttributeLower(\"multiple\");\n            set => SetFlagAttributeLower(\"multiple\", value);\n        }\n\n        /// <summary>\n        /// Gets or sets the 'name' HTML5 attribute.\n        /// </summary>\n        public string? Name\n        {\n            get => GetAttributeLower(\"name\");\n            set => SetAttributeLower(\"name\", value);\n        }\n\n        /// <summary>\n        /// Gets or sets the 'pattern' HTML5 attribute.\n        /// </summary>\n        public string? Pattern\n        {\n            get => GetAttributeLower(\"pattern\");\n            set => SetAttributeLower(\"pattern\", value);\n        }\n\n        /// <summary>\n        /// Gets or sets the 'placeholder' HTML5 attribute.\n        /// </summary>\n        public string? Placeholder\n        {\n            get => GetAttributeLower(\"placeholder\");\n            set => SetAttributeLower(\"placeholder\", value);\n        }\n\n        /// <summary>\n        /// Gets or sets the 'readonly' HTML5 attribute.\n        /// </summary>\n        public bool Readonly\n        {\n            get => HasAttributeLower(\"readonly\");\n            set => SetFlagAttributeLower(\"readonly\", value);\n        }\n\n        /// <summary>\n        /// Gets or sets the 'required' HTML5 attribute.\n        /// </summary>\n        public bool Required\n        {\n            get => HasAttributeLower(\"required\");\n            set => SetFlagAttributeLower(\"required\", value);\n        }\n\n        /// <summary>\n        /// Gets or sets the 'size' HTML5 attribute.\n        /// </summary>\n        public int? Size\n        {\n            get => GetIntAttribute(\"size\");\n            set => SetIntAttribute(\"size\", value);\n        }\n\n        /// <summary>\n        /// Gets or sets the 'src' HTML5 attribute.\n        /// </summary>\n        public string? Src\n        {\n            get => GetAttributeLower(\"src\");\n            set => SetAttributeLower(\"src\", value);\n        }\n\n        /// <summary>\n        /// Gets or sets the 'step' HTML5 attribute.\n        /// </summary>\n        public string? Step\n        {\n            get => GetAttributeLower(\"step\");\n            set => SetAttributeLower(\"step\", value);\n        }\n\n        /// <summary>\n        /// Gets or sets the 'type' HTML5 attribute.\n        /// </summary>\n        public string? Type\n        {\n            get => GetAttributeLower(\"type\");\n            set => SetAttributeLower(\"type\", value);\n        }\n\n        /// <summary>\n        /// Gets or sets the 'value' HTML5 attribute.\n        /// </summary>\n        public string? Value\n        {\n            get => GetAttributeLower(\"value\");\n            set => SetAttributeLower(\"value\", value);\n        }\n\n        /// <summary>\n        /// Gets or sets the 'width' HTML5 attribute.\n        /// </summary>\n        public int? Width\n        {\n            get => GetIntAttribute(\"width\");\n            set => SetIntAttribute(\"width\", value);\n        }\n\n        private readonly List<IFormFile> _files = new List<IFormFile>();\n        private void ClearFiles() => _files.Clear();\n        internal void AddFile(IFormFile file) => _files.Add(file);\n\n        /// <summary>\n        /// Collection of uploaded files for input elements with type='file'\n        /// </summary>\n        public IReadOnlyList<IFormFile> Files => _files;\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Elements/HtmlLabelElement.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 5/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System;\nusing System.ComponentModel;\n\nnamespace Integrative.Lara\n{\n    /// <summary>\n    /// Label element\n    /// </summary>\n    [Obsolete(\"Use HtmlLabelElement instead\")]\n    [EditorBrowsable(EditorBrowsableState.Never)]\n    public class Label : HtmlLabelElement\n    {\n    }\n\n    /// <summary>\n    /// The 'label' HTML5 element.\n    /// </summary>\n    /// <seealso cref=\"Element\" />\n    public class HtmlLabelElement : Element\n    {\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"HtmlLabelElement\"/> class.\n        /// </summary>\n        public HtmlLabelElement() : base(\"label\")\n        {\n        }\n\n        /// <summary>\n        /// Gets or sets the 'for' HTML5 attribute.\n        /// </summary>\n        public string? For\n        {\n            get => GetAttributeLower(\"for\");\n            set => SetAttributeLower(\"for\", value);\n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Elements/HtmlLiElement.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 5/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System;\nusing System.ComponentModel;\n\nnamespace Integrative.Lara\n{\n    /// <summary>\n    /// LI element\n    /// </summary>\n    [Obsolete(\"Use HtmlLiElement instead\")]\n    [EditorBrowsable(EditorBrowsableState.Never)]\n    public class ListItem : HtmlLiElement\n    {\n    }\n\n    /// <summary>\n    /// The 'li' HTML5 element\n    /// </summary>\n    /// <seealso cref=\"Element\" />\n    public class HtmlLiElement : Element\n    {\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"HtmlLiElement\"/> class.\n        /// </summary>\n        public HtmlLiElement() : base(\"li\")\n        {\n        }\n\n        /// <summary>\n        /// Gets or sets the 'value' property.\n        /// </summary>\n        public string? Value\n        {\n            get => GetAttributeLower(\"value\");\n            set => SetAttributeLower(\"value\", value);\n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Elements/HtmlLinkElement.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 5/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System;\nusing System.ComponentModel;\n\nnamespace Integrative.Lara\n{\n    /// <summary>\n    /// Link element\n    /// </summary>\n    [Obsolete(\"Use HtmlLinkElement instead\")]\n    [EditorBrowsable(EditorBrowsableState.Never)]\n    public class Link : HtmlLinkElement\n    {\n    }\n\n    /// <summary>\n    /// The 'link' HTML5 element.\n    /// </summary>\n    /// <seealso cref=\"Element\" />\n    public class HtmlLinkElement : Element\n    {\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"HtmlLinkElement\"/> class.\n        /// </summary>\n        public HtmlLinkElement() : base(\"link\")\n        {\n        }\n\n        /// <summary>\n        /// Gets or sets the 'crossorigin' HTML5 attribute.\n        /// </summary>\n        public string? CrossOrigin\n        {\n            get => GetAttributeLower(\"crossorigin\");\n            set => SetAttributeLower(\"crossorigin\", value);\n        }\n\n        /// <summary>\n        /// Gets or sets the 'href' HTML5 attribute.\n        /// </summary>\n        public string? HRef\n        {\n            get => GetAttributeLower(\"href\");\n            set => SetAttributeLower(\"href\", value);\n        }\n\n        /// <summary>\n        /// Gets or sets the 'hreflang' HTML5 attribute.\n        /// </summary>\n        public string? HRefLang\n        {\n            get => GetAttributeLower(\"hreflang\");\n            set => SetAttributeLower(\"hreflang\", value);\n        }\n\n        /// <summary>\n        /// Gets or sets the 'media' HTML5 attribute.\n        /// </summary>\n        public string? Media\n        {\n            get => GetAttributeLower(\"media\");\n            set => SetAttributeLower(\"media\", value);\n        }\n\n        /// <summary>\n        /// Gets or sets the 'rel' HTML5 attribute.\n        /// </summary>\n        public string? Rel\n        {\n            get => GetAttributeLower(\"rel\");\n            set => SetAttributeLower(\"rel\", value);\n        }\n\n        /// <summary>\n        /// Gets or sets the 'sizes' HTML5 attribute.\n        /// </summary>\n        public string? Sizes\n        {\n            get => GetAttributeLower(\"sizes\");\n            set => SetAttributeLower(\"sizes\", value);\n        }\n\n        /// <summary>\n        /// Gets or sets the 'type' HTML5 attribute.\n        /// </summary>\n        public string? Type\n        {\n            get => GetAttributeLower(\"type\");\n            set => SetAttributeLower(\"type\", value);\n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Elements/HtmlMetaElement.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 5/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System;\nusing System.ComponentModel;\n\nnamespace Integrative.Lara\n{\n    /// <summary>\n    /// Meta element\n    /// </summary>\n    [Obsolete(\"Use HtmlMetaElement instead\")]\n    [EditorBrowsable(EditorBrowsableState.Never)]\n    public class Meta : HtmlMetaElement\n    {\n    }\n\n    /// <summary>\n    /// The 'meta' HTML5 element.\n    /// </summary>\n    /// <seealso cref=\"Element\" />\n    public class HtmlMetaElement : Element\n    {\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"HtmlMetaElement\"/> class.\n        /// </summary>\n        public HtmlMetaElement() : base(\"meta\")\n        {\n        }\n\n        /// <summary>\n        /// Gets or sets the 'content' HTML5 attribute.\n        /// </summary>\n        public string? Content\n        {\n            get => GetAttributeLower(\"content\");\n            set => SetAttributeLower(\"content\", value);\n        }\n\n        /// <summary>\n        /// Gets or sets the 'httpequiv' HTML5 attribute.\n        /// </summary>\n        public string? HttpEquiv\n        {\n            get => GetAttributeLower(\"http-equiv\");\n            set => SetAttributeLower(\"http-equiv\", value);\n        }\n\n        /// <summary>\n        /// Gets or sets the 'name' HTML5 attribute.\n        /// </summary>\n        public string? Name\n        {\n            get => GetAttributeLower(\"name\");\n            set => SetAttributeLower(\"name\", value);\n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Elements/HtmlMeterElement.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 5/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System;\nusing System.ComponentModel;\n\nnamespace Integrative.Lara\n{\n    /// <summary>\n    /// Meter element\n    /// </summary>\n    [Obsolete(\"Use HtmlMeterElement\")]\n    [EditorBrowsable(EditorBrowsableState.Never)]\n    public class Meter : HtmlMeterElement\n    {\n    }\n\n    /// <summary>\n    /// The 'meter' HTML5 element.\n    /// </summary>\n    /// <seealso cref=\"Element\" />\n    public class HtmlMeterElement : Element\n    {\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"HtmlMeterElement\"/> class.\n        /// </summary>\n        public HtmlMeterElement() : base(\"meter\")\n        {\n        }\n\n        /// <summary>\n        /// Gets or sets the 'high' HTML5 attribute.\n        /// </summary>\n        public int? High\n        {\n            get => GetIntAttribute(\"high\");\n            set => SetIntAttribute(\"high\", value);\n        }\n\n        /// <summary>\n        /// Gets or sets the 'low' HTML5 attribute.\n        /// </summary>\n        public int? Low\n        {\n            get => GetIntAttribute(\"low\");\n            set => SetIntAttribute(\"low\", value);\n        }\n\n        /// <summary>\n        /// Gets or sets the 'max' HTML5 attribute.\n        /// </summary>\n        public int? Max\n        {\n            get => GetIntAttribute(\"max\");\n            set => SetIntAttribute(\"max\", value);\n        }\n\n        /// <summary>\n        /// Gets or sets the 'min' HTML5 attribute.\n        /// </summary>\n        public int? Min\n        {\n            get => GetIntAttribute(\"min\");\n            set => SetIntAttribute(\"min\", value);\n        }\n\n        /// <summary>\n        /// Gets or sets the 'optimum' HTML5 attribute.\n        /// </summary>\n        public int? Optimum\n        {\n            get => GetIntAttribute(\"optimum\");\n            set => SetIntAttribute(\"optimum\", value);\n        }\n\n        /// <summary>\n        /// Gets or sets the 'value' HTML5 attribute.\n        /// </summary>\n        public int? Value\n        {\n            get => GetIntAttribute(\"value\");\n            set => SetIntAttribute(\"value\", value);\n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Elements/HtmlOlElement.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 5/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System;\nusing System.ComponentModel;\n\nnamespace Integrative.Lara\n{\n    /// <summary>\n    /// Ordered list\n    /// </summary>\n    [Obsolete(\"Use HtmlOlElement instead\")]\n    [EditorBrowsable(EditorBrowsableState.Never)]\n    public class OrderedList : HtmlOlElement\n    {\n    }\n\n    /// <summary>\n    /// The 'ol' HTML5 element.\n    /// </summary>\n    /// <seealso cref=\"Element\" />\n    public class HtmlOlElement : Element\n    {\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"HtmlOlElement\"/> class.\n        /// </summary>\n        public HtmlOlElement() : base(\"ol\")\n        {\n        }\n\n        /// <summary>\n        /// Gets or sets the 'reversed' HTML5 attribute.\n        /// </summary>\n        public bool Reversed\n        {\n            get => HasAttributeLower(\"reversed\");\n            set => SetFlagAttributeLower(\"reversed\", value);\n        }\n\n        /// <summary>\n        /// Gets or sets the 'start' HTML5 attribute.\n        /// </summary>\n        public int? Start\n        {\n            get => GetIntAttribute(\"start\");\n            set => SetIntAttribute(\"start\", value);\n        }\n\n        /// <summary>\n        /// Gets or sets the 'type' HTML5 attribute.\n        /// </summary>\n        public string? Type\n        {\n            get => GetAttributeLower(\"type\");\n            set => SetAttributeLower(\"type\", value);\n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Elements/HtmlOptionElement.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 5/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System;\nusing System.ComponentModel;\n\nnamespace Integrative.Lara\n{\n    /// <summary>\n    /// Option element\n    /// </summary>\n    [Obsolete(\"Use HtmlOptionElement instead\")]\n    [EditorBrowsable(EditorBrowsableState.Never)]\n    public class OptionElement : HtmlOptionElement\n    {\n    }\n\n    /// <summary>\n    /// The 'option' HTML5 element.\n    /// </summary>\n    /// <seealso cref=\"Element\" />\n    public class HtmlOptionElement : Element\n    {\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"HtmlOptionElement\"/> class.\n        /// </summary>\n        public HtmlOptionElement() : base(\"option\")\n        {\n        }\n\n        internal override void NotifyValue(ElementEventValue entry)\n        {\n            BeginUpdate();\n            base.NotifyValue(entry);\n            NotifySelected(entry.Checked);\n            EndUpdate();\n        }\n\n        /// <summary>\n        /// Gets or sets the 'disabled' HTML5 attribute.\n        /// </summary>\n        public bool Disabled\n        {\n            get => HasAttributeLower(\"disabled\");\n            set => SetFlagAttributeLower(\"disabled\", value);\n        }\n\n        /// <summary>\n        /// Gets or sets the 'label' HTML5 attribute.\n        /// </summary>\n        public string? Label\n        {\n            get => GetAttributeLower(\"label\");\n            set => SetAttributeLower(\"label\", value);\n        }\n\n        /// <summary>\n        /// Gets or sets the 'selected' HTML5 attribute.\n        /// </summary>\n        public bool Selected\n        {\n            get => HasAttributeLower(\"selected\");\n            set => SetFlagAttributeLower(\"selected\", value);\n        }\n\n        /// <summary>\n        /// Gets or sets the 'value' HTML5 attribute.\n        /// </summary>\n        public string? Value\n        {\n            get => GetAttributeLower(\"value\");\n            set => SetAttributeLower(\"value\", value);\n        }\n\n        internal void NotifyAdded(string parentValue)\n        {\n            if (parentValue == Value)\n            {\n                Selected = true;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Elements/HtmlOptionGroupElement.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 5/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System;\nusing System.Collections.Generic;\nusing System.ComponentModel;\n\nnamespace Integrative.Lara\n{\n    /// <summary>\n    /// Option group\n    /// </summary>\n    [Obsolete(\"Use HtmlOptionGroupElement instead\")]\n    [EditorBrowsable(EditorBrowsableState.Never)]\n    public class OptionGroup : HtmlOptionGroupElement\n    {\n    }\n\n    /// <summary>\n    /// The 'optgroup' HTML5 element.\n    /// </summary>\n    /// <seealso cref=\"Element\" />\n    public class HtmlOptionGroupElement : Element\n    {\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"HtmlOptionGroupElement\"/> class.\n        /// </summary>\n        public HtmlOptionGroupElement() : base(\"optgroup\")\n        {\n        }\n\n        /// <summary>\n        /// Gets or sets the 'disabled' HTML5 attribute.\n        /// </summary>\n        public bool Disabled\n        {\n            get => HasAttributeLower(\"disabled\");\n            set => SetFlagAttributeLower(\"disabled\", value);\n        }\n\n        /// <summary>\n        /// Gets or sets the 'label' HTML5 attribute.\n        /// </summary>\n        public string? Label\n        {\n            get => GetAttributeLower(\"label\");\n            set => SetAttributeLower(\"label\", value);\n        }\n\n        /// <summary>\n        /// Gets the child options.\n        /// </summary>\n        public IEnumerable<HtmlOptionElement> Options => GetOptions();\n\n        private IEnumerable<HtmlOptionElement> GetOptions()\n        {\n            foreach (var node in Children)\n            {\n                if (node is HtmlOptionElement option)\n                {\n                    yield return option;\n                }\n            }\n        }\n\n        private protected override void OnChildAdded(Node child)\n        {\n            BeginUpdate();\n            base.OnChildAdded(child);\n            if (ParentElement is HtmlSelectElement parent\n                && child is HtmlOptionElement option\n                && !string.IsNullOrEmpty(parent.Value))\n            {\n                option.NotifyAdded(parent.Value);\n            }\n            EndUpdate();\n        }\n\n        internal void NotifyAdded(string parentValue)\n        {\n            foreach (var child in GetOptions())\n            {\n                if (child.Value == parentValue)\n                {\n                    child.Selected = true;\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Elements/HtmlParagraphElement.cs",
    "content": "﻿/*\nCopyright (c) 2020-2021 Integrative Software LLC\nCreated: 12/2020\nAuthor: Pablo Carbonell\n*/\n\nnamespace Integrative.Lara\n{\n    /// <summary>\n    /// HTML p element\n    /// </summary>\n    public class HtmlParagraphElement : Element\n    {\n        /// <summary>\n        /// constructor\n        /// </summary>\n        public HtmlParagraphElement() : base(\"p\")\n        {\n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Elements/HtmlScriptElement.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 5/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System;\nusing System.ComponentModel;\n\nnamespace Integrative.Lara\n{\n    /// <summary>\n    /// Script element\n    /// </summary>\n    [Obsolete(\"Use HtmlScriptElement instead\")]\n    [EditorBrowsable(EditorBrowsableState.Never)]\n    public class Script : HtmlScriptElement\n    {\n    }\n\n    /// <summary>\n    /// The 'script' HTML5 element.\n    /// </summary>\n    /// <seealso cref=\"Element\" />\n    public class HtmlScriptElement : Element\n    {\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"HtmlScriptElement\"/> class.\n        /// </summary>\n        public HtmlScriptElement() : base(\"script\")\n        {\n        }\n\n        /// <summary>\n        /// Gets or sets the 'async' HTML5 attribute.\n        /// </summary>\n        public bool Async\n        {\n            get => HasAttributeLower(\"async\");\n            set => SetFlagAttributeLower(\"async\", value);\n        }\n\n        /// <summary>\n        /// Gets or sets the 'charset' HTML5 attribute.\n        /// </summary>\n        public string? Charset\n        {\n            get => GetAttributeLower(\"charset\");\n            set => SetAttributeLower(\"charset\", value);\n        }\n\n        /// <summary>\n        /// Gets or sets the 'defer' HTML5 attribute.\n        /// </summary>\n        public bool Defer\n        {\n            get => HasAttributeLower(\"defer\");\n            set => SetFlagAttributeLower(\"defer\", value);\n        }\n\n        /// <summary>\n        /// Gets or sets the 'src' HTML5 attribute.\n        /// </summary>\n        public string? Src\n        {\n            get => GetAttributeLower(\"src\");\n            set => SetAttributeLower(\"src\", value);\n        }\n\n        /// <summary>\n        /// Gets or sets the 'type' HTML5 attribute.\n        /// </summary>\n        public string? Type\n        {\n            get => GetAttributeLower(\"type\");\n            set => SetAttributeLower(\"type\", value);\n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Elements/HtmlSelectElement.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 5/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System;\nusing System.Collections.Generic;\nusing System.ComponentModel;\n\nnamespace Integrative.Lara\n{\n    /// <summary>\n    /// Select element\n    /// </summary>\n    [Obsolete(\"Use HtmlSelectElement instead\")]\n    [EditorBrowsable(EditorBrowsableState.Never)]\n    public class SelectElement : HtmlSelectElement\n    {\n    }\n\n    /// <summary>\n    /// The 'select' HTML5 element.\n    /// </summary>\n    /// <seealso cref=\"Element\" />\n    public class HtmlSelectElement : Element\n    {\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"HtmlSelectElement\"/> class.\n        /// </summary>\n        public HtmlSelectElement() : base(\"select\")\n        {\n        }\n\n        internal override void NotifyValue(ElementEventValue entry)\n        {\n            base.NotifyValue(entry);\n            NotifyValue(entry.Value);\n        }\n\n        /// <summary>\n        /// Adds an option.\n        /// </summary>\n        /// <param name=\"value\">The option's value.</param>\n        /// <param name=\"text\">The option's text.</param>\n        public HtmlOptionElement AddOption(string value, string text)\n        {\n            var option = new HtmlOptionElement\n            {\n                Value = value\n            };\n            option.AppendChild(new TextNode(text));\n            AppendChild(option);\n            return option;\n        }\n\n        /// <summary>\n        /// Gets or sets the 'autofocus' HTML5 attribute.\n        /// </summary>\n        public bool Autofocus\n        {\n            get => HasAttributeLower(\"autofocus\");\n            set => SetFlagAttributeLower(\"autofocus\", value);\n        }\n\n        /// <summary>\n        /// Gets or sets the 'disabled' HTML5 attribute.\n        /// </summary>\n        public bool Disabled\n        {\n            get => HasAttributeLower(\"disabled\");\n            set => SetFlagAttributeLower(\"disabled\", value);\n        }\n\n        /// <summary>\n        /// Gets or sets the 'multiple' HTML5 attribute.\n        /// </summary>\n        public bool Multiple\n        {\n            get => HasAttributeLower(\"multiple\");\n            set => SetFlagAttributeLower(\"multiple\", value);\n        }\n\n        /// <summary>\n        /// Gets or sets the 'name' HTML5 attribute.\n        /// </summary>\n        public string? Name\n        {\n            get => GetAttributeLower(\"name\");\n            set => SetAttributeLower(\"name\", value);\n        }\n\n        /// <summary>\n        /// Gets or sets the 'required' HTML5 attribute.\n        /// </summary>\n        public bool Required\n        {\n            get => HasAttributeLower(\"required\");\n            set => SetFlagAttributeLower(\"required\", value);\n        }\n\n        /// <summary>\n        /// Gets or sets the 'size' HTML5 attribute.\n        /// </summary>\n        public int? Size\n        {\n            get => GetIntAttribute(\"size\");\n            set => SetIntAttribute(\"size\", value);\n        }\n\n        /// <summary>\n        /// Gets or sets the 'value' HTML5 attribute.\n        /// </summary>\n        public string? Value\n        {\n            get => GetAttributeLower(\"value\");\n            set => SetAttributeLower(\"value\", value);\n        }\n\n        internal override void AttributeChanged(string attribute, string? value)\n        {\n            BeginUpdate();\n            base.AttributeChanged(attribute, value);\n            if (attribute == \"value\")\n            {\n                UpdateChildOptions(value);\n            }\n            EndUpdate();\n        }\n\n        private void UpdateChildOptions(string? value)\n        {\n            if (Multiple)\n            {\n                SelectNonExclusiveOption(value);\n            }\n            else\n            {\n                SelectOnlyOption(value);\n            }\n        }\n\n        private void SelectNonExclusiveOption(string? value)\n        {\n            foreach (var option in GetOptions())\n            {\n                if (option.Value == value)\n                {\n                    option.Selected = true;\n                }\n            }\n        }\n\n        private void SelectOnlyOption(string? value)\n        {\n            foreach (var option in GetOptions())\n            {\n                option.Selected = (option.Value == value);\n            }\n        }\n\n        private protected override void OnChildAdded(Node child)\n        {\n            BeginUpdate();\n            base.OnChildAdded(child);\n            var value = Value;\n            if (!string.IsNullOrEmpty(value))\n            {\n                if (child is HtmlOptionElement option)\n                {\n                    option.NotifyAdded(value);\n                }\n                else if (child is HtmlOptionGroupElement group)\n                {\n                    group.NotifyAdded(value);\n                }\n            }\n            EndUpdate();\n        }\n\n        /// <summary>\n        /// Gets the child options.\n        /// </summary>\n        public IEnumerable<HtmlOptionElement> Options => GetOptions();\n\n        private IEnumerable<HtmlOptionElement> GetOptions()\n        {\n            foreach (var child in Children)\n            {\n                if (child is HtmlOptionElement option)\n                {\n                    yield return option;\n                }\n                else if (child is HtmlOptionGroupElement group)\n                {\n                    foreach (var grandchild in group.Options)\n                    {\n                        yield return grandchild;\n                    }\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Elements/HtmlSpanElement.cs",
    "content": "﻿/*\nCopyright (c) 2020-2021 Integrative Software LLC\nCreated: 12/2020\nAuthor: Pablo Carbonell\n*/\n\nnamespace Integrative.Lara\n{\n    /// <summary>\n    /// HTML span element\n    /// </summary>\n    public class HtmlSpanElement : Element\n    {\n        /// <summary>\n        /// Constructor\n        /// </summary>\n        public HtmlSpanElement() : base(\"span\")\n        {\n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Elements/HtmlTableCellElement.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 5/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System;\nusing System.ComponentModel;\n\nnamespace Integrative.Lara\n{\n    /// <summary>\n    /// Table cell element\n    /// </summary>\n    [Obsolete(\"Use HtmlTableCellElement instead\")]\n    [EditorBrowsable(EditorBrowsableState.Never)]\n    public class TableCell : HtmlTableCellElement\n    {\n    }\n\n    /// <summary>\n    /// The 'td' HTML5 element.\n    /// </summary>\n    /// <seealso cref=\"Element\" />\n    public class HtmlTableCellElement : Element\n    {\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"HtmlTableCellElement\"/> class.\n        /// </summary>\n        public HtmlTableCellElement() : base(\"td\")\n        {\n        }\n\n        /// <summary>\n        /// Gets or sets the 'colspan' HTML5 attribute.\n        /// </summary>\n        public int? ColSpan\n        {\n            get => GetIntAttribute(\"colspan\");\n            set => SetIntAttribute(\"colspan\", value);\n        }\n\n        /// <summary>\n        /// Gets or sets the 'headers' HTML5 attribute.\n        /// </summary>\n        public string? Headers\n        {\n            get => GetAttributeLower(\"headers\");\n            set => SetAttributeLower(\"headers\", value);\n        }\n\n        /// <summary>\n        /// Gets or sets the 'rowspan' HTML5 attribute.\n        /// </summary>\n        public int? RowSpan\n        {\n            get => GetIntAttribute(\"rowspan\");\n            set => SetIntAttribute(\"rowspan\", value);\n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Elements/HtmlTableElement.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 5/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System;\nusing System.ComponentModel;\n\nnamespace Integrative.Lara\n{\n    /// <summary>\n    /// Table element\n    /// </summary>\n    [Obsolete(\"Use HtmlTableElement instead\")]\n    [EditorBrowsable(EditorBrowsableState.Never)]\n    public class Table : HtmlTableElement\n    {\n    }\n\n    /// <summary>\n    /// The 'table' HTML5 element.\n    /// </summary>\n    /// <seealso cref=\"Element\" />\n    public class HtmlTableElement : Element\n    {\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"HtmlTableElement\"/> class.\n        /// </summary>\n        public HtmlTableElement() : base(\"table\")\n        {\n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Elements/HtmlTableHeaderElement.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 5/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System;\nusing System.ComponentModel;\n\nnamespace Integrative.Lara\n{\n    /// <summary>\n    /// Table header element\n    /// </summary>\n    [Obsolete(\"Use HtmlTableHeaderElement instead\")]\n    [EditorBrowsable(EditorBrowsableState.Never)]\n    public class TableHeader : HtmlTableHeaderElement\n    {\n    }\n\n    /// <summary>\n    /// The 'th' HTML5 element.\n    /// </summary>\n    /// <seealso cref=\"Element\" />\n    public class HtmlTableHeaderElement : Element\n    {\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"HtmlTableHeaderElement\"/> class.\n        /// </summary>\n        public HtmlTableHeaderElement() : base(\"th\")\n        {\n        }\n\n        /// <summary>\n        /// Gets or sets the 'abbr' HTML5 attribute.\n        /// </summary>\n        public string? Abbr\n        {\n            get => GetAttributeLower(\"abbr\");\n            set => SetAttributeLower(\"abbr\", value);\n        }\n\n        /// <summary>\n        /// Gets or sets the 'colspan' HTML5 attribute.\n        /// </summary>\n        public int? ColSpan\n        {\n            get => GetIntAttribute(\"colspan\");\n            set => SetIntAttribute(\"colspan\", value);\n        }\n\n        /// <summary>\n        /// Gets or sets the 'headers' HTML5 attribute.\n        /// </summary>\n        public string? Headers\n        {\n            get => GetAttributeLower(\"headers\");\n            set => SetAttributeLower(\"headers\", value);\n        }\n\n        /// <summary>\n        /// Gets or sets the 'rowspan' HTML5 attribute.\n        /// </summary>\n        public int? RowSpan\n        {\n            get => GetIntAttribute(\"rowspan\");\n            set => SetIntAttribute(\"rowspan\", value);\n        }\n\n        /// <summary>\n        /// Gets or sets the 'scope' HTML5 attribute.\n        /// </summary>\n        public string? Scope\n        {\n            get => GetAttributeLower(\"scope\");\n            set => SetAttributeLower(\"scope\", value);\n        }\n\n        /// <summary>\n        /// Gets or sets the 'sorted' HTML5 attribute.\n        /// </summary>\n        public string? Sorted\n        {\n            get => GetAttributeLower(\"sorted\");\n            set => SetAttributeLower(\"sorted\", value);\n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Elements/HtmlTableRowElement.cs",
    "content": "﻿/*\nCopyright (c) 2021 Integrative Software LLC\nCreated: 2/2021\nAuthor: Pablo Carbonell\n*/\n\nnamespace Integrative.Lara\n{\n    /// <summary>\n    /// The 'tr' HTML5 element.\n    /// </summary>\n    public class HtmlTableRowElement : Element\n    {\n        /// <summary>\n        /// Constructor\n        /// </summary>\n        public HtmlTableRowElement() : base(\"tr\")\n        {\n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Elements/HtmlTableSectionElement.cs",
    "content": "﻿/*\nCopyright (c) 2020-2021 Integrative Software LLC\nCreated: 12/2020\nAuthor: Pablo Carbonell\n*/\n\nusing System.Collections.Generic;\n\nnamespace Integrative.Lara\n{\n    /// <summary>\n    /// HTML table section types\n    /// </summary>\n    public enum HtmlTableSectionType\n    {\n        /// <summary>\n        /// Table body\n        /// </summary>\n        Body,\n\n        /// <summary>\n        /// Table header\n        /// </summary>\n        Head,\n\n        /// <summary>\n        /// Table footer\n        /// </summary>\n        Foot\n    }\n\n    /// <summary>\n    /// HTML table body element\n    /// </summary>\n    public class HtmlTableSectionElement : Element\n    {\n        /// <summary>\n        /// Associates a table section type with an element tag\n        /// </summary>\n        private static readonly Dictionary<HtmlTableSectionType, string> _SectionTags\n            = new()\n            {\n                { HtmlTableSectionType.Body, \"tbody\" },\n                { HtmlTableSectionType.Head, \"thead\" },\n                { HtmlTableSectionType.Foot, \"tfoot\" }\n            };\n\n        /// <summary>\n        /// constructor\n        /// </summary>\n        public HtmlTableSectionElement(HtmlTableSectionType type)\n            : base(_SectionTags[type])\n        {\n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Elements/HtmlTextAreaElement.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 5/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System;\nusing System.ComponentModel;\n\nnamespace Integrative.Lara\n{\n    /// <summary>\n    /// TextArea element\n    /// </summary>\n    [Obsolete(\"Use HtmlTextArea instead\")]\n    [EditorBrowsable(EditorBrowsableState.Never)]\n    public class TextArea : HtmlTextAreaElement\n    {\n    }\n\n    /// <summary>\n    /// The 'textarea' HTML5 element.\n    /// </summary>\n    /// <seealso cref=\"Element\" />\n    public class HtmlTextAreaElement : Element\n    {\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"HtmlTextAreaElement\"/> class.\n        /// </summary>\n        public HtmlTextAreaElement() : base(\"textarea\")\n        {\n        }\n\n        internal override void NotifyValue(ElementEventValue entry)\n        {\n            base.NotifyValue(entry);\n            NotifyValue(entry.Value);\n        }\n\n        /// <summary>\n        /// Gets or sets the 'autofocus' HTML5 attribute.\n        /// </summary>\n        public bool Autofocus\n        {\n            get => HasAttributeLower(\"autofocus\");\n            set => SetFlagAttributeLower(\"autofocus\", value);\n        }\n\n        /// <summary>\n        /// Gets or sets the 'cols' HTML5 attribute.\n        /// </summary>\n        public int? Cols\n        {\n            get => GetIntAttribute(\"cols\");\n            set => SetIntAttribute(\"cols\", value);\n        }\n\n        /// <summary>\n        /// Gets or sets the 'dirname' HTML5 attribute.\n        /// </summary>\n        public string? Dirname\n        {\n            get => GetAttributeLower(\"dirname\");\n            set => SetAttributeLower(\"dirname\", value);\n        }\n\n        /// <summary>\n        /// Gets or sets the 'disabled' HTML5 attribute.\n        /// </summary>\n        public bool Disabled\n        {\n            get => HasAttributeLower(\"disabled\");\n            set => SetFlagAttributeLower(\"disabled\", value);\n        }\n\n        /// <summary>\n        /// Gets or sets the 'maxlength' HTML5 attribute.\n        /// </summary>\n        public int? MaxLength\n        {\n            get => GetIntAttribute(\"maxlength\");\n            set => SetIntAttribute(\"maxlength\", value);\n        }\n\n        /// <summary>\n        /// Gets or sets the 'name' HTML5 attribute.\n        /// </summary>\n        public string? Name\n        {\n            get => GetAttributeLower(\"name\");\n            set => SetAttributeLower(\"name\", value);\n        }\n\n        /// <summary>\n        /// Gets or sets the 'placeholder' HTML5 attribute.\n        /// </summary>\n        public string? Placeholder\n        {\n            get => GetAttributeLower(\"placeholder\");\n            set => SetAttributeLower(\"placeholder\", value);\n        }\n\n        /// <summary>\n        /// Gets or sets the 'readonly' HTML5 attribute.\n        /// </summary>\n        public bool Readonly\n        {\n            get => HasAttributeLower(\"readonly\");\n            set => SetFlagAttributeLower(\"readonly\", value);\n        }\n\n        /// <summary>\n        /// Gets or sets the 'readonly' HTML5 attribute.\n        /// </summary>\n        public bool Required\n        {\n            get => HasAttributeLower(\"required\");\n            set => SetFlagAttributeLower(\"required\", value);\n        }\n\n        /// <summary>\n        /// Gets or sets the 'rows' HTML5 attribute.\n        /// </summary>\n        public int? Rows\n        {\n            get => GetIntAttribute(\"rows\");\n            set => SetIntAttribute(\"rows\", value);\n        }\n\n        /// <summary>\n        /// Gets or sets the 'value' property.\n        /// </summary>\n        public string? Value\n        {\n            get => GetAttributeLower(\"value\");\n            set => SetAttributeLower(\"value\", value);\n        }\n\n        /// <summary>\n        /// Gets or sets the 'wrap' HTML5 attribute.\n        /// </summary>\n        public string? Wrap\n        {\n            get => GetAttributeLower(\"wrap\");\n            set => SetAttributeLower(\"wrap\", value);\n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Elements/HtmlTitleElement.cs",
    "content": "﻿/*\nCopyright (c) 2021 Integrative Software LLC\nCreated: 2/2021\nAuthor: Pablo Carbonell\n*/\n\nnamespace Integrative.Lara\n{\n    /// <summary>\n    /// HTML5 'title' element\n    /// </summary>\n    public class HtmlTitleElement : Element\n    {\n        /// <summary>\n        /// Constructor\n        /// </summary>\n        public HtmlTitleElement() : base(\"title\")\n        {\n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/GlobalSuppressions.cs",
    "content": "﻿// This file is used by Code Analysis to maintain SuppressMessage\n// attributes that are applied to this project.\n// Project-level suppressions either have no target or are given\n// a specific target and scoped to a namespace, type, member, etc.\n\n[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage(\"Reliability\", \"CA2007:Consider calling ConfigureAwait on the awaited task\",\n    Justification = \"No sync context in ASP.NET Core\",\n    Scope = \"namespaceanddescendants\",\n    Target = \"Integrative.Lara\")]\n"
  },
  {
    "path": "src/LaraUI/LaraUI.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\n \n  <PropertyGroup>\n    <TargetFramework>netstandard2.1</TargetFramework>\n    <RootNamespace>Integrative.Lara</RootNamespace>\n    <AssemblyName>Integrative.Lara</AssemblyName>\n    <TypeScriptToolsVersion>Latest</TypeScriptToolsVersion>\n    <RunPostBuildEvent>Always</RunPostBuildEvent>\n    <Version>0.10.5</Version>\n    <Authors>Integrative Software LLC</Authors>\n    <Company>Integrative Software LLC</Company>\n    <Product>Lara Web Engine</Product>\n    <Description>Lara Web Engine is a lightweight C# framework for web user interface development.</Description>\n    <Copyright>Copyright (c) Integrative Software LLC</Copyright>\n    <LangVersion>latest</LangVersion>\n    <Nullable>enable</Nullable>\n  </PropertyGroup>\n\n  <PropertyGroup>\n    <PackageLicenseFile>LICENSE</PackageLicenseFile>\n    <PackageProjectUrl>https://github.com/integrativesoft/lara</PackageProjectUrl>\n    <RepositoryUrl>https://github.com/integrativesoft/lara</RepositoryUrl>\n    <PackageTags>lara, web, html, html5, desktop, gui, cross, framework, mac, osx, platform, ui, blazor, razor</PackageTags>\n    <AssemblyVersion>0.10.5</AssemblyVersion>\n    <FileVersion>0.10.5</FileVersion>\n    <PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance>\n    <GeneratePackageOnBuild>false</GeneratePackageOnBuild>\n    <PublishRepositoryUrl>true</PublishRepositoryUrl>\n    <IncludeSymbols>true</IncludeSymbols>\n    <SymbolPackageFormat>snupkg</SymbolPackageFormat>    \n  </PropertyGroup>\n\n  <ItemGroup>\n    <PackageReference Include=\"Microsoft.SourceLink.GitHub\" Version=\"1.1.1\" PrivateAssets=\"All\" />\n  </ItemGroup>  \n\n  <ItemGroup>\n    <None Remove=\"Assets\\Error.svg\" />\n    <None Include=\"..\\..\\Assets\\Integrative.png\">\n      <Pack>True</Pack>\n      <PackagePath></PackagePath>\n    </None>\n  </ItemGroup>\n  \n  <ItemGroup>\n    <None Include=\"..\\..\\LICENSE\">\n      <Pack>True</Pack>\n      <PackagePath></PackagePath>\n    </None>\n    <EmbeddedResource Include=\"..\\LaraClient\\dist\\lara-client.js\" Link=\"lara-client.js\" />\n    <EmbeddedResource Include=\"Assets\\Error.svg\" />\n  </ItemGroup>\n\n  <ItemGroup>\n    <PackageReference Include=\"Microsoft.AspNetCore\" Version=\"2.2.0\" />\n    <PackageReference Include=\"Microsoft.AspNetCore.WebSockets\" Version=\"2.2.1\" />\n  </ItemGroup>\n\n  <ItemGroup>\n    <Compile Update=\"Resources.Designer.cs\">\n      <DesignTime>True</DesignTime>\n      <AutoGen>True</AutoGen>\n      <DependentUpon>Resources.resx</DependentUpon>\n    </Compile>\n  </ItemGroup>\n\n  <ItemGroup>\n    <EmbeddedResource Update=\"Resources.resx\">\n      <Generator>ResXFileCodeGenerator</Generator>\n      <LastGenOutput>Resources.Designer.cs</LastGenOutput>\n    </EmbeddedResource>\n  </ItemGroup>\n\n  <PropertyGroup>\n    <DocumentationFile>C:\\Users\\Pablo\\OneDrive\\2019\\LaraUI\\src\\LaraUI\\Integrative.Lara.xml</DocumentationFile>\n    <PackageReleaseNotes>Version 0.10.4:\n- Bumped packages</PackageReleaseNotes>\n    <PackageIcon>Integrative.png</PackageIcon>\n  </PropertyGroup>\n\n</Project>\n"
  },
  {
    "path": "src/LaraUI/LaraUI.csproj.DotSettings",
    "content": "﻿<wpf:ResourceDictionary xml:space=\"preserve\" xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" xmlns:s=\"clr-namespace:System;assembly=mscorlib\" xmlns:ss=\"urn:shemas-jetbrains-com:settings-storage-xaml\" xmlns:wpf=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\">\n\t<s:Boolean x:Key=\"/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=autocomplete/@EntryIndexedValue\">True</s:Boolean>\n\t<s:Boolean x:Key=\"/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=components/@EntryIndexedValue\">True</s:Boolean>\n\t<s:Boolean x:Key=\"/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=delta/@EntryIndexedValue\">True</s:Boolean>\n\t<s:Boolean x:Key=\"/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=dom/@EntryIndexedValue\">True</s:Boolean>\n\t<s:Boolean x:Key=\"/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=elements/@EntryIndexedValue\">True</s:Boolean>\n\t<s:Boolean x:Key=\"/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=main/@EntryIndexedValue\">True</s:Boolean>\n\t<s:Boolean x:Key=\"/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=middleware/@EntryIndexedValue\">True</s:Boolean>\n\t<s:Boolean x:Key=\"/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=reactive/@EntryIndexedValue\">True</s:Boolean>\n\t<s:Boolean x:Key=\"/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=tools/@EntryIndexedValue\">True</s:Boolean></wpf:ResourceDictionary>"
  },
  {
    "path": "src/LaraUI/Main/Application.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 10/2019\nAuthor: Pablo Carbonell\n*/\n\nusing Microsoft.AspNetCore.Hosting;\nusing System;\nusing System.Net;\nusing System.Threading;\nusing System.Threading.Tasks;\n\nnamespace Integrative.Lara\n{\n    /// <summary>\n    /// Represents a hosted Lara application\n    /// </summary>\n    public sealed class Application : IDisposable\n    {\n        private readonly Published _published;\n\n        private IModeController? _modeController;\n\n        /// <summary>\n        /// Web host instance created after calling the Start() method\n        /// </summary>\n        // ReSharper disable once MemberCanBePrivate.Global\n        public IWebHost? Host { get; private set; }\n\n        /// <summary>\n        /// Defines default error pages\n        /// </summary>\n        public ErrorPages ErrorPages { get; }\n\n        /// <summary>\n        /// Constructor\n        /// </summary>\n        public Application()\n        {\n            _published = new Published();\n            ErrorPages = new ErrorPages(_published);\n            ErrorPages.PublishErrorImage();\n            PublishService(new WebServiceContent\n            {\n                Address = AutocompleteService.Address,\n                ContentType = \"application/json\",\n                Method = \"POST\",\n                Factory = () => new AutocompleteService()\n            });\n        }\n\n        internal Published GetPublished() => _published;\n\n        /// <summary>\n        /// Removes all published addresses\n        /// </summary>\n        public void ClearAllPublished()\n        {\n            _published.ClearAll();\n        }\n\n        /// <summary>\n        /// Disposable implementation\n        /// </summary>\n        public void Dispose()\n        {\n            ClearAllPublished();\n            _published.Dispose();\n            Host?.Dispose();\n        }\n\n        #region Publishing\n\n        /// <summary>\n        /// Publishes a page with a component\n        /// </summary>\n        /// <param name=\"address\">The URL address of the page</param>\n        /// <param name=\"nodeFactory\">Handler that creates instances of the component</param>\n        public void PublishPage(string address, Func<Node> nodeFactory)\n            => _published.Publish(address, new PagePublished(() => new SingleElementPage(nodeFactory)));\n\n        /// <summary>\n        /// Publishes a page.\n        /// </summary>\n        /// <param name=\"address\">The URL address of the page</param>\n        /// <param name=\"pageFactory\">Handler that creates instances of the page</param>\n        public void PublishPage(string address, Func<IPage> pageFactory)\n            => _published.Publish(address, new PagePublished(pageFactory));\n\n        /// <summary>\n        /// Publishes static content.\n        /// </summary>\n        /// <param name=\"address\">The URL address of the content.</param>\n        /// <param name=\"content\">The static content to be published.</param>\n        public void PublishFile(string address, StaticContent content)\n            => _published.Publish(address, content);\n\n        /// <summary>\n        /// Publishes a web service\n        /// </summary>\n        /// <param name=\"content\">Web service settings</param>\n        public void PublishService(WebServiceContent content)\n        {\n            content = content ?? throw new ArgumentNullException(nameof(content));\n            _published.Publish(content);\n        }\n\n        /// <summary>\n        /// Publishes a web service\n        /// </summary>\n        /// <param name=\"content\">Binary web service settings</param>\n        public void PublishService(BinaryServiceContent content)\n        {\n            content = content ?? throw new ArgumentNullException(nameof(content));\n            _published.Publish(content);\n        }\n\n        /// <summary>\n        /// Unpublishes an address and its associated content.\n        /// </summary>\n        /// <param name=\"path\">The path.</param>\n        public void UnPublish(string path)\n        {\n            _published.UnPublish(path);\n        }\n\n        /// <summary>\n        /// Publishes all classes marked with the attributes [LaraPage] and [LaraWebService]\n        /// </summary>\n        public void PublishAssemblies()\n            => AssembliesReader.LoadAssemblies(this);\n\n        /// <summary>\n        /// Unpublished a web service\n        /// </summary>\n        /// <param name=\"address\">The URL address of the web service</param>\n        /// <param name=\"method\">The HTTP method of the web service</param>\n        public void UnPublish(string address, string method)\n        {\n            address = address ?? throw new ArgumentNullException(nameof(address));\n            method = method ?? throw new ArgumentNullException(nameof(method));\n            _published.UnPublish(address, method);\n        }\n\n        internal bool TryGetNode(string path, out IPublishedItem item)\n            => _published.TryGetNode(path, out item);\n\n        internal bool TryGetConnection(Guid guid, out Connection connection)\n            => _published.Connections.TryGetConnection(guid, out connection);\n\n        internal Connection CreateConnection(IPAddress remoteIp)\n            => GetController().CreateConnection(remoteIp);\n\n        internal Task ClearEmptyConnection(Connection connection)\n            => _published.Connections.ClearEmptyConnection(connection);\n\n        private IModeController GetController()\n        {\n            return _modeController ?? throw new MissingMemberException(nameof(Application), nameof(_modeController));\n        }\n\n        #endregion\n\n        #region Web Components\n\n        /// <summary>\n        /// Registers a specific web component\n        /// </summary>\n        /// <param name=\"options\">Web component publish options</param>\n        public void PublishComponent(WebComponentOptions options)\n        {\n            options = options ?? throw new ArgumentNullException(nameof(options));\n            _published.Publish(options);\n        }\n\n        /// <summary>\n        /// Unregisters a specific web component\n        /// </summary>\n        /// <param name=\"tagName\">Tag name to unpublish</param>\n        public void UnPublishWebComponent(string tagName)\n            => _published.UnPublishWebComponent(tagName);\n\n        internal bool TryGetComponent(string tagName, out Type type)\n            => _published.TryGetComponent(tagName, out type);\n\n        #endregion\n\n        #region Server\n\n        /// <summary>\n        /// Starts the web server\n        /// </summary>\n        /// <returns>Task</returns>\n        // ReSharper disable once UnusedMember.Global\n        public Task Start()\n            => Start(new StartServerOptions());\n\n        /// <summary>\n        /// Starts the web server. Use with 'await'.\n        /// </summary>\n        /// <param name=\"options\">The server options.</param>\n        /// <returns>Task</returns>\n        public async Task Start(StartServerOptions options)\n        {\n            options = options ?? throw new ArgumentNullException(nameof(options));\n            Host?.Dispose();\n            CreateModeController(options.Mode);\n            Host = await GetController().Start(this, options);\n        }\n\n        internal void CreateModeController(ApplicationMode mode)\n        {\n            if (_modeController == null || _modeController.Mode != mode)\n            {\n                _modeController = ModeControllerFactory.Create(this, mode);\n            }\n        }\n\n        /// <summary>\n        /// Stops the web server gracefully\n        /// </summary>\n        /// <param name=\"token\">Token to indicate when the stop should not be graceful anymore</param>\n        /// <returns>Task</returns>\n        public Task Stop(CancellationToken token = default) => GetHost().StopAsync(token);\n\n        /// <summary>\n        /// Returns a task that is completed when the server stops\n        /// </summary>\n        /// <param name=\"token\">Token to trigger shutdown</param>\n        /// <returns>Task</returns>\n        // ReSharper disable once UnusedMember.Global\n        public Task WaitForShutdown(CancellationToken token = default) => Host.WaitForShutdownAsync(token);\n\n        internal IWebHost GetHost()\n        {\n            return Host ?? throw new MissingMemberException(nameof(Application), nameof(Host));\n        }\n\n        #endregion\n\n        #region Application behavior modes\n\n        internal double KeepAliveInterval => GetController().KeepAliveInterval;\n\n        internal bool AllowLocalhostOnly => GetController().LocalhostOnly;\n\n        internal int DiscardDelay => GetController().DiscardDelay;\n\n        #endregion\n\n        #region Testing methods\n\n        internal void SetHost(IWebHost host)\n        {\n            Host = host;\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Main/BaseContext.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 10/2019\nAuthor: Pablo Carbonell\n*/\n\nusing Microsoft.AspNetCore.Http;\n\nnamespace Integrative.Lara\n{\n    internal abstract class BaseContext : ILaraContext\n    {\n        public HttpContext Http { get; }\n        public Application Application { get; }\n\n        internal BaseContext(Application app, HttpContext http)\n        {\n            LaraUI.InternalContext.Value = this;\n            Application = app;\n            Http = http;\n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Main/BinaryServiceContent.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 11/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System;\n\nnamespace Integrative.Lara\n{\n    /// <summary>\n    /// Binary web service content definitions\n    /// </summary>\n    public sealed class BinaryServiceContent\n    {\n        /// <summary>\n        /// The URL of the web service (e.g. '/MyService')\n        /// </summary>\n        public string Address { get; set; } = string.Empty;\n\n        /// <summary>\n        /// The HTTP method (e.g. 'POST')\n        /// </summary>\n        public string Method { get; set; } = \"POST\";\n\n        /// <summary>\n        /// The web service's reponse content-type (e.g. 'image/gif')\n        /// </summary>\n        public string ContentType { get; set; } = string.Empty;\n\n        /// <summary>\n        /// The method for creating instances of the web service class\n        /// </summary>\n        public Func<IBinaryService>? Factory { get; set; }\n\n        internal Func<IBinaryService> GetFactory()\n        {\n            return Factory ?? throw new MissingMemberException(nameof(BinaryServiceContent), nameof(Factory));\n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Main/BinaryServicePublished.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 11/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System;\nusing System.Threading.Tasks;\nusing Microsoft.AspNetCore.Http;\n\nnamespace Integrative.Lara\n{\n    internal sealed class BinaryServicePublished : IPublishedItem\n    {\n        public Func<IBinaryService> Factory { get; }\n        private string ContentType { get; }\n\n        public BinaryServicePublished(BinaryServiceContent content)\n        {\n            Factory = content.GetFactory();\n            ContentType = content.ContentType;\n        }\n\n        public async Task Run(Application app, HttpContext http, LaraOptions options)\n        {\n            var context = new WebServiceContext(app, http)\n            {\n                RequestBody = await MiddlewareCommon.ReadBody(http).ConfigureAwait(false)\n            };\n            var handler = Factory();\n            var data = Array.Empty<byte>();\n            if (await MiddlewareCommon.RunHandler(http, async () =>\n            {\n                data = await handler.Execute();\n            }).ConfigureAwait(false))\n            {\n                await SendReply(context, data);\n            }\n        }\n\n        private async Task SendReply(WebServiceContext context, byte[] data)\n        {\n            WebServicePublished.SendHeader(context, ContentType);\n            await MiddlewareCommon.WriteBuffer(context.Http, data);\n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Main/Connection.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 5/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System;\nusing System.Collections.Generic;\nusing System.Net;\nusing System.Threading.Tasks;\n\nnamespace Integrative.Lara\n{\n    internal sealed class Connection\n    {\n        public Guid Id { get; }\n        public IPAddress RemoteIp { get; }\n        private readonly Dictionary<Guid, Document> _documents;\n\n        public SessionStorage Storage { get; }\n        public Session Session { get; }\n\n        public AsyncEvent Closing { get; } = new AsyncEvent();\n\n        public Connection(Guid id, IPAddress remoteId)\n        {\n            Id = id;\n            RemoteIp = remoteId;\n            _documents = new Dictionary<Guid, Document>();\n            Storage = new SessionStorage();\n            Session = new Session(this);\n        }\n\n        public bool TryGetDocument(Guid virtualId, out Document document)\n        {\n            if (!_documents.TryGetValue(virtualId, out document)) return false;\n            document.UpdateTimestamp();\n            return true;\n\n        }\n\n        public Document CreateDocument(IPage page, double keepAliveInterval)\n        {\n            var virtualId = Connections.CreateCryptographicallySecureGuid();\n            var document = new Document(page, virtualId, keepAliveInterval);\n            _documents.Add(virtualId, document);\n            return document;\n        }\n\n        public async Task Discard(Guid documentId)\n        {\n            if (_documents.TryGetValue(documentId, out var document))\n            {\n                await document.NotifyUnload();\n                // ReSharper disable once SuspiciousTypeConversion.Global\n                if (document.Page is IDisposable disposable)\n                {\n                    disposable.Dispose();\n                }\n                _documents.Remove(documentId);\n            }\n        }\n\n        public IEnumerable<KeyValuePair<Guid, Document>> GetDocuments() => _documents;\n\n        public bool IsEmpty => _documents.Count == 0;\n\n        private bool _closing;\n\n        public async Task Close()\n        {\n            if (_closing) return;\n            _closing = true;\n            await Closing.InvokeAsync(this, new EventArgs());\n            Session.Close();\n            _closing = false;\n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Main/Connections.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 5/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System;\nusing System.Collections.Concurrent;\nusing System.Collections.Generic;\nusing System.Net;\nusing System.Security.Cryptography;\nusing System.Threading.Tasks;\n\nnamespace Integrative.Lara\n{\n    internal sealed class Connections : IDisposable\n    {\n        private readonly ConcurrentDictionary<Guid, Connection> _connections;\n        private readonly StaleConnectionsCollector _collector;\n\n        public Connections()\n            : this(StaleConnectionsCollector.DefaultTimerInterval, StaleConnectionsCollector.DefaultExpireInterval)\n        {\n        }\n\n        public Connections(double cleanupInterval, double expireInterval)\n        {\n            _connections = new ConcurrentDictionary<Guid, Connection>();\n            _collector = new StaleConnectionsCollector(this, cleanupInterval, expireInterval);\n        }\n\n        public double StaleCollectionInterval\n        {\n            get => _collector.TimerInterval;\n            set => _collector.TimerInterval = value;\n        }\n\n        public double StaleExpirationInterval\n        {\n            get => _collector.ExpirationInterval;\n            set => _collector.ExpirationInterval = value;\n        }\n\n        public Connection CreateConnection(IPAddress remoteIp)\n        {\n            var id = CreateCryptographicallySecureGuid();\n            var connection = new Connection(id, remoteIp);\n            _connections.TryAdd(id, connection);\n            return connection;\n        }\n\n        public bool TryGetConnection(Guid id, out Connection connection)\n        {\n            return _connections.TryGetValue(id, out connection);\n        }\n\n        public async Task Discard(Guid key)\n        {\n            if (_connections.TryGetValue(key, out var connection))\n            {\n                await connection.Close();\n                _connections.TryRemove(key, out _);\n            }\n        }\n\n        public void Clear()\n        {\n            _connections.Clear();\n        }\n\n        public static Guid CreateCryptographicallySecureGuid()\n        {\n            using var provider = new RNGCryptoServiceProvider();\n            var bytes = new byte[16];\n            provider.GetBytes(bytes);\n            return new Guid(bytes);\n        }\n\n        public async Task ClearEmptyConnection(Connection connection)\n        {\n            if (connection.IsEmpty)\n            {\n                await Discard(connection.Id);\n            }\n        }\n\n        public IEnumerable<KeyValuePair<Guid, Connection>> GetConnections() => _connections;\n\n        private bool _disposed;\n\n        public void Dispose()\n        {\n            if (_disposed) return;\n            _disposed = true;\n            _connections.Clear();\n            _collector.Dispose();\n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Main/GlobalConstants.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 5/2019\nAuthor: Pablo Carbonell\n*/\n\nnamespace Integrative.Lara\n{\n    internal static class GlobalConstants\n    {\n        public const string CookieSessionId = \"_Lara_SessionId\";\n        public const string GuidFormat = \"N\";\n        public const string WindowUnload = \"_window_unload\";\n        public const string ServerSideEvent = \"_server_event\";\n        public const string MessageKey = \"_message\";\n        public const string FilePrefix = \"file/\";\n\n#if DEBUG\n        public const string LibraryAddress = \"/LaraUI-v{0}-debug.js\";\n#else\n        public const string LibraryAddress = \"/LaraUI-v{0}.js\";\n#endif\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Main/IBinaryService.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 11/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System.Threading.Tasks;\n\nnamespace Integrative.Lara\n{\n    /// <summary>\n    /// Binary Service handler class\n    /// </summary>\n    public interface IBinaryService\n    {\n        /// <summary>\n        /// Executes the web service\n        /// </summary>\n        /// <returns>Response's body</returns>\n        Task<byte[]> Execute();\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Main/INavigation.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 6/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System.Threading.Tasks;\n\nnamespace Integrative.Lara\n{\n    /// <summary>\n    /// Methods related to document navigation\n    /// </summary>\n    public interface INavigation\n    {\n        /// <summary>\n        /// Replaces the specified location.\n        /// </summary>\n        /// <param name=\"location\">The new URL location.</param>\n        void Replace(string location);\n\n        /// <summary>\n        /// Flushes the partial changes on the document to the client. Useful to report progress. Use with 'await'.\n        /// </summary>\n        /// <returns>Task</returns>\n        Task FlushPartialChanges();\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Main/IPage.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 5/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System.Threading.Tasks;\n\nnamespace Integrative.Lara\n{\n    /// <summary>\n    /// Interface for web pages\n    /// </summary>\n    public interface IPage\n    {\n        /// <summary>\n        /// Called when replying to the initial HTTP GET request.\n        /// </summary>\n        /// <returns>Task</returns>\n        Task OnGet();\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Main/IPageContext.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 5/2019\nAuthor: Pablo Carbonell\n*/\n\nusing Microsoft.AspNetCore.Http;\nusing System;\nusing System.ComponentModel;\nusing System.Threading.Tasks;\n\nnamespace Integrative.Lara\n{\n    /// <summary>\n    /// Shared interface for both IPageContext and IWebServiceContext\n    /// </summary>\n    public interface ILaraContext\n    {\n        /// <summary>\n        /// Gets the .NET Core HttpContext instance\n        /// </summary>\n        HttpContext Http { get; }\n\n        /// <summary>\n        /// Parent Application object\n        /// </summary>\n        Application Application { get; }\n    }\n\n    /// <summary>\n    /// The execution context for events.\n    /// </summary>\n    public interface IPageContext : ILaraContext\n    {\n        /// <summary>\n        /// Gets the current document.\n        /// </summary>\n        Document Document { get; }\n\n        /// <summary>\n        /// Bridge to execute JavaScript on the client\n        /// </summary>\n        // ReSharper disable once InconsistentNaming\n        IJsBridge JSBridge { get; }\n\n        /// <summary>\n        /// Methods related to navigation\n        /// </summary>\n        INavigation Navigation { get; }\n\n        /// <summary>\n        /// Session tools\n        /// </summary>\n        Session Session { get; }\n    }\n\n    /// <summary>\n    /// Bridge to execute JavaScript on the client\n    /// </summary>\n    // ReSharper disable once InconsistentNaming\n    public interface IJsBridge\n    {\n        /// <summary>\n        /// Submits the specified java script code to execute on the client. The code is executed after the current event finishes on the server and control returns to the client.\n        /// </summary>\n        /// <param name=\"javaScriptCode\">The JavaScript code to execute.</param>\n        /// <param name=\"payload\">Optional payload to send to the client</param>\n        void Submit(string javaScriptCode, string? payload = null);\n\n        /// <summary>\n        /// Register a custom event that can be called from JavaScript code\n        /// </summary>\n        /// <param name=\"messageId\">Message type identifier</param>\n        /// <param name=\"handler\">The handler for the event.</param>\n        [Obsolete(\"Use instead AddMessageListener() and RemoveMessageListener().\")]\n        [EditorBrowsable(EditorBrowsableState.Never)]\n        void OnMessage(string messageId, Func<Task> handler);\n\n        /// <summary>\n        /// Register a listener to a custom event that can be called from JavaScript code\n        /// </summary>\n        /// <param name=\"messageId\">Message type identifier</param>\n        /// <param name=\"handler\">Handler to execute</param>\n        void AddMessageListener(string messageId, Func<MessageEventArgs, Task> handler);\n\n        /// <summary>\n        /// Unregister a listener to a custom event called from JavaScript code\n        /// </summary>\n        /// <param name=\"messageId\">Message type identifier</param>\n        /// <param name=\"handler\">Handler to execute</param>\n        void RemoveMessageListener(string messageId, Func<MessageEventArgs, Task> handler);\n\n        /// <summary>\n        /// Gets extra event data that can be passed by the client on custom events.\n        /// </summary>\n        /// <value>\n        /// The event data.\n        /// </value>\n        string? EventData { get; }\n\n        /// <summary>\n        /// Makes the client start listening for ServerEventFlush() notifications.\n        /// </summary>\n        void ServerEventsOn();\n\n        /// <summary>\n        /// Makes the client stop listening for ServerEventFlush() notifications.\n        /// </summary>\n        /// <returns>Task</returns>\n        Task ServerEventsOff();\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Main/IPublishedItem.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 5/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System.Threading.Tasks;\nusing Microsoft.AspNetCore.Http;\n\nnamespace Integrative.Lara\n{\n    internal interface IPublishedItem\n    {\n        Task Run(Application app, HttpContext http, LaraOptions options);\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Main/IWebService.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 6/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System.Threading.Tasks;\n\nnamespace Integrative.Lara\n{\n    /// <summary>\n    /// Web Service handler class\n    /// </summary>\n    public interface IWebService\n    {\n        /// <summary>\n        /// Executes the web service\n        /// </summary>\n        /// <returns>Response's body</returns>\n        Task<string> Execute();\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Main/IWebServiceContext.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 6/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System.Diagnostics.CodeAnalysis;\nusing System.Net;\n\nnamespace Integrative.Lara\n{\n    /// <summary>\n    /// Context for web service requests\n    /// </summary>\n    public interface IWebServiceContext : ILaraContext\n    {\n        /// <summary>\n        /// Request's body sent by the client\n        /// </summary>\n        string RequestBody { get; }\n\n        /// <summary>\n        /// Status code to return\n        /// </summary>\n        HttpStatusCode StatusCode { get; set; }\n\n        /// <summary>\n        /// Gets a Session object when available\n        /// </summary>\n        /// <param name=\"session\">Session object</param>\n        /// <returns>true when found</returns>\n        bool TryGetSession([NotNullWhen(true)] out Session? session);\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Main/JSBridge.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 5/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System;\nusing System.ComponentModel;\nusing System.Threading.Tasks;\n\nnamespace Integrative.Lara\n{\n    internal sealed class JsBridge : IJsBridge\n    {\n        private readonly PageContext _parent;\n\n        public string? EventData { get; internal set; } = string.Empty;\n\n        public JsBridge(PageContext parent)\n        {\n            _parent = parent;\n        }\n\n        public void Submit(string javaScriptCode, string? payload = null)\n        {\n            _parent.Document.Enqueue(new SubmitJsDelta\n            {\n                Code = javaScriptCode,\n                Payload = payload\n            });\n        }\n\n        [Obsolete(\"Use instead AddMessageListener() and RemoveMessageListener().\")]\n        [EditorBrowsable(EditorBrowsableState.Never)]\n        public void OnMessage(string key, Func<Task> handler)\n        {\n            _parent.Document.OnMessage(key, handler);\n        }\n\n        public void AddMessageListener(string messageId, Func<MessageEventArgs, Task> handler)\n        {\n            _parent.Document.AddMessageListener(messageId, handler);\n        }\n\n        public void RemoveMessageListener(string messageId, Func<MessageEventArgs, Task> handler)\n        {\n            _parent.Document.RemoveMessageListener(messageId, handler);\n        }\n\n        public void ServerEventsOn() => _parent.Document.ServerEventsOn();\n\n        public Task ServerEventsOff() => _parent.Document.ServerEventsOff();\n\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Main/LaraBinaryServiceAttribute.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 11/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System;\n\nnamespace Integrative.Lara\n{\n    /// <summary>\n    /// Marks a class as a web service that replies an array of bytes\n    /// </summary>\n    [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]\n    public class LaraBinaryServiceAttribute : Attribute\n    {\n        /// <summary>\n        /// Web Service's address (e.g. '/myWS')\n        /// </summary>\n        public string Address { get; set; } = string.Empty;\n\n        /// <summary>\n        /// Web Service's method (e.g. 'POST')\n        /// </summary>\n        public string Method { get; set; } = \"POST\";\n\n        /// <summary>\n        /// Web Service's response content-type HTTP header (e.g. 'image/gif')\n        /// </summary>\n        public string ContentType { get; set; } = string.Empty;\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Main/LaraPageAttribute.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 7/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System;\n\nnamespace Integrative.Lara\n{\n    /// <summary>\n    /// Declares a class as a web page that gets published with LaraUI.PublishAssemblies()\n    /// </summary>\n    [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]\n    public sealed class LaraPageAttribute : Attribute\n    {\n        /// <summary>\n        /// Page's address (e.g. '/myPage')\n        /// </summary>\n        public string Address { get; set; } = string.Empty;\n\n        /// <summary>\n        /// Default constructor\n        /// </summary>\n        public LaraPageAttribute()\n        {\n        }\n\n        /// <summary>\n        /// Constuctor with address\n        /// </summary>\n        /// <param name=\"address\">Page's address</param>\n        public LaraPageAttribute(string address)\n            : this()\n        {\n            Address = address;\n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Main/LaraUI.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 5/2019\nAuthor: Pablo Carbonell\n*/\n\nusing Microsoft.AspNetCore.Hosting;\nusing System;\nusing System.ComponentModel;\nusing System.Resources;\nusing System.Threading;\nusing System.Threading.Tasks;\n\n[assembly: NeutralResourcesLanguage(\"en-US\")]\nnamespace Integrative.Lara\n{\n    /// <summary>\n    /// The main Lara static class  \n    /// </summary>\n    // ReSharper disable once InconsistentNaming\n    public static class LaraUI\n    {\n        #region Default application (obsolete)\n\n        private const string PublishObsolete = \"Publishing on the default static application has been deprecated, instead create an Application object.\";\n\n        internal static Application DefaultApplication { get; } = new Application();\n\n        /// <summary>\n        /// Defines default error pages\n        /// </summary>\n        [Obsolete(PublishObsolete)]\n        [EditorBrowsable(EditorBrowsableState.Never)]\n        public static ErrorPages ErrorPages => DefaultApplication.ErrorPages;\n\n        /// <summary>\n        /// Removes all published elements\n        /// </summary>\n        [Obsolete(PublishObsolete)]\n        [EditorBrowsable(EditorBrowsableState.Never)]\n        public static void ClearAll() => DefaultApplication.ClearAllPublished();\n\n        /// <summary>\n        /// Publishes a page.\n        /// </summary>\n        /// <param name=\"address\">The URL address of the page.</param>\n        /// <param name=\"pageFactory\">Handler that creates instances of the page</param>\n        [Obsolete(PublishObsolete)]\n        [EditorBrowsable(EditorBrowsableState.Never)]\n        public static void Publish(string address, Func<IPage> pageFactory)\n            => DefaultApplication.PublishPage(address, pageFactory);\n\n        /// <summary>\n        /// Publishes static content.\n        /// </summary>\n        /// <param name=\"address\">The URL address of the content.</param>\n        /// <param name=\"content\">The static content to be published.</param>\n        [Obsolete(PublishObsolete)]\n        [EditorBrowsable(EditorBrowsableState.Never)]\n        public static void Publish(string address, StaticContent content)\n            => DefaultApplication.PublishFile(address, content);\n\n        /// <summary>\n        /// Publishes a web service\n        /// </summary>\n        /// <param name=\"content\">Web service settings</param>\n        [Obsolete(PublishObsolete)]\n        [EditorBrowsable(EditorBrowsableState.Never)]\n        public static void Publish(WebServiceContent content)\n            => DefaultApplication.PublishService(content);\n\n        /// <summary>\n        /// Unpublishes an address and its associated content.\n        /// </summary>\n        /// <param name=\"path\">The path.</param>\n        [Obsolete(PublishObsolete)]\n        [EditorBrowsable(EditorBrowsableState.Never)]\n        public static void UnPublish(string path)\n            => DefaultApplication.UnPublish(path);\n\n        /// <summary>\n        /// Publishes all classes marked with the attributes [LaraPage] and [LaraWebService]\n        /// </summary>\n        [Obsolete(PublishObsolete)]\n        [EditorBrowsable(EditorBrowsableState.Never)]\n        public static void PublishAssemblies()\n            => AssembliesReader.LoadAssemblies(DefaultApplication);\n\n        /// <summary>\n        /// Unpublished a web service\n        /// </summary>\n        /// <param name=\"address\">The URL address of the web service</param>\n        /// <param name=\"method\">The HTTP method of the web service</param>\n        [Obsolete(PublishObsolete)]\n        [EditorBrowsable(EditorBrowsableState.Never)]\n        public static void UnPublish(string address, string method)\n            => DefaultApplication.UnPublish(address, method);\n\n\n        /// <summary>\n        /// Registers a specific web component\n        /// </summary>\n        /// <param name=\"options\">Web component publush options</param>\n        [Obsolete(PublishObsolete)]\n        [EditorBrowsable(EditorBrowsableState.Never)]\n        public static void Publish(WebComponentOptions options)\n            => DefaultApplication.PublishComponent(options);\n\n        /// <summary>\n        /// Unregisters a specific web component\n        /// </summary>\n        /// <param name=\"tagName\">Tag name to unpublish</param>\n        [Obsolete(PublishObsolete)]\n        [EditorBrowsable(EditorBrowsableState.Never)]\n        public static void UnPublishWebComponent(string tagName)\n            => DefaultApplication.UnPublishWebComponent(tagName);\n\n        #endregion\n\n        #region Context variables\n\n        internal static AsyncLocal<ILaraContext> InternalContext { get; } = new AsyncLocal<ILaraContext>();\n\n        /// <summary>\n        /// Returns the context associated with the current task. See also 'Page' and 'Service'.\n        /// </summary>\n        public static ILaraContext Context\n            => InternalContext.Value ?? throw new InvalidOperationException(Resources.NoCurrentContext);\n\n        /// <summary>\n        /// Returns the Page context associated the current task, when executing Page events\n        /// </summary>\n        public static IPageContext Page\n            => InternalContext.Value as IPageContext ?? throw new InvalidOperationException(Resources.NoCurrentPage);\n\n        /// <summary>\n        /// Returns the WebService context associated with the current task, when executing web services\n        /// </summary>\n        public static IWebServiceContext Service\n            => InternalContext.Value as IWebServiceContext ?? throw new InvalidOperationException(Resources.NoCurrentService);\n\n        /// <summary>\n        /// Returns the current document (same as Page.Document)\n        /// </summary>\n        public static Document Document\n            => GetContextDocument(Page) ?? throw new InvalidOperationException(Resources.NoCurrentDocument);\n\n        internal static Document? GetContextDocument(IPageContext? context)\n        {\n            return context?.Document;\n        }\n\n        #endregion\n\n        #region Tools\n\n        /// <summary>\n        /// Starts the web server. Use with 'await'.\n        /// </summary>\n        /// <returns>Task</returns>\n        [Obsolete(PublishObsolete)]\n        [EditorBrowsable(EditorBrowsableState.Never)]\n        public static Task<IWebHost> StartServer()\n            => StartServer(new StartServerOptions());\n\n        /// <summary>\n        /// Starts the web server. Use with 'await'.\n        /// </summary>\n        /// <param name=\"options\">The server options.</param>\n        /// <returns>Task</returns>\n        // ReSharper disable once MemberCanBePrivate.Global\n        [Obsolete(PublishObsolete)]\n        [EditorBrowsable(EditorBrowsableState.Never)]\n        public static async Task<IWebHost> StartServer(StartServerOptions options)\n        {\n            await DefaultApplication.Start(options);\n            return DefaultApplication.GetHost();\n        }\n        \n        /// <summary>\n        /// Launches the user's default web browser on the specified address.\n        /// </summary>\n        /// <param name=\"address\">The address.</param>\n        public static void LaunchBrowser(string address)\n            => LaraTools.LaunchBrowser(address);\n\n        /// <summary>\n        /// Launches the user's default web browser on the first address of the host passed in parameters.\n        /// </summary>\n        /// <param name=\"host\">The host.</param>\n        public static void LaunchBrowser(IWebHost host)\n                    => LaraTools.LaunchBrowser(host);\n\n        /// <summary>\n        /// Gets the first URL associated with the given host.\n        /// </summary>\n        /// <param name=\"host\">The host.</param>\n        /// <returns>string with URL</returns>\n        // ReSharper disable once InconsistentNaming\n        public static string GetFirstURL(IWebHost host)\n        {\n            host = host ?? throw new ArgumentNullException(nameof(host));\n            return LaraTools.GetFirstUrl(host);\n        }\n\n        /// <summary>\n        /// JSON tools\n        /// </summary>\n        // ReSharper disable once InconsistentNaming\n        public static LaraJson JSON { get; } = new LaraJson();\n\n        /// <summary>\n        /// Flushes page modifications to the client on long-running events. Useful to report progress.\n        /// </summary>\n        public static Task FlushPartialChanges()\n        {\n            return Page.Navigation.FlushPartialChanges();\n        }\n\n        #endregion\n\n        #region Internal tools\n\n        /// <summary>\n        /// Shorthand for LaraUI.JSON.Parse(LaraUI.Service.RequestBody)\n        /// </summary>\n        /// <typeparam name=\"T\">Type for parsing</typeparam>\n        /// <returns>Instance of T</returns>\n        public static T ParseRequest<T>()\n        where T : class\n        {\n            return JSON.Parse<T>(Service.RequestBody);\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Main/LaraWebServiceAttribute.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 7/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System;\n\nnamespace Integrative.Lara\n{\n    /// <summary>\n    /// Declares a class as a web service that gets published with LaraUI.PublishAssemblies()\n    /// </summary>\n    [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]\n    public sealed class LaraWebServiceAttribute : Attribute\n    {\n        /// <summary>\n        /// Web Service's address (e.g. '/myWS')\n        /// </summary>\n        public string Address { get; set; } = string.Empty;\n\n        /// <summary>\n        /// Web Service's method (e.g. 'POST')\n        /// </summary>\n        public string Method { get; set; } = \"POST\";\n\n        /// <summary>\n        /// Web Service's response content-type HTTP header (e.g. 'application/json')\n        /// </summary>\n        public string ContentType { get; set; } = \"application/json\";\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Main/Navigation.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 6/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System;\nusing System.Threading.Tasks;\n\nnamespace Integrative.Lara\n{\n    internal sealed class Navigation : INavigation\n    {\n        private readonly PageContext _context;\n\n        public string? RedirectLocation { get; private set; }\n\n        public Navigation(PageContext context)\n        {\n            _context = context;\n        }\n\n        public void Replace(string location)\n        {\n            if (_context.Http.Request.Method == \"GET\")\n            {\n                ReplaceGet(location);\n            }\n            else\n            {\n                ReplacePost(location);\n            }\n        }\n\n        private void ReplaceGet(string location)\n        {\n            RedirectLocation = location;\n        }\n\n        private void ReplacePost(string location)\n        {\n            ReplaceDelta.Enqueue(_context.Document, location);\n        }\n\n        public async Task FlushPartialChanges()\n        {\n            if (_context.Socket == null)\n            {\n                throw new InvalidOperationException(Resources.FlushNotAvailable);\n            }\n            if (_context.Document.HasPendingChanges)\n            {\n                await PostEventHandler.FlushPartialChanges(_context.Socket, _context.Document);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Main/PageContext.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 5/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System;\nusing System.Net.WebSockets;\nusing Microsoft.AspNetCore.Http;\n\nnamespace Integrative.Lara\n{\n    internal sealed class PageContext : BaseContext, IPageContext\n    {\n        public Document? DocumentInternal { get; internal set; }\n\n        public Document Document\n            => DocumentInternal ?? throw new MissingMemberException(nameof(PageContext), nameof(Document));\n\n        private readonly JsBridge _bridge;\n        private readonly Navigation _navigation;\n        private readonly Connection _connection;\n\n        public PageContext(Application app, HttpContext http, Connection connection)\n            : base(app, http)\n        {\n            _navigation = new Navigation(this);\n            _bridge = new JsBridge(this);\n            _connection = connection;\n        }\n\n        public Session Session => _connection.Session;\n\n        internal WebSocket? Socket { get; set; }\n\n        public IJsBridge JSBridge => _bridge;\n        public INavigation Navigation => _navigation;\n\n        public string? RedirectLocation => _navigation.RedirectLocation;\n\n        internal void SetExtraData(string? data) => _bridge.EventData = data;\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Main/PagePublished.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 5/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System;\nusing System.Globalization;\nusing System.Net;\nusing System.Threading.Tasks;\nusing Microsoft.AspNetCore.Http;\n\nnamespace Integrative.Lara\n{\n    internal sealed class PagePublished : IPublishedItem\n    {\n        private readonly Func<IPage> _factory;\n\n        private HttpStatusCode StatusCode { get; } = HttpStatusCode.OK;\n\n        public PagePublished(Func<IPage> factory)\n        {\n            _factory = factory;\n        }\n\n        public PagePublished(Func<IPage> factory, HttpStatusCode status)\n            : this(factory)\n        {\n            StatusCode = status;\n        }\n\n        public async Task Run(Application app, HttpContext http, LaraOptions options)\n        {\n            var connection = GetConnection(app, http);\n            var execution = new PageContext(app, http, connection);\n            var page = CreateInstance();\n            var document = connection.CreateDocument(page, app.KeepAliveInterval);\n            execution.DocumentInternal = document;\n            if (await RunPage(app, http, page, options).ConfigureAwait(false))\n            {\n                await ProcessGetResult(http, document, execution, StatusCode);\n            }\n            if (document.CanDiscard)\n            {\n                await connection.Discard(document.VirtualId);\n            }\n        }\n\n        internal static async Task<bool> RunPage(Application app, HttpContext http, IPage page, LaraOptions options)\n        {\n            try\n            {\n                await page.OnGet();\n                return true;\n            }\n            catch (StatusCodeException status)\n            {\n                await ReplyStatusCodeError(app, http, status, options);\n                return false;\n            }\n        }\n\n        private static async Task ReplyStatusCodeError(Application app, HttpContext http, StatusCodeException status, LaraOptions options)\n        {\n            if (app.ErrorPages.TryGetPage(status.StatusCode, out var page))\n            {\n                await page.Run(app, http, options);\n            }\n            else\n            {\n                await MiddlewareCommon.SendStatusReply(http, status.StatusCode, status.Message);\n            }\n        }\n\n        internal IPage CreateInstance() => _factory();\n\n        internal static async Task ProcessGetResult(HttpContext http, Document document, PageContext execution, HttpStatusCode code)\n        {\n            if (!string.IsNullOrEmpty(execution.RedirectLocation))\n            {\n                http.Response.Redirect(execution.RedirectLocation);\n            }\n            else\n            {\n                document.OpenEventQueue();\n                var html = WriteDocument(execution.Document);\n                await ReplyDocument(http, html, code);\n            }\n        }\n\n        internal static Connection GetConnection(Application app, HttpContext http)\n        {\n            return MiddlewareCommon.TryFindConnection(app, http, out var connection) ? connection : CreateConnection(app, http);\n        }\n\n        private static Connection CreateConnection(Application app, HttpContext http)\n        {\n            var connection = app.CreateConnection(http.Connection.RemoteIpAddress);\n            http.Response.Cookies.Append(GlobalConstants.CookieSessionId,\n                connection.Id.ToString(GlobalConstants.GuidFormat, CultureInfo.InvariantCulture));\n            return connection;\n        }\n\n        private static string WriteDocument(Document document)\n        {\n            var writer = new DocumentWriter(document);\n            writer.Print();\n            return writer.ToString();\n        }\n\n        private static async Task ReplyDocument(HttpContext http, string html, HttpStatusCode code)\n        {\n            MiddlewareCommon.SetStatusCode(http, code);\n            MiddlewareCommon.AddHeaderTextHtml(http);\n            MiddlewareCommon.AddHeaderPreventCaching(http);\n            await MiddlewareCommon.WriteUtf8Buffer(http, html);\n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Main/Published.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 5/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System;\nusing System.Collections.Generic;\n\nnamespace Integrative.Lara\n{\n    internal sealed class Published : IDisposable\n    {\n        private readonly Dictionary<string, IPublishedItem> _published;\n        private readonly ComponentRegistry _components;\n        \n        public Connections Connections { get; }\n\n        public Published()\n        {\n            _published = new Dictionary<string, IPublishedItem>();\n            _components = new ComponentRegistry();\n            Connections = new Connections();\n        }\n\n        public void ClearAll()\n        {\n            _published.Clear();\n            _components.Clear();\n            Connections.Clear();\n        }\n\n        private bool _disposed;\n\n        public void Dispose()\n        {\n            if (_disposed) return;\n            _disposed = true;\n            _published.Clear();\n            Connections.Dispose();\n        }\n\n        public void Publish(string path, IPublishedItem item)\n        {\n            _published.Remove(path);\n            _published.Add(path, item);\n        }\n\n        public void Publish(WebServiceContent content)\n        {\n            var combined = CombineAddress(content.Address, content.Method);\n            _published.Remove(combined);\n            _published.Add(combined, new WebServicePublished(content));\n        }\n\n        public void Publish(BinaryServiceContent content)\n        {\n            var combined = CombineAddress(content.Address, content.Method);\n            _published.Remove(combined);\n            _published.Add(combined, new BinaryServicePublished(content));\n        }\n\n        private static string CombineAddress(string address, string method)\n        {\n            ValidateAddress(address);\n            ValidateMethod(method);\n            return CombinePathMethod(address, method.ToUpperInvariant());\n        }\n\n        public static string CombinePathMethod(string path, string method)\n        {\n            if (method == \"GET\")\n            {\n                return path;\n            }\n\n            return path + \">\" + method;\n        }\n\n        internal static void ValidateMethod(string? method)\n        {\n            if (string.IsNullOrEmpty(method))\n            {\n                throw new ArgumentException(Resources.SpecifyMethodService);\n            }\n        }\n\n        internal static void ValidateAddress(string? path)\n        {\n            if (string.IsNullOrEmpty(path))\n            {\n                throw new ArgumentException(Resources.SpecifyAddressService);\n            }\n        }\n\n        public void UnPublish(string path)\n        {\n            _published.Remove(path);\n        }\n\n        public void UnPublish(string path, string method)\n        {\n            var combined = CombinePathMethod(path, method.ToUpperInvariant());\n            _published.Remove(combined);\n        }\n\n        public bool TryGetNode(string path, out IPublishedItem item)\n        {\n            return _published.TryGetValue(path, out item);\n        }\n\n        public void Publish(WebComponentOptions options)\n        {\n            _components.Register(options.ComponentTagName, options.GetComponentType());\n        }\n\n        public void UnPublishWebComponent(string componentTagName)\n        {\n            _components.Unregister(componentTagName);\n        }\n\n        public bool TryGetComponent(string tagName, out Type type)\n        {\n            return _components.TryGetComponent(tagName, out type);\n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Main/Session.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 6/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System;\n\nnamespace Integrative.Lara\n{\n    /// <summary>\n    /// Session information\n    /// </summary>\n    public sealed class Session\n    {\n        private readonly Connection _parent;\n\n        /// <summary>\n        /// Occurs when the user closes all browser tabs\n        /// </summary>\n        public event EventHandler? Closing;\n\n        internal event EventHandler? CloseComplete;\n\n        internal Session(Connection parent)\n        {\n            _parent = parent;\n        }\n\n        internal void Close()\n        {\n            var args = new EventArgs();\n            try\n            {\n                Closing?.Invoke(this, args);\n            }\n            // ReSharper disable once EmptyGeneralCatchClause\n            catch\n            {\n            }\n            CloseComplete?.Invoke(this, args);\n        }\n\n        /// <summary>\n        /// Returns an ID that uniquely identifies the UI session\n        /// </summary>\n        public Guid SessionId => _parent.Id;\n\n        /// <summary>\n        /// Stores a key value pair\n        /// </summary>\n        /// <param name=\"key\">Identifier of the value to store</param>\n        /// <param name=\"value\">Value to store</param>\n        public void SaveValue(string key, string value)\n            => _parent.Storage.Save(key, value);\n\n        /// <summary>\n        /// Removes a stored value\n        /// </summary>\n        /// <param name=\"key\">Identifier of the value stored</param>\n        public void RemoveValue(string key)\n            => _parent.Storage.Remove(key);\n\n        /// <summary>\n        /// Retrieves a value stored\n        /// </summary>\n        /// <param name=\"key\">Identifier of the value stored</param>\n        /// <param name=\"value\">Value stored</param>\n        /// <returns>true if found, false otherwise</returns>\n        public bool TryGetValue(string key, out string value)\n            => _parent.Storage.TryGetValue(key, out value);\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Main/SessionStorage.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 6/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System.Collections.Generic;\n\nnamespace Integrative.Lara\n{\n    internal sealed class SessionStorage\n    {\n        private readonly Dictionary<string, string> _values;\n        private readonly object _mylock;\n\n        public SessionStorage()\n        {\n            _values = new Dictionary<string, string>();\n            _mylock = new object();\n        }\n\n        public void Save(string key, string value)\n        {\n            lock (_mylock)\n            {\n                _values.Remove(key);\n                _values.Add(key, value);\n            }\n        }\n\n        public void Remove(string key)\n        {\n            lock (_mylock)\n            {\n                _values.Remove(key);\n            }\n        }\n\n        public bool TryGetValue(string key, out string value)\n        {\n            lock (_mylock)\n            {\n                return _values.TryGetValue(key, out value);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Main/SingleElementPage.cs",
    "content": "﻿/*\nCopyright (c) 2020-2021 Integrative Software LLC\nCreated: 12/2020\nAuthor: Pablo Carbonell\n*/\n\nusing System;\nusing System.Threading.Tasks;\n\nnamespace Integrative.Lara\n{\n    internal class SingleElementPage : IPage\n    {\n        private Func<Node> ContentFactory { get; }\n\n        public SingleElementPage(Func<Node> contentFactory)\n        {\n            ContentFactory = contentFactory;\n        }\n\n        public Task OnGet()\n        {\n            var node = ContentFactory();\n            LaraUI.Document.Body.AppendChild(node);\n            return Task.CompletedTask;\n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Main/StaleConnectionsCollector.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 5/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System;\nusing System.Collections.Generic;\nusing System.Threading.Tasks;\nusing System.Timers;\n\nnamespace Integrative.Lara\n{\n    internal sealed class StaleConnectionsCollector : IDisposable\n    {\n        public const double DefaultTimerInterval = 5 * 60 * 1000;      // 5 minutes to trigger updates\n        public const double DefaultExpireInterval = 4 * 3600 * 1000;   // 4 hours to expire\n\n        private readonly Connections _connections;\n        private readonly Timer _timer;\n\n        public double ExpirationInterval { get; set; }\n\n        public double TimerInterval\n        {\n            get => _timer.Interval;\n            set => _timer.Interval = value;\n        }\n\n        public StaleConnectionsCollector(Connections connections,\n            double timerInterval = DefaultTimerInterval, double expireInternal = DefaultExpireInterval)\n        {\n            _connections = connections;\n            ExpirationInterval = expireInternal;\n            _timer = new Timer\n            {\n                Interval = timerInterval\n            };\n            _timer.Elapsed += TimerElapsedHandler;\n            _timer.Start();\n        }\n\n        private bool _disposed;\n\n        public void Dispose()\n        {\n            if (_disposed) return;\n            _disposed = true;\n            _timer.Stop();\n            _timer.Dispose();\n        }\n\n        private async void TimerElapsedHandler(object sender, ElapsedEventArgs e)\n        {\n            if (!_disposed && !_cleaning)\n            {\n                await CleanupExpiredHandler();\n            }\n        }\n\n        private bool _cleaning;\n\n        internal async Task CleanupExpiredHandler()\n        {\n            _cleaning = true;\n            _timer.Enabled = false;\n            await CleanupNonDisposed();\n            _timer.Enabled = true;\n            _cleaning = false;\n        }\n\n        private async Task CleanupNonDisposed()\n        {\n            var minRequired = DateTime.UtcNow.AddMilliseconds(-ExpirationInterval);\n            var list = new List<KeyValuePair<Guid, Connection>>();\n            foreach (var pair in _connections.GetConnections())\n            {\n                await CleanupExpired(pair.Value, minRequired);\n                if (pair.Value.IsEmpty)\n                {\n                    list.Add(pair);\n                }\n            }\n            foreach (var pair in list)\n            {\n                if (pair.Value.IsEmpty)\n                {\n                    await _connections.Discard(pair.Key);\n                }\n            }\n        }\n\n        internal static async Task CleanupExpired(Connection connection, DateTime minRequired)\n        {\n            var list = new List<KeyValuePair<Guid, Document>>();\n            foreach (var pair in connection.GetDocuments())\n            {\n                if (pair.Value.LastUtc < minRequired)\n                {\n                    list.Add(pair);\n                }\n            }\n            foreach (var pair in list)\n            {\n                if (pair.Value.LastUtc < minRequired)\n                {\n                    await connection.Discard(pair.Key);\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Main/StaticContent.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 5/2019\nAuthor: Pablo Carbonell\n*/\n\nusing Microsoft.AspNetCore.Http;\nusing System;\nusing System.Globalization;\nusing System.Net;\nusing System.Threading.Tasks;\n\nnamespace Integrative.Lara\n{\n    /// <summary>\n    /// Static content to publish on the web server\n    /// </summary>\n    public class StaticContent : IPublishedItem\n    {\n        private const float RequiredCompressionFactor = 0.9f;\n\n        private readonly byte[] _bytes;\n\n        /// <summary>\n        /// Returns the byte array that is sent to clients\n        /// </summary>\n        /// <returns>byte array</returns>\n        public byte[] GetBytes() => _bytes;\n\n        /// <summary>\n        /// Gets the 'content-type' HTTP header for the static content\n        /// </summary>\n        /// <value>\n        /// The 'content-type' value for the content.\n        /// </value>\n        public string ContentType { get; } = string.Empty;\n\n        /// <summary>\n        /// Gets the e-Tag value for the content.\n        /// </summary>\n        /// <value>\n        /// The e-Tag value.\n        /// </value>\n        public string ETag { get; }\n\n        /// <summary>\n        /// Gets a value indicating whether this <see cref=\"StaticContent\"/> is compressed.\n        /// </summary>\n        /// <value>\n        ///   <c>true</c> if compressed; otherwise, <c>false</c>.\n        /// </value>\n        public bool Compressed { get; }\n\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"StaticContent\"/> class.\n        /// </summary>\n        /// <param name=\"bytes\">The byte array.</param>\n        /// <param name=\"contentType\">The 'content-type' HTTP header value.</param>\n        /// <exception cref=\"NullReferenceException\">The parameter 'bytes' cannot be null.</exception>\n        public StaticContent(byte[] bytes, string contentType)\n            : this(bytes)\n        {\n            ContentType = contentType;\n        }\n\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"StaticContent\"/> class.\n        /// </summary>\n        /// <param name=\"bytes\">The byte array.</param>\n        public StaticContent(byte[] bytes)\n        {\n            bytes = bytes ?? throw new ArgumentNullException(nameof(bytes));\n            var compressed = LaraTools.Compress(bytes);\n            long required = (int)Math.Floor(bytes.LongLength * RequiredCompressionFactor);\n            if (compressed.Length > required)\n            {\n                _bytes = bytes;\n                Compressed = false;\n            }\n            else\n            {\n                _bytes = compressed;\n                Compressed = true;\n            }\n            ETag = ComputeETag(bytes);\n        }\n\n        /// <summary>\n        /// Calculates an ETag value based on the given array of bytes\n        /// </summary>\n        /// <param name=\"bytes\">Array of bytes</param>\n        /// <returns>Calculated ETag</returns>\n        public static string ComputeETag(byte[] bytes)\n        {\n            var hash = ComputeHash(bytes);\n            return \"\\\"\" + hash.ToString(CultureInfo.InvariantCulture) + \"\\\"\";\n        }\n\n        /// <summary>\n        /// Formats a hash value in the eTag format\n        /// </summary>\n        /// <param name=\"hash\">Hash value</param>\n        /// <returns>Formatted hash value in eTag format</returns>\n        public static string FormatETag(int hash)\n        {\n            return \"\\\"\" + hash.ToString(CultureInfo.InvariantCulture) + \"\\\"\";\n        }\n\n        /// <summary>\n        /// Computes a hash value for an array of bytes.\n        /// </summary>\n        /// <param name=\"data\">Array of bytes</param>\n        /// <returns>Calculated hash</returns>\n        // ReSharper disable once MemberCanBePrivate.Global\n        public static int ComputeHash(params byte[] data)\n        {\n            data = data ?? throw new ArgumentNullException(nameof(data));\n            unchecked\n            {\n                const int p = 16777619;\n                var hash = (int)2166136261;\n\n                // ReSharper disable once LoopCanBeConvertedToQuery\n                foreach (var c in data)\n                    hash = (hash ^ c) * p;\n\n                hash += hash << 13;\n                hash ^= hash >> 7;\n                hash += hash << 3;\n                hash ^= hash >> 17;\n                hash += hash << 5;\n                return hash;\n            }\n        }\n\n        /// <summary>\n        /// Public method used by the Lara framework\n        /// </summary>\n        /// <param name=\"app\">Lara application</param>\n        /// <param name=\"http\">Http context</param>\n        /// <param name=\"options\">Lara options</param>\n        /// <returns>Task</returns>\n        public async Task Run(Application app, HttpContext http, LaraOptions options)\n        {\n            http = http ?? throw new ArgumentNullException(nameof(http));\n            if (IsMatchETag(http.Request.Headers))\n            {\n                SendMatchStatus(http);\n            }\n            else\n            {\n                await SendContent(http);\n            }\n        }\n\n        private bool IsMatchETag(IHeaderDictionary headers)\n        {\n            if (!headers.TryGetValue(\"If-None-Match\", out var values)) return false;\n            var eTagClient = values[^1];\n            return ETag == eTagClient;\n\n        }\n\n        private static void SendMatchStatus(HttpContext http)\n        {\n            MiddlewareCommon.SetStatusCode(http, HttpStatusCode.NotModified);\n        }\n\n        private async Task SendContent(HttpContext http)\n        {\n            var headers = http.Response.Headers;\n            headers.Add(\"Content-Type\", ContentType);\n            headers.Add(\"Cache-Control\", \"no-cache\");\n            headers.Add(\"ETag\", ETag);\n            if (Compressed)\n            {\n                headers.Add(\"Content-Encoding\", \"deflate\");\n            }\n            await MiddlewareCommon.WriteBuffer(http, _bytes);\n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Main/TemplateBuilder.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 5/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System.Globalization;\n\nnamespace Integrative.Lara\n{\n    internal static class TemplateBuilder\n    {\n        private static readonly string _LibraryUrl;\n\n        static TemplateBuilder()\n        {\n            _LibraryUrl = ClientLibraryHandler.GetLibraryPath();\n        }\n\n        public static void Build(Document document, double keepAliveInterval)\n        {\n            var head = document.Head;\n\n            // lang\n            document.Lang = \"en\";\n\n            // UTF-8\n            var meta = Element.Create(\"meta\");\n            meta.SetAttribute(\"charset\", \"utf-8\");\n            head.AppendChild(meta);\n\n            // LaraClient.js\n            var script = new HtmlScriptElement\n            {\n                Src = _LibraryUrl,\n                Defer = true\n            };\n            head.AppendChild(script);\n\n            // initialization script\n            var tag = Element.Create(\"script\");\n            var id = document.VirtualId.ToString(GlobalConstants.GuidFormat, CultureInfo.InvariantCulture);\n            var interval = keepAliveInterval.ToString(CultureInfo.InvariantCulture);\n            var code = $\"document.addEventListener('DOMContentLoaded', function() {{ LaraUI.initialize('{id}', {interval}); }});\";\n            tag.AppendChild(new TextNode { Data = code });\n            head.AppendChild(tag);\n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Main/WebServiceContent.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 6/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System;\n\nnamespace Integrative.Lara\n{\n    /// <summary>\n    /// Web server content definitions\n    /// </summary>\n    public sealed class WebServiceContent\n    {\n        /// <summary>\n        /// The URL of the web service (e.g. '/MyService')\n        /// </summary>\n        public string Address { get; set; } = string.Empty;\n\n        /// <summary>\n        /// The HTTP method (e.g. 'POST')\n        /// </summary>\n        public string Method { get; set; } = \"POST\";\n\n        /// <summary>\n        /// The web service's reponse content-type (e.g. 'application/json')\n        /// </summary>\n        public string ContentType { get; set; } = \"application/json\";\n\n        /// <summary>\n        /// The method for creating instances of the web service class\n        /// </summary>\n        public Func<IWebService>? Factory { get; set; }\n\n        internal Func<IWebService> GetFactory()\n        {\n            return Factory ?? throw new MissingMemberException(nameof(WebServiceContent), nameof(Factory));\n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Main/WebServiceContext.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 6/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System.Diagnostics.CodeAnalysis;\nusing System.Net;\nusing Microsoft.AspNetCore.Http;\n\nnamespace Integrative.Lara\n{\n    internal sealed class WebServiceContext : BaseContext, IWebServiceContext\n    {\n        public string RequestBody { get; set; } = string.Empty;\n        public HttpStatusCode StatusCode { get; set; } = HttpStatusCode.OK;\n\n        public WebServiceContext(Application app, HttpContext http)\n            : base(app, http)\n        {\n        }\n\n        public bool TryGetSession([NotNullWhen(true)] out Session? session)\n        {\n            if (MiddlewareCommon.TryFindConnection(Application, Http, out var connection))\n            {\n                session = connection.Session;\n                return true;\n            }\n\n            session = default;\n            return false;\n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Main/WebServicePublished.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 6/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System;\nusing System.Threading.Tasks;\nusing Microsoft.AspNetCore.Http;\n\nnamespace Integrative.Lara\n{\n    internal sealed class WebServicePublished : IPublishedItem\n    {\n        public Func<IWebService> Factory { get; }\n        private string ContentType { get; }\n\n        public WebServicePublished(WebServiceContent content)\n        {\n            Factory = content.GetFactory();\n            ContentType = content.ContentType;\n        }\n\n        public async Task Run(Application app, HttpContext http, LaraOptions options)\n        {\n            var context = new WebServiceContext(app, http)\n            {\n                RequestBody = await MiddlewareCommon.ReadBody(http).ConfigureAwait(false)\n            };\n            var handler = Factory();\n            var data = string.Empty;\n            if (await MiddlewareCommon.RunHandler(http, async () =>\n            {\n                data = await handler.Execute();\n            }).ConfigureAwait(false))\n            {\n                await SendReply(context, data);\n            }\n        }\n\n        private async Task SendReply(WebServiceContext context, string data)\n        {\n            SendHeader(context, ContentType);\n            await MiddlewareCommon.WriteUtf8Buffer(context.Http, data);\n        }\n\n        internal static void SendHeader(WebServiceContext context, string contentType)\n        {\n            var http = context.Http;\n            var headers = http.Response.Headers;\n            if (!string.IsNullOrEmpty(contentType))\n            {\n                headers.Add(\"Content-Type\", contentType);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Middleware/BaseHandler.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 5/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System.Threading.Tasks;\nusing Microsoft.AspNetCore.Http;\n\nnamespace Integrative.Lara\n{\n    internal abstract class BaseHandler\n    {\n        private readonly RequestDelegate _next;\n\n        protected BaseHandler(RequestDelegate next)\n        {\n            _next = next;\n        }\n\n        public async Task Invoke(HttpContext http)\n        {\n            try\n            {\n                await TryInvoke(http);\n            }\n            catch (StatusCodeException e)\n            {\n                var text = $\"HTTP error {(int)e.StatusCode} '{e.StatusCode}': {e.Message}\";\n                await MiddlewareCommon.SendStatusReply(http, e.StatusCode, text);\n            }\n        }\n\n        private async Task TryInvoke(HttpContext http)\n        {\n            if (!await ProcessRequest(http).ConfigureAwait(false))\n            {\n                await _next.Invoke(http);\n            }\n        }\n\n        internal abstract Task<bool> ProcessRequest(HttpContext http);\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Middleware/BrowserAppController.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 11/2019\nAuthor: Pablo Carbonell\n*/\n\nusing Microsoft.AspNetCore.Hosting;\nusing System.Net;\nusing System.Threading;\nusing System.Threading.Tasks;\n\nnamespace Integrative.Lara\n{\n    internal class BrowserAppController : BaseModeController\n    {\n        private const double DefaultTimerInterval = 20 * 1000;          // 20 seconds to trigger updates\n        private const double DefaultExpireInterval = 60 * 1000;         // 60 seconds to expire\n\n        internal const double BrowserAppKeepAliveInterval = DefaultExpireInterval / 2.5;\n\n        private Connection? _connection;\n\n        public override int DiscardDelay => 200;\n\n        public BrowserAppController(Application app)\n            : base(app, ApplicationMode.BrowserApp)\n        {\n        }\n\n        public override async Task<IWebHost> Start(Application app, StartServerOptions options)\n        {\n            var connections = app.GetPublished().Connections;\n            connections.StaleCollectionInterval = DefaultTimerInterval;\n            connections.StaleExpirationInterval = DefaultExpireInterval;\n            var host = await base.Start(app, options);\n            LaraUI.LaunchBrowser(host);\n            return host;\n        }\n\n        public override double KeepAliveInterval => BrowserAppKeepAliveInterval;\n\n        public override Connection CreateConnection(IPAddress remoteIp)\n        {\n            if (!AcceptConnection(remoteIp))\n            {\n                throw new StatusForbiddenException(Resources.BrowserAppConnectionRejected);\n            }\n            _connection = base.CreateConnection(remoteIp);\n            _connection.Closing.Subscribe(Stop);\n            return _connection;\n        }\n\n        private Task Stop()\n        {\n            using var source = new CancellationTokenSource();\n            var token = source.Token;\n            var tasks = new[]\n            {\n                App.Stop(token),\n                SignalStop(source)\n            };\n            return Task.WhenAll(tasks);\n        }\n\n        private static Task SignalStop(CancellationTokenSource source)\n        {\n            source.Cancel();\n            return Task.CompletedTask;\n        }\n\n        private bool AcceptConnection(IPAddress remoteIp)\n        {\n            return _connection == null && IPAddress.IsLoopback(remoteIp);\n        }\n\n        public override bool LocalhostOnly => true;\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Middleware/ClientEventMessage.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 5/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System.Collections.Generic;\nusing System.Runtime.Serialization;\n\nnamespace Integrative.Lara\n{\n    [DataContract]\n    internal sealed class ClientEventMessage\n    {\n        [DataMember]\n        public List<ElementEventValue>? Values { get; set; }\n\n        [DataMember]\n        public string? ExtraData { get; set; }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Middleware/ClientLibraryHandler.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 5/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System;\nusing System.Diagnostics;\nusing System.IO;\nusing System.Reflection;\nusing System.Text;\nusing System.Threading.Tasks;\nusing Microsoft.AspNetCore.Http;\n\nnamespace Integrative.Lara\n{\n    internal sealed class ClientLibraryHandler : BaseHandler\n    {\n        private const string ResourceName = \"Integrative.Lara.lara-client.js\";\n\n        private readonly string _address;\n        private readonly byte[] _library;\n\n        public ClientLibraryHandler(RequestDelegate next) : base(next)\n        {\n            var assembly = GetCurrentAssembly();\n            var version = GetLibraryVersion(assembly);\n            _address = BuildLibraryAddress(version);\n            var js = LoadLibrary(assembly);\n            _library = Encoding.UTF8.GetBytes(js);\n        }\n\n        private static Assembly GetCurrentAssembly()\n        {\n            return Assembly.GetAssembly(typeof(ClientLibraryHandler));\n        }\n\n        private static string GetLibraryVersion(Assembly assembly)\n        {\n            var info = FileVersionInfo.GetVersionInfo(assembly.Location);\n            var version = info.FileVersion;\n            return version;\n        }\n\n        private static string BuildLibraryAddress(string version)\n        {\n            version = version.Replace('.', '-');\n            return GlobalConstants.LibraryAddress.Replace(\"{0}\", version, StringComparison.InvariantCulture);\n        }\n\n        public static string GetLibraryPath()\n        {\n            var assembly = GetCurrentAssembly();\n            var version = GetLibraryVersion(assembly);\n            return BuildLibraryAddress(version);\n        }\n\n        private static string LoadLibrary(Assembly assembly)\n        {\n            using var stream = assembly.GetManifestResourceStream(ResourceName);\n            if (stream == null) throw new InvalidOperationException(Resources.ResourceNotFound);\n            using var reader = new StreamReader(stream);\n            return reader.ReadToEnd();\n        }\n\n        public static byte[] LoadFile(Assembly assembly, string name)\n        {\n            using var stream = assembly.GetManifestResourceStream(name);\n            if (stream == null) throw new InvalidOperationException(Resources.ResourceNotFound);\n            var bytes = new byte[stream.Length];\n            stream.Read(bytes, 0, bytes.Length);\n            return bytes;\n        }\n\n        internal override async Task<bool> ProcessRequest(HttpContext http)\n        {\n            if (http.Request.Method != \"GET\" || http.Request.Path != _address) return false;\n            await SendLibrary(http);\n            return true;\n\n        }\n\n        private async Task SendLibrary(HttpContext http)\n        {\n#if DEBUG\n            MiddlewareCommon.AddHeaderPreventCaching(http);\n#else\n            MiddlewareCommon.AddHeaderNeverExpires(http);\n#endif\n            MiddlewareCommon.AddHeaderJSON(http);\n            await MiddlewareCommon.WriteBuffer(http, _library);\n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Middleware/ContentTypes.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 5/2019\nAuthor: Pablo Carbonell\n*/\n\nnamespace Integrative.Lara\n{\n    /// <summary>\n    /// Constants for 'content-type' HTTP header\n    /// </summary>\n    public static class ContentTypes\n    {\n        ///<summary>Used to denote the encoding necessary for files containing JavaScript source code. The alternative MIME type for this file type is text/javascript.</summary>\n        public const string ApplicationXJavascript = \"application/x-javascript\";\n\n        ///<summary>24bit Linear PCM audio at 8-48kHz, 1-N channels; Defined in RFC 3190</summary>\n        public const string AudioL24 = \"audio/L24\";\n\n        ///<summary>Adobe Flash files for example with the extension .swf</summary>\n        public const string ApplicationXShockwaveFlash = \"application/x-shockwave-flash\";\n\n        ///<summary>Arbitrary binary data.[5] Generally speaking this type identifies files that are not associated with a specific application. Contrary to past assumptions by software packages such as Apache this is not a type that should be applied to unknown files. In such a case, a server or application should not indicate a content type, as it may be incorrect, but rather, should omit the type in order to allow the recipient to guess the type.[6]</summary>\n        public const string ApplicationOctetStream = \"application/octet-stream\";\n\n        ///<summary>Atom feeds</summary>\n        public const string ApplicationAtomXml = \"application/atom+xml\";\n\n        ///<summary>Cascading Style Sheets; Defined in RFC 2318</summary>\n        public const string TextCss = \"text/css\";\n\n        ///<summary>commands; subtype resident in Gecko browsers like Firefox 3.5</summary>\n        public const string TextCmd = \"text/cmd\";\n\n        ///<summary>Comma-separated values; Defined in RFC 4180</summary>\n        public const string TextCsv = \"text/csv\";\n\n        ///<summary>deb (file format), a software package format used by the Debian project</summary>\n        public const string ApplicationXDeb = \"application/x-deb\";\n\n        ///<summary>Defined in RFC 1847</summary>\n        public const string MultipartEncrypted = \"multipart/encrypted\";\n\n        ///<summary>Defined in RFC 1847</summary>\n        public const string MultipartSigned = \"multipart/signed\";\n\n        ///<summary>Defined in RFC 2616</summary>\n        public const string MessageHttp = \"message/http\";\n\n        ///<summary>Defined in RFC 4735</summary>\n        public const string ModelExample = \"model/example\";\n\n        ///<summary>device-independent document in DVI format</summary>\n        public const string ApplicationXDvi = \"application/x-dvi\";\n\n        ///<summary>DTD files; Defined by RFC 3023</summary>\n        public const string ApplicationXmlDtd = \"application/xml-dtd\";\n\n        ///<summary>ECMAScript/JavaScript; Defined in RFC 4329</summary>\n        public const string ApplicationJavascript = \"application/javascript\";\n\n        ///<summary>ECMAScript/JavaScript; Defined in RFC 4329 (equivalent to application/javascript but with stricter processing rules)</summary>\n        public const string ApplicationEcmascript = \"application/ecmascript\";\n\n        ///<summary>EDI EDIFACT data; Defined in RFC 1767</summary>\n        public const string ApplicationEdifact = \"application/EDIFACT\";\n\n        ///<summary>EDI X12 data; Defined in RFC 1767</summary>\n        public const string ApplicationEdiX12 = \"application/EDI-X12\";\n\n        ///<summary>Email; Defined in RFC 2045 and RFC 2046</summary>\n        public const string MessagePartial = \"message/partial\";\n\n        ///<summary>Email; EML files, MIME files, MHT files, MHTML files; Defined in RFC 2045 and RFC 2046</summary>\n        public const string MessageRfc822 = \"message/rfc822\";\n\n        ///<summary>Extensible Markup Language; Defined in RFC 3023</summary>\n        public const string TextXml = \"text/xml\";\n\n        ///<summary>Extensible Markup Language; Defined in RFC 3023</summary>\n        public const string ApplicationXml = \"application/xml\";\n\n        ///<summary>Flash video (FLV files)</summary>\n        public const string VideoXFlv = \"video/x-flv\";\n\n        ///<summary>GIF image; Defined in RFC 2045 and RFC 2046</summary>\n        public const string ImageGif = \"image/gif\";\n\n        ///<summary>GoogleWebToolkit data</summary>\n        public const string TextXGwtRpc = \"text/x-gwt-rpc\";\n\n        ///<summary>Gzip</summary>\n        public const string ApplicationXGzip = \"application/x-gzip\";\n\n        ///<summary>HTML; Defined in RFC 2854</summary>\n        public const string TextHtml = \"text/html\";\n\n        ///<summary>HTML; Defined in RFC 2854</summary>\n        public const string TextHtmlUtf8 = \"text/html; charset=utf-8\";\n\n        ///<summary>ICO image; Registered[9]</summary>\n        public const string ImageVndMicrosoftIcon = \"image/vnd.microsoft.icon\";\n\n        ///<summary>IGS files, IGES files; Defined in RFC 2077</summary>\n        public const string ModelIges = \"model/iges\";\n\n        ///<summary>IMDN Instant Message Disposition Notification; Defined in RFC 5438</summary>\n        public const string MessageImdnXml = \"message/imdn+xml\";\n\n        ///<summary>JavaScript Object Notation JSON; Defined in RFC 4627</summary>\n        public const string ApplicationJson = \"application/json\";\n\n        ///<summary>JavaScript Object Notation (JSON) Patch; Defined in RFC 6902</summary>\n        public const string ApplicationJsonPatch = \"application/json-patch+json\";\n\n        ///<summary>JPEG JFIF image; Associated with Internet Explorer; Listed in ms775147(v=vs.85) - Progressive JPEG, initiated before global browser support for progressive JPEGs (Microsoft and Firefox).</summary>\n        public const string ImagePjpeg = \"image/pjpeg\";\n\n        ///<summary>JPEG JFIF image; Defined in RFC 2045 and RFC 2046</summary>\n        public const string ImageJpeg = \"image/jpeg\";\n\n        ///<summary>jQuery template data</summary>\n        public const string TextXJqueryTmpl = \"text/x-jquery-tmpl\";\n\n        ///<summary>KML files (e.g. for Google Earth)</summary>\n        public const string ApplicationVndGoogleEarthKmlXml = \"application/vnd.google-earth.kml+xml\";\n\n        ///<summary>LaTeX files</summary>\n        public const string ApplicationXLatex = \"application/x-latex\";\n\n        ///<summary>Matroska open media format</summary>\n        public const string VideoXMatroska = \"video/x-matroska\";\n\n        ///<summary>Microsoft Excel files</summary>\n        public const string ApplicationVndMsExcel = \"application/vnd.ms-excel\";\n\n        ///<summary>Microsoft Powerpoint files</summary>\n        public const string ApplicationVndMsPowerpoint = \"application/vnd.ms-powerpoint\";\n\n        ///<summary>Microsoft Word files[15]</summary>\n        public const string ApplicationMsword = \"application/msword\";\n\n        ///<summary>MIME Email; Defined in RFC 2045 and RFC 2046</summary>\n        public const string MultipartAlternative = \"multipart/alternative\";\n\n        ///<summary>MIME Email; Defined in RFC 2045 and RFC 2046</summary>\n        public const string MultipartMixed = \"multipart/mixed\";\n\n        ///<summary>MIME Email; Defined in RFC 2387 and used by MHTML (HTML mail)</summary>\n        public const string MultipartRelated = \"multipart/related\";\n\n        ///<summary>MIME Webform; Defined in RFC 2388</summary>\n        public const string MultipartFormData = \"multipart/form-data\";\n\n        /// <summary>Body contains a URL-encoded query string as per RFC 1867</summary>\n        public const string ApplicationWwwFormUrlEncoded = \"application/x-www-form-urlencoded\";\n\n        ///<summary>Mozilla XUL files</summary>\n        public const string ApplicationVndMozillaXulXml = \"application/vnd.mozilla.xul+xml\";\n\n        ///<summary>MP3 or other MPEG audio; Defined in RFC 3003</summary>\n        public const string AudioMpeg = \"audio/mpeg\";\n\n        ///<summary>MP4 audio</summary>\n        public const string AudioMp4 = \"audio/mp4\";\n\n        ///<summary>MP4 video; Defined in RFC 4337</summary>\n        public const string VideoMp4 = \"video/mp4\";\n\n        ///<summary>MPEG-1 video with multiplexed audio; Defined in RFC 2045 and RFC 2046</summary>\n        public const string VideoMpeg = \"video/mpeg\";\n\n        ///<summary>MSH files, MESH files; Defined in RFC 2077, SILO files</summary>\n        public const string ModelMesh = \"model/mesh\";\n\n        ///<summary>mulaw audio at 8 kHz, 1 channel; Defined in RFC 2046</summary>\n        public const string AudioBasic = \"audio/basic\";\n\n        ///<summary>Ogg Theora or other video (with audio); Defined in RFC 5334</summary>\n        public const string VideoOgg = \"video/ogg\";\n\n        ///<summary>Ogg Vorbis, Speex, Flac and other audio; Defined in RFC 5334</summary>\n        public const string AudioOgg = \"audio/ogg\";\n\n        ///<summary>Ogg, a multimedia bitstream container format; Defined in RFC 5334</summary>\n        public const string ApplicationOgg = \"application/ogg\";\n\n        ///<summary>OP</summary>\n        public const string ApplicationXopXml = \"application/xop+xml\";\n\n        ///<summary>p12 files</summary>\n        public const string ApplicationXPkcs12 = \"application/x-pkcs12\";\n\n        ///<summary>p7b and spc files</summary>\n        public const string ApplicationXPkcs7Certificates = \"application/x-pkcs7-certificates\";\n\n        ///<summary>p7c files</summary>\n        public const string ApplicationXPkcs7Mime = \"application/x-pkcs7-mime\";\n\n        ///<summary>p7r files</summary>\n        public const string ApplicationXPkcs7Certreqresp = \"application/x-pkcs7-certreqresp\";\n\n        ///<summary>p7s files</summary>\n        public const string ApplicationXPkcs7Signature = \"application/x-pkcs7-signature\";\n\n        ///<summary>Portable Document Format, PDF has been in use for document exchange on the Internet since 1993; Defined in RFC 3778</summary>\n        public const string ApplicationPdf = \"application/pdf\";\n\n        ///<summary>Portable Network Graphics; Registered,[8] Defined in RFC 2083</summary>\n        public const string ImagePng = \"image/png\";\n\n        ///<summary>PostScript; Defined in RFC 2046</summary>\n        public const string ApplicationPostscript = \"application/postscript\";\n\n        ///<summary>QuickTime video; Registered[10]</summary>\n        public const string VideoQuicktime = \"video/quicktime\";\n\n        ///<summary>RAR archive files</summary>\n        public const string ApplicationXRarCompressed = \"application/x-rar-compressed\";\n\n        ///<summary>RealAudio; Documented in RealPlayer Customer Support Answer 2559</summary>\n        public const string AudioVndRnRealaudio = \"audio/vnd.rn-realaudio\";\n\n        ///<summary>Resource Description Framework; Defined by RFC 3870</summary>\n        public const string ApplicationRdfXml = \"application/rdf+xml\";\n\n        ///<summary>RSS feeds</summary>\n        public const string ApplicationRssXml = \"application/rss+xml\";\n\n        ///<summary>SOAP; Defined by RFC 3902</summary>\n        public const string ApplicationSoapXml = \"application/soap+xml\";\n\n        ///<summary>StuffIt archive files</summary>\n        public const string ApplicationXStuffit = \"application/x-stuffit\";\n\n        ///<summary>SVG vector image; Defined in SVG Tiny 1.2 Specification Appendix M</summary>\n        public const string ImageSvgXml = \"image/svg+xml\";\n\n        ///<summary>Tag Image File Format (only for Baseline TIFF); Defined in RFC 3302</summary>\n        public const string ImageTiff = \"image/tiff\";\n\n        ///<summary>Tarball files</summary>\n        public const string ApplicationXTar = \"application/x-tar\";\n\n        ///<summary>Textual data; Defined in RFC 2046 and RFC 3676</summary>\n        public const string TextPlain = \"text/plain\";\n\n        ///<summary>TrueType Font No registered MIME type, but this is the most commonly used</summary>\n        public const string ApplicationXFontTtf = \"application/x-font-ttf\";\n\n        ///<summary>vCard (contact information); Defined in RFC 6350</summary>\n        public const string TextVcard = \"text/vcard\";\n\n        ///<summary>Vorbis encoded audio; Defined in RFC 5215</summary>\n        public const string AudioVorbis = \"audio/vorbis\";\n\n        ///<summary>WAV audio; Defined in RFC 2361</summary>\n        public const string AudioVndWave = \"audio/vnd.wave\";\n\n        ///<summary>Web Open Font Format; (candidate recommendation; use application/x-font-woff until standard is official)</summary>\n        public const string ApplicationFontWoff = \"application/font-woff\";\n\n        ///<summary>WebM Matroska-based open media format</summary>\n        public const string VideoWebm = \"video/webm\";\n\n        ///<summary>WebM open media format</summary>\n        public const string AudioWebm = \"audio/webm\";\n\n        ///<summary>Windows Media Audio Redirector; Documented in Microsoft help page</summary>\n        public const string AudioXMsWax = \"audio/x-ms-wax\";\n\n        ///<summary>Windows Media Audio; Documented in Microsoft KB 288102</summary>\n        public const string AudioXMsWma = \"audio/x-ms-wma\";\n\n        ///<summary>Windows Media Video; Documented in Microsoft KB 288102</summary>\n        public const string VideoXMsWmv = \"video/x-ms-wmv\";\n\n        ///<summary>WRL files, VRML files; Defined in RFC 2077</summary>\n        public const string ModelVrml = \"model/vrml\";\n\n        ///<summary>X3D ISO standard for representing 3D computer graphics, X3D XML files</summary>\n        public const string ModelX3DXml = \"model/x3d+xml\";\n\n        ///<summary>X3D ISO standard for representing 3D computer graphics, X3DB binary files</summary>\n        public const string ModelX3DBinary = \"model/x3d+binary\";\n\n        ///<summary>X3D ISO standard for representing 3D computer graphics, X3DV VRML files</summary>\n        public const string ModelX3DVrml = \"model/x3d+vrml\";\n\n        ///<summary>XHTML; Defined by RFC 3236</summary>\n        public const string ApplicationXhtmlXml = \"application/xhtml+xml\";\n\n        ///<summary>ZIP archive files; Registered[7]</summary>\n        public const string ApplicationZip = \"application/zip\";\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Middleware/DefaultErrorPage.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 9/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System.Threading.Tasks;\n\nnamespace Integrative.Lara\n{\n    internal class DefaultErrorPage : IPage\n    {\n        public string Title { get; set; } = string.Empty;\n        public string Message { get; set; } = string.Empty;\n\n        public Task OnGet()\n        {\n            LoadBootstrap();\n            ShowContent();\n            return Task.CompletedTask;\n        }\n\n        private static void LoadBootstrap()\n        {\n            var head = LaraUI.Page.Document.Head;\n            head.AppendChild(new HtmlLinkElement\n            {\n                Rel = \"stylesheet\",\n                HRef = \"https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css\"\n            });\n            head.AppendChild(new HtmlScriptElement\n            {\n                Src = \"https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js\",\n                Defer = true\n            });\n            head.AppendChild(new HtmlScriptElement\n            {\n                Src = \"https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js\",\n                Defer = true\n            });\n        }\n\n        private void ShowContent()\n        {\n            LaraUI.Document.Body.Child(\n                new HtmlDivElement { Class = \"container mt-2\"} .Child(\n                    new HtmlDivElement { Class = \"jumbotron\" } .Child(\n                        new HtmlImageElement\n                        {\n                            Src = ServerLauncher.ErrorAddress + \".svg\",\n                            Height = \"100px\"\n                        },\n                        Document.CreateElement(\"h1\")\n                            .Wrap(x => x.Class = \"display-4\")\n                            .Wrap(x => x.InnerText = Title)\n                        ),\n                        Document.CreateElement(\"p\")\n                            .Wrap(x => x.InnerText = Message)\n                    )                    \n                );\n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Middleware/DiscardHandler.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 5/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System.Threading.Tasks;\nusing Microsoft.AspNetCore.Http;\n\nnamespace Integrative.Lara\n{\n    internal sealed class DiscardHandler : BaseHandler\n    {\n        private readonly Application _app;\n\n        public DiscardHandler(Application app, RequestDelegate next) : base(next)\n        {\n            _app = app;\n        }\n\n        internal override async Task<bool> ProcessRequest(HttpContext http)\n        {\n            if (http.Request.Method != \"POST\" || http.Request.Path != \"/_discard\" ||\n                !DiscardParameters.TryParse(http, out var parameters) ||\n                !MiddlewareCommon.TryFindConnection(_app, http, out var connection)) return false;\n            await Task.Delay(_app.DiscardDelay);\n            await connection.Discard(parameters.DocumentId);\n            await _app.ClearEmptyConnection(connection);\n            return true;\n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Middleware/DiscardParameters.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 5/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System;\nusing System.Diagnostics.CodeAnalysis;\nusing Microsoft.AspNetCore.Http;\n\nnamespace Integrative.Lara\n{\n    internal sealed class DiscardParameters\n    {\n        public Guid DocumentId { get; private set; }\n\n        public static bool TryParse(HttpContext context, [NotNullWhen(true)] out DiscardParameters? parameters)\n        {\n            var query = context.Request.Query;\n            return TryParse(query, out parameters);\n        }\n\n        public static bool TryParse(IQueryCollection query, [NotNullWhen(true)] out DiscardParameters? parameters)\n        {\n            if (MiddlewareCommon.TryGetParameter(query, \"doc\", out var documentText)\n                && Guid.TryParseExact(documentText, GlobalConstants.GuidFormat, out var documentId))\n            {\n                parameters = new DiscardParameters\n                {\n                    DocumentId = documentId,\n                };\n                return true;\n            }\n\n            parameters = default;\n            return false;\n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Middleware/ErrorPages.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 9/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System;\nusing System.Collections.Generic;\nusing System.Net;\n\nnamespace Integrative.Lara\n{\n    /// <summary>\n    /// This class defines a set of default error pages\n    /// </summary>\n    public sealed class ErrorPages\n    {\n        private readonly Dictionary<HttpStatusCode, PagePublished> _map;\n        private readonly Dictionary<HttpStatusCode, PagePublished> _defaults;\n        private readonly Published _published;\n\n        internal ErrorPages(Published published)\n        {\n            _published = published;\n            _map = new Dictionary<HttpStatusCode, PagePublished>();\n            _defaults = new Dictionary<HttpStatusCode, PagePublished>\n            {\n                { HttpStatusCode.NotFound, new PagePublished(DefaultNotFound, HttpStatusCode.NotFound) },\n                { HttpStatusCode.InternalServerError, new PagePublished(DefaultServerError, HttpStatusCode.InternalServerError) }\n            };\n        }\n\n        /// <summary>\n        /// Defines a default page to show for GET requests with a given error code\n        /// </summary>\n        /// <param name=\"code\">status code</param>\n        /// <param name=\"factory\">handler that creates page</param>\n        public void SetDefaultPage(HttpStatusCode code, Func<IPage> factory)\n        {\n            _map.Remove(code);\n            var page = new PagePublished(factory);\n            _map.Add(code, page);\n        }\n\n        /// <summary>\n        /// Removes a default error page associated with a status code\n        /// </summary>\n        /// <param name=\"code\">status code</param>\n        public void Remove(HttpStatusCode code)\n        {\n            _map.Remove(code);\n        }\n\n        internal PagePublished GetPage(HttpStatusCode code)\n        {\n            TryGetPage(code, out var page);\n            return page;\n        }\n\n        internal bool TryGetPage(HttpStatusCode code, out PagePublished page)\n        {\n            return _map.TryGetValue(code, out page)\n                || _defaults.TryGetValue(code, out page);\n        }\n\n        internal IPage DefaultNotFound()\n        {\n            var url = LaraUI.Context.Http.Request.Path;\n            return new DefaultErrorPage\n            {\n                Title = \"Not Found\",\n                Message = $\"The requested URL '{url}' was not found on this server.\"\n            };\n        }\n\n        internal IPage DefaultServerError()\n        {\n            return new DefaultErrorPage\n            {\n                Title = \"Internal Server Error\",\n                Message = Resources.ServerErrorMessage\n            };\n        }\n\n        internal void PublishErrorPage()\n        {\n            const string address = ServerLauncher.ErrorAddress;\n            var page = new PagePublished(DefaultServerError, HttpStatusCode.InternalServerError);\n            _published.Publish(address, page);\n            var combined = Published.CombinePathMethod(address, \"POST\");\n            _published.Publish(combined, page);\n        }\n\n        internal void PublishErrorImage()\n        {\n            const string address = ServerLauncher.ErrorAddress + \".svg\";\n            var assembly = typeof(LaraUI).Assembly;\n            var bytes = ClientLibraryHandler.LoadFile(assembly, \"Integrative.Lara.Assets.Error.svg\");\n            _published.Publish(address, new StaticContent(bytes, \"image/svg+xml\"));\n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Middleware/EventParameters.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 5/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System;\nusing System.Diagnostics.CodeAnalysis;\nusing System.Globalization;\nusing System.Runtime.CompilerServices;\nusing System.Runtime.Serialization;\nusing System.Threading.Tasks;\nusing Microsoft.AspNetCore.Http;\n\n[assembly: InternalsVisibleTo(\"DynamicProxyGenAssembly2\")]\nnamespace Integrative.Lara\n{\n    [DataContract]\n    internal class EventParameters\n    {\n        [DataMember]\n        public Guid DocumentId { get; set; }\n\n        [DataMember]\n        public string ElementId { get; set; } = string.Empty;\n\n        [DataMember]\n        public string EventName { get; set; } = string.Empty;\n\n        [DataMember]\n        public long EventNumber { get; set; }\n\n        [DataMember(IsRequired = false)]\n        public ClientEventMessage? Message { get; set; }\n\n        public virtual IFormFileCollection? Files { get; set; }\n\n        public static bool TryParse(IQueryCollection query, [NotNullWhen(true)] out EventParameters? parameters)\n        {\n            if (MiddlewareCommon.TryGetParameter(query, \"doc\", out var documentText)\n                && MiddlewareCommon.TryGetParameter(query, \"el\", out var elementId)\n                && MiddlewareCommon.TryGetParameter(query, \"ev\", out var eventName)\n                && MiddlewareCommon.TryGetParameter(query, \"seq\", out var sequence)\n                && long.TryParse(sequence, NumberStyles.Any, CultureInfo.InvariantCulture, out var eventNumber)\n                && Guid.TryParseExact(documentText, GlobalConstants.GuidFormat, out var documentId))\n            {\n                parameters = new EventParameters\n                {\n                    DocumentId = documentId,\n                    ElementId = elementId,\n                    EventName = eventName,\n                    EventNumber = eventNumber\n                };\n                return true;\n            }\n\n            parameters = default;\n            return false;\n        }\n\n        public async Task ReadAjaxMessage(HttpContext http)\n        {\n            if (!http.Request.HasFormContentType)\n            {\n                return;\n            }\n            var form = await http.Request.ReadFormAsync();  // TODO: cancellation token for shutdown\n            if (form.TryGetValue(GlobalConstants.MessageKey, out var values))\n            {\n                Message = LaraTools.Deserialize<ClientEventMessage>(values);\n            }\n            Files = form.Files;\n        }\n    }\n\n    [DataContract]\n    internal class SocketEventParameters : EventParameters\n    {\n        [DataMember(IsRequired = false)]\n        public FormFileCollection? SocketFiles { get; set; }\n\n        public override IFormFileCollection? Files => SocketFiles;\n    }\n}"
  },
  {
    "path": "src/LaraUI/Middleware/FormFile.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 12/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System;\nusing System.IO;\nusing System.Runtime.Serialization;\nusing System.Threading;\nusing System.Threading.Tasks;\nusing Microsoft.AspNetCore.Http;\n\nnamespace Integrative.Lara\n{\n    [DataContract]\n    internal class FormFile : IFormFile\n    {\n        [DataMember]\n        public string ContentType { get; set; } = string.Empty;\n\n        [DataMember]\n        public string ContentDisposition { get; set; } = string.Empty;\n\n        [DataMember]\n        public string Name { get; set; } = string.Empty;\n\n        [DataMember]\n        public string FileName { get; set; } = string.Empty;\n\n        [DataMember]\n        public string Content { get; set; } = string.Empty;\n\n        private readonly HeaderDictionary _headers = new HeaderDictionary();\n        public IHeaderDictionary Headers => _headers;\n\n        [DataMember]\n        public long Length { get; set; }\n\n        public void CopyTo(Stream target)\n        {\n            var bytes = GetBytes();\n            target.Write(bytes, 0, bytes.Length);\n        }\n\n        public Task CopyToAsync(Stream target, CancellationToken cancellationToken = default)\n        {\n            var bytes = GetBytes();\n            return target.WriteAsync(bytes, 0, bytes.Length, cancellationToken);\n        }\n\n        public Stream OpenReadStream()\n        {\n            var bytes = GetBytes();\n            return new MemoryStream(bytes);\n        }\n\n        private byte[] GetBytes()\n        {\n            return Convert.FromBase64String(Content);\n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Middleware/FormFileCollection.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 12/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System;\nusing System.Collections;\nusing System.Collections.Generic;\nusing System.Runtime.Serialization;\nusing Microsoft.AspNetCore.Http;\n\nnamespace Integrative.Lara\n{\n    [DataContract]\n    internal class FormFileCollection : IFormFileCollection\n    {\n        [DataMember]\n        public List<FormFile>? InnerList { get; set; }\n\n        public int Count => GetCount();\n\n        private int GetCount()\n        {\n            return InnerList?.Count ?? 0;\n        }\n\n        public IFormFile this[string name] => GetInnerList().Find(x => x.Name == name);\n\n        IFormFile IReadOnlyList<IFormFile>.this[int index] => GetInnerList()[index];\n\n        private List<FormFile> GetInnerList()\n        {\n            return InnerList ?? throw new MissingMemberException(nameof(FormFileCollection), nameof(InnerList));\n        }\n\n        public IFormFile GetFile(string name)\n        {\n            return this[name];\n        }\n\n        public IReadOnlyList<IFormFile> GetFiles(string name)\n        {\n            return GetInnerList().FindAll(x => x.Name == name);\n        }\n\n        IEnumerator<IFormFile> IEnumerable<IFormFile>.GetEnumerator()\n        {\n            return GetEnumeratorInternal();\n        }\n\n        public IEnumerator GetEnumerator()\n        {\n            return GetEnumeratorInternal();\n        }\n\n        private IEnumerator<IFormFile> GetEnumeratorInternal()\n        {\n            return InnerList?.GetEnumerator() ?? GetEmptyEnumerator();\n        }\n\n        private static IEnumerator<IFormFile> GetEmptyEnumerator()\n        {\n            yield break;\n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Middleware/IModeController.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 11/2019\nAuthor: Pablo Carbonell\n*/\n\nusing Microsoft.AspNetCore.Hosting;\nusing System.Net;\nusing System.Threading.Tasks;\n\nnamespace Integrative.Lara\n{\n    internal interface IModeController\n    {\n        Task<IWebHost> Start(Application app, StartServerOptions options);\n        Connection CreateConnection(IPAddress remoteIp);\n        double KeepAliveInterval { get; }\n        ApplicationMode Mode { get; }\n        bool LocalhostOnly { get; }\n        int DiscardDelay { get; }\n    }\n\n    internal static class ModeControllerFactory\n    {\n        public static IModeController Create(Application app, ApplicationMode mode)\n        {\n            return mode == ApplicationMode.BrowserApp\n                ? new BrowserAppController(app)\n                : new BaseModeController(app, ApplicationMode.Default);\n        }\n    }    \n\n    internal class BaseModeController : IModeController\n    {\n        internal const double DefaultKeepAliveInterval\n            = StaleConnectionsCollector.DefaultExpireInterval / 2.5;  // at least 2 message attempts per expire period\n\n        protected readonly Application App;\n\n        public virtual int DiscardDelay => 3000;\n\n        public BaseModeController(Application app, ApplicationMode mode)\n        {\n            App = app;\n            Mode = mode;\n        }\n\n        public virtual double KeepAliveInterval => DefaultKeepAliveInterval;\n\n        public ApplicationMode Mode { get; }\n\n        public virtual bool LocalhostOnly => false;\n\n        public virtual Connection CreateConnection(IPAddress remoteIp)\n        {\n            var connections = App.GetPublished().Connections;\n            return connections.CreateConnection(remoteIp);\n        }\n\n        public virtual Task<IWebHost> Start(Application app, StartServerOptions options)\n        {\n            return ServerLauncher.StartServer(app, options);\n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Middleware/KeepAliveHandler.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 11/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System;\nusing System.Diagnostics.CodeAnalysis;\nusing System.Threading.Tasks;\nusing Microsoft.AspNetCore.Http;\n\nnamespace Integrative.Lara\n{\n    internal class KeepAliveHandler : BaseHandler\n    {\n        private static readonly Task<bool> _TaskFalse = Task.FromResult(false);\n        private static readonly Task<bool> _TaskTrue = Task.FromResult(true);\n\n        private const string EventPrefix = \"/_keepAlive\";\n        private const string AjaxMethod = \"POST\";\n\n        private readonly Application _app;\n\n        public KeepAliveHandler(Application app, RequestDelegate next) : base(next)\n        {\n            _app = app;\n        }\n\n        internal override Task<bool> ProcessRequest(HttpContext http)\n        {\n            if (!IsMatch(http))\n            {\n                return _TaskFalse;\n            }\n            TryGetDocument(http, out _);\n            return _TaskTrue;\n        }\n\n        private static bool IsMatch(HttpContext http)\n        {\n            return http.Request.Path == EventPrefix\n                && !http.WebSockets.IsWebSocketRequest\n                && http.Request.Method == AjaxMethod;\n        }\n\n        // ReSharper disable once UnusedMethodReturnValue.Local\n        private bool TryGetDocument(HttpContext http, [NotNullWhen(true)] out Document? document)\n        {\n            document = default;\n            return MiddlewareCommon.TryGetParameter(http.Request.Query, \"doc\", out var text)\n                && Guid.TryParseExact(text, GlobalConstants.GuidFormat, out var documentId)\n                && MiddlewareCommon.TryFindConnection(_app, http, out var connection)\n                && connection.TryGetDocument(documentId, out document);\n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Middleware/LaraMiddleware.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 5/2019\nAuthor: Pablo Carbonell\n*/\n\nusing Microsoft.AspNetCore.Http;\nusing System;\nusing System.Threading.Tasks;\n\nnamespace Integrative.Lara\n{\n    /// <summary>\n    /// Lara middleware class for the ASP.NET Core framework\n    /// </summary>\n    public sealed class LaraMiddleware\n    {\n        private readonly RequestDelegate _next;\n\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"LaraMiddleware\"/> class.\n        /// </summary>\n        /// <param name=\"next\">The next middleware</param>\n        /// <param name=\"app\">Lara application</param>\n        /// <param name=\"options\">Configuration options</param>\n        public LaraMiddleware(RequestDelegate next, Application app, LaraOptions options)\n        {\n            options = options ?? throw new ArgumentNullException(nameof(options));\n            next = new ClientLibraryHandler(next).Invoke;\n            next = new PublishedItemHandler(next, app, options).Invoke;\n            next = new DiscardHandler(app, next).Invoke;\n            next = new KeepAliveHandler(app, next).Invoke;\n            _next = new PostEventHandler(app, next).Invoke;\n        }\n\n        /// <summary>\n        /// Invokes this middleware.\n        /// </summary>\n        /// <param name=\"http\">The HttpContext.</param>\n        /// <returns>Task</returns>\n        public async Task Invoke(HttpContext http)\n        {\n            await _next.Invoke(http);\n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Middleware/LocalhostFilter.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 4/2019\nAuthor: Pablo Carbonell\n*/\n\nusing Microsoft.AspNetCore.Http;\nusing Microsoft.Extensions.Logging;\nusing System;\nusing System.Net;\nusing System.Threading.Tasks;\n\nnamespace Integrative.Lara\n{\n    /// <summary>\n    /// A middleware class to allow requests from localhost only.\n    /// </summary>\n    public sealed class LocalhostFilter\n    {\n        private readonly RequestDelegate _next;\n        private readonly ILogger<LocalhostFilter> _logger;\n\n        /// <summary>\n        /// Constructor\n        /// </summary>\n        /// <param name=\"next\">Next middleware delegate</param>\n        /// <param name=\"logger\">Logger</param>\n        public LocalhostFilter(RequestDelegate next, ILogger<LocalhostFilter> logger)\n        {\n            _next = next;\n            _logger = logger;\n        }\n\n        /// <summary>\n        /// Invokes this middleware\n        /// </summary>\n        /// <param name=\"context\">The HttpContext</param>\n        /// <returns>Task</returns>\n        public Task Invoke(HttpContext context)\n        {\n            context = context ?? throw new ArgumentNullException(nameof(context));\n            var remote = context.Connection.RemoteIpAddress;\n            if (IPAddress.IsLoopback(remote)) return _next.Invoke(context);\n            var msg = $\"Forbidden request from {remote}\";\n            _logger.LogInformation(msg);\n            return MiddlewareCommon.SendStatusReply(context, HttpStatusCode.Forbidden, Resources.Http403);\n\n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Middleware/MiddlewareCommon.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 5/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System;\nusing System.Diagnostics.CodeAnalysis;\nusing System.IO;\nusing System.Net;\nusing System.Net.WebSockets;\nusing System.Text;\nusing System.Threading;\nusing System.Threading.Tasks;\nusing Microsoft.AspNetCore.Http;\n\nnamespace Integrative.Lara\n{\n    internal static class MiddlewareCommon\n    {\n        public static async Task SendStatusReply(HttpContext context, HttpStatusCode code, string text)\n        {\n            SetStatusCode(context, code);\n            AddHeaderPreventCaching(context);\n            await WriteUtf8Buffer(context, text);\n        }\n\n        public static async Task WriteUtf8Buffer(HttpContext http, string text)\n        {\n            var buffer = Encoding.UTF8.GetBytes(text);\n            await WriteBuffer(http, buffer);\n        }\n\n        public static async Task WriteBuffer(HttpContext http, byte[] buffer)\n        {\n            await http.Response.Body.WriteAsync(buffer.AsMemory(0, buffer.Length));\n        }\n\n        public static void SetStatusCode(HttpContext http, HttpStatusCode code)\n        {\n            http.Response.StatusCode = (int)code;\n        }\n\n        public static void AddHeaderPreventCaching(HttpContext context)\n        {\n            context.Response.Headers.Add(\"Cache-Control\", \"no-cache, no-store, must-revalidate\");\n        }\n\n        public static void AddHeaderNeverExpires(HttpContext context)\n        {\n            context.Response.Headers.Add(\"Cache-Control\", \"max-age=31556926\");\n        }\n\n        public static void AddHeaderTextHtml(HttpContext http)\n        {\n            http.Response.Headers.Add(\"Content-Type\", \"text/html; charset=utf-8\");\n        }\n\n        // ReSharper disable once InconsistentNaming\n        public static void AddHeaderJSON(HttpContext http)\n        {\n            http.Response.Headers.Add(\"Content-Type\", \"application/json\");\n        }\n\n        public static bool TryFindConnection(Application app, HttpContext http, [NotNullWhen(true)] out Connection? connection)\n        {\n            connection = null;\n            return http.Request.Cookies.TryGetValue(GlobalConstants.CookieSessionId, out var value)\n                && Guid.TryParseExact(value, GlobalConstants.GuidFormat, out var guid)\n                && app.TryGetConnection(guid, out connection)\n                && connection.RemoteIp.Equals(http.Connection.RemoteIpAddress);\n        }\n\n        public static bool TryGetParameter(IQueryCollection query, string name, [NotNullWhen(true)] out string? value)\n        {\n            if (query.TryGetValue(name, out var values)\n                && values.Count > 0)\n            {\n                value = values[0];\n                return true;\n            }\n\n            value = default;\n            return false;\n        }\n\n        public static async Task<(bool, T?)>\n            ReadWebSocketMessage<T>(WebSocket socket, int maxSize) where T : class\n        {\n            var buffer = new ArraySegment<byte>(new byte[8192]);\n            using var ms = new MemoryStream();\n            WebSocketReceiveResult result;\n            do\n            {\n                result = await socket.ReceiveAsync(buffer, CancellationToken.None);\n                ms.Write(buffer.Array, buffer.Offset, result.Count);\n            }\n            while (!result.EndOfMessage && result.Count <= maxSize);\n            return ProcessWebSocketMessage<T>(maxSize, ms, result);\n        }\n\n        internal static (bool, T?) ProcessWebSocketMessage<T>(int maxSize,\n            MemoryStream ms, WebSocketReceiveResult result) where T : class\n        {\n            ms.Seek(0, SeekOrigin.Begin);\n            if (result.MessageType != WebSocketMessageType.Text)\n            {\n                return (false, default);\n            }\n\n            if (result.Count > maxSize)\n            {\n                return (false, default);\n            }\n            try\n            {\n                var parameters = LaraTools.Deserialize<T>(ms);\n                return (true, parameters);\n            }\n            catch\n            {\n                return (false, default);\n            }\n        }\n\n        public static async Task<string> ReadBody(HttpContext http)\n        {\n            if (http.Request.Body == null)\n            {\n                return string.Empty;\n            }\n            using var reader = new StreamReader(http.Request.Body, Encoding.UTF8);\n            return await reader.ReadToEndAsync();\n        }\n\n        public static async Task<bool> RunHandler(HttpContext http, Func<Task> handler)\n        {\n            try\n            {\n                await handler();\n                return true; \n            }\n            catch (StatusCodeException e)\n            {\n                await SendStatusReply(http, e.StatusCode, e.Message);\n                return false;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Middleware/NotFoundMiddleware.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 5/2019\nAuthor: Pablo Carbonell\n*/\n\nusing Microsoft.AspNetCore.Http;\nusing System.Net;\nusing System.Threading.Tasks;\n\nnamespace Integrative.Lara\n{\n    /// <summary>\n    /// A middleware to show a simple 'not found' page\n    /// </summary>\n    public class NotFoundMiddleware\n    {\n        private readonly LaraOptions _options;\n        private readonly Application _app;\n\n        /// <summary>\n        /// Creates an instance of NotFoundMiddleware\n        /// </summary>\n        /// <param name=\"next\">Next middleware</param>\n        /// <param name=\"app\">Lara application</param>\n        /// <param name=\"options\">Configuration options</param>\n        // ReSharper disable once UnusedParameter.Local\n        public NotFoundMiddleware(RequestDelegate next, Application app, LaraOptions options)\n        {\n            _options = options;\n            _app = app;\n        }\n\n        /// <summary>\n        /// Invokes this middleware\n        /// </summary>\n        /// <param name=\"context\">The HttpContext.</param>\n        /// <returns>Task</returns>\n        public Task Invoke(HttpContext context)\n        {\n            var page = _app.ErrorPages.GetPage(HttpStatusCode.NotFound);\n            return page.Run(_app, context, _options);\n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Middleware/PostEventContext.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 6/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System;\nusing System.Net.WebSockets;\nusing System.Threading.Tasks;\nusing Microsoft.AspNetCore.Http;\n\nnamespace Integrative.Lara\n{\n    internal class PostEventContext\n    {\n        public Application Application { get; set; }\n        public HttpContext Http { get; set; }\n\n        public EventParameters? Parameters { get; set; }\n        public WebSocket? Socket { get; set; }\n        public Connection? Connection { get; set; }\n        public Document? Document { get; set; } \n        public Element? Element { get; set; }\n\n        public PostEventContext(Application app, HttpContext http)\n        {\n            Application = app;\n            Http = http;\n        }\n\n        public bool SocketRemainsOpen()\n            => Document != null\n            && Parameters != null\n            && Document.SocketRemainsOpen(Parameters.EventName);\n\n        public bool IsWebSocketRequest =>\n            Http.WebSockets.IsWebSocketRequest;\n\n        public virtual Task<TaskCompletionSource<bool>> GetSocketCompletion()\n        {\n            var socket = Socket ?? throw new MissingMemberException(nameof(PostEventContext), nameof(Socket));\n            return GetDocument().GetSocketCompletion(socket);\n        }\n\n        public Document GetDocument()\n        {\n            return Document ?? throw new MissingMemberException(nameof(PostEventContext), nameof(Document));\n        }\n\n        public Connection GetConnection()\n        {\n            return Connection ?? throw new MissingMemberException(nameof(PostEventContext), nameof(Connection));\n        }\n\n        public WebSocket GetSocket()\n        {\n            return Socket ?? throw new MissingMemberException(nameof(PostEventContext), nameof(Socket));\n        }\n\n        public EventParameters GetParameters()\n        {\n            return Parameters ?? throw new MissingMemberException(nameof(PostEventContext), nameof(EventParameters));\n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Middleware/PostEventHandler.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 5/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System;\nusing System.Diagnostics.CodeAnalysis;\nusing System.Net;\nusing System.Net.WebSockets;\nusing System.Text;\nusing System.Threading;\nusing System.Threading.Tasks;\nusing Microsoft.AspNetCore.Http;\n\nnamespace Integrative.Lara\n{\n    internal sealed class PostEventHandler : BaseHandler\n    {\n        public const string EventPrefix = \"/_event\";\n        private const string AjaxMethod = \"POST\";\n        private const int MaxSizeBytes = 1024000;\n\n        public static event EventHandler? EventComplete;\n        private static readonly EventArgs _EventArgs = new EventArgs();\n\n        private readonly Application _app;\n\n        public PostEventHandler(Application app, RequestDelegate next) : base(next)\n        {\n            _app = app;\n        }\n\n        internal override async Task<bool> ProcessRequest(HttpContext http)\n        {\n            if (http.Request.Path != EventPrefix)\n            {\n                return false;\n            }\n\n            if (http.WebSockets.IsWebSocketRequest)\n            {\n                await ProcessWebSocketEvent(_app, http);\n                return true;\n            }\n\n            if (http.Request.Method != AjaxMethod) return false;\n            await ProcessAjaxRequest(_app, http);\n            return true;\n        }\n\n        private static async Task ProcessWebSocketEvent(Application app, HttpContext http)\n        {\n            var socket = await http.WebSockets.AcceptWebSocketAsync();\n            var result = await MiddlewareCommon.ReadWebSocketMessage<SocketEventParameters>(socket, MaxSizeBytes);\n            var context = new PostEventContext(app, http)\n            {\n                Http = http,\n                Socket = socket,\n                Parameters = result.Item2\n            };\n            if (result.Item1)\n            {\n                await ProcessRequest(context);\n            }\n            else\n            {\n                await context.Socket.CloseAsync(WebSocketCloseStatus.InvalidPayloadData,\n                    \"Bad request\", CancellationToken.None);\n            }\n        }\n\n        internal static async Task ProcessAjaxRequest(Application app, HttpContext http)\n        {\n            if (EventParameters.TryParse(http.Request.Query, out var parameters))\n            {\n                await parameters.ReadAjaxMessage(http);\n                var post = new PostEventContext(app, http)\n                {\n                    Parameters = parameters\n                };\n                await ProcessRequest(post);\n            }\n            else\n            {\n                await MiddlewareCommon.SendStatusReply(http, HttpStatusCode.BadRequest, Resources.BadRequest);\n            }\n        }\n\n        private static async Task ProcessRequest(PostEventContext context)\n        {\n            if (MiddlewareCommon.TryFindConnection(context.Application, context.Http, out var connection)\n                && context.Parameters != null\n                && connection.TryGetDocument(context.Parameters.DocumentId, out var document))\n            {\n                context.Connection = connection;\n                context.Document = document;\n                await ProcessRequestDocument(context);\n            }\n            else\n            {\n                await SendEvent(context, EventResultType.NoSession);\n            }\n        }\n\n        internal static async Task ProcessRequestDocument(PostEventContext context)\n        {\n            var document = context.GetDocument();\n            var parameters = context.GetParameters();\n            var proceed = await document.WaitForTurn(parameters.EventNumber);\n            if (!proceed)\n            {\n                await SendEvent(context, EventResultType.OutOfSequence);\n            }\n            else if (string.IsNullOrEmpty(parameters.ElementId))\n            {\n                await ProcessRequestDocument(context, document);\n            }\n            else if (document.TryGetElementById(parameters.ElementId, out var element))\n            {\n                context.Element = element;\n                await ProcessRequestDocument(context, document);\n            }\n            else\n            {\n                await SendEvent(context, EventResultType.NoElement);\n            }\n        }\n\n        private static async Task ProcessRequestDocument(PostEventContext context, Document document)\n        {\n            Task release;\n            using (await document.Semaphore.UseWaitAsync())\n            {\n                release = await RunEvent(context);\n            }\n            await release;\n        }\n\n        private static async Task<Task> RunEvent(PostEventContext post)\n        {\n            var connection = post.GetConnection();\n            var document = post.GetDocument();\n            var context = new PageContext(post.Application, post.Http, connection)\n            {\n                Socket = post.Socket,\n                DocumentInternal = document\n            };\n            ProcessMessageIfNeeded(context, post.Parameters);\n            return await RunEventHandler(post);\n        }\n\n        internal static async Task<Task> RunEventHandler(PostEventContext post)\n        {\n            if (!await MiddlewareCommon.RunHandler(post.Http,\n                () => NotifyEventHandler(post))) return Task.CompletedTask;\n            var document = post.GetDocument();\n            var queue = document.FlushQueue();\n            return await SendReply(post, queue);\n\n        }\n\n        private static Task NotifyEventHandler(PostEventContext post)\n        {\n            var parameters = post.GetParameters();\n            if (post.Element != null) return post.Element.NotifyEvent(parameters.EventName);\n            var document = post.GetDocument();\n            return document.NotifyEvent(parameters.EventName);\n\n        }\n\n        internal static void ProcessMessageIfNeeded(PageContext context, EventParameters? parameters)\n        {\n            if (parameters == null)\n            {\n                return;\n            }\n            var message = parameters.Message;\n            if (message != null)\n            {\n                context.SetExtraData(message.ExtraData);\n                if (message.Values != null)\n                {\n                    ProcessMessage(context.Document, message);\n                }\n            }\n            var files = parameters.Files;\n            if (files != null)\n            {\n                ProcessFiles(context.Document, files);\n            }\n        }\n\n        private static void ProcessMessage(Document document, ClientEventMessage message)\n        {\n            document = document ?? throw new ArgumentNullException(nameof(document));\n            message = message ?? throw new ArgumentNullException(nameof(message));\n            if (message.Values == null) return;\n            foreach (var row in message.Values)\n            {\n                if (document.TryGetElementById(row.ElementId, out var element))\n                {\n                    element.NotifyValue(row);\n                }\n            }\n        }\n\n        private static void ProcessFiles(Document document, IFormFileCollection files)\n        {\n            foreach (var file in files)\n            {\n                ProcessFile(document, file);\n            }\n        }\n\n        private static void ProcessFile(Document document, IFormFile file)\n        {\n            var name = file.Name;\n            if (!TryParsePrefix(name, GlobalConstants.FilePrefix, out var id)) return;\n            if (document.TryGetElementById(id, out var element)\n                && element is HtmlInputElement input)\n            {\n                input.AddFile(file);\n            }\n        }\n\n        private static bool TryParsePrefix(string name, string prefix, [NotNullWhen(true)] out string? elementId)\n        {\n            if (name.StartsWith(prefix, StringComparison.InvariantCulture))\n            {\n                elementId = name[prefix.Length..];\n                return true;\n            }\n\n            elementId = default;\n            return false;\n        }\n\n        internal static async Task<Task> SendReply(PostEventContext post, string json)\n        {\n            var result = Task.CompletedTask;\n            if (post.IsWebSocketRequest)\n            {\n                if (post.SocketRemainsOpen())\n                {\n                    var completion = await post.GetSocketCompletion();\n                    return completion.Task;\n                }\n\n                await SendSocketReply(post, json);\n            }\n            else\n            {\n                await SendAjaxReply(post.Http, json);\n            }\n            EventComplete?.Invoke(post.Http, _EventArgs);\n            return result;\n        }\n\n        private static async Task SendSocketReply(PostEventContext post, string json)\n        {\n            var socket = post.GetSocket();\n            await FlushMessage(socket, json);\n            await CloseSocket(socket);\n        }\n\n        public static Task CloseSocket(WebSocket socket)\n        {\n            return socket.CloseAsync(WebSocketCloseStatus.NormalClosure, \"\", CancellationToken.None);\n        }\n\n        public static async Task FlushMessage(WebSocket socket, string json)\n        {\n            var buffer = BuildArraySegment(json);\n            await socket.SendAsync(buffer, WebSocketMessageType.Text, true, CancellationToken.None);\n        }\n\n        public static async Task FlushPartialChanges(WebSocket socket, Document document)\n        {\n            var json = document.FlushQueue();\n            await FlushMessage(socket, json);\n        }\n\n        internal static ArraySegment<byte> BuildArraySegment(string json)\n        {\n            if (string.IsNullOrEmpty(json))\n            {\n                return new ArraySegment<byte>(Array.Empty<byte>());\n            }\n\n            var bytes = Encoding.UTF8.GetBytes(json);\n            return new ArraySegment<byte>(bytes);\n        }\n\n        private static async Task SendAjaxReply(HttpContext http, string json)\n        {\n            MiddlewareCommon.AddHeaderJSON(http);\n            await MiddlewareCommon.WriteUtf8Buffer(http, json);\n        }\n\n        private static async Task SendEvent(PostEventContext post, EventResultType type)\n        {\n            var reply = new EventResult\n            {\n                ResultType = type\n            };\n            var json = reply.ToJSON();\n            await SendReply(post, json);\n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Middleware/PublishedItemHandler.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 6/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System.Threading.Tasks;\nusing Microsoft.AspNetCore.Http;\n\nnamespace Integrative.Lara\n{\n    internal sealed class PublishedItemHandler : BaseHandler\n    {\n        private readonly LaraOptions _options;\n        private readonly Application _app;\n\n        public PublishedItemHandler(RequestDelegate next, Application app, LaraOptions options) : base(next)\n        {\n            _options = options;\n            _app = app;\n        }\n\n        internal override async Task<bool> ProcessRequest(HttpContext http)\n        {\n            var combined = Published.CombinePathMethod(http.Request.Path, http.Request.Method);\n            if (!_app.TryGetNode(combined, out var item)) return false;\n            await item.Run(_app, http, _options);\n            return true;\n\n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Middleware/Sequencer.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 10/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System.Collections.Generic;\nusing System.Threading.Tasks;\n\nnamespace Integrative.Lara\n{\n    internal class Sequencer\n    {\n        private static readonly Task<bool> _TaskProceed = Task.FromResult(true);\n        private static readonly Task<bool> _TaskAbort = Task.FromResult(false);\n\n        private readonly object _lock;\n        private readonly Dictionary<long, TaskCompletionSource<bool>> _pending;\n        private long _next;\n\n        public Sequencer()\n        {\n            _lock = new object();\n            _pending = new Dictionary<long, TaskCompletionSource<bool>>();\n            _next = 1;\n        }\n\n        public Task<bool> WaitForTurn(long turnNumber)\n        {\n            if (turnNumber == 0)\n            {\n                return _TaskProceed;\n            }\n            TaskCompletionSource<bool>? completion;\n            lock (_lock)\n            {\n                if (turnNumber == _next)\n                {\n                    _next++;\n                    FlushPending();\n                    return _TaskProceed;\n                }\n\n                if (turnNumber > _next)\n                {\n                    completion = new TaskCompletionSource<bool>();\n                    _pending.Add(turnNumber, completion);\n                }\n                else\n                {\n                    return _TaskAbort;\n                }\n            }\n            return completion.Task;\n        }\n\n        public void AbortAll()\n        {\n            lock (_lock)\n            {\n                foreach (var item in _pending.Values)\n                {\n                    item.SetResult(false);\n                }\n                _pending.Clear();\n            }\n        }\n\n        private void FlushPending()\n        {\n            while (_pending.TryGetValue(_next, out var source))\n            {\n                _pending.Remove(_next);\n                source.SetResult(true);\n                _next++;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Middleware/ServerEvent.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 8/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System;\nusing System.Threading.Tasks;\n\nnamespace Integrative.Lara\n{\n    /// <summary>\n    /// The ServerEvent disposable class represents the life cycle of a server-side event.\n    /// </summary>\n    public sealed class ServerEvent : IDisposable\n    {\n        private readonly Document _document;\n        private readonly IDisposable _access;\n\n        private bool _disposed;\n\n        internal ServerEvent(Document document)\n        {\n            _document = document;\n            _access = document.Semaphore.UseWait();\n        }\n\n        /// <summary>\n        /// Flushes partial changes made to the document. \n        /// </summary>\n        /// <returns>Task</returns>\n        public Task FlushPartialChanges()\n        {\n            VerifyNotDisposed();\n            return _document.ServerEventFlush();\n        }\n\n        internal void VerifyNotDisposed()\n        {\n            if (_disposed)\n            {\n                throw new InvalidOperationException(Resources.ServerEventAlreadyDisposed);\n            }\n        }\n\n        /// <summary>\n        /// The dispose method flushes all pending changes in the document.\n        /// </summary>\n        public void Dispose()\n        {\n            _disposed = true;\n            _document.ServerEventFlush().Wait();\n            _access.Dispose();\n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Middleware/ServerEventsController.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 8/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System;\nusing System.Net.WebSockets;\nusing System.Threading.Tasks;\n\nnamespace Integrative.Lara\n{\n    internal class ServerEventsController\n    {\n        private readonly Document _document;\n\n        public ServerEventsController(Document document)\n        {\n            _document = document;\n        }\n\n        private bool _serverEventsEnabled;\n        private WebSocket? _serverEventsSocket;\n        private TaskCompletionSource<bool>? _completion;\n        private bool _flushPending;\n\n        public ServerEventsStatus ServerEventsStatus\n            => CalculateServerEventsStatus(_serverEventsEnabled, _serverEventsSocket);\n\n        internal static ServerEventsStatus CalculateServerEventsStatus(bool enabled, WebSocket? socket)\n        {\n            if (!enabled)\n            {\n                return ServerEventsStatus.Disabled;\n            }\n\n            return socket == null ? ServerEventsStatus.Connecting : ServerEventsStatus.Enabled;\n        }\n\n        public void ServerEventsOn()\n        {\n            if (_serverEventsEnabled) return;\n            _document.Enqueue(new ServerEventsDelta());\n            _serverEventsEnabled = true;\n        }\n\n        public Task ServerEventsOff()\n        {\n            _serverEventsEnabled = false;\n            return DiscardSocket();\n        }\n\n        public Task NotifyUnload() => DiscardSocket();\n\n        private async Task DiscardSocket()\n        {\n            if (_serverEventsSocket != null)\n            {\n                _completion?.SetResult(true);\n                await PostEventHandler.CloseSocket(_serverEventsSocket);\n                _serverEventsSocket = null;\n            }\n        }\n\n        public bool SocketRemainsOpen(string eventName)\n        {\n            return _serverEventsEnabled\n                && eventName == GlobalConstants.ServerSideEvent;\n        }\n\n        public async Task ServerEventFlush()\n        {\n            if (PrepareFlush())\n            {\n                var json = _document.FlushQueue();\n                if (_serverEventsSocket != null)\n                {\n                    await PostEventHandler.FlushMessage(_serverEventsSocket, json);\n                }\n            }\n        }\n\n        private bool PrepareFlush()\n        {\n            if (!_serverEventsEnabled)\n            {\n                throw new InvalidOperationException(Resources.ServerEventsNotEnabled);\n            }\n\n            if (!_document.HasPendingChanges)\n            {\n                return false;\n            }\n\n            if (_serverEventsSocket != null) return true;\n            _flushPending = true;\n            return false;\n        }\n\n        public async Task<TaskCompletionSource<bool>> GetSocketCompletion(WebSocket socket)\n        {\n            await DiscardSocket();\n            _serverEventsSocket = socket;\n            _completion = new TaskCompletionSource<bool>();\n            await FlushIfPending();\n            return _completion;\n        }\n\n        private async Task FlushIfPending()\n        {\n            if (_flushPending)\n            {\n                _flushPending = false;\n                await ServerEventFlush();\n            }\n        }\n\n        public ServerEvent StartServerEvent()\n        {\n            return new ServerEvent(_document);\n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Middleware/StatusCodeException.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 6/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System;\nusing System.Net;\n\nnamespace Integrative.Lara\n{\n    /// <summary>\n    /// Exception that returns a specific HTTP status code\n    /// </summary>\n    public class StatusCodeException : Exception\n    {\n        /// <summary>\n        /// Status code to respond to the client\n        /// </summary>\n        public HttpStatusCode StatusCode { get; set; } = HttpStatusCode.InternalServerError;\n\n        /// <summary>\n        /// Constructor\n        /// </summary>\n        public StatusCodeException()\n        {\n        }\n\n        /// <summary>\n        /// Constructor\n        /// </summary>\n        /// <param name=\"message\">Exception message</param>\n        public StatusCodeException(string message)\n            : base(message)\n        {\n        }\n\n        /// <summary>\n        /// Constructor\n        /// </summary>\n        /// <param name=\"message\">Exception message</param>\n        /// <param name=\"inner\">Inner exception</param>\n        public StatusCodeException(string message, Exception inner)\n            : base(message, inner)\n        {\n        }\n\n        /// <summary>\n        /// Constructor\n        /// </summary>\n        /// <param name=\"status\">Status code</param>\n        public StatusCodeException(HttpStatusCode status)\n        {\n            StatusCode = status;\n        }\n\n        /// <summary>\n        /// Constructor\n        /// </summary>\n        /// <param name=\"status\">Status code </param>\n        /// <param name=\"message\">Message</param>\n        public StatusCodeException(HttpStatusCode status, string message)\n            : base(message)\n        {\n            StatusCode = status;\n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Middleware/StatusForbiddenException.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 6/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System;\nusing System.Net;\n\nnamespace Integrative.Lara\n{\n    /// <summary>\n    /// Exception that returns an HTTP status code of Forbidden\n    /// </summary>\n    public class StatusForbiddenException : StatusCodeException\n    {\n        /// <summary>\n        /// Constructor\n        /// </summary>\n        public StatusForbiddenException()\n            : base(HttpStatusCode.Forbidden)\n        {\n        }\n\n        /// <summary>\n        /// Constructor\n        /// </summary>\n        /// <param name=\"message\">Exception message</param>\n        public StatusForbiddenException(string message)\n            : base(HttpStatusCode.Forbidden, message)\n        {\n        }\n\n        /// <summary>\n        /// Constructor\n        /// </summary>\n        /// <param name=\"message\">Exception message</param>\n        /// <param name=\"inner\">Inner exception</param>\n        public StatusForbiddenException(string message, Exception inner)\n            : base(message, inner)\n        {\n            StatusCode = HttpStatusCode.Forbidden;\n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/NewVersionChecklist.txt",
    "content": "﻿new version checklist:\n- edit NuGet documentation with what's new in version\n- in the LaraClient folder run: npm run release\n- compile Release C#\n- upload XML documentation to CDN with Filezilla\n- pack.bat nuget package\n- update wiki link to to latest CDN link\n- upload NuGet package\n"
  },
  {
    "path": "src/LaraUI/Reactive/BindableBase.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 8/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System.ComponentModel;\nusing System.Runtime.CompilerServices;\n\nnamespace Integrative.Lara\n{\n    /// <summary>\n    ///     Implementation of <see cref=\"INotifyPropertyChanged\" /> to simplify models.\n    /// </summary>\n    public abstract class BindableBase : INotifyPropertyChanged\n    {\n        /// <summary>\n        ///     Multicast event for property change notifications.\n        /// </summary>\n        public event PropertyChangedEventHandler? PropertyChanged;\n\n        /// <summary>\n        ///     Checks if a property already matches a desired value.  Sets the property and\n        ///     notifies listeners only when necessary.\n        /// </summary>\n        /// <typeparam name=\"T\">Type of the property.</typeparam>\n        /// <param name=\"storage\">Reference to a property with both getter and setter.</param>\n        /// <param name=\"value\">Desired value for the property.</param>\n        /// <param name=\"propertyName\">\n        ///     Name of the property used to notify listeners.  This\n        ///     value is optional and can be provided automatically when invoked from compilers that\n        ///     support CallerMemberName.\n        /// </param>\n        /// <returns>\n        ///     True if the value was changed, false if the existing value matched the\n        ///     desired value.\n        /// </returns>\n        protected bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string? propertyName = null)\n        {\n            if (Equals(storage, value))\n            {\n                return false;\n            }\n            storage = value;\n            OnPropertyChanged(propertyName);\n            return true;\n        }\n\n        /// <summary>\n        ///     Notifies listeners that a property value has changed.\n        /// </summary>\n        /// <param name=\"propertyName\">\n        ///     Name of the property used to notify listeners.  This\n        ///     value is optional and can be provided automatically when invoked from compilers\n        ///     that support <see cref=\"CallerMemberNameAttribute\" />.\n        /// </param>\n        protected void OnPropertyChanged([CallerMemberName] string? propertyName = null)\n        {\n            if (_holdCounter > 0)\n            {\n                _pendingEvents = true;\n            }\n            else\n            {\n                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));\n            }\n        }\n\n        private int _holdCounter;\n        private bool _pendingEvents;\n\n        /// <summary>\n        /// Holds all property changed notifications.\n        /// </summary>\n        public void BeginUpdate()\n        {\n            _holdCounter++;\n        }\n\n        /// <summary>\n        /// Stops holding property changed notifications.\n        /// If any property changed pending since BeginUpdate, a single property changed event is triggered.\n        /// </summary>\n        public void EndUpdate()\n        {\n            _holdCounter--;\n            if (_holdCounter != 0 || !_pendingEvents) return;\n            _pendingEvents = false;\n            OnPropertyChanged(string.Empty);\n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Reactive/BindingExtensions.cs",
    "content": "﻿/*\nCopyright (c) 2020-2021 Integrative Software LLC\nCreated: 12/2020\nAuthor: Pablo Carbonell\n*/\n\nusing System;\nusing System.Collections.ObjectModel;\nusing System.ComponentModel;\n\nnamespace Integrative.Lara\n{\n    /// <summary>\n    /// Extensions for element binding operations\n    /// </summary>\n    public static class BindingExtensions\n    {\n        #region bind properties\n\n        /// <summary>\n        /// Executes code whenever a source object triggers the PropertyChanged event\n        /// </summary>\n        /// <typeparam name=\"TNode\"></typeparam>\n        /// <param name=\"node\"></param>\n        /// <param name=\"source\"></param>\n        /// <param name=\"onSourceChange\"></param>\n        /// <returns></returns>\n        public static TNode Bind<TNode>(\n            this TNode node,\n            INotifyPropertyChanged source,\n            Action<TNode> onSourceChange)\n            where TNode : Element\n        {\n            node = node ?? throw new ArgumentNullException(nameof(node));\n            source = source ?? throw new ArgumentNullException(nameof(source));\n            node.AddSubscription(source, () => onSourceChange(node));\n            return node;\n        }\n\n        /// <summary>\n        /// Executes code whenever the element triggers the PropertyChanged event\n        /// </summary>\n        /// <typeparam name=\"TNode\"></typeparam>\n        /// <param name=\"node\"></param>\n        /// <param name=\"onChange\"></param>\n        /// <returns></returns>\n        public static TNode BindBack<TNode>(\n            this TNode node,\n            Action<TNode> onChange)\n            where TNode : Element\n        {\n            node = node ?? throw new ArgumentNullException(nameof(node));\n            node.AddSubscription(node, () => onChange(node));\n            return node;\n        }\n\n        #endregion\n\n        #region bind children\n\n        /// <summary>\n        /// Updates the element's children collection based on an observable collection\n        /// </summary>\n        /// <typeparam name=\"TParent\"></typeparam>\n        /// <typeparam name=\"TValue\"></typeparam>\n        /// <param name=\"element\"></param>\n        /// <param name=\"source\"></param>\n        /// <param name=\"childFactory\"></param>\n        /// <returns></returns>\n        public static TParent BindChildren<TParent, TValue>(\n            this TParent element,\n            ObservableCollection<TValue> source,\n            Func<TValue, Element> childFactory)\n            where TParent : Element\n        {\n            element = element ?? throw new ArgumentNullException(nameof(element));\n            source = source ?? throw new ArgumentNullException(nameof(source));\n            element.ClearChildren();\n            foreach (var item in source)\n            {\n                element.AppendChild(childFactory(item));\n            }\n            element.SubscribeChildren(source, (_, args) =>\n            {\n                var updater = new CollectionUpdater<TValue>(childFactory, element, args);\n                updater.Run();\n            });\n            return element;\n        }\n\n        /// <summary>\n        /// Creates element nodes based on a source observable collection\n        /// </summary>\n        /// <typeparam name=\"TParent\"></typeparam>\n        /// <typeparam name=\"TValue\"></typeparam>\n        /// <param name=\"element\"></param>\n        /// <param name=\"source\"></param>\n        /// <param name=\"childFactory\"></param>\n        /// <returns></returns>\n        public static TParent ForEach<TParent, TValue>(\n            this TParent element,\n            ObservableCollection<TValue> source,\n            Func<TValue, Element> childFactory)\n            where TParent : Element\n        {\n            element = element ?? throw new ArgumentNullException(nameof(element));\n            var fragment = new Fragment();\n            element.AppendChild(fragment);\n            fragment.BindChildren(source, childFactory);\n            return element;\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Reactive/BindingOptions.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 8/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System;\nusing System.Collections.ObjectModel;\nusing System.ComponentModel;\nusing System.Linq.Expressions;\n\nnamespace Integrative.Lara\n{\n    /// <summary>\n    /// Base class for binding options\n    /// </summary>\n    public abstract class BindOptions\n    {\n    }\n\n    /// <summary>\n    /// Base class for property-changed based bindings\n    /// </summary>\n    public abstract class BindPropertyOptions : BindOptions\n    {\n    }\n\n    /// <summary>\n    /// Base class for property-changed based bindings of source type T\n    /// </summary>\n    /// <typeparam name=\"T\">Type of data source</typeparam>\n    public abstract class BindPropertyOptions<T> : BindPropertyOptions\n        where T : class, INotifyPropertyChanged\n    {\n        /// <summary>\n        /// Instance to bind to\n        /// </summary>\n        public T? BindObject { get; set; }\n    }\n\n    /// <summary>\n    /// Binding options for generic modification handler\n    /// </summary>\n    /// <typeparam name=\"T\">Type of data source</typeparam>\n    public sealed class BindHandlerOptions<T> : BindPropertyOptions<T>\n        where T : class, INotifyPropertyChanged\n    {\n        /// <summary>\n        /// Action to update the element whenever the data source is modified\n        /// </summary>\n        public Action<T, Element>? ModifiedHandler { get; set; }\n    }\n\n    /// <summary>\n    /// Abstract class for text-property bindings\n    /// </summary>\n    /// <typeparam name=\"TData\">Data type for data source instance</typeparam>\n    /// <typeparam name=\"TValue\">Data type for data source property</typeparam>\n    public abstract class BindPropertyOptions<TData, TValue> : BindPropertyOptions<TData>\n        where TData : class, INotifyPropertyChanged\n    {\n        /// <summary>\n        /// Function to retrieve the target value from instance that's tracked\n        /// </summary>\n        public Func<TData, TValue>? Property { get; set; }\n    }\n\n    /// <summary>\n    /// Binding options for inner text\n    /// </summary>\n    /// <typeparam name=\"T\">Type of data source object</typeparam>\n    public sealed class BindInnerTextOptions<T> : BindPropertyOptions<T, string>\n        where T : class, INotifyPropertyChanged\n    {\n    }\n\n    /// <summary>\n    /// Binding options for attributes\n    /// </summary>\n    /// <typeparam name=\"T\">Type of data source object</typeparam>\n    public sealed class BindAttributeOptions<T> : BindPropertyOptions<T, string>\n        where T : class, INotifyPropertyChanged\n    {\n        /// <summary>\n        /// Attribute to bind\n        /// </summary>\n        public string Attribute { get; set; } = string.Empty;\n    }\n\n    /// <summary>\n    /// Binding options for flag attributes\n    /// </summary>\n    /// <typeparam name=\"T\">Source data type</typeparam>\n    public sealed class BindFlagAttributeOptions<T> : BindPropertyOptions<T, bool>\n        where T : class, INotifyPropertyChanged\n    {\n        /// <summary>\n        /// Attribute to bind\n        /// </summary>\n        public string Attribute { get; set; } = string.Empty;\n    }\n\n    /// <summary>\n    /// Binding options to toggle element classes\n    /// </summary>\n    /// <typeparam name=\"T\">Source data type</typeparam>\n    public sealed class BindToggleClassOptions<T> : BindPropertyOptions<T, bool>\n        where T : class, INotifyPropertyChanged\n    {\n        /// <summary>\n        /// Element class to toggle\n        /// </summary>\n        public string ClassName { get; set; } = string.Empty;\n    }\n\n    /// <summary>\n    /// Base class for two-way binding\n    /// </summary>\n    /// <typeparam name=\"TData\">Type of data source</typeparam>\n    /// <typeparam name=\"TValue\">Type of data property</typeparam>\n    public abstract class BindInputOptions<TData, TValue> : BindPropertyOptions<TData>\n        where TData : class, INotifyPropertyChanged\n    {\n        /// <summary>\n        /// Attribute to bind\n        /// </summary>\n        public string Attribute { get; set; } = \"\";\n\n        /// <summary>\n        /// Bind model property\n        /// </summary>\n        public Expression<Func<TData, TValue>>? Property { get; set; }\n    }\n\n    /// <summary>\n    /// Binding options for two-way binding of attributes\n    /// </summary>\n    /// <typeparam name=\"T\">Data source type</typeparam>\n    public sealed class BindInputOptions<T> : BindInputOptions<T, string?>\n        where T : class, INotifyPropertyChanged\n    {\n    }\n\n    /// <summary>\n    /// Binding options for two-way binding of flag attributes\n    /// </summary>\n    /// <typeparam name=\"T\">Data source type</typeparam>\n    public sealed class BindFlagInputOptions<T> : BindInputOptions<T, bool>\n        where T : class, INotifyPropertyChanged\n    {\n    }\n\n    /// <summary>\n    /// Binding options for child element collections\n    /// </summary>\n    public abstract class BindChildrenOptions : BindOptions\n    {\n    }\n\n    /// <summary>\n    /// Binding options for child element collections\n    /// </summary>\n    /// <typeparam name=\"T\">Type of items in observable collection</typeparam>\n    public sealed class BindChildrenOptions<T> : BindChildrenOptions\n    {\n        /// <summary>\n        /// Collection that is tracked\n        /// </summary>\n        public ObservableCollection<T> Collection { get; }\n\n        /// <summary>\n        /// Method for creating elements\n        /// </summary>\n        public Func<T, Element> CreateCallback { get; }\n\n        /// <summary>\n        /// Constructor\n        /// </summary>\n        /// <param name=\"collection\">Collection to bind</param>\n        /// <param name=\"createCallback\">Method for creating elements</param>\n        public BindChildrenOptions(ObservableCollection<T> collection, Func<T, Element> createCallback)\n        {\n            CreateCallback = createCallback;\n            Collection = collection;\n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Reactive/BindingSubscription.cs",
    "content": "﻿/*\nCopyright (c) 2020-2021 Integrative Software LLC\nCreated: 12/2020\nAuthor: Pablo Carbonell\n*/\n\nusing System.ComponentModel;\n\nnamespace Integrative.Lara\n{\n    internal class BindingSubscription\n    {\n        public INotifyPropertyChanged Source { get; }\n        public PropertyChangedEventHandler Handler { get; }\n\n        public BindingSubscription(\n            INotifyPropertyChanged source,\n            PropertyChangedEventHandler handler)\n        {\n            Source = source;\n            Handler = handler;\n            Source.PropertyChanged += handler;\n        }\n\n        public void Unsubscribe() => Source.PropertyChanged -= Handler;\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Reactive/CollectionUpdater.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 8/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System;\nusing System.Collections.Specialized;\n\nnamespace Integrative.Lara\n{\n    internal class CollectionUpdater<T>\n    {\n        private readonly Element _element;\n        private readonly NotifyCollectionChangedEventArgs _args;\n        private readonly Func<T, Element> _createCallback;\n\n        public CollectionUpdater(Func<T, Element> createCallback,\n            Element element,\n            NotifyCollectionChangedEventArgs args)\n        {\n            _createCallback = createCallback;\n            _element = element;\n            _args = args;\n        }\n\n        public void Run()\n        {\n            switch (_args.Action)\n            {\n                case NotifyCollectionChangedAction.Add:\n                    CollectionAdd();\n                    break;\n                case NotifyCollectionChangedAction.Move:\n                    CollectionMove();\n                    break;\n                case NotifyCollectionChangedAction.Remove:\n                    CollectionRemove();\n                    break;\n                case NotifyCollectionChangedAction.Replace:\n                    CollectionReplace();\n                    break;\n                default:\n                    CollectionReset(_element);\n                    break;\n            }\n        }\n\n        private void CollectionAdd()\n        {\n            var item = (T)_args.NewItems[0];\n            var childElement = _createCallback(item);\n            _element.AppendChild(childElement);\n        }\n\n        private void CollectionMove()\n        {\n            var removeIndex = _args.OldStartingIndex;\n            var addIndex = _args.NewStartingIndex;\n            _element.SwapChildren(addIndex, removeIndex);\n        }\n\n        private void CollectionRemove()\n        {\n            var index = _args.OldStartingIndex;\n            RemoveAt(index);\n        }\n\n        private void CollectionReplace()\n        {\n            var value = (T)_args.NewItems[0];\n            var index = _args.OldStartingIndex;\n            var childElement = _createCallback(value);\n            RemoveAt(index);\n            InsertAt(index, childElement);\n        }\n\n        private void RemoveAt(int index)\n        {\n            var child = _element.GetChildAt(index);\n            if (child is Element element)\n            {\n                element.UnbindAll();\n            }\n            _element.RemoveAt(index);\n        }\n\n        private void InsertAt(int index, Node child)\n        {\n            _element.InsertChildAt(index, child);\n        }\n\n        private static void CollectionReset(Element element)\n        {\n            UnbindChildren(element);\n            element.ClearChildren();\n        }\n\n        private static void UnbindChildren(Element element)\n        {\n            foreach (var node in element.Children)\n            {\n                if (node is Element childElement)\n                {\n                    childElement.UnbindAll();\n                }\n            }\n        }\n\n        public static void CollectionLoad(BindChildrenOptions<T> options, Element element)\n        {\n            CollectionReset(element);\n            foreach (var item in options.Collection)\n            {\n                var callback = options.CreateCallback;\n                var child = callback(item);\n                element.AppendChild(child);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Reactive/ObsoleteElement.cs",
    "content": "﻿/*\nCopyright (c) 2020-2021 Integrative Software LLC\nCreated: 12/2020\nAuthor: Pablo Carbonell\n*/\n\nusing System;\nusing System.ComponentModel;\nusing System.Linq.Expressions;\n\nnamespace Integrative.Lara\n{\n    /// <summary>\n    /// Obsolete methods on Element class\n    /// </summary>\n    public static class ObsoleteElement\n    {\n        internal const string BindObsolete = \"Use Bind(source, action) and BindBack(action) instead\";\n\n        /// <summary>\n        /// Binds an element to an action to be triggered whenever the source data changes\n        /// </summary>\n        /// <typeparam name=\"T\">Type of the source data</typeparam>\n        /// <param name=\"self\"></param>\n        /// <param name=\"options\">Binding options</param>\n        [Obsolete(BindObsolete)]\n        [EditorBrowsable(EditorBrowsableState.Never)]\n        public static void Bind<T>(this Element self, BindHandlerOptions<T> options)\n            where T : class, INotifyPropertyChanged\n        {\n            options = options ?? throw new ArgumentNullException(nameof(options));\n            var handler = options.ModifiedHandler ?? throw new ArgumentNullException(nameof(options.ModifiedHandler));\n            var source = options.BindObject ?? throw new ArgumentNullException(nameof(options.BindObject));\n\n            self.Bind(source, _ => handler(source, self));\n        }\n\n        /// <summary>\n        /// Binds an attribute\n        /// </summary>\n        /// <typeparam name=\"T\">Data type for data source instance</typeparam>\n        /// <param name=\"self\"></param>\n        /// <param name=\"options\">Attribute binding options</param>\n        [Obsolete(BindObsolete)]\n        [EditorBrowsable(EditorBrowsableState.Never)]\n        public static void BindAttribute<T>(this Element self, BindAttributeOptions<T> options)\n            where T : class, INotifyPropertyChanged\n        {\n            var source = options.BindObject ?? throw new ArgumentNullException(nameof(options.BindObject));\n            var property = options.Property ?? throw new ArgumentNullException(nameof(options.Property));\n            var attribute = options.Attribute;\n            self.Bind(source, x => x.SetAttribute(attribute, property(source)));\n        }\n\n        /// <summary>\n        /// Binds a flag attribute\n        /// </summary>\n        /// <typeparam name=\"T\">Data type for data source instance</typeparam>\n        /// <param name=\"self\"></param>\n        /// <param name=\"options\">Binding options</param>\n        [Obsolete(BindObsolete)]\n        [EditorBrowsable(EditorBrowsableState.Never)]\n        public static void BindFlagAttribute<T>(this Element self, BindFlagAttributeOptions<T> options)\n            where T : class, INotifyPropertyChanged\n        {\n            self.BindToggleAttribute(options);\n        }\n\n        /// <summary>\n        /// Binds a flag attribute\n        /// </summary>\n        /// <typeparam name=\"T\">Data type for data source instance</typeparam>\n        /// <param name=\"self\"></param>\n        /// <param name=\"options\">Binding options</param>\n        [Obsolete(BindObsolete)]\n        [EditorBrowsable(EditorBrowsableState.Never)]\n        public static void BindToggleAttribute<T>(this Element self, BindFlagAttributeOptions<T> options)\n            where T : class, INotifyPropertyChanged\n        {\n            var source = options.BindObject ?? throw new ArgumentNullException(nameof(options.BindObject));\n            var attribute = options.Attribute.ToLowerInvariant();\n            var property = options.Property ?? throw new ArgumentNullException(nameof(options.Property));\n            self.Bind(source, x => x.SetFlagAttributeLower(attribute, property(source)));\n        }\n\n        /// <summary>\n        /// Bindings to toggle an element class\n        /// </summary>\n        /// <typeparam name=\"T\">Data type for data source instance</typeparam>\n        /// <param name=\"self\"></param>\n        /// <param name=\"options\">Binding options</param>\n        [Obsolete(BindObsolete)]\n        [EditorBrowsable(EditorBrowsableState.Never)]\n        public static void BindToggleClass<T>(this Element self, BindToggleClassOptions<T> options)\n            where T : class, INotifyPropertyChanged\n        {\n            var source = options.BindObject ?? throw new ArgumentNullException(nameof(options.BindObject));\n            var property = options.Property ?? throw new ArgumentNullException(nameof(options.Property));\n            var className = options.ClassName;\n            if (string.IsNullOrWhiteSpace(className)) throw new ArgumentException(\"ClassName cannot be empty\");\n            self.Bind(source, x => x.ToggleClass(className, property(source)));\n        }\n\n        /// <summary>\n        /// Two-way bindings for element attributes (e.g. 'value' attribute populated by user)\n        /// </summary>\n        /// <typeparam name=\"T\">Source data type</typeparam>\n        /// <param name=\"self\"></param>\n        /// <param name=\"options\">Binding options</param>\n        [Obsolete(BindObsolete)]\n        [EditorBrowsable(EditorBrowsableState.Never)]\n        public static void BindInput<T>(this Element self, BindInputOptions<T> options)\n            where T : class, INotifyPropertyChanged\n        {\n            var source = options.BindObject\n                ?? throw new ArgumentNullException(nameof(options.BindObject));\n            var property = options.Property\n                ?? throw new ArgumentNullException(nameof(options.Property));\n            if (property.Body is not MemberExpression)\n            {\n                throw new ArgumentException(Resources.InvalidBindingExpression);\n            }\n\n            var attribute = options.Attribute;\n            if (string.IsNullOrWhiteSpace(attribute))\n            {\n                throw new ArgumentException(\"Attribute cannot be empty\");\n            }\n            attribute = attribute.ToLowerInvariant();\n            var setter = CompileSetter(property);\n            var getter = property.Compile();\n            self.Bind(source, _ =>\n            {\n                var value = getter(source);\n                self.SetAttributeLower(attribute, value);\n            });\n            self.BindBack(_ =>\n            {\n                var value = self.GetAttributeLower(attribute);\n                setter(source, value);\n            });\n        }\n\n        /// <summary>\n        /// Two-way bindings for element flag attributes (e.g. 'checked' attribute populated by user)\n        /// </summary>\n        /// <typeparam name=\"T\">Source data type</typeparam>\n        /// <param name=\"self\"></param>\n        /// <param name=\"options\">Binding options</param>\n        [Obsolete(BindObsolete)]\n        [EditorBrowsable(EditorBrowsableState.Never)]\n        public static void BindFlagInput<T>(this Element self, BindFlagInputOptions<T> options)\n            where T : class, INotifyPropertyChanged\n        {\n            var source = options.BindObject\n                ?? throw new ArgumentNullException(nameof(options.BindObject));\n            var property = options.Property\n                ?? throw new ArgumentNullException(nameof(options.Property));\n            if (property.Body is not MemberExpression)\n            {\n                throw new ArgumentException(Resources.InvalidBindingExpression);\n            }\n\n            var attribute = options.Attribute;\n            if (string.IsNullOrWhiteSpace(attribute))\n            {\n                throw new ArgumentException(\"Attribute cannot be empty\");\n            }\n            attribute = attribute.ToLowerInvariant();\n            var setter = CompileSetter(property);\n            var getter = property.Compile();\n            self.Bind(source, _ =>\n            {\n                var value = getter(source);\n                self.ToggleAttributeLower(attribute, value);\n            });\n            self.BindBack(_ =>\n            {\n                var value = self.HasAttributeLower(attribute);\n                setter(source, value);\n            });\n        }\n\n        internal static Action<TNode, TValue> CompileSetter<TNode, TValue>(\n            Expression<Func<TNode, TValue>> property)\n        {\n            if (property.Body is not MemberExpression member)\n            {\n                throw new ArgumentException(Resources.InvalidBindingExpression);\n            }\n            var param = Expression.Parameter(typeof(TValue), \"value\");\n            var set = Expression.Lambda<Action<TNode, TValue>>(\n                Expression.Assign(member, param), property.Parameters[0], param);\n            return set.Compile();\n        }\n\n        /// <summary>\n        /// Removes bindings for an attribute\n        /// </summary>\n        /// <param name=\"self\"></param>\n        /// <param name=\"attribute\">Attribute to remove bindings of</param>\n        [Obsolete(\"Has no effect anymore. Use UnbindAll instead.\")]\n        [EditorBrowsable(EditorBrowsableState.Never)]\n        public static void UnbindAttribute(this Element self, string attribute)\n        {\n        }\n\n        /// <summary>\n        /// Binds an element's inner text\n        /// </summary>\n        /// <typeparam name=\"T\">Type of source data</typeparam>\n        /// <param name=\"self\"></param>\n        /// <param name=\"options\">Inner text binding options</param>\n        [Obsolete(BindObsolete)]\n        [EditorBrowsable(EditorBrowsableState.Never)]\n        public static void BindInnerText<T>(this Element self, BindInnerTextOptions<T> options)\n            where T : class, INotifyPropertyChanged\n        {\n            var source = options.BindObject ?? throw new ArgumentNullException(nameof(options.BindObject));\n            var property = options.Property ?? throw new ArgumentNullException(nameof(options.Property));\n            self.Bind(source, x => x.InnerText = property(source));\n        }\n\n        /// <summary>\n        /// Removes inner text bindings\n        /// </summary>\n        [Obsolete(\"Has no effect anymore. Use UnbindAll instead.\")]\n        [EditorBrowsable(EditorBrowsableState.Never)]\n        public static void UnbindInnerText(this Element self)\n        {\n        }\n\n        /// <summary>\n        /// Removes bindings for the generic handler\n        /// </summary>\n        [Obsolete(\"Has no effect anymore. Use UnbindAll instead.\")]\n        [EditorBrowsable(EditorBrowsableState.Never)]\n        public static void UnbindHandler(this Element self)\n        {\n        }\n\n        /// <summary>\n        /// Removes bindings for any attributes\n        /// </summary>\n        [Obsolete(\"Has no effect anymore. Use UnbindAll instead.\")]\n        [EditorBrowsable(EditorBrowsableState.Never)]\n        public static void UnbindAttributes(this Element self)\n        {\n        }\n\n        /// <summary>\n        /// Binds the list of children to an observable collection\n        /// </summary>\n        /// <typeparam name=\"T\">Type for items in the collection</typeparam>\n        /// <param name=\"self\"></param>\n        /// <param name=\"options\">Children binding options</param>\n        [Obsolete(\"Use BindChildren(source, factory) instead\")]\n        [EditorBrowsable(EditorBrowsableState.Never)]\n        public static void BindChildren<T>(this Element self, BindChildrenOptions<T> options)\n        {\n            self.BindChildren(options.Collection, options.CreateCallback);\n        }\n\n        /// <summary>\n        /// Removes all bindings for the list of children\n        /// </summary>\n        [Obsolete(\"Has no effect anymore, use UnbindAll when needed\")]\n        [EditorBrowsable(EditorBrowsableState.Never)]\n        public static void UnbindChildren(this Element self)\n        {\n        }\n\n        /// <summary>\n        /// Clears all child nodes and replaces them with a single text node\n        /// </summary>\n        /// <param name=\"self\"></param>\n        /// <param name=\"text\">Text for the node</param>\n        [Obsolete(\"Use InnerText property instead.\")]\n        [EditorBrowsable(EditorBrowsableState.Never)]\n        public static void SetInnerText(this Element self, string text)\n        {\n            self.SetInnerEncode(text, true);\n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Resources.Designer.cs",
    "content": "﻿//------------------------------------------------------------------------------\n// <auto-generated>\n//     This code was generated by a tool.\n//     Runtime Version:4.0.30319.42000\n//\n//     Changes to this file may cause incorrect behavior and will be lost if\n//     the code is regenerated.\n// </auto-generated>\n//------------------------------------------------------------------------------\n\nnamespace Integrative.Lara {\n    using System;\n    \n    \n    /// <summary>\n    ///   A strongly-typed resource class, for looking up localized strings, etc.\n    /// </summary>\n    // This class was auto-generated by the StronglyTypedResourceBuilder\n    // class via a tool like ResGen or Visual Studio.\n    // To add or remove a member, edit your .ResX file then rerun ResGen\n    // with the /str option, or rebuild your VS project.\n    [global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"System.Resources.Tools.StronglyTypedResourceBuilder\", \"16.0.0.0\")]\n    [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]\n    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]\n    internal class Resources {\n        \n        private static global::System.Resources.ResourceManager resourceMan;\n        \n        private static global::System.Globalization.CultureInfo resourceCulture;\n        \n        [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute(\"Microsoft.Performance\", \"CA1811:AvoidUncalledPrivateCode\")]\n        internal Resources() {\n        }\n        \n        /// <summary>\n        ///   Returns the cached ResourceManager instance used by this class.\n        /// </summary>\n        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]\n        internal static global::System.Resources.ResourceManager ResourceManager {\n            get {\n                if (object.ReferenceEquals(resourceMan, null)) {\n                    global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager(\"Integrative.Lara.Resources\", typeof(Resources).Assembly);\n                    resourceMan = temp;\n                }\n                return resourceMan;\n            }\n        }\n        \n        /// <summary>\n        ///   Overrides the current thread's CurrentUICulture property for all\n        ///   resource lookups using this strongly typed resource class.\n        /// </summary>\n        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]\n        internal static global::System.Globalization.CultureInfo Culture {\n            get {\n                return resourceCulture;\n            }\n            set {\n                resourceCulture = value;\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to The Provider propery cannot be null..\n        /// </summary>\n        internal static string AutocompleteNullProvider {\n            get {\n                return ResourceManager.GetString(\"AutocompleteNullProvider\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Autocomplete requieres an input element that is already connected to a document..\n        /// </summary>\n        internal static string AutocompleteOrphan {\n            get {\n                return ResourceManager.GetString(\"AutocompleteOrphan\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Bad request.\n        /// </summary>\n        internal static string BadRequest {\n            get {\n                return ResourceManager.GetString(\"BadRequest\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Cycle detected: modification handlers should not modify the source data..\n        /// </summary>\n        internal static string BindingCycleDetected {\n            get {\n                return ResourceManager.GetString(\"BindingCycleDetected\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Lara applications running in BrowserApp mode are secured to allow only 1 connection..\n        /// </summary>\n        internal static string BrowserAppConnectionRejected {\n            get {\n                return ResourceManager.GetString(\"BrowserAppConnectionRejected\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Cannot add an element inside itself..\n        /// </summary>\n        internal static string CannotAddInsideItself {\n            get {\n                return ResourceManager.GetString(\"CannotAddInsideItself\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Cannot remove from parent, the node has no parent element..\n        /// </summary>\n        internal static string CannotRemoveNoParent {\n            get {\n                return ResourceManager.GetString(\"CannotRemoveNoParent\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Invalid tag name. It needs to have a &apos;-&apos; and cannot have spaces..\n        /// </summary>\n        internal static string DashRequired {\n            get {\n                return ResourceManager.GetString(\"DashRequired\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to The &apos;EventName&apos; property cannot be null or empty..\n        /// </summary>\n        internal static string EventNameNull {\n            get {\n                return ResourceManager.GetString(\"EventNameNull\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to The FlushPartialChanges() method is available only for events declared with the option LongRunnning = true..\n        /// </summary>\n        internal static string FlushNotAvailable {\n            get {\n                return ResourceManager.GetString(\"FlushNotAvailable\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to To use the focus() method, first add the element to a document..\n        /// </summary>\n        internal static string FocusDisconnected {\n            get {\n                return ResourceManager.GetString(\"FocusDisconnected\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to HTTP 403: Forbidden..\n        /// </summary>\n        internal static string Http403 {\n            get {\n                return ResourceManager.GetString(\"Http403\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Input element cannot be null..\n        /// </summary>\n        internal static string InputElementNull {\n            get {\n                return ResourceManager.GetString(\"InputElementNull\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Invalid expression for binding. Expecting a property setter..\n        /// </summary>\n        internal static string InvalidBindingExpression {\n            get {\n                return ResourceManager.GetString(\"InvalidBindingExpression\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Invalid tag name..\n        /// </summary>\n        internal static string InvalidTagName {\n            get {\n                return ResourceManager.GetString(\"InvalidTagName\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Component types must inherit from the Component class..\n        /// </summary>\n        internal static string MustInherit {\n            get {\n                return ResourceManager.GetString(\"MustInherit\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to LaraUI.Context not available.\n        /// </summary>\n        internal static string NoCurrentContext {\n            get {\n                return ResourceManager.GetString(\"NoCurrentContext\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to There is no current document..\n        /// </summary>\n        internal static string NoCurrentDocument {\n            get {\n                return ResourceManager.GetString(\"NoCurrentDocument\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to No page context available..\n        /// </summary>\n        internal static string NoCurrentPage {\n            get {\n                return ResourceManager.GetString(\"NoCurrentPage\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to No service context available..\n        /// </summary>\n        internal static string NoCurrentService {\n            get {\n                return ResourceManager.GetString(\"NoCurrentService\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to There is no current session..\n        /// </summary>\n        internal static string NoCurrentSession {\n            get {\n                return ResourceManager.GetString(\"NoCurrentSession\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Invalid child/parent nodes specified..\n        /// </summary>\n        internal static string NodeNotFoundInsideParent {\n            get {\n                return ResourceManager.GetString(\"NodeNotFoundInsideParent\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Reference before/after node not found..\n        /// </summary>\n        internal static string ReferenceNodeNotFound {\n            get {\n                return ResourceManager.GetString(\"ReferenceNodeNotFound\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Resource not found.\n        /// </summary>\n        internal static string ResourceNotFound {\n            get {\n                return ResourceManager.GetString(\"ResourceNotFound\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to The server encountered an internal error or misconfiguration and was unable to complete your request..\n        /// </summary>\n        internal static string ServerErrorMessage {\n            get {\n                return ResourceManager.GetString(\"ServerErrorMessage\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to ServerEvent already disposed..\n        /// </summary>\n        internal static string ServerEventAlreadyDisposed {\n            get {\n                return ResourceManager.GetString(\"ServerEventAlreadyDisposed\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Server events are not enabled. Call ServerEventsOn() in order to use ServerEventFlush()..\n        /// </summary>\n        internal static string ServerEventsNotEnabled {\n            get {\n                return ResourceManager.GetString(\"ServerEventsNotEnabled\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to The &apos;slot&apos; attribute can only be modified on elements not yet attached to a parent..\n        /// </summary>\n        internal static string SlotOnlyParent {\n            get {\n                return ResourceManager.GetString(\"SlotOnlyParent\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Please specify the address for the web service..\n        /// </summary>\n        internal static string SpecifyAddressService {\n            get {\n                return ResourceManager.GetString(\"SpecifyAddressService\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Please specify the method for the web service (e.g. &apos;POST&apos;)..\n        /// </summary>\n        internal static string SpecifyMethodService {\n            get {\n                return ResourceManager.GetString(\"SpecifyMethodService\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Element tag names cannot contain spaces..\n        /// </summary>\n        internal static string TagNameSpaces {\n            get {\n                return ResourceManager.GetString(\"TagNameSpaces\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Too many Pop() calls..\n        /// </summary>\n        internal static string TooManyPops {\n            get {\n                return ResourceManager.GetString(\"TooManyPops\", resourceCulture);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Resources.resx",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<root>\n  <!-- \n    Microsoft ResX Schema \n    \n    Version 2.0\n    \n    The primary goals of this format is to allow a simple XML format \n    that is mostly human readable. The generation and parsing of the \n    various data types are done through the TypeConverter classes \n    associated with the data types.\n    \n    Example:\n    \n    ... ado.net/XML headers & schema ...\n    <resheader name=\"resmimetype\">text/microsoft-resx</resheader>\n    <resheader name=\"version\">2.0</resheader>\n    <resheader name=\"reader\">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>\n    <resheader name=\"writer\">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>\n    <data name=\"Name1\"><value>this is my long string</value><comment>this is a comment</comment></data>\n    <data name=\"Color1\" type=\"System.Drawing.Color, System.Drawing\">Blue</data>\n    <data name=\"Bitmap1\" mimetype=\"application/x-microsoft.net.object.binary.base64\">\n        <value>[base64 mime encoded serialized .NET Framework object]</value>\n    </data>\n    <data name=\"Icon1\" type=\"System.Drawing.Icon, System.Drawing\" mimetype=\"application/x-microsoft.net.object.bytearray.base64\">\n        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>\n        <comment>This is a comment</comment>\n    </data>\n                \n    There are any number of \"resheader\" rows that contain simple \n    name/value pairs.\n    \n    Each data row contains a name, and value. The row also contains a \n    type or mimetype. Type corresponds to a .NET class that support \n    text/value conversion through the TypeConverter architecture. \n    Classes that don't support this are serialized and stored with the \n    mimetype set.\n    \n    The mimetype is used for serialized objects, and tells the \n    ResXResourceReader how to depersist the object. This is currently not \n    extensible. For a given mimetype the value must be set accordingly:\n    \n    Note - application/x-microsoft.net.object.binary.base64 is the format \n    that the ResXResourceWriter will generate, however the reader can \n    read any of the formats listed below.\n    \n    mimetype: application/x-microsoft.net.object.binary.base64\n    value   : The object must be serialized with \n            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter\n            : and then encoded with base64 encoding.\n    \n    mimetype: application/x-microsoft.net.object.soap.base64\n    value   : The object must be serialized with \n            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter\n            : and then encoded with base64 encoding.\n\n    mimetype: application/x-microsoft.net.object.bytearray.base64\n    value   : The object must be serialized into a byte array \n            : using a System.ComponentModel.TypeConverter\n            : and then encoded with base64 encoding.\n    -->\n  <xsd:schema id=\"root\" xmlns=\"\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:msdata=\"urn:schemas-microsoft-com:xml-msdata\">\n    <xsd:import namespace=\"http://www.w3.org/XML/1998/namespace\" />\n    <xsd:element name=\"root\" msdata:IsDataSet=\"true\">\n      <xsd:complexType>\n        <xsd:choice maxOccurs=\"unbounded\">\n          <xsd:element name=\"metadata\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" use=\"required\" type=\"xsd:string\" />\n              <xsd:attribute name=\"type\" type=\"xsd:string\" />\n              <xsd:attribute name=\"mimetype\" type=\"xsd:string\" />\n              <xsd:attribute ref=\"xml:space\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"assembly\">\n            <xsd:complexType>\n              <xsd:attribute name=\"alias\" type=\"xsd:string\" />\n              <xsd:attribute name=\"name\" type=\"xsd:string\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"data\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"1\" />\n                <xsd:element name=\"comment\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"2\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" type=\"xsd:string\" use=\"required\" msdata:Ordinal=\"1\" />\n              <xsd:attribute name=\"type\" type=\"xsd:string\" msdata:Ordinal=\"3\" />\n              <xsd:attribute name=\"mimetype\" type=\"xsd:string\" msdata:Ordinal=\"4\" />\n              <xsd:attribute ref=\"xml:space\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"resheader\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"1\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" type=\"xsd:string\" use=\"required\" />\n            </xsd:complexType>\n          </xsd:element>\n        </xsd:choice>\n      </xsd:complexType>\n    </xsd:element>\n  </xsd:schema>\n  <resheader name=\"resmimetype\">\n    <value>text/microsoft-resx</value>\n  </resheader>\n  <resheader name=\"version\">\n    <value>2.0</value>\n  </resheader>\n  <resheader name=\"reader\">\n    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\n  </resheader>\n  <resheader name=\"writer\">\n    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\n  </resheader>\n  <data name=\"AutocompleteNullProvider\" xml:space=\"preserve\">\n    <value>The Provider propery cannot be null.</value>\n  </data>\n  <data name=\"AutocompleteOrphan\" xml:space=\"preserve\">\n    <value>Autocomplete requieres an input element that is already connected to a document.</value>\n  </data>\n  <data name=\"BadRequest\" xml:space=\"preserve\">\n    <value>Bad request</value>\n  </data>\n  <data name=\"BindingCycleDetected\" xml:space=\"preserve\">\n    <value>Cycle detected: modification handlers should not modify the source data.</value>\n  </data>\n  <data name=\"BrowserAppConnectionRejected\" xml:space=\"preserve\">\n    <value>Lara applications running in BrowserApp mode are secured to allow only 1 connection.</value>\n  </data>\n  <data name=\"CannotAddInsideItself\" xml:space=\"preserve\">\n    <value>Cannot add an element inside itself.</value>\n  </data>\n  <data name=\"CannotRemoveNoParent\" xml:space=\"preserve\">\n    <value>Cannot remove from parent, the node has no parent element.</value>\n  </data>\n  <data name=\"DashRequired\" xml:space=\"preserve\">\n    <value>Invalid tag name. It needs to have a '-' and cannot have spaces.</value>\n  </data>\n  <data name=\"EventNameNull\" xml:space=\"preserve\">\n    <value>The 'EventName' property cannot be null or empty.</value>\n  </data>\n  <data name=\"FlushNotAvailable\" xml:space=\"preserve\">\n    <value>The FlushPartialChanges() method is available only for events declared with the option LongRunnning = true.</value>\n  </data>\n  <data name=\"FocusDisconnected\" xml:space=\"preserve\">\n    <value>To use the focus() method, first add the element to a document.</value>\n  </data>\n  <data name=\"Http403\" xml:space=\"preserve\">\n    <value>HTTP 403: Forbidden.</value>\n  </data>\n  <data name=\"InputElementNull\" xml:space=\"preserve\">\n    <value>Input element cannot be null.</value>\n  </data>\n  <data name=\"InvalidBindingExpression\" xml:space=\"preserve\">\n    <value>Invalid expression for binding. Expecting a property setter.</value>\n  </data>\n  <data name=\"InvalidTagName\" xml:space=\"preserve\">\n    <value>Invalid tag name.</value>\n  </data>\n  <data name=\"MustInherit\" xml:space=\"preserve\">\n    <value>Component types must inherit from the Component class.</value>\n  </data>\n  <data name=\"NoCurrentContext\" xml:space=\"preserve\">\n    <value>LaraUI.Context not available</value>\n  </data>\n  <data name=\"NoCurrentDocument\" xml:space=\"preserve\">\n    <value>There is no current document.</value>\n  </data>\n  <data name=\"NoCurrentPage\" xml:space=\"preserve\">\n    <value>No page context available.</value>\n  </data>\n  <data name=\"NoCurrentService\" xml:space=\"preserve\">\n    <value>No service context available.</value>\n  </data>\n  <data name=\"NoCurrentSession\" xml:space=\"preserve\">\n    <value>There is no current session.</value>\n  </data>\n  <data name=\"NodeNotFoundInsideParent\" xml:space=\"preserve\">\n    <value>Invalid child/parent nodes specified.</value>\n  </data>\n  <data name=\"ReferenceNodeNotFound\" xml:space=\"preserve\">\n    <value>Reference before/after node not found.</value>\n  </data>\n  <data name=\"ServerErrorMessage\" xml:space=\"preserve\">\n    <value>The server encountered an internal error or misconfiguration and was unable to complete your request.</value>\n  </data>\n  <data name=\"ServerEventAlreadyDisposed\" xml:space=\"preserve\">\n    <value>ServerEvent already disposed.</value>\n  </data>\n  <data name=\"ServerEventsNotEnabled\" xml:space=\"preserve\">\n    <value>Server events are not enabled. Call ServerEventsOn() in order to use ServerEventFlush().</value>\n  </data>\n  <data name=\"SlotOnlyParent\" xml:space=\"preserve\">\n    <value>The 'slot' attribute can only be modified on elements not yet attached to a parent.</value>\n  </data>\n  <data name=\"SpecifyAddressService\" xml:space=\"preserve\">\n    <value>Please specify the address for the web service.</value>\n  </data>\n  <data name=\"SpecifyMethodService\" xml:space=\"preserve\">\n    <value>Please specify the method for the web service (e.g. 'POST').</value>\n  </data>\n  <data name=\"TagNameSpaces\" xml:space=\"preserve\">\n    <value>Element tag names cannot contain spaces.</value>\n  </data>\n  <data name=\"TooManyPops\" xml:space=\"preserve\">\n    <value>Too many Pop() calls.</value>\n  </data>\n  <data name=\"ResourceNotFound\" xml:space=\"preserve\">\n    <value>Resource not found</value>\n  </data>\n</root>"
  },
  {
    "path": "src/LaraUI/Tools/ApplicationBuilderLaraExtensions.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 5/2019\nAuthor: Pablo Carbonell\n*/\n\nusing Microsoft.AspNetCore.Builder;\nusing System;\nusing System.ComponentModel;\n\nnamespace Integrative.Lara\n{\n    /// <summary>\n    /// ASP.NET Core Extensions for using Lara\n    /// </summary>\n    public static class ApplicationBuilderLaraExtensions\n    {\n        /// <summary>\n        /// Use the Lara Web Engine.\n        /// </summary>\n        /// <param name=\"app\">The application.</param>\n        /// <param name=\"laraApp\">Lara application</param>\n        /// <param name=\"options\">The options.</param>\n        /// <returns>app in parameters</returns>\n        public static IApplicationBuilder UseLara(this IApplicationBuilder app, Application laraApp, LaraOptions options)\n        {\n            app = app ?? throw new ArgumentNullException(nameof(app));\n            laraApp = laraApp ?? throw new ArgumentNullException(nameof(laraApp));\n            options = options ?? throw new ArgumentNullException(nameof(options));\n            laraApp.CreateModeController(options.Mode);\n            if (options.PublishAssembliesOnStart)\n            {\n                laraApp.PublishAssemblies();\n            }\n            if (options.AllowLocalhostOnly || laraApp.AllowLocalhostOnly)\n            {\n                app.UseMiddleware<LocalhostFilter>();\n            }\n            if (options.AddWebSocketsMiddleware)\n            {\n                app.UseWebSockets();\n            }\n            app.UseMiddleware<LaraMiddleware>(laraApp, options);\n            if (options.ShowNotFoundPage)\n            {\n                app.UseMiddleware<NotFoundMiddleware>(laraApp, options);\n            }\n            return app;\n        }\n\n        /// <summary>\n        /// Use the Lara Web Engine.\n        /// </summary>\n        /// <param name=\"app\">The application.</param>\n        /// <param name=\"options\">The options.</param>\n        /// <returns>app in parameters</returns>\n        [Obsolete(\"Specify which Lara Application to use in the parameters of the call\")]\n        [EditorBrowsable(EditorBrowsableState.Never)]\n        public static IApplicationBuilder UseLara(this IApplicationBuilder app, LaraOptions options)\n            => UseLara(app, LaraUI.DefaultApplication, options);\n\n        /// <summary>\n        /// Use the Lara Web Engine.\n        /// </summary>\n        /// <param name=\"app\">The application.</param>\n        /// <returns>app in parameters</returns>\n        [Obsolete(\"Specify which Lara Application to use in the parameters of the call\")]\n        [EditorBrowsable(EditorBrowsableState.Never)]\n        public static IApplicationBuilder UseLara(this IApplicationBuilder app)\n        {\n            return UseLara(app, new LaraOptions());\n        }\n\n        /// <summary>\n        /// Use the Lara Web Engine\n        /// </summary>\n        /// <param name=\"app\">ASP.NET Core ApplicationBuilder</param>\n        /// <param name=\"laraApp\">Lara Application</param>\n        /// <returns></returns>\n        public static IApplicationBuilder UseLara(this IApplicationBuilder app, Application laraApp)\n        {\n            return UseLara(app, laraApp, new LaraOptions());\n        }\n\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Tools/AssembliesReader.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 7/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Reflection;\n\nnamespace Integrative.Lara\n{\n    internal static class AssembliesReader\n    {\n        public static void LoadAssemblies(Application app)\n        {\n            foreach (var assembly in GetAssembliesReferencingLara())\n            {\n                LoadAssembly(app, assembly);\n            }\n        }\n\n        private static IEnumerable<Assembly> GetAssembliesReferencingLara()\n        {\n            var definedIn = typeof(LaraWebServiceAttribute).Assembly.GetName().Name;\n            foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())\n            {\n                if (IncludeAssembly(assembly, definedIn))\n                {\n                    yield return assembly;\n                }\n            }\n        }\n\n        private static bool IncludeAssembly(Assembly assembly, string definedIn)\n        {\n            return (!assembly.GlobalAssemblyCache)\n                && ((assembly.GetName().Name == definedIn)\n                    || assembly.GetReferencedAssemblies().Any(a => a.Name == definedIn));\n        }\n\n        private static void LoadAssembly(Application app, Assembly assembly)\n        {\n            try\n            {\n                var types = assembly.GetTypes();\n                foreach (var type in types)\n                {\n                    LoadWebServices(app, type);\n                    LoadBinaryServices(app, type);\n                    LoadPages(app, type);\n                    LoadComponents(app, type);\n                }\n            }\n            catch (ReflectionTypeLoadException)\n            {\n            }\n        }\n\n        private static void LoadWebServices(Application app, Type type)\n        {\n            var services = type.GetCustomAttributes(typeof(LaraWebServiceAttribute), true);\n            foreach (LaraWebServiceAttribute entry in services)\n            {\n                VerifyType(type, \"LaraWebService\", typeof(IWebService));\n                app.PublishService(new WebServiceContent\n                {\n                    Address = entry.Address,\n                    ContentType = entry.ContentType,\n                    Factory = () => (IWebService)Activator.CreateInstance(type),\n                    Method = entry.Method\n                });\n            }\n        }\n\n        internal static void VerifyType(Type assemblyType, string attribute, Type requiredType)\n        {\n            if (requiredType.IsAssignableFrom(assemblyType)) return;\n            var message = $\"The class {assemblyType.FullName} marked as [{attribute}] needs to implement/inherit [{requiredType.Name}].\";\n            throw new InvalidOperationException(message);\n        }\n\n        private static void LoadBinaryServices(Application app, Type type)\n        {\n            var services = type.GetCustomAttributes(typeof(LaraBinaryServiceAttribute), true);\n            foreach (LaraBinaryServiceAttribute entry in services)\n            {\n                VerifyType(type, \"LaraBinaryService\", typeof(IBinaryService));\n                app.PublishService(new BinaryServiceContent\n                {\n                    Address = entry.Address,\n                    ContentType = entry.ContentType,\n                    Factory = () => (IBinaryService)Activator.CreateInstance(type),\n                    Method = entry.Method\n                });\n            }\n        }\n\n        private static void LoadPages(Application app, Type type)\n        {\n            var pages = type.GetCustomAttributes(typeof(LaraPageAttribute), true);\n            foreach (LaraPageAttribute entry in pages)\n            {\n                VerifyType(type, \"LaraPage\", typeof(IPage));\n                app.PublishPage(entry.Address, () => (IPage)Activator.CreateInstance(type));\n            }\n        }\n\n        private static void LoadComponents(Application app, Type type)\n        {\n            var components = type.GetCustomAttributes(typeof(LaraWebComponentAttribute), true);\n            foreach (LaraWebComponentAttribute entry in components)\n            {\n                VerifyType(type, \"LaraWebComponent\", typeof(WebComponent));\n                var tagName = string.IsNullOrEmpty(entry.ComponentTagName)\n                    ? Element.GetDefaultTagName(type)\n                    : entry.ComponentTagName;\n                app.PublishComponent(new WebComponentOptions\n                {\n                    ComponentTagName = tagName,\n                    ComponentType = type\n                });\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Tools/AsyncEvent.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 9/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System;\nusing System.Collections.Generic;\nusing System.Threading.Tasks;\n\nnamespace Integrative.Lara\n{\n    /// <summary>\n    /// Delegate for asynchronous events\n    /// </summary>\n    /// <typeparam name=\"T\">Type of event arguments</typeparam>\n    /// <param name=\"sender\">Sender of event</param>\n    /// <param name=\"args\">Event arguments</param>\n    /// <returns>Task</returns>\n    public delegate Task AsyncEventHandler<T>(object sender, T args) where T : EventArgs;\n\n    /// <summary>\n    /// Asynchronous event source\n    /// </summary>\n    public class AsyncEvent : AsyncEvent<EventArgs>\n    {\n    }\n\n    /// <summary>\n    /// Generic asynchronous event source\n    /// </summary>\n    /// <typeparam name=\"T\">Type of event arguments</typeparam>\n    public class AsyncEvent<T> where T : EventArgs\n    {\n        private readonly HashSet<AsyncEventHandler<T>> _handlers;\n        private readonly HashSet<AsyncEvent<T>> _batons;\n        private readonly HashSet<Func<Task>> _actions;\n        private readonly HashSet<Action> _voids;\n\n        /// <summary>\n        /// Constructor\n        /// </summary>\n        public AsyncEvent()\n        {\n            _handlers = new HashSet<AsyncEventHandler<T>>();\n            _batons = new HashSet<AsyncEvent<T>>();\n            _actions = new HashSet<Func<Task>>();\n            _voids = new HashSet<Action>();\n        }\n\n        /// <summary>\n        /// Adds an action to execute when this event is fired\n        /// </summary>\n        /// <param name=\"action\">action to execute</param>\n        public void Subscribe(Func<Task> action)\n        {\n            action = action ?? throw new ArgumentNullException(nameof(action));\n            _actions.Add(action);\n        }\n\n        /// <summary>\n        /// Adds a method to execute when the event is fired\n        /// </summary>\n        /// <param name=\"handler\">delegate</param>\n        public void Subscribe(AsyncEventHandler<T> handler)\n        {\n            handler = handler ?? throw new ArgumentNullException(nameof(handler));\n            _handlers.Add(handler);\n        }\n\n        /// <summary>\n        /// Adds an event to trigger when this event is fired\n        /// </summary>\n        /// <param name=\"other\">event to trigger</param>\n        public void Subscribe(AsyncEvent<T> other)\n        {\n            other = other ?? throw new ArgumentNullException(nameof(other));\n            _batons.Add(other);\n        }\n\n        /// <summary>\n        /// Subscribes an action to execute when the event is triggered\n        /// </summary>\n        /// <param name=\"handler\">Action</param>\n        public void Subscribe(Action handler)\n        {\n            handler = handler ?? throw new ArgumentNullException(nameof(handler));\n            _voids.Add(handler);\n        }\n\n        /// <summary>\n        /// Unsubscribes a previously subscribed handler\n        /// </summary>\n        /// <param name=\"handler\">handler</param>\n        public void Unsubscribe(AsyncEventHandler<T> handler)\n        {\n            handler = handler ?? throw new ArgumentNullException(nameof(handler));\n            _handlers.Remove(handler);\n        }\n\n        /// <summary>\n        /// Unsubscribes a previously subscribed handler\n        /// </summary>\n        /// <param name=\"handler\">handler</param>\n        public void Unsubscribe(AsyncEvent<T> handler)\n        {\n            handler = handler ?? throw new ArgumentNullException(nameof(handler));\n            _batons.Remove(handler);\n        }\n\n        /// <summary>\n        /// Unsubscribes a previously subscribed action\n        /// </summary>\n        /// <param name=\"action\">Action</param>\n        public void Unsubscribe(Func<Task> action)\n        {\n            action = action ?? throw new ArgumentNullException(nameof(action));\n            _actions.Remove(action);\n        }\n\n        /// <summary>\n        /// Unsubscribes a previously subscribed action\n        /// </summary>\n        /// <param name=\"action\">Action</param>\n        public void Unsubscribe(Action action)\n        {\n            action = action ?? throw new ArgumentNullException(nameof(action));\n            _voids.Remove(action);\n        }\n\n        /// <summary>\n        /// Invokes an event\n        /// </summary>\n        /// <param name=\"sender\">Event's source</param>\n        /// <param name=\"args\">Event's arguments</param>\n        /// <returns>Task</returns>\n        public async Task InvokeAsync(object sender, T args)\n        {\n            foreach (var handler in _handlers)\n            {\n                await handler(sender, args);\n            }\n            foreach (var baton in _batons)\n            {\n                await baton.InvokeAsync(sender, args);\n            }\n            foreach (var action in _actions)\n            {\n                await action();\n            }\n            foreach (var method in _voids)\n            {\n                method();\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Tools/ClassEditor.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 5/2019\nAuthor: Pablo Carbonell\n*/\n\nnamespace Integrative.Lara\n{\n    internal static class ClassEditor\n    {\n        public static string? AddClass(string? previous, string name)\n        {\n            if (HasClass(previous, name))\n            {\n                return previous;\n            }\n\n            if (string.IsNullOrWhiteSpace(previous))\n            {\n                return name;\n            }\n            return previous.TrimEnd() + \" \" + name;\n        }\n\n        public static string? RemoveClass(string? previous, string name)\n        {\n            if (string.IsNullOrWhiteSpace(name)\n                || string.IsNullOrWhiteSpace(previous))\n            {\n                return previous;\n            }\n            name = name.Trim();\n            previous = previous.Trim();\n            if (previous == name)\n            {\n                return \"\";\n            }\n\n            if (previous.StartsWith(name + \" \", System.StringComparison.InvariantCulture))\n            {\n                return previous.Substring(name.Length + 1);\n            }\n            if (previous.EndsWith(\" \" + name, System.StringComparison.InvariantCulture))\n            {\n                return previous.Substring(0, previous.Length - name.Length - 1);\n            }\n\n            var index = previous.IndexOf(\" \" + name + \" \", System.StringComparison.InvariantCulture);\n            if (index == -1)\n            {\n                return previous;\n            }\n            var left = previous.Substring(0, index);\n            var right = previous.Substring(index + name.Length + 1);\n            return left + right;\n        }\n\n        public static string? ToggleClass(string? previous, string name, bool value)\n        {\n            return value ? AddClass(previous, name) : RemoveClass(previous, name);\n        }\n\n        public static string? ToggleClass(string? previous, string name)\n        {\n            var value = HasClass(previous, name);\n            return ToggleClass(previous, name, !value);\n        }\n\n        public static bool HasClass(string? elementClass, string className)\n        {\n            if (string.IsNullOrWhiteSpace(className))\n            {\n                return true;\n            }\n\n            if (string.IsNullOrWhiteSpace(elementClass))\n            {\n                return false;\n            }\n            elementClass = elementClass.Trim();\n            return elementClass == className\n                   || elementClass.StartsWith(className + \" \", System.StringComparison.InvariantCulture)\n                   || elementClass.EndsWith(\" \" + className, System.StringComparison.InvariantCulture)\n                   || elementClass.Contains(\" \" + className + \" \", System.StringComparison.InvariantCulture);\n        }\n\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Tools/DocumentLocal.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 9/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System;\nusing System.Collections.Generic;\n\nnamespace Integrative.Lara\n{\n    /// <summary>\n    /// Represents ambient data that is local to the current document.\n    /// </summary>\n    /// <typeparam name=\"T\">Value</typeparam>\n    public class DocumentLocal<T>\n    {\n        private readonly Dictionary<Document, T> _storage;\n\n        /// <summary>\n        /// Constructor\n        /// </summary>\n        public DocumentLocal()\n        {\n            _storage = new Dictionary<Document, T>();\n        }\n\n        /// <summary>\n        /// Value\n        /// </summary>\n        public T Value\n        {\n            get => GetValue();\n            set => SetValue(value);\n        }\n\n        private T GetValue()\n        {\n            var document = GetDocument();\n            _storage.TryGetValue(document, out var value);\n            return value;\n        }\n\n        private void SetValue(T value)\n        {\n            var document = GetDocument();\n            if (_storage.TryGetValue(document, out var previous))\n            {\n                if (LaraTools.SameValue(previous, value))\n                {\n                    return;\n                }\n                _storage.Remove(document);\n                _storage.Add(document, value);\n            }\n            else\n            {\n                _storage.Add(document, value);\n                document.UnloadComplete += (_, _) => _storage.Remove(document);\n            }\n        }\n\n        private static Document GetDocument()\n        {\n            if (LaraUI.Page == null)\n            {\n                throw new InvalidOperationException(Resources.NoCurrentDocument);\n            }\n            return LaraUI.Page.Document;\n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Tools/LaraBuilder.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 5/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System;\nusing System.Collections.Generic;\nusing System.Collections.ObjectModel;\nusing System.ComponentModel;\nusing System.Linq.Expressions;\nusing System.Threading.Tasks;\n\nnamespace Integrative.Lara\n{\n    /// <summary>\n    /// Class to build pages more easily\n    /// </summary>\n    public sealed class LaraBuilder\n    {\n        #region Constructor\n\n        private readonly Stack<Element> _stack;\n\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"LaraBuilder\"/> class.\n        /// </summary>\n        /// <param name=\"startingElement\">The starting element to build on.</param>\n        public LaraBuilder(Element startingElement)\n        {\n            startingElement = startingElement ?? throw new ArgumentNullException(nameof(startingElement));\n            _stack = new Stack<Element>();\n            _stack.Push(startingElement);\n        }\n\n        #endregion\n\n        #region Pushing and popping elements\n\n        /// <summary>\n        /// Adds a new element and positions the builder into it.\n        /// </summary>\n        /// <param name=\"tagName\">Element's tag name.</param>\n        /// <param name=\"className\">Element's class name</param>\n        /// <param name=\"id\">element's identifier.</param>\n        /// <returns>This instance</returns>\n        public LaraBuilder Push(string tagName, string? className = null, string? id = null)\n        {\n            return Push(tagName, className, id, out _);\n        }\n\n        /// <summary>\n        /// Adds a new element and positions the builder into it.\n        /// </summary>\n        /// <param name=\"tagName\">Element's tag name.</param>\n        /// <param name=\"className\">Element's class name</param>\n        /// <param name=\"id\">The identifier.</param>\n        /// <param name=\"added\">The element added.</param>\n        /// <returns>This instance</returns>\n        public LaraBuilder Push(string tagName, string? className, string? id, out Element added)\n        {\n            added = Element.Create(tagName);\n            if (!string.IsNullOrEmpty(id))\n            {\n                added.Id = id;\n            }\n            if (!string.IsNullOrEmpty(className))\n            {\n                added.Class = className;\n            }\n            return Push(added);\n        }\n\n        /// <summary>\n        /// Adds a new namespace-specific element (e.g. 'svg') and positions the builder into it.\n        /// </summary>\n        /// <param name=\"ns\">The namespace of the element.</param>\n        /// <param name=\"tagName\">The element's tag name</param>\n        /// <returns>This instance</returns>\n        // ReSharper disable once InconsistentNaming\n        public LaraBuilder PushNS(string ns, string tagName)\n        {\n            var added = Element.CreateNS(ns, tagName);\n            return Push(added);\n        }\n\n        /// <summary>\n        /// Adds an element and positions the builder into it.\n        /// </summary>\n        /// <param name=\"element\">The element.</param>\n        /// <returns>This instance</returns>\n        public LaraBuilder Push(Element element)\n        {\n            element = element ?? throw new ArgumentNullException(nameof(element));\n            _stack.Push(element);\n            return this;\n        }\n\n        /// <summary>\n        /// Adds an element and positions the builder into it.\n        /// </summary>\n        /// <param name=\"element\">The element to add.</param>\n        /// <param name=\"className\">Name of the class.</param>\n        /// <returns>This instance</returns>\n        public LaraBuilder Push(Element element, string? className)\n        {\n            className = className ?? throw new ArgumentNullException(nameof(className));\n            Push(element);\n            element.Class = className;\n            return this;\n        }\n\n        /// <summary>\n        /// Positions the builder one position back on the element stack.\n        /// </summary>\n        /// <returns>This instance</returns>\n        /// <exception cref=\"InvalidOperationException\">Too many Pop() calls.</exception>\n        public LaraBuilder Pop()\n        {\n            if (_stack.Count <= 1)\n            {\n                throw new InvalidOperationException(Resources.TooManyPops);\n            }\n            var pop = _stack.Pop();\n            if (_stack.Count <= 0) return this;\n            var current = _stack.Peek();\n            current.AppendChild(pop);\n            return this;\n        }\n\n        /// <summary>\n        /// Returns the current element in the out variable\n        /// </summary>\n        /// <param name=\"element\">Current element</param>\n        /// <returns>This instance</returns>\n        public LaraBuilder GetCurrent(out Element element)\n        {\n            element = _stack.Peek();\n            return this;\n        }\n\n        #endregion\n\n        #region Adding nodes\n\n        /// <summary>\n        /// Adds a text node.\n        /// </summary>\n        /// <param name=\"text\">The text.</param>\n        /// <param name=\"encode\">if set to <c>true</c> [encode].</param>\n        /// <returns>This instance</returns>\n        [Obsolete(\"Use AppendText() or AppendData() instead of AddTextNode\")]\n        [EditorBrowsable(EditorBrowsableState.Never)]\n        public LaraBuilder AddTextNode(string text, bool encode = true)\n        {\n            return AddTextNode(new TextNode(text, encode));\n        }\n\n        /// <summary>\n        /// Adds a text node.\n        /// </summary>\n        /// <param name=\"node\">The node.</param>\n        /// <returns>This instance</returns>\n        [Obsolete(\"Use AppendText() or AppendData() instead of AddTextNode\")]\n        [EditorBrowsable(EditorBrowsableState.Never)]\n        public LaraBuilder AddTextNode(TextNode node)\n        {\n            node = node ?? throw new ArgumentNullException(nameof(node));\n            return AddNode(node);\n        }\n\n        /// <summary>\n        /// Appends text to the current element\n        /// </summary>\n        /// <param name=\"text\">Text to append</param>\n        /// <returns>This instance</returns>\n        public LaraBuilder AppendText(string text)\n        {\n            return AppendEncode(text, true);\n        }\n\n        /// <summary>\n        /// Appends raw HTML to the current element\n        /// </summary>\n        /// <param name=\"data\">raw HTML</param>\n        /// <returns>This instance</returns>\n        public LaraBuilder AppendData(string data)\n        {\n            return AppendEncode(data, false);\n        }\n\n        private LaraBuilder AppendEncode(string value, bool encode)\n        {\n            if (!string.IsNullOrEmpty(value))\n            {\n                _stack.Peek().AppendEncode(value, encode);\n            }\n            return this;\n        }\n\n        /// <summary>\n        /// Clears all child elements and replaces them with a text node\n        /// </summary>\n        /// <param name=\"text\">Text</param>\n        /// <returns>This instance</returns>\n        public LaraBuilder InnerText(string text)\n        {\n            return InnerEncode(text, true);\n        }\n\n        /// <summary>\n        /// Clears all child elements and replaces them with a text node\n        /// </summary>\n        /// <param name=\"data\">raw HTML code</param>\n        /// <returns>This instance</returns>\n        public LaraBuilder InnerData(string data)\n        {\n            return InnerEncode(data, false);\n        }\n\n        private LaraBuilder InnerEncode(string data, bool encode)\n        {\n            if (!string.IsNullOrEmpty(data))\n            {\n                _stack.Peek().SetInnerEncode(data, encode);\n            }\n            return this;\n        }\n\n        /// <summary>\n        /// Adds a node.\n        /// </summary>\n        /// <param name=\"node\">The node.</param>\n        /// <returns>This instance</returns>\n        public LaraBuilder AddNode(Node node)\n        {\n            node = node ?? throw new ArgumentNullException(nameof(node));\n            var current = _stack.Peek();\n            current.AppendChild(node);\n            return this;\n        }\n\n        /// <summary>\n        /// Adds a collection of nodes.\n        /// </summary>\n        /// <param name=\"nodes\">The nodes.</param>\n        /// <returns>This instance</returns>\n        public LaraBuilder AddNodes(IEnumerable<Node> nodes)\n        {\n            nodes = nodes ?? throw new ArgumentNullException(nameof(nodes));\n            var current = _stack.Peek();\n            foreach (var node in nodes)\n            {\n                current.AppendChild(node);\n            }\n            return this;\n        }\n\n        /// <summary>\n        /// Adds a collection of elements.\n        /// </summary>\n        /// <param name=\"nodes\">The nodes.</param>\n        /// <returns>This instance</returns>\n        public LaraBuilder AddNodes(IEnumerable<Element> nodes)\n        {\n            nodes = nodes ?? throw new ArgumentNullException(nameof(nodes));\n            var current = _stack.Peek();\n            foreach (var node in nodes)\n            {\n                current.AppendChild(node);\n            }\n            return this;\n        }\n\n        /// <summary>\n        /// Adds the elements generated by the executing the specified handler.\n        /// </summary>\n        /// <param name=\"action\">The action.</param>\n        /// <returns>This instance</returns>\n        public LaraBuilder Add(Action<LaraBuilder> action)\n        {\n            action = action ?? throw new ArgumentNullException(nameof(action));\n            action(this);\n            return this;\n        }\n\n        #endregion\n\n        #region Current element attributes\n\n        /// <summary>\n        /// Sets an attribute on the current element.\n        /// </summary>\n        /// <param name=\"attribute\">The attribute.</param>\n        /// <param name=\"value\">The value.</param>\n        /// <returns>Thsi instance</returns>\n        public LaraBuilder Attribute(string attribute, string value)\n        {\n            var current = _stack.Peek();\n            current.SetAttribute(attribute, value);\n            return this;\n        }\n\n        /// <summary>\n        /// Creates an ID for the current element if it doesn't have one\n        /// </summary>\n        /// <returns>This instance</returns>\n        [Obsolete(\"Not needed anymore\")]\n        [EditorBrowsable(EditorBrowsableState.Never)]\n        public LaraBuilder EnsureElementId()\n        {\n            return this;\n        }\n\n        /// <summary>\n        /// Sets a flag attribute value.\n        /// </summary>\n        /// <param name=\"attribute\">The attribute.</param>\n        /// <param name=\"value\">if set to <c>true</c> [value].</param>\n        /// <returns>This instance</returns>\n        public LaraBuilder FlagAttribute(string attribute, bool value)\n        {\n            var current = _stack.Peek();\n            current.SetFlagAttribute(attribute, value);\n            return this;\n        }\n\n        /// <summary>\n        /// Adds a class to the current element\n        /// </summary>\n        /// <param name=\"className\">class to add</param>\n        /// <returns>This instance</returns>\n        public LaraBuilder AddClass(string className)\n        {\n            _stack.Peek().AddClass(className);\n            return this;\n        }\n\n        /// <summary>\n        /// Removes a class from the current element\n        /// </summary>\n        /// <param name=\"className\">Class to remove</param>\n        /// <returns>This instance</returns>\n        public LaraBuilder RemoveClass(string className)\n        {\n            _stack.Peek().RemoveClass(className);\n            return this;\n        }\n\n        /// <summary>\n        /// Adds or removes a class from the current element\n        /// </summary>\n        /// <param name=\"className\">Class to toggle</param>\n        /// <returns>This instamce</returns>\n        public LaraBuilder ToggleClass(string className)\n        {\n            _stack.Peek().ToggleClass(className);\n            return this;\n        }\n\n        /// <summary>\n        /// Adds or removes a class from the current element\n        /// </summary>\n        /// <param name=\"className\">Class to toggle</param>\n        /// <param name=\"value\">true to add, false to remove</param>\n        /// <returns>This instance</returns>\n        public LaraBuilder ToggleClass(string className, bool value)\n        {\n            _stack.Peek().ToggleClass(className, value);\n            return this;\n        }\n\n        #endregion\n\n        #region Current element events\n\n        /// <summary>\n        /// Associates an event handler with the current element.\n        /// </summary>\n        /// <param name=\"settings\">The settings.</param>\n        /// <returns>This instance</returns>\n        public LaraBuilder On(EventSettings settings)\n        {\n            _stack.Peek().On(settings);\n            return this;\n        }\n\n        /// <summary>\n        /// Associates an event handler with the current element.\n        /// </summary>\n        /// <param name=\"eventName\">Name of the event.</param>\n        /// <param name=\"handler\">The handler.</param>\n        /// <returns>This instance</returns>\n        public LaraBuilder On(string eventName, Func<Task> handler)\n        {\n            _stack.Peek().On(eventName, handler);\n            return this;\n        }\n\n        /// <summary>\n        /// Associates an event handler with the current element.\n        /// </summary>\n        /// <param name=\"eventName\">Name of the event</param>\n        /// <param name=\"handler\">Action to execute</param>\n        /// <returns>This instance</returns>\n        public LaraBuilder On(string eventName, Action handler)\n        {\n            _stack.Peek().On(eventName, () =>\n            {\n                handler();\n                return Task.CompletedTask;\n            });\n            return this;\n        }\n\n        /// <summary>\n        /// Listens to an event, only purpose to flush data from the client to the server.\n        /// This forces the server to read input values on the client on the given event name.\n        /// </summary>\n        /// <param name=\"eventName\">event to listen</param>\n        /// <returns>This instance</returns>\n        public LaraBuilder FlushEvent(string eventName)\n        {\n            return On(eventName, () => Task.CompletedTask);\n        }\n\n        #endregion\n\n        #region Bindings\n\n        /// <summary>\n        /// Executes code whenever a source object triggers the PropertyChanged event\n        /// </summary>\n        /// <param name=\"source\"></param>\n        /// <param name=\"onSourceChange\"></param>\n        /// <returns></returns>\n        public LaraBuilder Bind(INotifyPropertyChanged source, Action<Element> onSourceChange)\n        {\n            BindingExtensions.Bind(_stack.Peek(), source, onSourceChange);\n            return this;\n        }\n\n        /// <summary>\n        /// Executes code whenever the element triggers the PropertyChanged event\n        /// </summary>\n        /// <param name=\"onChange\"></param>\n        /// <returns></returns>\n        public LaraBuilder BindBack(Action<Element> onChange)\n        {\n            BindingExtensions.BindBack(_stack.Peek(), onChange);\n            return this;\n        }\n\n        /// <summary>\n        /// Updates the element's children collection based on an observable collection\n        /// </summary>\n        /// <typeparam name=\"TValue\"></typeparam>\n        /// <param name=\"source\"></param>\n        /// <param name=\"childFactory\"></param>\n        /// <returns></returns>\n        public LaraBuilder BindChildren<TValue>(ObservableCollection<TValue> source, Func<TValue, Element> childFactory)\n        {\n            BindingExtensions.BindChildren(_stack.Peek(), source, childFactory);\n            return this;\n        }\n\n        #endregion\n\n        #region Current element bindings\n\n        /// <summary>\n        /// Adds bindings for an attribute\n        /// </summary>\n        /// <typeparam name=\"T\">Data type for data source</typeparam>\n        /// <param name=\"attribute\">Attribute</param>\n        /// <param name=\"instance\">Data source instance</param>\n        /// <param name=\"property\">Data source's property</param>\n        /// <returns>This instance</returns>\n        [Obsolete(ObsoleteElement.BindObsolete)]\n        [EditorBrowsable(EditorBrowsableState.Never)]\n        public LaraBuilder BindAttribute<T>(string attribute, T instance, Func<string> property)\n            where T : class, INotifyPropertyChanged\n        {\n            return BindAttribute(new BindAttributeOptions<T>\n            {\n                Attribute = attribute,\n                BindObject = instance,\n                Property = _ => property()\n            });\n        }\n\n        /// <summary>\n        /// Adds bindings for a flag attribute\n        /// </summary>\n        /// <typeparam name=\"T\">Data type for data source</typeparam>\n        /// <param name=\"attribute\">Attribute</param>\n        /// <param name=\"instance\">Data source instance</param>\n        /// <param name=\"property\">Data source property</param>\n        /// <returns>This instance</returns>\n        [Obsolete(\"Use BindToggleAttribute() instead.\")]\n        [EditorBrowsable(EditorBrowsableState.Never)]\n        public LaraBuilder BindFlagAttribute<T>(string attribute, T instance, Func<bool> property)\n            where T : class, INotifyPropertyChanged\n        {\n            return BindToggleAttribute(attribute, instance, property);\n        }\n\n        /// <summary>\n        /// Adds bindings for a flag attribute\n        /// </summary>\n        /// <typeparam name=\"T\">Data type for data source</typeparam>\n        /// <param name=\"attribute\">Attribute</param>\n        /// <param name=\"instance\">Data source instance</param>\n        /// <param name=\"property\">Data source property</param>\n        /// <returns>This instance</returns>\n        [Obsolete(ObsoleteElement.BindObsolete)]\n        [EditorBrowsable(EditorBrowsableState.Never)]\n        public LaraBuilder BindToggleAttribute<T>(string attribute, T instance, Func<bool> property)\n            where T : class, INotifyPropertyChanged\n        {\n            return BindToggleAttribute(new BindFlagAttributeOptions<T>\n            {\n                Attribute = attribute,\n                BindObject = instance,\n                Property = _ => property()\n            });\n        }\n\n        /// <summary>\n        /// Adds bindings for toggling an element class\n        /// </summary>\n        /// <typeparam name=\"T\">Data type for the data source</typeparam>\n        /// <param name=\"className\">Class name</param>\n        /// <param name=\"instance\">Data source instance</param>\n        /// <param name=\"property\">Data source property</param>\n        /// <returns>This instance</returns>\n        [Obsolete(ObsoleteElement.BindObsolete)]\n        [EditorBrowsable(EditorBrowsableState.Never)]\n        public LaraBuilder BindToggleClass<T>(string className, T instance, Func<bool> property)\n            where T : class, INotifyPropertyChanged\n        {\n            return BindToggleClass(new BindToggleClassOptions<T>\n            {\n                ClassName = className,\n                BindObject = instance,\n                Property = _ => property()\n            });\n        }\n\n        /// <summary>\n        /// Adds bindings for an attribute\n        /// </summary>\n        /// <typeparam name=\"T\">Data type for data source instance</typeparam>\n        /// <param name=\"options\">Binding options</param>\n        /// <returns>This instance</returns>\n        [Obsolete(ObsoleteElement.BindObsolete)]\n        [EditorBrowsable(EditorBrowsableState.Never)]\n        public LaraBuilder BindAttribute<T>(BindAttributeOptions<T> options)\n            where T : class, INotifyPropertyChanged\n        {\n            _stack.Peek().BindAttribute(options);\n            return this;\n        }\n\n        /// <summary>\n        /// Adds bindings for a flag attribute\n        /// </summary>\n        /// <typeparam name=\"T\">Data type for data source instance</typeparam>\n        /// <param name=\"options\">Binding options</param>\n        /// <returns>This instance</returns>\n        [Obsolete(\"Use BindToggleAttribute() instead.\")]\n        [EditorBrowsable(EditorBrowsableState.Never)]\n        public LaraBuilder BindFlagAttribute<T>(BindFlagAttributeOptions<T> options)\n            where T : class, INotifyPropertyChanged\n        {\n            return BindToggleAttribute(options);\n        }\n\n        /// <summary>\n        /// Adds bindings for a flag attribute\n        /// </summary>\n        /// <typeparam name=\"T\">Data type for data source instance</typeparam>\n        /// <param name=\"options\">Binding options</param>\n        /// <returns>This instance</returns>\n        [Obsolete(ObsoleteElement.BindObsolete)]\n        [EditorBrowsable(EditorBrowsableState.Never)]\n        public LaraBuilder BindToggleAttribute<T>(BindFlagAttributeOptions<T> options)\n            where T : class, INotifyPropertyChanged\n        {\n            _stack.Peek().BindToggleAttribute(options);\n            return this;\n        }\n\n        /// <summary>\n        /// Adds bindings for toggling classes\n        /// </summary>\n        /// <typeparam name=\"T\">Data type for data source instance</typeparam>\n        /// <param name=\"options\">Binding options</param>\n        /// <returns>This instance</returns>\n        [Obsolete(ObsoleteElement.BindObsolete)]\n        [EditorBrowsable(EditorBrowsableState.Never)]\n        public LaraBuilder BindToggleClass<T>(BindToggleClassOptions<T> options)\n            where T : class, INotifyPropertyChanged\n        {\n            _stack.Peek().BindToggleClass(options);\n            return this;\n        }\n\n        /// <summary>\n        /// Adds bindings for an attribute\n        /// </summary>\n        /// <typeparam name=\"T\">Data type for data source instance</typeparam>\n        /// <param name=\"attribute\">Attribute</param>\n        /// <param name=\"instance\">Data source instance</param>\n        /// <param name=\"property\">Data source's property</param>\n        /// <returns>This instance</returns>\n        [Obsolete(ObsoleteElement.BindObsolete)]\n        [EditorBrowsable(EditorBrowsableState.Never)]\n        public LaraBuilder BindAttribute<T>(string attribute, T instance, Func<T, string> property)\n            where T : class, INotifyPropertyChanged\n        {\n            return BindAttribute(new BindAttributeOptions<T>\n            {\n                Attribute = attribute,\n                BindObject = instance,\n                Property = property\n            });\n        }\n\n        /// <summary>\n        /// Adds bindings for a flag attribute\n        /// </summary>\n        /// <typeparam name=\"T\">Data type for data source instance</typeparam>\n        /// <param name=\"attribute\">Attribute</param>\n        /// <param name=\"instance\">Data source instance</param>\n        /// <param name=\"property\">Data source property</param>\n        /// <returns>This instance</returns>\n        [Obsolete(\"Use instad the BindToggleAttribute() method.\")]\n        [EditorBrowsable(EditorBrowsableState.Never)]\n        public LaraBuilder BindFlagAttribute<T>(string attribute, T instance, Func<T, bool> property)\n            where T : class, INotifyPropertyChanged\n        {\n            return BindToggleAttribute(attribute, instance, property);\n        }\n\n        /// <summary>\n        /// Adds bindings for a flag attribute\n        /// </summary>\n        /// <typeparam name=\"T\">Data type for data source instance</typeparam>\n        /// <param name=\"attribute\">Attribute</param>\n        /// <param name=\"instance\">Data source instance</param>\n        /// <param name=\"property\">Data source property</param>\n        /// <returns>This instance</returns>\n        [Obsolete(ObsoleteElement.BindObsolete)]\n        [EditorBrowsable(EditorBrowsableState.Never)]\n        public LaraBuilder BindToggleAttribute<T>(string attribute, T instance, Func<T, bool> property)\n            where T : class, INotifyPropertyChanged\n        {\n            return BindToggleAttribute(new BindFlagAttributeOptions<T>\n            {\n                Attribute = attribute,\n                BindObject = instance,\n                Property = property\n            });\n        }\n\n        /// <summary>\n        /// Adds bindings for toggling a class\n        /// </summary>\n        /// <typeparam name=\"T\">Data type for data source instance</typeparam>\n        /// <param name=\"className\">Class name</param>\n        /// <param name=\"instance\">Data source instance</param>\n        /// <param name=\"property\">Data source property</param>\n        /// <returns>This instance</returns>\n        [Obsolete(ObsoleteElement.BindObsolete)]\n        [EditorBrowsable(EditorBrowsableState.Never)]\n        public LaraBuilder BindToggleClass<T>(string className, T instance, Func<T, bool> property)\n            where T : class, INotifyPropertyChanged\n        {\n            return BindToggleClass(new BindToggleClassOptions<T>\n            {\n                ClassName = className,\n                BindObject = instance,\n                Property = property\n            });\n        }\n\n        /// <summary>\n        /// Adds bindings for inner text\n        /// </summary>\n        /// <typeparam name=\"T\">Type of data source</typeparam>\n        /// <param name=\"instance\">Data source</param>\n        /// <param name=\"property\">Data source's property</param>\n        /// <returns>This instance</returns>\n        [Obsolete(ObsoleteElement.BindObsolete)]\n        [EditorBrowsable(EditorBrowsableState.Never)]\n        public LaraBuilder BindInnerText<T>(T instance, Func<string> property)\n            where T : class, INotifyPropertyChanged\n        {\n            return BindInnerText(new BindInnerTextOptions<T>\n            {\n                BindObject = instance,\n                Property = _ => property()\n            });\n        }\n\n        /// <summary>\n        /// Adds bindings for inner text\n        /// </summary>\n        /// <typeparam name=\"T\">Type of data source</typeparam>\n        /// <param name=\"instance\">Data source</param>\n        /// <param name=\"property\">Data source's property</param>\n        /// <returns>This instance</returns>\n        [Obsolete(ObsoleteElement.BindObsolete)]\n        [EditorBrowsable(EditorBrowsableState.Never)]\n        public LaraBuilder BindInnerText<T>(T instance, Func<T, string> property)\n            where T : class, INotifyPropertyChanged\n        {\n            return BindInnerText(new BindInnerTextOptions<T>\n            {\n                BindObject = instance,\n                Property = property\n            });\n        }\n\n        /// <summary>\n        /// Adds bindings for inner text\n        /// </summary>\n        /// <typeparam name=\"T\">Type of data source</typeparam>\n        /// <param name=\"options\">Inner tetx binding options</param>\n        /// <returns>This instance</returns>\n        [Obsolete(ObsoleteElement.BindObsolete)]\n        [EditorBrowsable(EditorBrowsableState.Never)]\n        public LaraBuilder BindInnerText<T>(BindInnerTextOptions<T> options)\n            where T : class, INotifyPropertyChanged\n        {\n            _stack.Peek().BindInnerText(options);\n            return this;\n        }\n\n        /// <summary>\n        /// Adds bindings for the children collection\n        /// </summary>\n        /// <typeparam name=\"T\">Type of elements in the ovservable collection</typeparam>\n        /// <param name=\"collection\">Observable collection</param>\n        /// <param name=\"creator\">Handler to create elements</param>\n        /// <returns>This instance</returns>\n        public LaraBuilder BindChildren<T>(ObservableCollection<T> collection, Func<Element> creator)\n        {\n            BindingExtensions.BindChildren(_stack.Peek(), collection, _ => creator());\n            return this;\n        }\n\n        /// <summary>\n        /// Adds bindings for the children collection\n        /// </summary>\n        /// <typeparam name=\"T\">Type of elements in observable collection</typeparam>\n        /// <param name=\"options\">Children bindings options</param>\n        /// <returns>This instance</returns>\n        [Obsolete(\"Use BindChildren(source, factory) instead\")]\n        [EditorBrowsable(EditorBrowsableState.Never)]\n        public LaraBuilder BindChildren<T>(BindChildrenOptions<T> options)\n        {\n            _stack.Peek().BindChildren(options);\n            return this;\n        }\n\n        /// <summary>\n        /// Adds a two-way binding for an attribute (e.g. 'value' attribute)\n        /// </summary>\n        /// <typeparam name=\"T\">Type of data source</typeparam>\n        /// <param name=\"options\">Binding options</param>\n        /// <returns>This instance</returns>\n        [Obsolete(ObsoleteElement.BindObsolete)]\n        [EditorBrowsable(EditorBrowsableState.Never)]\n        public LaraBuilder BindInput<T>(BindInputOptions<T> options)\n            where T : class, INotifyPropertyChanged\n        {\n            _stack.Peek().BindInput(options);\n            return this;\n        }\n\n        /// <summary>\n        /// Adds a two-way binding for an attribute (e.g. 'value' attribute)\n        /// </summary>\n        /// <typeparam name=\"T\">Type of data source</typeparam>\n        /// <param name=\"attribute\">Attribute</param>\n        /// <param name=\"data\">Data source</param>\n        /// <param name=\"property\">Data source property</param>\n        /// <returns>This instance</returns>\n        [Obsolete(ObsoleteElement.BindObsolete)]\n        [EditorBrowsable(EditorBrowsableState.Never)]\n        public LaraBuilder BindInput<T>(string attribute, T data, Expression<Func<T, string?>> property)\n            where T : class, INotifyPropertyChanged\n        {\n            return BindInput(new BindInputOptions<T>\n            {\n                Attribute = attribute,\n                BindObject = data,\n                Property = property\n            });\n        }\n\n        /// <summary>\n        /// Adds a two-way binding for a flag attribute (e.g. 'checked' attribute)\n        /// </summary>\n        /// <typeparam name=\"T\">Type of data source</typeparam>\n        /// <param name=\"options\">Binding options</param>\n        /// <returns>This instance</returns>\n        [Obsolete(ObsoleteElement.BindObsolete)]\n        [EditorBrowsable(EditorBrowsableState.Never)]\n        public LaraBuilder BindFlagInput<T>(BindFlagInputOptions<T> options)\n            where T : class, INotifyPropertyChanged\n        {\n            _stack.Peek().BindFlagInput(options);\n            return this;\n        }\n\n        /// <summary>\n        /// Adds a two-way binding for a flag attribute (e.g. 'checked' attribute)\n        /// </summary>\n        /// <typeparam name=\"T\">Type of data source</typeparam>\n        /// <param name=\"attribute\">Attribute</param>\n        /// <param name=\"data\">Data source</param>\n        /// <param name=\"property\">Data source property</param>\n        /// <returns>This instance</returns>\n        [Obsolete(ObsoleteElement.BindObsolete)]\n        [EditorBrowsable(EditorBrowsableState.Never)]\n        public LaraBuilder BindFlagInput<T>(string attribute, T data, Expression<Func<T, bool>> property)\n            where T : class, INotifyPropertyChanged\n        {\n            return BindFlagInput(new BindFlagInputOptions<T>\n            {\n                Attribute = attribute,\n                BindObject = data,\n                Property = property\n            });\n        }\n\n        /// <summary>\n        /// Associates the current element with a data source and an action to update the element whenever the source is modified\n        /// </summary>\n        /// <typeparam name=\"T\">Type of the data source</typeparam>\n        /// <param name=\"instance\">Data source</param>\n        /// <param name=\"action\">Action to update the element</param>\n        /// <returns>This instance</returns>\n        [Obsolete(ObsoleteElement.BindObsolete)]\n        [EditorBrowsable(EditorBrowsableState.Never)]\n        public LaraBuilder Bind<T>(T instance, Action action)\n            where T : class, INotifyPropertyChanged\n        {\n            return Bind(new BindHandlerOptions<T>\n            {\n                ModifiedHandler = (_, _) => action(),\n                BindObject = instance\n            });\n        }\n\n        /// <summary>\n        /// Associates the current element with a data source and an action to update the element whenever the source is modified\n        /// </summary>\n        /// <typeparam name=\"T\">Type of the data source</typeparam>\n        /// <param name=\"instance\">Data source</param>\n        /// <param name=\"action\">Action to update the element</param>\n        /// <returns>This instance</returns>\n        [Obsolete(ObsoleteElement.BindObsolete)]\n        [EditorBrowsable(EditorBrowsableState.Never)]\n        public LaraBuilder Bind<T>(T instance, Action<T, Element> action)\n            where T : class, INotifyPropertyChanged\n        {\n            return Bind(new BindHandlerOptions<T>\n            {\n                ModifiedHandler = action,\n                BindObject = instance\n            });\n        }\n\n        /// <summary>\n        /// Associates the current element with a data source and an action to update the element whenever the source is modified\n        /// </summary>\n        /// <typeparam name=\"T\">Type of the data source</typeparam>\n        /// <param name=\"options\">Binding options</param>\n        /// <returns>This instance</returns>\n        [Obsolete(ObsoleteElement.BindObsolete)]\n        [EditorBrowsable(EditorBrowsableState.Never)]\n        public LaraBuilder Bind<T>(BindHandlerOptions<T> options)\n            where T : class, INotifyPropertyChanged\n        {\n            _stack.Peek().Bind(options);\n            return this;\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Tools/LaraJson.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 6/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System;\nusing System.Diagnostics.CodeAnalysis;\nusing System.Net;\nusing System.Runtime.Serialization;\n\nnamespace Integrative.Lara\n{\n    /// <summary>\n    /// JSON operations\n    /// </summary>\n    public sealed class LaraJson\n    {\n        /// <summary>\n        /// Parses a JSON string into a class. The class needs to be decorated with the DataContract attribute.\n        /// </summary>\n        /// <typeparam name=\"T\">Class type</typeparam>\n        /// <param name=\"json\">Source JSON string</param>\n        /// <param name=\"result\">Class instance created</param>\n        /// <returns>true when successful, false otherwise</returns>\n        // ReSharper disable once MemberCanBeMadeStatic.Global\n        public bool TryParse<T>(string json, [NotNullWhen(true)] out T? result) where T : class\n        {\n            try\n            {\n                result = LaraTools.Deserialize<T>(json);\n                return (result != null);\n            }\n            catch (SerializationException)\n            {\n                result = default;\n                return false;\n            }\n        }\n\n        /// <summary>\n        /// Parses a JSON string. If parsing fails, throws a StatusCodeException that returns a Bad Request (400).\n        /// The class needs to be decorated with the DataContract attribute.\n        /// </summary>\n        /// <typeparam name=\"T\">Class type</typeparam>\n        /// <param name=\"json\">JSON source text</param>\n        /// <returns>Instance of deserialized class</returns>\n        public T Parse<T>(string json) where T : class\n        {\n            T? result;\n            try\n            {\n                result = LaraTools.Deserialize<T>(json);\n            }\n            catch (Exception e)\n            {\n                var outer = new StatusCodeException(Resources.BadRequest, e)\n                {\n                    StatusCode = HttpStatusCode.BadRequest\n                };\n                throw outer;\n            }\n            if (result == null)\n            {\n                throw new StatusCodeException(HttpStatusCode.BadRequest, Resources.BadRequest);\n            }\n            return result;\n        }\n\n        /// <summary>\n        /// Serializes a class decorated with DataContract to a JSON string\n        /// </summary>\n        /// <typeparam name=\"T\">Type of class</typeparam>\n        /// <param name=\"instance\">Instance to serialize</param>\n        /// <returns>JSON string</returns>\n        public string Stringify<T>(T instance) => LaraTools.Serialize(instance);\n\n        /// <summary>\n        /// Serializes a class decorated with DataContract to a JSON string\n        /// </summary>\n        /// <param name=\"instance\">Instance to serialize</param>\n        /// <param name=\"type\">Type of class</param>\n        /// <returns>JSON string</returns>\n        public string Stringify(object instance, Type type) => LaraTools.Serialize(instance, type);\n\n        internal LaraJson()\n        {\n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Tools/LaraOptions.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 5/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System;\nusing System.Diagnostics;\nusing System.Net;\nusing Microsoft.AspNetCore.Hosting;\n\nnamespace Integrative.Lara\n{\n    /// <summary>\n    /// Defines preset modes for applications\n    /// </summary>\n    public enum ApplicationMode\n    {\n        /// <summary>\n        /// Default mode as a regular web site\n        /// </summary>\n        Default,\n\n        /// <summary>\n        /// Launches a browser tab and terminates the host when the user closes away the tab and its child tabs.\n        /// Also, will prevent any further connections besides the one created upon launching the browser.\n        /// </summary>\n        BrowserApp\n    }\n\n    /// <summary>\n    /// Lara Web Engine options\n    /// </summary>\n    public class LaraOptions\n    {\n        /// <summary>\n        /// Looks for classes decorated with Lara attributes and registers them. Default is True.\n        /// </summary>\n        public bool PublishAssembliesOnStart { get; set; }\n\n        /// <summary>\n        /// Gets or sets a value indicating whether to allow localhost requests only.\n        /// </summary>\n        /// <value>\n        ///   <c>true</c> if [allow localhost only]; otherwise, <c>false</c>.\n        /// </value>\n        public bool AllowLocalhostOnly { get; set; }\n\n        /// <summary>\n        /// Gets or sets a value indicating whether Lara will show its default 'not found' page.\n        /// </summary>\n        /// <value>\n        ///   <c>true</c> if [show not found page]; otherwise, <c>false</c>.\n        /// </value>\n        public bool ShowNotFoundPage { get; set; } = true;\n\n        /// <summary>\n        /// Defines an application mode\n        /// </summary>\n        public ApplicationMode Mode { get; set; } = ApplicationMode.Default;\n\n        /// <summary>\n        /// Gets or sets a value indicating whether Lara will include ASP.NET Core WebSocket middleware. Always set to include unless you are manually including the middleware yourself.\n        /// </summary>\n        /// <value>\n        ///   <c>true</c> if [add web sockets middleware]; otherwise, <c>false</c>.\n        /// </value>\n        public bool AddWebSocketsMiddleware { get; set; } = true;\n    }\n\n    /// <summary>\n    /// Lara options for starting a web server\n    /// </summary>\n    /// <seealso cref=\"LaraOptions\" />\n    public class StartServerOptions : LaraOptions\n    {\n        /// <summary>\n        /// Gets or sets the port to use when Lara opens a new server. If zero, a dynamic port wil be assigned.\n        /// </summary>\n        /// <value>\n        /// The port number.\n        /// </value>\n        public int Port { get; set; }\n\n        /// <summary>\n        /// Gets or sets the IP address where the host is listening. By default, this is the loopback address.\n        /// </summary>\n        // ReSharper disable once InconsistentNaming\n        public IPAddress IPAddress { get; set; } = IPAddress.Loopback;\n\n        /// <summary>\n        /// Gets or sets a value indicating whether to show execution exceptions.\n        /// </summary>\n        /// <value>\n        ///   <c>true</c> if [show exceptions]; otherwise, <c>false</c>.\n        /// </value>\n        // ReSharper disable once MemberCanBePrivate.Global\n        public bool ShowExceptions { get; set; }\n\n        /// <summary>\n        /// Defines an optional method to set additional configuration for the asp.net core instance\n        /// </summary>\n        // ReSharper disable once UnusedAutoPropertyAccessor.Global\n        public Action<IWebHostBuilder>? AdditionalConfiguration { get; set; }\n        \n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"StartServerOptions\"/> class.\n        /// </summary>\n        public StartServerOptions()\n        {\n            ShowExceptions = Debugger.IsAttached;\n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Tools/LaraTools.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 5/2019\nAuthor: Pablo Carbonell\n*/\n\nusing Microsoft.AspNetCore.Hosting;\nusing Microsoft.AspNetCore.Hosting.Server.Features;\nusing System;\nusing System.Diagnostics;\nusing System.Diagnostics.CodeAnalysis;\nusing System.IO;\nusing System.IO.Compression;\nusing System.Linq;\nusing System.Runtime.CompilerServices;\nusing System.Runtime.InteropServices;\nusing System.Runtime.Serialization.Json;\nusing System.Text;\n\n[assembly: InternalsVisibleTo(\"Tests\")]\nnamespace Integrative.Lara\n{\n    internal static class LaraTools\n    {\n        public static void LaunchBrowser(IWebHost host)\n        {\n            var address = GetFirstUrl(host);\n            LaunchBrowser(address);\n        }\n\n        public static string GetFirstUrl(IWebHost webHost)\n        {\n            return webHost.ServerFeatures\n                .Get<IServerAddressesFeature>()\n                .Addresses\n                .First();\n        }\n\n        public static void LaunchBrowser(string url)\n        {\n            if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))\n            {\n                Process.Start(new ProcessStartInfo(\"cmd\", $\"/c start {url}\"));\n            }\n            else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))\n            {\n                Process.Start(\"xdg-open\", url);\n            }\n            else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))\n            {\n                Process.Start(\"open\", url);\n            }\n        }\n\n        public static string Serialize<T>(T instance)\n        {\n            return Serialize(instance, typeof(T));\n        }\n\n        public static string Serialize(object? instance, Type type)\n        {\n            if (instance == null)\n            {\n                return string.Empty;\n            }\n            var stream = new MemoryStream();\n            using var reader = new StreamReader(stream);\n            var serializer = new DataContractJsonSerializer(type);\n            serializer.WriteObject(stream, instance);\n            stream.Position = 0;\n            return reader.ReadToEnd();\n        }\n\n        public static T? Deserialize<T>(string json) where T : class\n        {\n            using var stream = new MemoryStream(Encoding.UTF8.GetBytes(json));\n            return Deserialize<T>(stream);\n        }\n\n        public static T? Deserialize<T>(Stream stream) where T : class\n        {\n            var serializer = new DataContractJsonSerializer(typeof(T));\n            return serializer.ReadObject(stream) as T;\n        }\n\n        public static byte[] Compress(byte[] data)\n        {\n            var output = new MemoryStream();\n            using (var stream = new DeflateStream(output, CompressionLevel.Fastest))\n            {\n                stream.Write(data, 0, data.Length);\n            }\n            return output.ToArray();\n        }\n\n        public static bool SameValue<T>([AllowNull] T previous, [AllowNull] T value)\n        {\n            if (previous == null)\n            {\n                return value == null;\n            }\n\n            return previous.Equals(value);\n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Tools/NoCurrentSessionException.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 11/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System;\n\nnamespace Integrative.Lara\n{\n    /// <summary>\n    /// The operation requested requires a current session and there isn't one\n    /// </summary>\n    public class NoCurrentSessionException : InvalidOperationException\n    {\n        /// <summary>\n        /// Constructor\n        /// </summary>\n        /// <param name=\"message\">Message</param>\n        public NoCurrentSessionException(string message) : base(message)\n        {\n        }\n\n        /// <summary>\n        /// Constructor\n        /// </summary>\n        /// <param name=\"message\">Message</param>\n        /// <param name=\"innerException\">Inner exception</param>\n        public NoCurrentSessionException(string message, Exception innerException) : base(message, innerException)\n        {\n        }\n\n        /// <summary>\n        /// Constructor\n        /// </summary>\n        public NoCurrentSessionException()\n        {\n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Tools/SemaphoreSlimExtensions.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 5/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System;\nusing System.Threading;\nusing System.Threading.Tasks;\n\nnamespace Integrative.Lara\n{\n    internal static class SemaphoreSlimExtensions\n    {\n        public static async Task<IDisposable> UseWaitAsync(\n            this SemaphoreSlim semaphore,\n            CancellationToken cancelToken = default)\n        {\n            await semaphore.WaitAsync(cancelToken);\n            return new ReleaseWrapper(semaphore);\n        }\n\n        public static IDisposable UseWait(this SemaphoreSlim semaphore,\n            CancellationToken cancelToken = default)\n        {\n            semaphore.Wait(cancelToken);\n            return new ReleaseWrapper(semaphore);\n        }\n\n        private class ReleaseWrapper : IDisposable\n        {\n            private readonly SemaphoreSlim _semaphore;\n\n            private bool _disposed;\n\n            public ReleaseWrapper(SemaphoreSlim semaphore)\n            {\n                _semaphore = semaphore;\n            }\n\n            public void Dispose()\n            {\n                if (_disposed) return;\n                _disposed = true;\n                _semaphore.Release();\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Tools/ServerLauncher.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 5/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System.Threading;\nusing System.Threading.Tasks;\nusing Microsoft.AspNetCore.Builder;\nusing Microsoft.AspNetCore.Hosting;\n\nnamespace Integrative.Lara\n{\n    internal static class ServerLauncher\n    {\n        public const string ErrorAddress = \"/Error\";\n\n        public static async Task<IWebHost> StartServer(Application app, StartServerOptions options)\n        {\n            var host = CreateBrowserHost(app, options);\n            await host.StartAsync(CancellationToken.None);\n            return host;\n        }\n\n        private static IWebHost CreateBrowserHost(Application laraApp, StartServerOptions options)\n        {\n            var address = options.IPAddress;\n            var port = options.Port;\n            var builder = new WebHostBuilder()\n                .UseKestrel(kestrel => kestrel.Listen(address, port))\n                .Configure(app =>\n                {\n                    ConfigureApp(app, laraApp, options);\n                });\n            options.AdditionalConfiguration?.Invoke(builder);\n            return builder.Build();\n        }\n\n        private static void ConfigureApp(IApplicationBuilder app, Application laraApp, StartServerOptions options)\n        {\n            ConfigureExceptions(app, laraApp, options);\n            app.UseLara(laraApp, options);\n        }\n\n        internal static void ConfigureExceptions(IApplicationBuilder app, Application laraApp, StartServerOptions options)\n        {\n            if (options.ShowExceptions)\n            {\n                app.UseDeveloperExceptionPage();\n            }\n            else\n            {\n                app.UseExceptionHandler(ErrorAddress);\n                laraApp.ErrorPages.PublishErrorPage();\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/Tools/SessionLocal.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 9/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System.Collections.Generic;\nusing System.Diagnostics.CodeAnalysis;\n\nnamespace Integrative.Lara\n{\n    /// <summary>\n    /// Represents ambient data that is local to a given session\n    /// </summary>\n    /// <typeparam name=\"T\">Type of data to store</typeparam>\n    public class SessionLocal<T>\n    {\n        private readonly Dictionary<Session, T> _storage;\n\n        /// <summary>\n        /// Constructor\n        /// </summary>\n        public SessionLocal()\n        {\n            _storage = new Dictionary<Session, T>();\n        }\n\n        /// <summary>\n        /// Value\n        /// </summary>\n        [MaybeNull]\n        public T Value\n        {\n            get => GetValue();\n            set => SetValue(value);\n        }\n        \n        private T GetValue()\n        {\n            var session = GetSession();\n            _storage.TryGetValue(session, out var value);\n            return value;\n        }\n\n        private void SetValue([AllowNull] T value)\n        {\n            var session = GetSession();\n            if (_storage.TryGetValue(session, out var previous))\n            {\n                if (LaraTools.SameValue(previous, value))\n                {\n                    return;\n                }\n                _storage.Remove(session);\n                Store(value, session);\n            }\n            else\n            {\n                Store(value, session);\n                session.CloseComplete += (_, _) => _storage.Remove(session);\n            }\n        }\n\n        private void Store([AllowNull] T value, Session session)\n        {\n            if (value != null)\n            {\n                _storage.Add(session, value);\n            }\n        }\n\n        private static Session GetSession()\n        {\n            if (LaraUI.Context is IPageContext page)\n            {\n                return page.Session;\n            }\n\n            if (LaraUI.Context is IWebServiceContext service\n                && service.TryGetSession(out var session))\n            {\n                return session;\n            }\n            throw new NoCurrentSessionException(Resources.NoCurrentSession);\n        }\n\n    }\n}\n"
  },
  {
    "path": "src/LaraUI/docfx.json",
    "content": "{\n  \"metadata\": [\n    {\n      \"src\": [\n        {\n          \"files\": [\n            \"**.csproj\"\n          ],\n          \"src\": \"C:\\\\Users\\\\Pablo\\\\OneDrive\\\\2019\\\\LaraUI\\\\src\\\\LaraUI\"\n        }\n      ],\n      \"dest\": \"api\",\n      \"disableGitFeatures\": false,\n      \"disableDefaultFilter\": false\n    }\n  ],\n  \"build\": {\n    \"content\": [\n      {\n        \"files\": [\n          \"api/**.yml\",\n          \"api/index.md\"\n        ]\n      },\n      {\n        \"files\": [\n          \"articles/**.md\",\n          \"articles/**/toc.yml\",\n          \"toc.yml\",\n          \"*.md\"\n        ]\n      }\n    ],\n    \"resource\": [\n      {\n        \"files\": [\n          \"images/**\"\n        ]\n      }\n    ],\n    \"overwrite\": [\n      {\n        \"files\": [\n          \"apidoc/**.md\"\n        ],\n        \"exclude\": [\n          \"obj/**\",\n          \"_site/**\"\n        ]\n      }\n    ],\n    \"dest\": \"_site\",\n    \"globalMetadataFiles\": [],\n    \"fileMetadataFiles\": [],\n    \"template\": [\n      \"default\"\n    ],\n    \"postProcessors\": [],\n    \"markdownEngineName\": \"markdig\",\n    \"noLangKeyword\": false,\n    \"keepFileLink\": false,\n    \"cleanupCacheHistory\": false,\n    \"disableGitFeatures\": false\n  }\n}"
  },
  {
    "path": "src/LaraUI/pack.bat",
    "content": "dotnet pack -c RELEASE"
  },
  {
    "path": "src/LaraUI.sln",
    "content": "﻿\nMicrosoft Visual Studio Solution File, Format Version 12.00\n# Visual Studio Version 16\nVisualStudioVersion = 16.0.28803.452\nMinimumVisualStudioVersion = 10.0.40219.1\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"LaraUI\", \"LaraUI\\LaraUI.csproj\", \"{1CF006B1-8B28-45BC-B9C8-1E548ACD115D}\"\nEndProject\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"SampleProject\", \"SampleProject\\SampleProject.csproj\", \"{C40FC41C-4FED-4502-A912-33BCE42954AD}\"\nEndProject\nProject(\"{2150E333-8FDC-42A3-9474-1A3956D46DE8}\") = \"Solution Items\", \"Solution Items\", \"{0518A1A9-5F3F-4081-96E4-68B771F81A26}\"\n\tProjectSection(SolutionItems) = preProject\n\t\t..\\.gitignore = ..\\.gitignore\n\t\tLaraUI.sln.licenseheader = LaraUI.sln.licenseheader\n\tEndProjectSection\nEndProject\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"Tests\", \"Tests\\Tests.csproj\", \"{E93241E3-DCE0-47AF-BA01-2F1C02864F67}\"\nEndProject\nProject(\"{7CF6DF6D-3B04-46F8-A40B-537D21BCA0B4}\") = \"LaraDocumentation\", \"LaraDocumentation\\LaraDocumentation.shfbproj\", \"{0019560B-ED95-4921-9050-8AD37BF9D7EF}\"\nEndProject\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"WikiExamples\", \"Boilerplate\\WikiExamples.csproj\", \"{BB775C40-4D4B-4B5E-A665-07653EC6EFD6}\"\nEndProject\nGlobal\n\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\n\t\tDebug|Any CPU = Debug|Any CPU\n\t\tDebug|x64 = Debug|x64\n\t\tRelease|Any CPU = Release|Any CPU\n\t\tRelease|x64 = Release|x64\n\tEndGlobalSection\n\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\n\t\t{1CF006B1-8B28-45BC-B9C8-1E548ACD115D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{1CF006B1-8B28-45BC-B9C8-1E548ACD115D}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{1CF006B1-8B28-45BC-B9C8-1E548ACD115D}.Debug|x64.ActiveCfg = Debug|Any CPU\n\t\t{1CF006B1-8B28-45BC-B9C8-1E548ACD115D}.Debug|x64.Build.0 = Debug|Any CPU\n\t\t{1CF006B1-8B28-45BC-B9C8-1E548ACD115D}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{1CF006B1-8B28-45BC-B9C8-1E548ACD115D}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{1CF006B1-8B28-45BC-B9C8-1E548ACD115D}.Release|x64.ActiveCfg = Release|Any CPU\n\t\t{1CF006B1-8B28-45BC-B9C8-1E548ACD115D}.Release|x64.Build.0 = Release|Any CPU\n\t\t{C40FC41C-4FED-4502-A912-33BCE42954AD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{C40FC41C-4FED-4502-A912-33BCE42954AD}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{C40FC41C-4FED-4502-A912-33BCE42954AD}.Debug|x64.ActiveCfg = Debug|x64\n\t\t{C40FC41C-4FED-4502-A912-33BCE42954AD}.Debug|x64.Build.0 = Debug|x64\n\t\t{C40FC41C-4FED-4502-A912-33BCE42954AD}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{C40FC41C-4FED-4502-A912-33BCE42954AD}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{C40FC41C-4FED-4502-A912-33BCE42954AD}.Release|x64.ActiveCfg = Release|x64\n\t\t{C40FC41C-4FED-4502-A912-33BCE42954AD}.Release|x64.Build.0 = Release|x64\n\t\t{E93241E3-DCE0-47AF-BA01-2F1C02864F67}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{E93241E3-DCE0-47AF-BA01-2F1C02864F67}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{E93241E3-DCE0-47AF-BA01-2F1C02864F67}.Debug|x64.ActiveCfg = Debug|x64\n\t\t{E93241E3-DCE0-47AF-BA01-2F1C02864F67}.Debug|x64.Build.0 = Debug|x64\n\t\t{E93241E3-DCE0-47AF-BA01-2F1C02864F67}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{E93241E3-DCE0-47AF-BA01-2F1C02864F67}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{E93241E3-DCE0-47AF-BA01-2F1C02864F67}.Release|x64.ActiveCfg = Release|x64\n\t\t{E93241E3-DCE0-47AF-BA01-2F1C02864F67}.Release|x64.Build.0 = Release|x64\n\t\t{0019560B-ED95-4921-9050-8AD37BF9D7EF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{0019560B-ED95-4921-9050-8AD37BF9D7EF}.Debug|x64.ActiveCfg = Debug|Any CPU\n\t\t{0019560B-ED95-4921-9050-8AD37BF9D7EF}.Debug|x64.Build.0 = Debug|Any CPU\n\t\t{0019560B-ED95-4921-9050-8AD37BF9D7EF}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{0019560B-ED95-4921-9050-8AD37BF9D7EF}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{0019560B-ED95-4921-9050-8AD37BF9D7EF}.Release|x64.ActiveCfg = Release|Any CPU\n\t\t{0019560B-ED95-4921-9050-8AD37BF9D7EF}.Release|x64.Build.0 = Release|Any CPU\n\t\t{BB775C40-4D4B-4B5E-A665-07653EC6EFD6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{BB775C40-4D4B-4B5E-A665-07653EC6EFD6}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{BB775C40-4D4B-4B5E-A665-07653EC6EFD6}.Debug|x64.ActiveCfg = Debug|Any CPU\n\t\t{BB775C40-4D4B-4B5E-A665-07653EC6EFD6}.Debug|x64.Build.0 = Debug|Any CPU\n\t\t{BB775C40-4D4B-4B5E-A665-07653EC6EFD6}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{BB775C40-4D4B-4B5E-A665-07653EC6EFD6}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{BB775C40-4D4B-4B5E-A665-07653EC6EFD6}.Release|x64.ActiveCfg = Release|Any CPU\n\t\t{BB775C40-4D4B-4B5E-A665-07653EC6EFD6}.Release|x64.Build.0 = Release|Any CPU\n\tEndGlobalSection\n\tGlobalSection(SolutionProperties) = preSolution\n\t\tHideSolutionNode = FALSE\n\tEndGlobalSection\n\tGlobalSection(ExtensibilityGlobals) = postSolution\n\t\tSolutionGuid = {2ADA43B8-3C29-40D4-BF2A-32BFC3C0D173}\n\tEndGlobalSection\nEndGlobal\n"
  },
  {
    "path": "src/LaraUI.sln.licenseheader",
    "content": "﻿extensions: designer.cs generated.cs\nextensions: .cs .cpp .h .js .ts\n/*\nCopyright (c) %CurrentYear% Integrative Software LLC\nCreated: %CreationMonth%/%CreationYear%\nAuthor: Pablo Carbonell\n*/\n\nextensions: .aspx .ascx\n<%-- \nCopyright (c) %CurrentYear% Integrative Software LLC\nCreated: %CreationMonth%/%CreationYear%\nAuthor: Pablo Carbonell\n--%>\nextensions: .vb\n'Copyright (c) %CurrentYear% Integrative Software LLC\n'Created: %CreationMonth%/%CreationYear%\n'Author: Pablo Carbonell\nextensions:  .xml .config .xsd\n<!--\nCopyright (c) %CurrentYear% Integrative Sofware LLC\nCreated: %CreationMonth%/%CreationYear%\nAuthor: Pablo Carbonell\n-->"
  },
  {
    "path": "src/SampleProject/Common/CountryList.cs",
    "content": "﻿/*\nCopyright (c) 2020-2021 Integrative Software LLC\nCreated: 12/2020\nAuthor: Pablo Carbonell\n*/\n\nusing System.Collections.Generic;\n\nnamespace SampleProject.Common\n{\n    internal class Country\n    {\n        public Country(string code, string name)\n        {\n            Code = code;\n            Name = name;\n        }\n\n        public string Code { get; init; }\n        public string Name { get; init; }\n    }\n\n    internal static class CountryList\n    {\n        private static readonly Dictionary<string, string> _Countries\n            = new Dictionary<string, string>()\n            {\n                { \"AF\", \"Afghanistan\" },\n                { \"AL\", \"Albania\" },\n                { \"DZ\", \"Algeria\" },\n                { \"AS\", \"American Samoa\" },\n                { \"AD\", \"Andorra\" },\n                { \"AO\", \"Angola\" },\n                { \"AI\", \"Anguilla\" },\n                { \"AQ\", \"Antarctica\" },\n                { \"AG\", \"Antigua And Barbuda\" },\n                { \"AR\", \"Argentina\" },\n                { \"AM\", \"Armenia\" },\n                { \"AW\", \"Aruba\" },\n                { \"AU\", \"Australia\" },\n                { \"AT\", \"Austria\" },\n                { \"AZ\", \"Azerbaijan\" },\n                { \"BS\", \"Bahamas\" },\n                { \"BH\", \"Bahrain\" },\n                { \"BD\", \"Bangladesh\" },\n                { \"BB\", \"Barbados\" },\n                { \"BY\", \"Belarus\" },\n                { \"BE\", \"Belgium\" },\n                { \"BZ\", \"Belize\" },\n                { \"BJ\", \"Benin\" },\n                { \"BM\", \"Bermuda\" },\n                { \"BT\", \"Bhutan\" },\n                { \"BO\", \"Bolivia\" },\n                { \"BA\", \"Bosnia And Herzegovina\" },\n                { \"BW\", \"Botswana\" },\n                { \"BV\", \"Bouvet Island\" },\n                { \"BR\", \"Brazil\" },\n                { \"IO\", \"British Indian Ocean Territory\" },\n                { \"BN\", \"Brunei Darussalam\" },\n                { \"BG\", \"Bulgaria\" },\n                { \"BF\", \"Burkina Faso\" },\n                { \"BI\", \"Burundi\" },\n                { \"KH\", \"Cambodia\" },\n                { \"CM\", \"Cameroon\" },\n                { \"CA\", \"Canada\" },\n                { \"CV\", \"Cape Verde\" },\n                { \"KY\", \"Cayman Islands\" },\n                { \"CF\", \"Central African Republic\" },\n                { \"TD\", \"Chad\" },\n                { \"CL\", \"Chile\" },\n                { \"CN\", \"China\" },\n                { \"CX\", \"Christmas Island\" },\n                { \"CC\", \"Cocos (keeling) Islands\" },\n                { \"CO\", \"Colombia\" },\n                { \"KM\", \"Comoros\" },\n                { \"CG\", \"Congo\" },\n                { \"CD\", \"Congo, The Democratic Republic Of The\" },\n                { \"CK\", \"Cook Islands\" },\n                { \"CR\", \"Costa Rica\" },\n                { \"CI\", \"Cote D'ivoire\" },\n                { \"HR\", \"Croatia\" },\n                { \"CU\", \"Cuba\" },\n                { \"CY\", \"Cyprus\" },\n                { \"CZ\", \"Czech Republic\" },\n                { \"DK\", \"Denmark\" },\n                { \"DJ\", \"Djibouti\" },\n                { \"DM\", \"Dominica\" },\n                { \"DO\", \"Dominican Republic\" },\n                { \"TP\", \"East Timor\" },\n                { \"EC\", \"Ecuador\" },\n                { \"EG\", \"Egypt\" },\n                { \"SV\", \"El Salvador\" },\n                { \"GQ\", \"Equatorial Guinea\" },\n                { \"ER\", \"Eritrea\" },\n                { \"EE\", \"Estonia\" },\n                { \"ET\", \"Ethiopia\" },\n                { \"FK\", \"Falkland Islands (malvinas)\" },\n                { \"FO\", \"Faroe Islands\" },\n                { \"FJ\", \"Fiji\" },\n                { \"FI\", \"Finland\" },\n                { \"FR\", \"France\" },\n                { \"GF\", \"French Guiana\" },\n                { \"PF\", \"French Polynesia\" },\n                { \"TF\", \"French Southern Territories\" },\n                { \"GA\", \"Gabon\" },\n                { \"GM\", \"Gambia\" },\n                { \"GE\", \"Georgia\" },\n                { \"DE\", \"Germany\" },\n                { \"GH\", \"Ghana\" },\n                { \"GI\", \"Gibraltar\" },\n                { \"GR\", \"Greece\" },\n                { \"GL\", \"Greenland\" },\n                { \"GD\", \"Grenada\" },\n                { \"GP\", \"Guadeloupe\" },\n                { \"GU\", \"Guam\" },\n                { \"GT\", \"Guatemala\" },\n                { \"GN\", \"Guinea\" },\n                { \"GW\", \"Guinea-bissau\" },\n                { \"GY\", \"Guyana\" },\n                { \"HT\", \"Haiti\" },\n                { \"HM\", \"Heard Island And Mcdonald Islands\" },\n                { \"VA\", \"Holy See (vatican City State)\" },\n                { \"HN\", \"Honduras\" },\n                { \"HK\", \"Hong Kong\" },\n                { \"HU\", \"Hungary\" },\n                { \"IS\", \"Iceland\" },\n                { \"IN\", \"India\" },\n                { \"ID\", \"Indonesia\" },\n                { \"IR\", \"Iran, Islamic Republic Of\" },\n                { \"IQ\", \"Iraq\" },\n                { \"IE\", \"Ireland\" },\n                { \"IL\", \"Israel\" },\n                { \"IT\", \"Italy\" },\n                { \"JM\", \"Jamaica\" },\n                { \"JP\", \"Japan\" },\n                { \"JO\", \"Jordan\" },\n                { \"KZ\", \"Kazakstan\" },\n                { \"KE\", \"Kenya\" },\n                { \"KI\", \"Kiribati\" },\n                { \"KP\", \"Korea, Democratic People's Republic Of\" },\n                { \"KR\", \"Korea, Republic Of\" },\n                { \"KV\", \"Kosovo\" },\n                { \"KW\", \"Kuwait\" },\n                { \"KG\", \"Kyrgyzstan\" },\n                { \"LA\", \"Lao People's Democratic Republic\" },\n                { \"LV\", \"Latvia\" },\n                { \"LB\", \"Lebanon\" },\n                { \"LS\", \"Lesotho\" },\n                { \"LR\", \"Liberia\" },\n                { \"LY\", \"Libyan Arab Jamahiriya\" },\n                { \"LI\", \"Liechtenstein\" },\n                { \"LT\", \"Lithuania\" },\n                { \"LU\", \"Luxembourg\" },\n                { \"MO\", \"Macau\" },\n                { \"MK\", \"Macedonia, The Former Yugoslav Republic Of\" },\n                { \"MG\", \"Madagascar\" },\n                { \"MW\", \"Malawi\" },\n                { \"MY\", \"Malaysia\" },\n                { \"MV\", \"Maldives\" },\n                { \"ML\", \"Mali\" },\n                { \"MT\", \"Malta\" },\n                { \"MH\", \"Marshall Islands\" },\n                { \"MQ\", \"Martinique\" },\n                { \"MR\", \"Mauritania\" },\n                { \"MU\", \"Mauritius\" },\n                { \"YT\", \"Mayotte\" },\n                { \"MX\", \"Mexico\" },\n                { \"FM\", \"Micronesia, Federated States Of\" },\n                { \"MD\", \"Moldova, Republic Of\" },\n                { \"MC\", \"Monaco\" },\n                { \"MN\", \"Mongolia\" },\n                { \"MS\", \"Montserrat\" },\n                { \"ME\", \"Montenegro\" },\n                { \"MA\", \"Morocco\" },\n                { \"MZ\", \"Mozambique\" },\n                { \"MM\", \"Myanmar\" },\n                { \"NA\", \"Namibia\" },\n                { \"NR\", \"Nauru\" },\n                { \"NP\", \"Nepal\" },\n                { \"NL\", \"Netherlands\" },\n                { \"AN\", \"Netherlands Antilles\" },\n                { \"NC\", \"New Caledonia\" },\n                { \"NZ\", \"New Zealand\" },\n                { \"NI\", \"Nicaragua\" },\n                { \"NE\", \"Niger\" },\n                { \"NG\", \"Nigeria\" },\n                { \"NU\", \"Niue\" },\n                { \"NF\", \"Norfolk Island\" },\n                { \"MP\", \"Northern Mariana Islands\" },\n                { \"NO\", \"Norway\" },\n                { \"OM\", \"Oman\" },\n                { \"PK\", \"Pakistan\" },\n                { \"PW\", \"Palau\" },\n                { \"PS\", \"Palestinian Territory, Occupied\" },\n                { \"PA\", \"Panama\" },\n                { \"PG\", \"Papua New Guinea\" },\n                { \"PY\", \"Paraguay\" },\n                { \"PE\", \"Peru\" },\n                { \"PH\", \"Philippines\" },\n                { \"PN\", \"Pitcairn\" },\n                { \"PL\", \"Poland\" },\n                { \"PT\", \"Portugal\" },\n                { \"PR\", \"Puerto Rico\" },\n                { \"QA\", \"Qatar\" },\n                { \"RE\", \"Reunion\" },\n                { \"RO\", \"Romania\" },\n                { \"RU\", \"Russian Federation\" },\n                { \"RW\", \"Rwanda\" },\n                { \"SH\", \"Saint Helena\" },\n                { \"KN\", \"Saint Kitts And Nevis\" },\n                { \"LC\", \"Saint Lucia\" },\n                { \"PM\", \"Saint Pierre And Miquelon\" },\n                { \"VC\", \"Saint Vincent And The Grenadines\" },\n                { \"WS\", \"Samoa\" },\n                { \"SM\", \"San Marino\" },\n                { \"ST\", \"Sao Tome And Principe\" },\n                { \"SA\", \"Saudi Arabia\" },\n                { \"SN\", \"Senegal\" },\n                { \"RS\", \"Serbia\" },\n                { \"SC\", \"Seychelles\" },\n                { \"SL\", \"Sierra Leone\" },\n                { \"SG\", \"Singapore\" },\n                { \"SK\", \"Slovakia\" },\n                { \"SI\", \"Slovenia\" },\n                { \"SB\", \"Solomon Islands\" },\n                { \"SO\", \"Somalia\" },\n                { \"ZA\", \"South Africa\" },\n                { \"GS\", \"South Georgia And The South Sandwich Islands\" },\n                { \"ES\", \"Spain\" },\n                { \"LK\", \"Sri Lanka\" },\n                { \"SD\", \"Sudan\" },\n                { \"SR\", \"Suriname\" },\n                { \"SJ\", \"Svalbard And Jan Mayen\" },\n                { \"SZ\", \"Swaziland\" },\n                { \"SE\", \"Sweden\" },\n                { \"CH\", \"Switzerland\" },\n                { \"SY\", \"Syrian Arab Republic\" },\n                { \"TW\", \"Taiwan, Province Of China\" },\n                { \"TJ\", \"Tajikistan\" },\n                { \"TZ\", \"Tanzania, United Republic Of\" },\n                { \"TH\", \"Thailand\" },\n                { \"TG\", \"Togo\" },\n                { \"TK\", \"Tokelau\" },\n                { \"TO\", \"Tonga\" },\n                { \"TT\", \"Trinidad And Tobago\" },\n                { \"TN\", \"Tunisia\" },\n                { \"TR\", \"Turkey\" },\n                { \"TM\", \"Turkmenistan\" },\n                { \"TC\", \"Turks And Caicos Islands\" },\n                { \"TV\", \"Tuvalu\" },\n                { \"UG\", \"Uganda\" },\n                { \"UA\", \"Ukraine\" },\n                { \"AE\", \"United Arab Emirates\" },\n                { \"GB\", \"United Kingdom\" },\n                { \"US\", \"United States\" },\n                { \"UM\", \"United States Minor Outlying Islands\" },\n                { \"UY\", \"Uruguay\" },\n                { \"UZ\", \"Uzbekistan\" },\n                { \"VU\", \"Vanuatu\" },\n                { \"VE\", \"Venezuela\" },\n                { \"VN\", \"Viet Nam\" },\n                { \"VG\", \"Virgin Islands, British\" },\n                { \"VI\", \"Virgin Islands, U.s.\" },\n                { \"WF\", \"Wallis And Futuna\" },\n                { \"EH\", \"Western Sahara\" },\n                { \"YE\", \"Yemen\" },\n                { \"ZM\", \"Zambia\" },\n                { \"ZW\", \"Zimbabwe\" },\n            };\n\n        public static IEnumerable<Country> SearchCountries(string term)\n        {\n            if (string.IsNullOrEmpty(term)) yield break;\n            var upper = term.ToUpperInvariant();\n            foreach (var pair in _Countries)\n            {\n                var nameUpper = pair.Value.ToUpperInvariant();\n                if (nameUpper.Contains(upper))\n                {\n                    yield return new Country(pair.Key, pair.Value);\n                }\n            }\n        }        \n    }\n}\n"
  },
  {
    "path": "src/SampleProject/Common/CountrySelector.cs",
    "content": "﻿/*\nCopyright (c) 2020-2021 Integrative Software LLC\nCreated: 12/2020\nAuthor: Pablo Carbonell\n*/\n\nusing Integrative.Lara;\nusing System.Collections.Generic;\nusing System.Threading.Tasks;\n\nnamespace SampleProject.Common\n{\n    internal class CountrySelector : WebComponent, IAutocompleteProvider\n    {\n        public CountrySelector()\n        {\n            var auto = new AutocompleteElement\n            {\n                Class = \"form-control\"\n            };\n            var span = new HtmlSpanElement();\n            ShadowRoot.Children = new Node[]\n            {\n                new HtmlDivElement\n                {\n                    Class = \"form-row mt-3\",\n                    Children = new Node[]\n                    {\n                        new HtmlDivElement\n                        {\n                            Class = \"form-group col-md-3\",\n                            Children = new Node[]\n                            {\n                                new HtmlLabelElement\n                                {\n                                    InnerText = \"Select country\"\n                                },\n                                auto\n                            }\n                        }\n                    }\n                },\n                new HtmlDivElement\n                {\n                    Class = \"form-row mb-2\",\n                    Children = new Node[]\n                    {\n                        new HtmlButtonElement\n                        {\n                            InnerText = \"Show country code\",\n                            Class = \"btn btn-primary\"\n                        }\n                        .Event(\"click\", () => span.InnerText = auto.Value),\n                        new HtmlDivElement\n                        {\n                            Class = \"ml-2 pt-2\",\n                            Children = new Node[] { span }\n                        }\n                    }\n                },\n            };\n            auto.Start(new AutocompleteOptions\n            {\n                AutoFocus = true,\n                MinLength = 2,\n                Strict = true,\n                Provider = this\n            });\n        }\n\n        public Task<AutocompleteResponse> GetAutocompleteList(string term)\n        {\n            var suggestions = new List<AutocompleteEntry>();\n            foreach (var country in CountryList.SearchCountries(term))\n            {\n                suggestions.Add(new AutocompleteEntry\n                {\n                    Label = country.Name,\n                    Code = country.Code\n                });\n            }\n            var result = new AutocompleteResponse\n            {\n                Suggestions = suggestions\n            };\n            return Task.FromResult(result);\n        }\n    }\n}\n"
  },
  {
    "path": "src/SampleProject/Common/SampleAppBootstrap.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 5/2019\nAuthor: Pablo Carbonell\n*/\n\nusing Integrative.Lara;\n\nnamespace SampleProject.Common\n{\n    internal static class SampleAppBootstrap\n    {\n        public static void AppendTo(Element head)\n        {\n            head.AppendChild(new HtmlLinkElement\n            {\n                Rel = \"stylesheet\",\n                HRef = \"https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css\"\n            });\n            head.AppendChild(new HtmlScriptElement\n            {\n                Src = \"https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js\",\n                Defer = true\n            });\n            head.AppendChild(new HtmlScriptElement\n            {\n                Src = \"https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js\",\n                Defer = true\n            });\n        }\n    }\n}\n"
  },
  {
    "path": "src/SampleProject/Common/Tools.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 9/2019\nAuthor: Pablo Carbonell\n*/\n\nusing Integrative.Lara;\nusing System;\nusing System.Reflection;\n\nnamespace SampleProject.Common\n{\n    internal class Tools\n    {\n        public static byte[] LoadEmbeddedResource(Assembly assembly, string resourceName)\n        {\n            using var stream = assembly.GetManifestResourceStream(resourceName);\n            if (stream == null) throw new InvalidOperationException($\"Resource not found: {resourceName}\");\n            var bytes = new byte[stream.Length];\n            stream.Read(bytes, 0, bytes.Length);\n            return bytes;\n        }\n\n        public static string GetSpinnerHtml(string message)\n        {\n            var div = new HtmlDivElement\n            {\n                Class = \"d-flex justify-content-center\",\n                Children = new Node[]\n                {\n                    new HtmlDivElement\n                    {\n                        Class = \"spinner-border\",\n                    },\n                    new HtmlDivElement\n                    {\n                        Class = \"ml-2\",\n                        InnerText = message\n                    }\n                }\n            };\n            return div.GetHtml();\n        }\n    }\n}\n"
  },
  {
    "path": "src/SampleProject/Components/CheckboxSample.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 5/2019\nAuthor: Pablo Carbonell\n*/\n\nusing Integrative.Lara;\nusing System.Threading.Tasks;\n\nnamespace SampleProject.Components\n{\n    internal class CheckboxSample : WebComponent\n    {\n        public CheckboxSample()\n        {\n            var checkbox = new HtmlInputElement\n            {\n                Id = \"mycheckbox\",\n                Type = \"checkbox\",\n                Class = \"form-check-input\"\n            };\n            var toggle = new HtmlButtonElement\n            {\n                Class = \"btn btn-primary\",\n                InnerText = \"Toggle\"\n            };\n            toggle.On(\"click\", () =>\n            {\n                checkbox.Checked = !checkbox.Checked;\n                return Task.CompletedTask;\n            });\n            ShadowRoot.Children = new Node[]\n            {\n                new HtmlDivElement\n                {\n                    Class = \"form-row\",\n                    Children = new Node[]\n                    {\n                        new HtmlDivElement\n                        {\n                            Class = \"form-group col-md-2 my-1\",\n                            Children = new Node[]\n                            {\n                                new HtmlDivElement\n                                {\n                                    Class = \"form-check\",\n                                    Children = new Node[]\n                                    {\n                                        checkbox,\n                                        new HtmlLabelElement\n                                        {\n                                            For = checkbox.Id,\n                                            InnerText = \"Check me out\"\n                                        }\n                                    }\n                                }\n                            }\n                        },\n                        new HtmlDivElement\n                        {\n                            Class = \"form-group col-md-1\",\n                            Children = new Node[]\n                            {\n                                toggle\n                            }\n                        }\n                    }\n                }\n            };\n        }\n    }\n}\n"
  },
  {
    "path": "src/SampleProject/Components/CounterSample.cs",
    "content": "﻿/*\nCopyright (c) 2020-2021 Integrative Software LLC\nCreated: 1/2020\nAuthor: Pablo Carbonell\n*/\n\nusing Integrative.Lara;\n\nnamespace SampleProject.Components\n{\n    internal class CounterSample : WebComponent\n    {\n        private int _counter = 5;\n        private int Counter { get => _counter; set => SetProperty(ref _counter, value); }\n\n        private static int TextToInt(string? value)\n        {\n            return int.TryParse(value, out var result) ? result : 0;\n        }\n\n        public CounterSample()\n        {\n            ShadowRoot.Children = new Node[]\n            {\n                new HtmlDivElement\n                {\n                    Class = \"form-row\",\n                    Children = new Node[]\n                    {\n                        new HtmlDivElement\n                        {\n                            Class = \"form-group col-md-2\",\n                            Children = new Node[]\n                            {\n                                new HtmlInputElement\n                                {\n                                    Type = \"number\",\n                                    Class = \"form-control\"\n                                }\n                                .Bind(this, x => x.Value = Counter.ToString())\n                                .BindBack(x => Counter = TextToInt(x.Value))\n                            }\n                        },\n                        new HtmlDivElement\n                        {\n                            Class = \"form-group col-md-1\",\n                            Children = new Node[]\n                            {\n                                new HtmlButtonElement\n                                {\n                                    InnerText = \"Increase\",\n                                    Class = \"btn btn-primary\"\n                                }\n                                .Event(\"click\", () => Counter++)\n                            }\n                        }\n                    }\n                }\n            };\n        }\n    }\n}\n"
  },
  {
    "path": "src/SampleProject/Components/KitchenSinkComponent.cs",
    "content": "﻿/*\nCopyright (c) 2020-2021 Integrative Software LLC\nCreated: 12/2020\nAuthor: Pablo Carbonell\n*/\n\nusing Integrative.Lara;\nusing SampleProject.Common;\n\nnamespace SampleProject.Components\n{\n    internal class KitchenSinkComponent : WebComponent\n    {\n        public KitchenSinkComponent()\n        {\n            ShadowRoot.Children = new Node[]\n            {\n                new HtmlDivElement\n                {\n                    Class = \"container p-4\",\n                    Children = new Node[]\n                    {\n                        new CounterSample(),\n                        new CheckboxSample(),\n                        new MultiselectSample(),\n                        new LockingSample(),\n                        new LongRunningSample(),\n                        new CountrySelector(),\n                        new HtmlDivElement\n                        {\n                            Class = \"mt-3\",\n                            Children = new Node[]\n                            {\n                                new HtmlDivElement\n                                {\n                                    Class = \"mt-2\",\n                                    Children = new Node[]\n                                    {\n                                        new HtmlDivElement\n                                        {\n                                            Children = new Node[]\n                                            {\n                                                new HtmlAnchorElement\n                                                {\n                                                    HRef = \"/upload\",\n                                                    Target = \"_blank\",\n                                                    InnerText = \"File upload example\"\n                                                }\n                                            }\n                                        },\n                                        new HtmlDivElement\n                                        {\n                                            Children = new Node[]\n                                            {\n                                                new HtmlAnchorElement\n                                                {\n                                                    HRef = \"/server\",\n                                                    Target = \"_blank\",\n                                                    InnerText = \"Server-side events\"\n                                                }\n                                            }\n                                        }\n                                    }\n                                },\n                            }\n                        }\n                    }\n                }\n            };\n        }\n    }\n}\n"
  },
  {
    "path": "src/SampleProject/Components/LockingSample.cs",
    "content": "﻿/*\nCopyright (c) 2020-2021 Integrative Software LLC\nCreated: 12/2020\nAuthor: Pablo Carbonell\n*/\n\nusing Integrative.Lara;\nusing SampleProject.Common;\nusing System.Threading.Tasks;\n\nnamespace SampleProject.Components\n{\n    internal class LockingSample : WebComponent\n    {\n        public LockingSample()\n        {\n            ShadowRoot.Children = new Node[]\n            {\n                new HtmlDivElement\n                {\n                    Class = \"form-row\",\n                    Children = new Node[]\n                    {\n                        new HtmlButtonElement\n                        {\n                            Class = \"btn btn-primary my-2\",\n                            InnerText = \"Action that blocks the UI\"\n                        }\n                        .Event(new EventSettings\n                        {\n                            EventName = \"click\",\n                            Handler = () => Task.Delay(1000),\n                            BlockOptions = new BlockOptions\n                            {\n                                ShowHtmlMessage = Tools.GetSpinnerHtml(\"Brewing coffee...\")\n                            }\n                        })\n                    }\n                },\n            };\n        }\n    }\n}\n"
  },
  {
    "path": "src/SampleProject/Components/LongRunningSample.cs",
    "content": "﻿/*\nCopyright (c) 2020-2021 Integrative Software LLC\nCreated: 1/2020\nAuthor: Pablo Carbonell\n*/\n\nusing Integrative.Lara;\nusing System.Threading.Tasks;\n\nnamespace SampleProject.Components\n{\n    internal class LongRunningSample : WebComponent\n    {\n        private static readonly string[] _Steps = {\n            \"Putting grounds into container\",\n            \"Covering coffee and water mixture\",\n            \"Filtering coffee and water mixture\",\n            \"Serving...\"\n        };\n\n        public LongRunningSample()\n        {\n            ShadowRoot.Children = new Node[]\n            {\n                new HtmlDivElement\n                {\n                    Class = \"form-row\",\n                    Children = new Node[]\n                    {\n                        new HtmlButtonElement\n                        {\n                            Class = \"btn btn-primary my-2\",\n                            InnerText = \"Long-running action\"\n                        }\n                        .Extract(out var button)\n                    }\n                },\n                new HtmlDivElement\n                {\n                    Class = \"card text-center\",\n                    Style = \"display: none; width: 18rem\",\n                    Children = new Node[]\n                    {\n                        new HtmlImageElement\n                        {\n                            Class = \"card-img-top mt-2\",\n                            Height = \"100\",\n                            Src = \"/Coffee.svg\",\n                            Alt = \"Coffee mug\"\n                        },\n                        new HtmlDivElement\n                        {\n                            Class = \"card-body\",\n                            Children = new Node[]\n                            {\n                                new HtmlHeadingElement(5)\n                                {\n                                    Class = \"card-title\",\n                                    InnerText = \"Preparing...\"\n                                },\n                                new HtmlParagraphElement\n                                {\n                                    Class = \"card=-text\"\n                                }\n                                .Extract(out var text)\n                            }\n                        }\n                    }\n                }\n                .Extract(out var card)\n            };\n            button.On(new EventSettings\n            {\n                EventName = \"click\",\n                LongRunning = true,\n                BlockOptions = new BlockOptions\n                {\n                    ShowElementId = card.Id\n                },\n                Handler = async () =>\n                {\n                    foreach (var step in _Steps)\n                    {\n                        text.InnerText = step;\n                        await LaraUI.FlushPartialChanges();\n                        await Task.Delay(800);\n                    }\n                    text.ClearChildren();\n                }\n            });\n        }\n    }\n}\n"
  },
  {
    "path": "src/SampleProject/Components/MultiselectSample.cs",
    "content": "﻿/*\nCopyright (c) 2020-2021 Integrative Software LLC\nCreated: 12/2020\nAuthor: Pablo Carbonell\n*/\n\nusing Integrative.Lara;\n\nnamespace SampleProject.Components\n{\n    internal class MultiselectSample : WebComponent\n    {\n        public MultiselectSample()\n        {\n            var combo = new HtmlSelectElement\n            {\n                Class = \"form-control\",\n                Multiple = true\n            };\n            combo.AddOption(\"N\", \"North\");\n            combo.AddOption(\"E\", \"East\");\n            combo.AddOption(\"S\", \"South\");\n            combo.AddOption(\"W\", \"West\");\n            var toggle = new HtmlButtonElement\n            {\n                Class = \"btn btn-primary\",\n                InnerText = \"Toggle\"\n            };\n            toggle.On(\"click\", () =>\n            {\n                foreach (var child in combo.Children)\n                {\n                    if (child is not HtmlOptionElement option) continue;\n                    option.Selected = !option.Selected;\n                }\n            });\n            ShadowRoot.Children = new Node[]\n            {\n                new HtmlDivElement\n                {\n                    Class = \"form-row\",\n                    Children = new Node[]\n                    {\n                        new HtmlDivElement\n                        {\n                            Class = \"form-group col-md-2\",\n                            Children = new Node[] { combo }\n                        },\n                        new HtmlDivElement\n                        {\n                            Class = \"form-group col-md-1\",\n                            Children = new Node[] { toggle }\n                        }\n                    }\n                }\n            };\n        }\n    }\n}\n"
  },
  {
    "path": "src/SampleProject/Components/SelectSample.cs",
    "content": "﻿/*\nCopyright (c) 2020-2021 Integrative Software LLC\nCreated: 12/2020\nAuthor: Pablo Carbonell\n*/\n\nusing Integrative.Lara;\n\nnamespace SampleProject.Components\n{\n    internal class SelectSample : WebComponent\n    {\n        public int Test { get; init; }\n\n        public SelectSample()\n        {\n            ShadowRoot.Child(\n                new HtmlDivElement { Class = \"form-row\" }.Child(\n                    new HtmlDivElement { Class = \"form-group col-md-2\" }.Child(\n                        new WeekdayCombo().Extract(out var combo)\n                    ),\n                    new HtmlDivElement { Class = \"form-group col-md-1\"}.Child(\n                        new HtmlButtonElement\n                            { InnerText = \"Advance\", Class =\"btn btn-primary\" }\n                            .Event(\"click\", () => combo.NextDay())\n                    )\n                ));\n        }\n    }\n}\n"
  },
  {
    "path": "src/SampleProject/Components/UploadSample.cs",
    "content": "﻿/*\nCopyright (c) 2020-2021 Integrative Software LLC\nCreated: 12/2020\nAuthor: Pablo Carbonell\n*/\n\nusing Integrative.Lara;\nusing System.Collections.Generic;\nusing System.Threading.Tasks;\n\nnamespace SampleProject.Components\n{\n    internal class UploadSample : WebComponent\n    {\n        public UploadSample()\n        {\n            var file = new HtmlInputElement\n            {\n                Type = \"file\",\n                Multiple = true,\n                Style = \"margin: 5px\"\n            };\n            var span = new HtmlSpanElement();\n            ShadowRoot.Children = new Node[]\n            {\n                new HtmlDivElement\n                {\n                    Children = new Node[]\n                    {\n                        file\n                    }\n                },\n                new HtmlDivElement\n                {\n                    Children = new Node[]\n                    {\n                        new HtmlButtonElement\n                        {\n                            InnerText = \"Upload via Ajax\",\n                            Style = \"margin: 5px\"\n                        }\n                        .Event(new EventSettings\n                        {\n                            EventName = \"click\",\n                            UploadFiles = true,\n                            Handler = () => {\n                                span.InnerText = GetUploadText(file);\n                                return Task.CompletedTask;\n                                },\n                        }),\n                        new HtmlButtonElement\n                        {\n                            InnerText = \"Upload via Websocket\",\n                            Style = \"margin: 5px\"\n                        }\n                        .Event(new EventSettings\n                        {\n                            EventName = \"click\",\n                            UploadFiles = true,\n                            Handler = () => {\n                                span.InnerText = GetUploadText(file);\n                                return Task.CompletedTask;\n                                },\n                            LongRunning = true\n                        }),\n                    }\n                },\n                new HtmlDivElement\n                {\n                    Style = \"margin: 5px\",\n                    Children = new Node[] { span }\n                }\n            };\n        }\n\n        private static string GetUploadText(HtmlInputElement input)\n        {\n            if (input.Files.Count == 0)\n            {\n                return \"No files uploaded\";\n            }\n\n            return \"Uploaded: \" + string.Join(\", \", GetFileNames(input));\n        }\n\n        private static IEnumerable<string> GetFileNames(HtmlInputElement input)\n        {\n            foreach (var file in input.Files)\n            {\n                var text = $\"{file.FileName} ({file.Length} bytes)\";\n                yield return text;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/SampleProject/Components/WeekdayCombo.cs",
    "content": "﻿/*\nCopyright (c) 2020-2021 Integrative Software LLC\nCreated: 12/2020\nAuthor: Pablo Carbonell\n*/\n\nusing Integrative.Lara;\n\nnamespace SampleProject.Components\n{\n    public class WeekdayCombo : WebComponent\n    {\n        private static readonly string[] _WeekDays\n            = { \"Monday\", \"Tuesday\", \"Wednesday\", \"Thursday\",\n            \"Friday\", \"Saturday\", \"Sunday\" };\n\n        private int _weekday;\n        private int Weekday { get => _weekday; set => SetProperty(ref _weekday, value); }\n\n        public WeekdayCombo()\n        {\n            var combo = new HtmlSelectElement { Class = \"form-control\", Id = \"MyWeek\" };\n            for (var index = 0; index < _WeekDays.Length; index++)\n            {\n                combo.AddOption(index.ToString(), _WeekDays[index]);\n            }\n            combo.Bind(this, _ => combo.Value = Weekday.ToString());\n            combo.BindBack(_ => Weekday = int.Parse(combo.Value ?? \"\"));\n            ShadowRoot.Children = new[] { combo };\n        }\n\n        public void NextDay()\n            => Weekday = (Weekday + 1) % _WeekDays.Length;\n    }\n}\n"
  },
  {
    "path": "src/SampleProject/LaraSample.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <TargetFramework>netcoreapp2.1</TargetFramework>\n    <Platforms>x64;AnyCPU</Platforms>\n    <RuntimeIdentifiers>win-x64;osx-x64;linux-x64</RuntimeIdentifiers>\n    <OutputType>WinExe</OutputType>\n    <ApplicationIcon />\n    <StartupObject />\n    <LangVersion>latest</LangVersion>\n  </PropertyGroup>\n\n  <ItemGroup>\n    <PackageReference Include=\"Integrative.Lara\" Version=\"0.4.12\" />\n  </ItemGroup>\n\n</Project>\n"
  },
  {
    "path": "src/SampleProject/Main/Program.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 5/2019\nAuthor: Pablo Carbonell\n*/\n\nusing Integrative.Lara;\nusing SampleProject.Pages;\nusing System;\nusing System.Threading.Tasks;\n\nnamespace SampleProject.Main\n{\n    internal static class Program\n    {\n        private static async Task Main()\n        {\n            // create application\n            using var app = new Application();\n            KitchenSinkPage.PublishMugImage(app);\n            app.PublishPage(\"/\", () => new KitchenSinkPage());\n            app.PublishPage(\"/upload\", () => new UploadFilePage());\n            app.PublishPage(\"/server\", () => new ServerEventsPage());\n\n            // start application\n            await app.Start(new StartServerOptions { Port = 8182 });\n            Console.WriteLine(\"Listening on http://localhost:8182/\");\n            LaraUI.LaunchBrowser(\"http://localhost:8182\");\n\n            // wait for shutdown\n            await app.WaitForShutdown();\n        }\n    }\n}\n"
  },
  {
    "path": "src/SampleProject/Pages/KitchenSinkPage.cs",
    "content": "﻿/*\nCopyright (c) 2020-2021 Integrative Software LLC\nCreated: 12/2020\nAuthor: Pablo Carbonell\n*/\n\nusing Integrative.Lara;\nusing SampleProject.Components;\nusing SampleProject.Common;\nusing System.Threading.Tasks;\n\nnamespace SampleProject.Pages\n{\n    internal class KitchenSinkPage : IPage\n    {\n        public Task OnGet()\n        {\n            var document = LaraUI.Page.Document;\n\n            // This sample application loads the CSS library 'Bootstrap'\n            SampleAppBootstrap.AppendTo(document.Head);\n\n            document.Body.AppendChild(new KitchenSinkComponent());\n\n            return Task.CompletedTask;\n        }\n\n        public static void PublishMugImage(Application app)\n        {\n            var assembly = typeof(KitchenSinkPage).Assembly;\n            var bytes = Tools.LoadEmbeddedResource(assembly, \"SampleProject.Assets.Coffee.svg\");\n            app.PublishFile(\"/Coffee.svg\", new StaticContent(bytes, \"image/svg+xml\"));\n        }\n    }\n}\n"
  },
  {
    "path": "src/SampleProject/Pages/ServerEventsPage.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 8/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System.Threading.Tasks;\nusing Integrative.Lara;\n\nnamespace SampleProject.Pages\n{\n    internal class ServerEventsPage : IPage\n    {\n        private readonly HtmlSpanElement _span;\n\n        public ServerEventsPage()\n        {\n            _span = new HtmlSpanElement();\n            _span.AppendText(\"waiting for server event...\");\n        }\n\n        public Task OnGet()\n        {\n            LaraUI.Page.JSBridge.ServerEventsOn();\n            LaraUI.Page.Document.Body.AppendChild(_span);\n            Task.Run(DelayedTask);\n            return Task.CompletedTask;\n        }\n\n        private async void DelayedTask()\n        {\n            await Task.Delay(4000);\n            using var access = LaraUI.Document.StartServerEvent();\n            _span.ClearChildren();\n            _span.AppendText(\"server event executed\");\n        }\n    }\n}\n"
  },
  {
    "path": "src/SampleProject/Pages/UploadFilePage.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 11/2019\nAuthor: Pablo Carbonell\n*/\n\nusing Integrative.Lara;\nusing SampleProject.Components;\nusing System.Threading.Tasks;\n\nnamespace SampleProject.Pages\n{\n    internal class UploadFilePage : IPage\n    {\n        public Task OnGet()\n        {\n            LaraUI.Document.Body.AppendChild(new UploadSample());\n            return Task.CompletedTask;\n        }\n    }\n}\n"
  },
  {
    "path": "src/SampleProject/Properties/launchSettings.json",
    "content": "{\n  \"profiles\": {\n    \"SampleProject\": {\n      \"commandName\": \"Project\"\n    }\n  }\n}"
  },
  {
    "path": "src/SampleProject/SampleProject.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <TargetFramework>net5.0</TargetFramework>\n    <Platforms>x64;AnyCPU</Platforms>\n    <RuntimeIdentifiers>win-x64;osx-x64;linux-x64</RuntimeIdentifiers>\n    <OutputType>Exe</OutputType>\n    <ApplicationIcon />\n    <StartupObject />\n    <LangVersion>latest</LangVersion>\n    <Nullable>enable</Nullable>\n  </PropertyGroup>\n\n  <ItemGroup>\n    <None Remove=\"Assets\\Coffee.svg\" />\n  </ItemGroup>\n\n  <ItemGroup>\n    <EmbeddedResource Include=\"Assets\\Coffee.svg\" />\n  </ItemGroup>\n\n  <ItemGroup>\n    <ProjectReference Include=\"..\\LaraUI\\LaraUI.csproj\" />\n  </ItemGroup>\n\n</Project>\n"
  },
  {
    "path": "src/Tests/Components/AutocompleteTesting.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 11/2019\nAuthor: Pablo Carbonell\n*/\n\nusing Integrative.Lara.Tests.Main;\nusing Integrative.Lara.Tests.Middleware;\nusing Moq;\nusing System.Collections.Generic;\nusing System.Threading.Tasks;\nusing Xunit;\n\nnamespace Integrative.Lara.Tests.Components\n{\n    internal class MyProvider : IAutocompleteProvider\n    {\n        public Task<AutocompleteResponse> GetAutocompleteList(string term)\n        {\n            var list = new List<AutocompleteEntry>();\n            var response = new AutocompleteResponse\n            {\n                Suggestions = list\n            };\n            list.Add(new AutocompleteEntry\n            {\n                Code = \"R\",\n                Label = \"Red\"\n            });\n            list.Add(new AutocompleteEntry\n            {\n                Code = \"G\",\n                Label = \"Green\"\n            });\n            list.Add(new AutocompleteEntry\n            {\n                Code = \"B\",\n                Label = \"Blue\"\n            });\n            return Task.FromResult(response);\n        }\n    }\n\n    public class AutocompleteTesting : DummyContextTesting\n    {\n        public AutocompleteTesting()\n        {\n            Context.Application.PublishComponent(new WebComponentOptions\n            {\n                ComponentTagName = AutocompleteElement.CustomTag,\n                ComponentType = typeof(AutocompleteElement)\n            });\n        }\n\n        [Fact]\n        public void InnerInputValue()\n        {\n            var x = new AutocompleteElement\n            {\n                Value = \"abc\"\n            };\n            Assert.Equal(\"abc\", x.Value);\n            Assert.Equal(\"abc\", x.InnerInput.Value);\n        }\n\n        [Fact]\n        public void AutocompleteOptionsStore()\n        {\n            var provider = new MyProvider();\n            var options = new AutocompleteOptions\n            {\n                Provider = provider,\n                AutoFocus = true,\n                MinLength = 2,\n                Strict = true\n            };\n            Assert.Same(provider, options.Provider);\n            Assert.True(options.AutoFocus);\n            Assert.Equal(2, options.MinLength);\n            Assert.True(options.Strict);\n        }\n\n        [Fact]\n        public void AutocompleteStarts()\n        {\n            LaraUI.InternalContext.Value = Context;\n            var x = new AutocompleteElement();\n            var provider = new MyProvider();\n            var options = new AutocompleteOptions\n            {\n                Provider = provider,\n                AutoFocus = true,\n                MinLength = 2,\n                Strict = true\n            };\n            x.Start(options);\n            var doc = new Document(new MyPage(), 100);\n            var bridge = new Mock<IJsBridge>();\n            Context.JSBridge = bridge.Object;\n            \n            const string code = \"LaraUI.autocompleteApply(context.Payload);\";\n            var payload = new AutocompletePayload\n            {\n                AutoFocus = options.AutoFocus,\n                ElementId = x.InnerInput.Id,\n                MinLength = options.MinLength,\n                Strict = options.Strict\n            };\n            var json = LaraUI.JSON.Stringify(payload);\n\n            bridge.Setup(x1 => x1.Submit(code, json));\n            doc.Body.AppendChild(x);\n            bridge.Verify(x2 => x2.Submit(code, json), Times.Once);\n        }\n\n        [Fact]\n        public void AutocompleteStartStop()\n        {\n            LaraUI.InternalContext.Value = Context;\n            var x = new AutocompleteElement();\n            var provider = new MyProvider();\n            var options = new AutocompleteOptions\n            {\n                Provider = provider,\n                AutoFocus = true,\n                MinLength = 2,\n                Strict = true\n            };\n\n            var doc = new Document(new MyPage(), 100);\n            var bridge = new Mock<IJsBridge>();\n            Context.JSBridge = bridge.Object;\n            doc.Body.AppendChild(x);\n\n            x.Start(options);\n            Assert.Equal(1, AutocompleteService.RegisteredCount);\n\n            x.Stop();\n            Assert.Equal(0, AutocompleteService.RegisteredCount);\n        }\n\n        [Fact]\n        public void OnDisconnectStops()\n        {\n            LaraUI.InternalContext.Value = Context;\n            var x = new AutocompleteElement();\n            var provider = new MyProvider();\n            var options = new AutocompleteOptions\n            {\n                Provider = provider,\n                AutoFocus = true,\n                MinLength = 2,\n                Strict = true\n            };\n\n            var doc = new Document(new MyPage(), 100);\n            var bridge = new Mock<IJsBridge>();\n            Context.JSBridge = bridge.Object;\n            doc.Body.AppendChild(x);\n\n            x.Start(options);\n            Assert.Equal(1, AutocompleteService.RegisteredCount);\n            Assert.Same(options, x.GetOptions());\n\n            x.Remove();\n            Assert.Equal(0, AutocompleteService.RegisteredCount);\n        }\n\n        [Fact]\n        public void AutocompleteEntry()\n        {\n            var x = new AutocompleteEntry\n            {\n                Code = \"a\",\n                Html = \"b\",\n                Label = \"c\",\n                Subtitle = \"d\"\n            };\n            Assert.Equal(\"a\", x.Code);\n            Assert.Equal(\"b\", x.Html);\n            Assert.Equal(\"c\", x.Label);\n            Assert.Equal(\"d\", x.Subtitle);\n        }\n\n        [Fact]\n        public void AutocompleteResponse()\n        {\n            var list = new List<AutocompleteEntry>();\n            var x = new AutocompleteResponse\n            {\n                Suggestions = list\n            };\n            Assert.Same(list, x.Suggestions);\n        }\n\n        [Fact]\n        public async void AutocompleteServiceRun()\n        {\n            LaraUI.InternalContext.Value = Context;\n            var x = new AutocompleteElement();\n            var provider = new MyProvider();\n            var options = new AutocompleteOptions\n            {\n                Provider = provider,\n                AutoFocus = true,\n                MinLength = 2,\n                Strict = true,                \n            };\n            var doc = new Document(new MyPage(), 100);\n            var bridge = new Mock<IJsBridge>();\n            Context.JSBridge = bridge.Object;\n            doc.Body.AppendChild(x);\n            x.Start(options);\n\n            var service = new AutocompleteService();\n            var request = new AutocompleteRequest\n            {\n                Key = x.AutocompleteId,\n                Term = \"B\"\n            };\n            Context.RequestBody = LaraUI.JSON.Stringify(request);\n            var text = await service.Execute();\n            var response = LaraUI.JSON.Parse<AutocompleteResponse>(text);\n            Assert.Equal(3, response.Suggestions!.Count);\n            var item = response.Suggestions[0];\n            Assert.Equal(\"Red\", item.Label);\n            Assert.Equal(\"R\", item.Code);\n        }\n\n        [Fact]\n        public void RegistryReplacesEntries()\n        {\n            LaraUI.InternalContext.Value = Context;\n            var x = new AutocompleteRegistry();\n            var auto1 = new AutocompleteElement();\n            var auto2 = new AutocompleteElement();\n            x.Set(\"a\", auto1);\n            x.Set(\"a\", auto2);\n            Assert.True(x.TryGet(\"a\", out var autoX));\n            Assert.Same(auto2, autoX);\n        }\n\n        [Fact]\n        public async void ExecuteNotFoundReturnsEmpty()\n        {\n            var request = new AutocompleteRequest\n            {\n                Key = \"a\",\n                Term = \"\"\n            };\n            var json = LaraUI.JSON.Stringify(request);\n            var result = await AutocompleteService.Execute(json);\n            Assert.Equal(string.Empty, result);\n        }\n\n    }\n}\n"
  },
  {
    "path": "src/Tests/Components/ComponentTesting.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 8/2019\nAuthor: Pablo Carbonell\n*/\n\nusing Microsoft.AspNetCore.Http;\nusing Moq;\nusing System;\nusing System.Collections.Generic;\nusing System.Net;\nusing System.Threading.Tasks;\nusing Xunit;\n\nnamespace Integrative.Lara.Tests.Components\n{\n    [LaraWebComponent(\"x-dummy\")]\n    internal class MyDummyComponent : WebComponent\n    {\n        public int Moved { get; private set; }\n\n        public MyDummyComponent() : base(\"x-dummy\")\n        {\n        }\n\n        protected override IEnumerable<string> GetObservedAttributes()\n        {\n            return new[] { \"class\" };\n        }\n\n        protected override void OnMove()\n        {\n            base.OnMove();\n            Moved++;\n        }\n    }\n\n    [LaraWebComponent(\"x-com\")]\n    [Obsolete(\"Old methods\")]\n    internal class Xcom : WebComponent\n    {\n        public Xcom() : base(\"x-com\")\n        {\n            var builder = new LaraBuilder(ShadowRoot);\n            builder.Push(\"div\", \"\", \"div1\")\n                .Push(\"div\", \"\", \"div1a\")\n                .Pop()\n            .Pop()\n            .Push(\"div\", \"\", \"div2\")\n            .Pop()\n            .AppendText(\"lalas\");\n        }\n    }\n\n    [LaraWebComponent(\"x-light\")]\n    internal class LightCom : WebComponent\n    {\n        public LightCom() : base(\"x-light\")\n        {\n        }\n    }\n\n    [LaraWebComponent(\"x-slotter\")]\n    internal class MySlotter : WebComponent\n    {\n        public MySlotter() : base(\"x-slotter\")\n        {\n            var div = Create(\"div\");\n            var slot = Create(\"slot\");\n            ShadowRoot.AppendChild(div);\n            div.AppendChild(slot);\n        }\n    }\n\n    [LaraWebComponent(\"x-twodiv\")]\n    internal class MyTwoDivComponent : WebComponent\n    {\n        public MyTwoDivComponent(bool useShadow) : base(\"x-twodiv\")\n        {\n            if (!useShadow) return;\n            ShadowRoot.AppendChild(Create(\"div\"));\n            ShadowRoot.AppendText(\"hello\");\n        }\n    }\n\n    [LaraWebComponent(\"x-obsolete\")]\n    internal class ObsoleteComponent : WebComponent\n    {\n        public ObsoleteComponent() : base(\"x-obsolete\")\n        {\n        }\n\n        public int Counter { get; private set; }\n\n        [Obsolete]\n        public void Test()\n        {\n            AttachShadow();\n            Counter++;\n        }\n    }\n\n    public class ComponentTesting : IDisposable\n    {\n        private readonly Application _app;\n\n        public ComponentTesting()\n        {\n            _app = new Application();\n            _app.PublishAssemblies();\n            var http = new Mock<HttpContext>();\n            var connection = new Connection(Guid.NewGuid(), IPAddress.Loopback);\n            var context = new PageContext(_app, http.Object, connection);\n            LaraUI.InternalContext.Value = context;\n        }\n\n        public void Dispose()\n        {\n            _app.Dispose();\n            GC.SuppressFinalize(this);\n        }\n\n        [Fact]\n        public void RegisterComponentSucceeds()\n        {\n            _app.PublishComponent(new WebComponentOptions\n            {\n                ComponentTagName = \"x-caca\",\n                ComponentType = typeof(MyComponent)\n            });\n            Assert.True(LaraUI.Context.Application.TryGetComponent(\"x-caca\", out var type));\n            _app.UnPublishWebComponent(\"x-caca\");\n            Assert.Equal(typeof(MyComponent), type);\n            Assert.False(LaraUI.Context.Application.TryGetComponent(\"x-caca\", out _));\n        }\n\n        private class MyComponent : WebComponent\n        {\n            public MyComponent() : base(\"x-caca\")\n            {\n            }\n        }\n\n        private class MyPage : IPage\n        {\n            public Task OnGet()\n            {\n                return Task.CompletedTask;\n            }\n        }\n\n        [Fact]\n        public void ServerEventsOnSucceeds()\n        {\n            var context = CreateMockPage();\n            Assert.Equal(ServerEventsStatus.Disabled, context.Document.ServerEventsStatus);\n            context.JSBridge.ServerEventsOn();\n            Assert.Equal(ServerEventsStatus.Connecting, context.Document.ServerEventsStatus);\n            context.JSBridge.ServerEventsOff();\n            Assert.Equal(ServerEventsStatus.Disabled, context.Document.ServerEventsStatus);\n        }\n\n        private PageContext CreateMockPage()\n        {\n            var http = new Mock<HttpContext>();\n            var guid = Guid.Parse(\"{5166FB58-FB45-4622-90E6-195E2448F2C9}\");\n            var connection = new Connection(guid, IPAddress.Loopback);\n            var document = new Document(new MyPage(), 100);\n            return new PageContext(_app, http.Object, connection)\n            {\n                DocumentInternal = document\n            };\n        }\n\n        [Fact]\n        [Obsolete(\"Old methods\")]\n        public void WebComponentListsAllDescendents()\n        {\n            var x = new Xcom();\n            var div = Element.Create(\"div\");\n            div.Id = \"lala\";\n            x.AppendChild(div);\n            var set = new HashSet<string>();\n            foreach (var node in GetAllDescendents(x))\n            {\n                if (node is Element child && !string.IsNullOrEmpty(child.Id))\n                {\n                    set.Add(child.Id);\n                }\n            }\n            Assert.Contains(\"div1\", set);\n            Assert.Contains(\"div2\", set);\n            Assert.Contains(\"div1a\", set);\n            Assert.Contains(\"lala\", set);\n        }\n\n        [Fact]\n        [Obsolete(\"Old methods\")]\n        public void FlattenedChildrenIncludesPrintedOnes()\n        {\n            var container = Element.Create(\"div\");\n            var x = new Xcom();\n            var div = Element.Create(\"div\");\n            div.Id = \"lala\";\n            x.AppendChild(div);\n            container.AppendChild(x);\n            var set = new HashSet<string>();\n            foreach (var node in GetFlattened(container))\n            {\n                if (node is Element child && !string.IsNullOrEmpty(child.Id))\n                {\n                    set.Add(child.Id);\n                }\n            }\n            Assert.Contains(\"div1\", set);\n            Assert.Contains(\"div2\", set);\n            Assert.Contains(\"div1a\", set);\n            Assert.DoesNotContain(\"lala\", set);\n        }\n\n        private static IEnumerable<Node> GetAllDescendents(Element element)\n        {\n            return RecursiveExtension(element, x => x.GetAllDescendants());\n        }\n\n        private static IEnumerable<Node> GetFlattened(Element element)\n        {\n            return RecursiveExtension(element, x => x.GetLightChildren());\n        }\n\n        private static IEnumerable<Node> RecursiveExtension(Element root, Func<Element, IEnumerable<Node>> method)\n        {\n            foreach (var node in method(root))\n            {\n                yield return node;\n                if (node is not Element child) continue;\n                foreach (var grandchild in RecursiveExtension(child, method))\n                {\n                    yield return grandchild;\n                }\n            }\n        }\n\n        [Fact]\n        [Obsolete(\"Old methods\")]\n        public void GetSlotElementFinds()\n        {\n            var x = new MySlotter();\n            var builder = new LaraBuilder(x);\n            builder.Push(\"div\", \"\", \"slot1\")\n            .Pop()\n            .Push(\"div\", \"\", \"slot2\")\n                .Push(\"div\", \"\", \"slot2a\")\n                .Pop()\n            .Pop()\n            .Push(\"div\", \"\", \"slot3\")\n                .Attribute(\"slot\", \"a\")\n            .Pop();\n            var set = new HashSet<string?>();\n            foreach (var item in x.GetSlottedElements(\"\"))\n            {\n                if (item is Element element)\n                {\n                    set.Add(element.Id);\n                }\n            }\n            Assert.Contains(\"slot1\", set);\n            Assert.Contains(\"slot2\", set);\n            Assert.DoesNotContain(\"slot2a\", set);\n            Assert.DoesNotContain(\"slot3\", set);\n            var list = new List<Node>(x.GetSlottedElements(\"a\"));\n            Assert.Single(list);\n            var child = list[0] as Element;\n            Assert.NotNull(child);\n            Assert.Equal(\"slot3\", child!.Id);\n        }\n\n        [Fact]\n        public void ComponentNotifiedAttributeChanged()\n        {\n            _app.PublishComponent(new WebComponentOptions\n            {\n                ComponentTagName = \"x-att\",\n                ComponentType = typeof(MyAttributeSubscriptor)\n            });\n            var x = new MyAttributeSubscriptor();\n            x.SetAttribute(\"data-lala\", \"lolo\");\n            Assert.Equal(\"lolo\", x.MyData);\n        }\n\n        private class MyAttributeSubscriptor : WebComponent\n        {\n            public MyAttributeSubscriptor() : base(\"x-att\")\n            {\n            }\n\n            public string? MyData { get; private set; }\n\n            protected override IEnumerable<string> GetObservedAttributes()\n            {\n                return new[] { \"data-lala\" };\n            }\n\n            protected override void OnAttributeChanged(string attribute)\n            {\n                if (attribute == \"data-lala\")\n                {\n                    MyData = GetAttribute(\"data-lala\");\n                }\n            }\n        }\n\n        [Fact]\n        public void ObservedOnlyAttributeDoesNothing()\n        {\n            var x = new MyDummyComponent\n            {\n                Class = \"lala\"\n            };\n            Assert.Equal(\"lala\", x.Class);\n        }\n\n        [Fact]\n        [Obsolete(\"Old methods\")]\n        public void PublishAssembliesComponent()\n        {\n            Assert.True(LaraUI.Context.Application.TryGetComponent(\"x-com\", out var type));\n            Assert.Same(typeof(Xcom), type);\n        }\n\n        [Fact]\n        [Obsolete(\"Old methods\")]\n        public void SlotsPrintHostElements()\n        {\n            var document = new Document(new MyPage(), BaseModeController.DefaultKeepAliveInterval);\n            var builder = new LaraBuilder(document.Body);\n            builder.Push(\"x-slotter\")\n                .Push(\"div\", \"lalala\")\n                    .AppendText(\"hello\")\n                .Pop()\n            .Pop();\n            var writer = new DocumentWriter(document);\n            writer.Print();\n            var html = writer.ToString();\n            Assert.Contains(\"lalala\", html);\n            Assert.Contains(\"hello\", html);\n            Assert.DoesNotContain(\"x-slotter\", html);\n        }\n\n        [Fact]\n        [Obsolete(\"Old methods\")]\n        public void OrphanSlotPrintsItself()\n        {\n            var document = new Document(new MyPage(), BaseModeController.DefaultKeepAliveInterval);\n            var builder = new LaraBuilder(document.Body);\n            builder.Push(\"slot\", \"lalala\").Pop();\n            var writer = new DocumentWriter(document);\n            writer.Print();\n            var html = writer.ToString();\n            Assert.Contains(\"lalala\", html);\n        }\n\n        [Fact]\n        public void SlotNameSetsAttribute()\n        {\n            var x = new Slot\n            {\n                Name = \"lala\"\n            };\n            Assert.Equal(\"lala\", x.Name);\n            Assert.Equal(\"lala\", x.GetAttribute(\"name\"));\n        }\n\n        [Fact]\n        public void WebComponentsRequireDash()\n        {\n            var registry = new ComponentRegistry();\n            var found = false;\n            try\n            {\n                registry.Register(\"baba\", typeof(MyComponent));\n            }\n            catch (ArgumentException)\n            {\n                found = true;\n            }\n            Assert.True(found);\n        }\n\n        [Fact]\n        public void WebComponentsMustInherit()\n        {\n            var registry = new ComponentRegistry();\n            var found = false;\n            try\n            {\n                registry.Register(\"x-lolo\", typeof(HtmlInputElement));\n            }\n            catch (InvalidOperationException)\n            {\n                found = true;\n            }\n            Assert.True(found);\n        }\n\n        [Fact]\n        public void CannotRegisterSameTagTwice()\n        {\n            var registry = new ComponentRegistry();\n            registry.Register(\"x-baba\", typeof(MyComponent));\n            var found = false;\n            try\n            {\n                registry.Register(\"x-baba\", typeof(MyComponent));\n            }\n            catch (InvalidOperationException)\n            {\n                found = true;\n            }\n            Assert.True(found);\n        }\n\n        [Fact]\n        public void VerifyComponentSameType()\n        {\n            Assert.False(WebComponent.VerifyType(\"x-com\", typeof(MyComponent), out _));\n        }\n\n        [Fact]\n        public void NotifyMovedCalledDirectly()\n        {\n            var document = new Document(new MyPage(), BaseModeController.DefaultKeepAliveInterval);\n            var x = new MyDummyComponent();\n            var div1 = Element.Create(\"div\");\n            var div2 = Element.Create(\"div\");\n            document.Body.AppendChild(div1);\n            document.Body.AppendChild(div2);\n            div1.AppendChild(x);\n            Assert.Equal(0, x.Moved);\n            div2.AppendChild(x);\n            Assert.Equal(1, x.Moved);\n        }\n\n        [Fact]\n        public void GetContentNodeReturnsShadowChildren()\n        {\n            var component = new MyTwoDivComponent(true);\n            var content = component.GetContentNode();\n            Assert.Equal(ContentNodeType.Array, content.Type);\n            var array = content as ContentArrayNode;\n            Assert.NotNull(array);\n            Assert.NotNull(array!.Nodes);\n            Assert.Equal(2, array.Nodes!.Count);\n            Assert.Equal(ContentNodeType.Element, array.Nodes[0].Type);\n            Assert.Equal(ContentNodeType.Text, array.Nodes[1].Type);\n        }\n\n        [Fact]\n        [Obsolete(\"old method\")]\n        public void AttachShadowExecutes()\n        {\n            var x = new ObsoleteComponent();\n            x.Test();\n            Assert.Equal(1, x.Counter);\n        }\n\n        [Fact]\n        public void ParentSlotNotSlotting()\n        {\n            var slot = new Slot\n            {\n                IsSlotted = true\n            };\n            var x = Element.Create(\"div\");\n            slot.AppendChild(x);\n            Assert.False(SlottedCalculator.IsParentSlotting(x));\n        }\n\n        [Fact]\n        public void ShadowLightSlottedEmpty()\n        {\n            var component = new MyDummyComponent();\n            var div = Element.Create(\"div\");\n            var x = component.GetShadow();\n            x.AppendChild(div);\n            Assert.Empty(x.GetLightSlotted());\n        }\n\n        [Fact]\n        public void ShadowNotPrintable()\n        {\n            var div = Element.Create(\"div\");\n            var component = new MyDummyComponent();\n            var x = component.GetShadow();\n            Assert.False(x.IsPrintable);\n            Assert.True(div.IsPrintable);\n            Assert.False(component.IsPrintable);\n        }\n\n        [Fact]\n        public void SlotNameMatches()\n        {\n            var x = new Slot\n            {\n                Name = \"red\"\n            };\n            Assert.True(x.MatchesName(\"red\"));\n        }\n\n        [Fact]\n        public void TriggerEventRuns()\n        {\n            var counter = 0;\n            var x = new MyDummyComponent();\n            x.On(\"click\", () =>\n            {\n                counter++;\n                return Task.CompletedTask;\n            });\n            x.TriggerEvent(\"click\");\n            x.TriggerEvent(\"lala\");\n            Assert.Equal(1, counter);\n        }\n\n        [Fact]\n        public void ContentPlaceholderClass()\n        {\n            var x = new ContentPlaceholder(\"test\");\n            Assert.Equal(\"test\", x.ElementId);\n        }\n        \n    }\n}\n"
  },
  {
    "path": "src/Tests/DOM/AttributesTesting.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 5/2019\nAuthor: Pablo Carbonell\n*/\n\nusing Integrative.Lara.Tests.Main;\nusing Integrative.Lara.Tests.Middleware;\nusing Microsoft.AspNetCore.Http;\nusing Moq;\nusing System;\nusing System.Text;\nusing Xunit;\n\nnamespace Integrative.Lara.Tests.DOM\n{\n    public class AttributesTesting : DummyContextTesting\n    {\n        [Fact]\n        public void HasAttributeFindsAttribute()\n        {\n            var x = new Attributes(Element.Create(\"button\"));\n            x.SetAttributeLower(\"href\", \"lala\");\n            Assert.True(x.HasAttribute(\"href\"));\n            Assert.False(x.HasAttribute(\"lala\"));\n            Assert.Equal(\"lala\", x.GetAttribute(\"HREF\"));\n        }\n\n        [Fact]\n        public void ValueAttributeEnqueued()\n        {\n            var document = CreateDocument();\n            var element = Element.Create(\"button\", \"mybutton\");\n            document.Body.AppendChild(element);\n            document.OpenEventQueue();\n            element.SetAttribute(\"value\", \"5\");\n            var queue = document.GetQueue();\n            Assert.NotEmpty(queue);\n            var peek = queue.Peek();\n            Assert.True(peek is SetValueDelta);\n        }\n\n        private static Document CreateDocument()\n        {\n            var guid = Guid.Parse(\"{0857AE93-8591-4CB6-887E-C449ABFCAA7A}\");\n            var page = new MyPage();\n            return new Document(page, guid, BaseModeController.DefaultKeepAliveInterval);\n        }\n\n        [Fact]\n        public void SetFlagAttributeAddsNullValue()\n        {\n            var element = Element.Create(\"span\");\n            element.Hidden = true;\n            Assert.True(element.HasAttribute(\"hidden\"));\n            Assert.Equal(\"\", element.GetAttribute(\"hidden\"));\n            var count = 0;\n            foreach (var pair in element.Attributes)\n            {\n                if (pair.Key == \"id\") continue;\n                count++;\n                Assert.Equal(\"hidden\", pair.Key);\n                Assert.Equal(\"\", pair.Value);\n            }\n            Assert.Equal(1, count);\n            element.Hidden = false;\n            Assert.False(element.HasAttribute(\"hidden\"));\n        }\n\n        [Fact]\n        public void GetNonExistingReturnsEmpty()\n        {\n            var element = Element.Create(\"button\");\n            Assert.Equal(string.Empty, element.GetAttribute(\"lele\"));\n        }\n\n        [Fact]\n        public void RemovingAttributeRemovesValue()\n        {\n            var element = Element.Create(\"button\");\n            element.SetAttribute(\"data-test\", \"lala\");\n            element.RemoveAttribute(\"data-test\");\n            Assert.Empty(element.GetAttribute(\"data-test\"));\n        }\n\n        [Fact]\n        public void ReplacingSameValueNoQueue()\n        {\n            var element = Element.Create(\"div\");\n            element.Class = \"lala\";\n            var document = new Document(new MyPage(), BaseModeController.DefaultKeepAliveInterval);\n            document.Body.AppendChild(element);\n            document.OpenEventQueue();\n            element.Class = \"lala\";\n            Assert.Empty(document.GetQueue());\n        }\n\n        [Fact]\n        public void NotifySelectedSetsSelected()\n        {\n            var option = new HtmlOptionElement();\n            option.NotifyValue(new ElementEventValue\n            {\n                Checked = true,\n            });\n            Assert.True(option.Selected);\n        }\n\n        [Fact]\n        public void CreateNsSetsXlmns()\n        {\n            var x = Element.CreateNS(\"abc\", \"svg\");\n            Assert.Equal(\"abc\", x.GetAttribute(\"xlmns\"));\n        }\n\n        [Fact]\n        public void RemoveAttributeMissingSucceeds()\n        {\n            var document = CreateDocument();\n            var x = Element.Create(\"div\");\n            document.Body.AppendChild(x);\n            document.OpenEventQueue();\n            x.RemoveAttribute(\"lala\");\n            Assert.Empty(document.GetQueue());            \n        }\n\n        [Fact]\n        public void AttributesNotifyValueRemovesPrevious()\n        {\n            var x = Element.Create(\"div\");\n            x.SetAttributeLower(\"value\", \"one\");\n            x.NotifyValue(\"two\");\n            Assert.Equal(\"two\", x.GetAttribute(\"value\"));\n        }\n\n        [Fact]\n        public void MaxLevelDeep()\n        {\n            var found = false;\n            try\n            {\n                DocumentWriter.VerifyNestedLevel(DocumentWriter.MaxLevelDeep + 1);\n            }\n            catch (InvalidOperationException)\n            {\n                found = true;\n            }\n            Assert.True(found);\n        }\n\n        [Fact]\n        public void ToggleClassFlipsClass()\n        {\n            Assert.Equal(\"lala\", ClassEditor.ToggleClass(\"lala lolo\", \"lolo\"));\n            Assert.Equal(\"lala lolo\", ClassEditor.ToggleClass(\"lala\", \"lolo\"));\n        }\n\n        [Fact]\n        public void ElementToggleClass()\n        {\n            var x = Element.Create(\"div\");\n            x.Class = \"lala lolo\";\n            x.ToggleClass(\"lolo\");\n            Assert.Equal(\"lala\", x.Class);\n        }\n\n        [Fact]\n        public void TagNameCannotHaveSpaces()\n        {\n            var found = false;\n            try\n            {\n                Element.Create(\" a\");\n            }\n            catch (ArgumentException)\n            {\n                found = true;\n            }\n            Assert.True(found);\n        }\n\n        [Fact]\n        public void NotifyFlagSkipsSameValue()\n        {\n            var input = new HtmlInputElement\n            {\n                Checked = true\n            };\n            input.NotifyChecked(true);\n            Assert.True(input.Checked);\n        }\n\n        [Fact]\n        public void NotifyValueSkipsSameValue()\n        {\n            var input = new HtmlInputElement\n            {\n                Value = \"hello\"\n            };\n            input.NotifyValue(\"hello\");\n            Assert.Equal(\"hello\", input.Value);\n        }\n\n        [Fact]\n        public void ModifySlotElementWithParent()\n        {\n            var x = Element.Create(\"div\");\n            var div = Element.Create(\"div\");\n            div.AppendChild(x);\n            Assert.ThrowsAny<InvalidOperationException>(() => x.SetAttributeLower(\"slot\", \"test\"));\n        }\n\n        [Fact]\n        [Obsolete(\"Old methods\")]\n        public void SetInnerText()\n        {\n            var x = Element.Create(\"div\");\n            x.SetInnerText(\"<a\");\n            Assert.Equal(\"<a\", x.InnerText);\n        }\n\n        [Fact]\n        public void AppendNodeInnerText()\n        {\n            var x = Element.Create(\"div\");\n            x.InnerText = \"a\";\n            var builder = new StringBuilder();\n            x.AppendNodeInnerText(builder);\n            Assert.Equal(\"a\", builder.ToString());\n        }\n\n        [Fact]\n        public void GetNodeInnerText()\n        {\n            var x = Element.Create(\"div\");\n            Assert.True(string.IsNullOrEmpty(x.InnerText));\n            var x1 = Element.Create(\"div\");\n            var x2 = Element.Create(\"div\");\n            x.AppendChild(x1);\n            x.AppendChild(x2);\n            x1.InnerText = \"<\";\n            x2.InnerText = \">\";\n            Assert.Equal(\"<>\", x.InnerText);\n        }\n\n        [Fact]\n        public void NodeInnerText()\n        {\n            var x = new TextNode();\n            Assert.Equal(string.Empty, x.InnerText);\n            x.InnerText = \"<<\";\n            Assert.Equal(\"<<\", x.InnerText);\n        }\n\n        [Fact]\n        public void InputFilesAdd()\n        {\n            var file = new Mock<IFormFile>();\n            file.Setup(x1 => x1.Name).Returns(\"abc\");\n            var x = new HtmlInputElement\n            {\n                Type = \"file\"\n            };\n            x.AddFile(file.Object);\n            Assert.Single(x.Files);\n            var found = x.Files[0];\n            Assert.Equal(\"abc\", found.Name);\n        }\n    }\n}\n"
  },
  {
    "path": "src/Tests/DOM/BindingsTesting.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 8/2019\nAuthor: Pablo Carbonell\n*/\n\nusing Integrative.Lara.Tests.Middleware;\nusing System;\nusing System.Collections.Generic;\nusing System.Collections.ObjectModel;\nusing Xunit;\n\nnamespace Integrative.Lara.Tests.DOM\n{\n    internal class MyInputData : BindableBase\n    {\n        private string? _myvalue;\n        public string? MyValue\n        {\n            get => _myvalue;\n            set => SetProperty(ref _myvalue, value);\n        }\n\n        private bool _mychecked;\n        public bool MyChecked\n        {\n            get => _mychecked;\n            set => SetProperty(ref _mychecked, value);\n        }\n    }\n\n    public class BindingsTesting : DummyContextTesting\n    {\n        [Fact]\n        public void SetInnerTextSetsText()\n        {\n            var x = Element.Create(\"span\");\n            x.InnerText = \"hello\";\n            Assert.Equal(1, x.ChildCount);\n            VerifyInnerData(x, \"hello\");\n        }\n\n        private static void VerifyInnerData(Element element, string data)\n        {\n            var node = element.GetChildAt(0) as TextNode;\n            Assert.NotNull(node);\n            Assert.Equal(data, node!.Data);\n        }\n\n        [Fact]\n        public void InnerTextReplacesPrevious()\n        {\n            const string bye = \"<bye\";\n            var x = Element.Create(\"span\");\n            x.InnerText = \"hello\";\n            x.InnerText = bye;\n            Assert.Equal(bye, x.InnerText);\n            VerifyInnerData(x, \"&lt;bye\");\n        }\n\n        [Fact]\n        [Obsolete(\"old methods\")]\n        public void BindInnerTextUpdates()\n        {\n            var data = new MyData();\n            var div = Element.Create(\"div\");\n            div.BindInnerText(new BindInnerTextOptions<MyData>\n            {\n                BindObject = data,\n                Property = x => x.Counter.ToString()\n            });\n            data.Counter = 5;\n            VerifyInnerData(div, \"5\");\n        }\n\n        [Fact]\n        [Obsolete(\"old methods\")]\n        public void BindGenericExecutes()\n        {\n            var data = new MyData();\n            var div = Element.Create(\"div\");\n            div.Bind(new BindHandlerOptions<MyData>\n            {\n                BindObject = data,\n                ModifiedHandler = (_, _) => div.InnerText = data.Counter.ToString()\n            });\n            data.Counter = 5;\n            VerifyInnerData(div, \"5\");\n        }\n\n        [Fact]\n        [Obsolete(\"old methods\")]\n        public void BindActionExecutes()\n        {\n            var data = new MyData();\n            var div = Element.Create(\"div\");\n            div.Bind(data, _ => div.InnerText = data.Counter.ToString());\n            data.Counter = 5;\n            VerifyInnerData(div, \"5\");\n        }\n\n        [Fact]\n        [Obsolete(\"old methods\")]\n        public void BindAttributeExecutes()\n        {\n            var data = new MyData();\n            var div = Element.Create(\"div\");\n            div.BindAttribute(new BindAttributeOptions<MyData>\n            {\n                Attribute = \"data-counter\",\n                BindObject = data,\n                Property = x => x.Counter.ToString()\n            });\n            data.Counter = 5;\n            Assert.Equal(\"5\", div.GetAttribute(\"data-counter\"));\n        }\n\n        [Fact]\n        [Obsolete(\"old methods\")]\n        public void BindChildrenUpdates()\n        {\n            var collection = new ObservableCollection<MyData>\n            {\n                new MyData()\n            };\n            var span = Element.Create(\"span\");\n            span.BindChildren(new BindChildrenOptions<MyData>(collection, MyCreateCallback));\n            collection.Add(new MyData());\n            Assert.Equal(2, span.ChildCount);\n        }\n\n        [Fact]\n        [Obsolete(\"old methods\")]\n        public void UnbindAllUnbinds()\n        {\n            var collection = new ObservableCollection<MyData>();\n            var data = new MyData();\n            var div = Element.Create(\"div\");\n            var span1 = Element.Create(\"span\");\n            var span2 = Element.Create(\"span\");\n            span2.Bind(data, _ => span2.InnerText = data.Counter.ToString());\n            div.BindAttribute(new BindAttributeOptions<MyData>\n            {\n                Attribute = \"data-counter\",\n                BindObject = data,\n                Property = x => x.Counter.ToString()\n            });\n            div.BindChildren(new BindChildrenOptions<MyData>(collection, _ => Element.Create(\"div\")));\n            span1.BindInnerText(new BindInnerTextOptions<MyData>\n            {\n                BindObject = data,\n                Property = x => x.Counter.ToString()\n            });\n            data.Counter = 5;\n            div.UnbindAll();\n            span1.UnbindAll();\n            span2.UnbindAll();\n            collection.Add(data);\n            data.Counter = 10;\n            VerifyInnerData(span1, \"5\");\n            VerifyInnerData(span2, \"5\");\n            Assert.Equal(0, div.ChildCount);\n            Assert.Equal(\"5\", div.GetAttribute(\"data-counter\"));\n        }\n\n        [Fact]\n        [Obsolete(\"old methods\")]\n        public void UnbindAtributeRuns()\n        {\n            var x = Element.Create(\"div\");\n            var data = new MyData();\n            x.BindAttribute(new BindAttributeOptions<MyData>\n            {\n                Attribute = \"data-counter\",\n                BindObject = data,\n                Property = y => y.Counter.ToString()\n            });\n            data.Counter = 5;\n            x.UnbindAll();\n            data.Counter = 10;\n            Assert.Equal(\"5\", x.GetAttribute(\"data-counter\"));\n        }\n\n        [Fact]\n        [Obsolete(\"old methods\")]\n        public void UnbindAttributeRemovesAllAttributes()\n        {\n            var x = Element.Create(\"div\");\n            var data = new MyData();\n            x.BindAttribute(new BindAttributeOptions<MyData>\n            {\n                Attribute = \"data-counter\",\n                BindObject = data,\n                Property = y => y.Counter.ToString()\n            });\n            x.BindAttribute(new BindAttributeOptions<MyData>\n            {\n                Attribute = \"data-counter2\",\n                BindObject = data,\n                Property = y => y.Counter.ToString()\n            });\n            data.Counter = 5;\n            x.UnbindAll();\n            data.Counter = 10;\n            Assert.Equal(\"5\", x.GetAttribute(\"data-counter\"));\n            Assert.Equal(\"5\", x.GetAttribute(\"data-counter2\"));\n        }\n\n        [Fact]\n        [Obsolete(\"old methods\")]\n        public void UnbindInnerTextWorks()\n        {\n            var div = Element.Create(\"div\");\n            var data = new MyData\n            {\n                Counter = 5\n            };\n            div.BindInnerText(new BindInnerTextOptions<MyData>\n            {\n                BindObject = data,\n                Property = x => x.Counter.ToString()\n            });\n            VerifyInnerData(div, \"5\");\n            div.UnbindAll();\n            data.Counter = 10;\n            VerifyInnerData(div, \"5\");\n        }\n\n        [Fact]\n        [Obsolete(\"old methods\")]\n        public void UnbindHandlerWorks()\n        {\n            var div = Element.Create(\"div\");\n            var data = new MyData();\n            div.Bind(data, _ => div.InnerText = data.Counter.ToString());\n            data.Counter = 3;\n            div.UnbindAll();\n            data.Counter = 8;\n            VerifyInnerData(div, \"3\");\n        }\n\n        [Fact]\n        [Obsolete(\"old methods\")]\n        public void UnbindChildrenWorks()\n        {\n            var collection = new ObservableCollection<MyData>();\n            var div = Element.Create(\"div\");\n            div.BindChildren(new BindChildrenOptions<MyData>(collection, _ => Element.Create(\"span\")));\n            collection.Add(new MyData());\n            div.UnbindAll();\n            collection.Clear();\n            Assert.NotEmpty(div.Children);\n        }\n\n        [Fact]\n        [Obsolete(\"old methods\")]\n        public void GenericBindingDetectsCycles()\n        {\n            var div = Element.Create(\"div\");\n            var data = new MyData();\n            div.BindAttribute(new BindAttributeOptions<MyData>\n            {\n                Attribute = \"data-counter\",\n                BindObject = data,\n                Property = x => x.Counter.ToString()\n            });\n            div.Bind(new BindHandlerOptions<MyData>\n            {\n                BindObject = data,\n                ModifiedHandler = (_, _) => data.Counter++\n            });\n            var found = false;\n            try\n            {\n                data.Counter = 3;\n            }\n            catch (InvalidOperationException)\n            {\n                found = true;\n            }\n            Assert.True(found);\n        }\n\n        [Obsolete(\"old methods\")]\n        private static Element MyCreateCallback(MyData arg)\n        {\n            var span = Element.Create(\"span\");\n            span.BindAttribute(new BindAttributeOptions<MyData>\n            {\n                BindObject = arg,\n                Attribute = \"data-counter\",\n                Property = _ => arg.Counter.ToString()\n            });\n            return span;\n        }\n\n        private class MyData : BindableBase\n        {\n            private int _counter;\n\n            public MyData()\n            {\n            }\n\n            public MyData(int counter)\n            {\n                Counter = counter;\n            }\n\n            public int Counter\n            {\n                get => _counter;\n                set => SetProperty(ref _counter, value);\n            }\n\n            public bool IsEven => (_counter % 2) == 0;\n\n            public override string ToString() => Counter.ToString();\n        }\n\n        [Fact]\n        public void BindableBaseSkipsUnncesaryEvents()\n        {\n            var raised = false;\n            var data = new MyData();\n            data.PropertyChanged += (_, _) => raised = true;\n            data.Counter = 0;\n            Assert.False(raised);\n        }\n\n        [Fact]\n        [Obsolete(\"old methods\")]\n        public void CollectionUpdaterMove()\n        {\n            var collection = new ObservableCollection<MyData>();\n            var div = Element.Create(\"div\");\n            div.BindChildren(new BindChildrenOptions<MyData>(collection, MyCreateCallback));\n            collection.Add(new MyData(10));\n            collection.Add(new MyData(20));\n            collection.Add(new MyData(30));\n            collection.Add(new MyData(40));\n            collection.Add(new MyData(50));\n            VerifyPositions(collection, div);\n            collection.Move(1, 2);\n            VerifyPositions(collection, div);\n            collection.RemoveAt(3);\n            VerifyPositions(collection, div);\n            collection[2] = new MyData(77);\n            VerifyPositions(collection, div);\n            collection.Clear();\n            VerifyPositions(collection, div);\n        }\n\n        private static void VerifyPositions(IReadOnlyList<MyData> collection, Element div)\n        {\n            Assert.Equal(collection.Count, div.ChildCount);\n            for (var index = 0; index < collection.Count; index++)\n            {\n                var data = collection[index];\n                VerifyPosition(div, index, data.Counter.ToString());\n            }\n        }\n\n        private static void VerifyPosition(Element div, int position, string value)\n        {\n            var child = (Element)div.GetChildAt(position);\n            var current = child.GetAttribute(\"data-counter\");\n            Assert.Equal(value, current);\n        }\n\n        [Fact]\n        [Obsolete(\"old method\")]\n        public void BindFlagAttributeBinds()\n        {\n            var div = Element.Create(\"div\");\n            var data = new MyData();\n            div.BindFlagAttribute(new BindFlagAttributeOptions<MyData>\n            {\n                Attribute = \"data-even\",\n                BindObject = data,\n                Property = x => x.IsEven\n            });\n            Assert.True(div.HasAttribute(\"data-even\"));\n            data.Counter++;\n            Assert.False(div.HasAttribute(\"data-even\"));\n        }\n\n        [Fact]\n        [Obsolete(\"old methods\")]\n        public void BindToggleClassBinds()\n        {\n            var div = Element.Create(\"div\");\n            var data = new MyData();\n            div.BindToggleClass(new BindToggleClassOptions<MyData>\n            {\n                ClassName = \"lala\",\n                BindObject = data,\n                Property = x => x.IsEven\n            });\n            Assert.True(div.HasClass(\"lala\"));\n            data.Counter++;\n            Assert.False(div.HasClass(\"lala\"));\n        }\n\n        [Fact]\n        [Obsolete(\"old method\")]\n        public void LaraFlagBinding()\n        {\n            var div = Element.Create(\"div\");\n            var builder = new LaraBuilder(div);\n            var data = new MyData();\n            builder.BindFlagAttribute(\"data-even1\", data, () => data.IsEven);\n            builder.BindFlagAttribute(\"data-even2\", data, x => x.IsEven);\n            Assert.True(div.HasAttribute(\"data-even1\"));\n            Assert.True(div.HasAttribute(\"data-even2\"));\n        }\n\n        [Fact]\n        public void BindableBaseHoldsEvents()\n        {\n            var counter = 0;\n            var data = new MyData();\n            data.PropertyChanged += (_, _) => counter++;\n            data.BeginUpdate();\n            data.Counter = 5;\n            Assert.Equal(0, counter);\n            data.EndUpdate();\n            Assert.Equal(1, counter);\n        }\n\n        [Fact]\n        [Obsolete(\"old methods\")]\n        public void InputBindingGetter()\n        {\n            var data = new MyInputData\n            {\n                MyValue = \"hello\"\n            };\n            var input = new HtmlInputElement();\n            input.BindInput(new BindInputOptions<MyInputData>\n            {\n                Attribute = \"value\",\n                BindObject = data,\n                Property = x => x.MyValue\n            });\n            Assert.Equal(\"hello\", input.Value);\n            data.MyValue = \"bye\";\n            Assert.Equal(\"bye\", input.Value);\n        }\n\n        [Fact]\n        [Obsolete(\"old methods\")]\n        public void InputBindingGetterLara()\n        {\n            var data = new MyInputData\n            {\n                MyValue = \"hello\"\n            };\n            var input = new HtmlInputElement();\n            var builder = new LaraBuilder(input);\n            builder.BindInput(\"value\", data, x => x.MyValue);\n            Assert.Equal(\"hello\", input.Value);\n            data.MyValue = \"bye\";\n            Assert.Equal(\"bye\", input.Value);\n        }\n\n        [Fact]\n        [Obsolete(\"old methods\")]\n        public void InputBindingGetterLaraFlag()\n        {\n            var data = new MyInputData\n            {\n                MyChecked = true\n            };\n            var input = new HtmlInputElement();\n            var builder = new LaraBuilder(input);\n            builder.BindFlagInput(\"checked\", data, x => x.MyChecked);\n            Assert.True(input.Checked);\n            data.MyChecked = false;\n            Assert.False(input.Checked);\n        }\n\n        [Fact]\n        [Obsolete(\"old methods\")]\n        public void InvalidSetterThrows()\n        {\n            var data = new MyInputData();\n            var x = new HtmlInputElement();\n            Assert.ThrowsAny<ArgumentException>(() =>\n            {\n                x.BindInput(new BindInputOptions<MyInputData>\n                {\n                    Attribute = \"value\",\n                    BindObject = data,\n                    Property = x1 => x1.MyValue + \"a\"\n                });\n            });\n        }\n\n        [Fact]\n        [Obsolete(\"old methods\")]\n        public void InputBindingCollects()\n        {\n            var input = new HtmlInputElement();\n            var data = new MyInputData();\n            input.BindInput(new BindInputOptions<MyInputData>\n            {\n                BindObject = data,\n                Attribute = \"value\",\n                Property = x => x.MyValue\n            });\n            input.Value = \"hello\";\n            Assert.Equal(\"hello\", data.MyValue);\n        }\n\n        [Fact]\n        [Obsolete(\"old methods\")]\n        public void InputBindingCollectsFlag()\n        {\n            var input = new HtmlInputElement();\n            var data = new MyInputData();\n            input.BindFlagInput(new BindFlagInputOptions<MyInputData>\n            {\n                BindObject = data,\n                Attribute = \"checked\",\n                Property = x => x.MyChecked\n            });\n            input.Checked = true;\n            Assert.True(data.MyChecked);\n        }\n\n        [Fact]\n        [Obsolete(\"old method\")]\n        public void LaraBindFlagAttribute()\n        {\n            var data = new MyInputData();\n            var div = Element.Create(\"div\");\n            var builder = new LaraBuilder(div);\n            builder.BindFlagAttribute(new BindFlagAttributeOptions<MyInputData>\n            {\n                Attribute = \"class\",\n                BindObject = data,\n                Property = x => x.MyChecked\n            });\n            data.MyChecked = true;\n            Assert.True(div.HasAttribute(\"class\"));\n        }\n    }\n}\n"
  },
  {
    "path": "src/Tests/DOM/BuilderTesting.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 6/2019\nAuthor: Pablo Carbonell\n*/\n\nusing Integrative.Lara.Tests.Middleware;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Net;\nusing System.Threading.Tasks;\nusing Xunit;\n\nnamespace Integrative.Lara.Tests.DOM\n{\n    public class BuilderTesting : DummyContextTesting\n    {\n        [Fact]\n        [Obsolete(\"Old methods\")]\n        public void PushAdds()\n        {\n            var root = Element.Create(\"div\");\n            var builder = new LaraBuilder(root);\n            builder.Push(\"button\", \"red\", \"mybutton\").Pop();\n            Assert.NotEmpty(root.Children);\n            var first = root.Children.FirstOrDefault() as HtmlButtonElement;\n            Assert.NotNull(first);\n            Assert.Equal(\"red\", first!.Class);\n            Assert.Equal(\"mybutton\", first.Id);\n        }\n\n        [Fact]\n        [Obsolete(\"Old methods\")]\n        public void TooManyPops()\n        {\n            var root = Element.Create(\"div\");\n            var builder = new LaraBuilder(root);\n            builder.Push(\"button\", \"red\").Pop();\n            DomOperationsTesting.Throws<InvalidOperationException>(() => builder.Pop());\n        }\n\n        [Fact]\n        [Obsolete(\"Old methods\")]\n        public void AddSiblings()\n        {\n            var root = Element.Create(\"div\");\n            var builder = new LaraBuilder(root);\n            builder.Push(\"button\", \"red\").Pop();\n            builder.Push(\"button\", \"red\").Pop();\n            Assert.Equal(2, root.ChildCount);\n        }\n\n        [Fact]\n        [Obsolete(\"Old methods\")]\n        public void AddTextNodeEncodes()\n        {\n            var root = Element.Create(\"div\");\n            var builder = new LaraBuilder(root);\n            builder.AppendText(\"&lt;\");\n            var node = root.Children.FirstOrDefault() as TextNode;\n            Assert.NotNull(node);\n            Assert.Equal(\"&amp;lt;\", node!.Data);\n        }\n\n        [Fact]\n        [Obsolete(\"Old methods\")]\n        public void AddElements()\n        {\n            var root = Element.Create(\"div\");\n            var builder = new LaraBuilder(root);\n            var list = new List<Element>()\n            {\n                new HtmlButtonElement(),\n                new HtmlOptionElement()\n            };\n            builder.AddNodes(list);\n            Assert.Equal(2, root.ChildCount);\n        }\n\n        [Fact]\n        [Obsolete(\"Old methods\")]\n        public void AddNodes()\n        {\n            var root = Element.Create(\"div\");\n            var builder = new LaraBuilder(root);\n            var list = new List<Node>()\n            {\n                new HtmlButtonElement(),\n                new HtmlOptionElement()\n            };\n            builder.AddNodes(list);\n            Assert.Equal(2, root.ChildCount);\n        }\n\n        [Fact]\n        [Obsolete(\"Old methods\")]\n        public void AddAction()\n        {\n            var root = Element.Create(\"div\");\n            var builder = new LaraBuilder(root);\n            builder.Add(MyAddAction);\n            Assert.Equal(1, root.ChildCount);\n        }\n\n        [Obsolete(\"Old methods\")]\n        private static void MyAddAction(LaraBuilder builder)\n        {\n            builder.AddNode(new HtmlButtonElement());\n        }\n\n        [Fact]\n        [Obsolete(\"Old methods\")]\n        public void SetAttribute()\n        {\n            var root = Element.Create(\"div\");\n            var builder = new LaraBuilder(root);\n            builder.Attribute(\"class\", \"red\");\n            Assert.Equal(\"red\", root.Class);\n        }\n\n        [Fact]\n        [Obsolete(\"Old methods\")]\n        public void SetFlag()\n        {\n            var root = Element.Create(\"div\");\n            var builder = new LaraBuilder(root);\n            builder.FlagAttribute(\"hidden\", true);\n            Assert.True(root.Hidden);\n        }\n\n        [Fact]\n        [Obsolete(\"Old methods\")]\n        public async void OnEvent()\n        {\n            var executed = false;\n            var root = Element.Create(\"div\");\n            var builder = new LaraBuilder(root);\n            builder.On(new EventSettings\n            {\n                EventName = \"click\",\n                Handler = () =>\n                {\n                    executed = true;\n                    return Task.CompletedTask;\n                }\n            });\n            await root.NotifyEvent(\"click\");\n            Assert.True(executed);\n        }\n\n        [Fact]\n        [Obsolete(\"Old methods\")]\n        public async void OnEventSimple()\n        {\n            var executed = false;\n            var root = Element.Create(\"div\");\n            var builder = new LaraBuilder(root);\n            builder.On(\"click\", () =>\n            {\n                executed = true;\n                return Task.CompletedTask;\n            });\n            await root.NotifyEvent(\"click\");\n            Assert.True(executed);\n        }\n\n        [Fact]\n        [Obsolete(\"Old methods\")]\n        public void PushClassName()\n        {\n            var root = Element.Create(\"div\");\n            var builder = new LaraBuilder(root);\n            builder.Push(\"div\", \"red\").Pop();\n            Assert.NotEmpty(root.Children);\n            var child = root.Children.FirstOrDefault() as Element;\n            Assert.NotNull(child);\n            Assert.Equal(\"red\", child!.Class);\n        }\n\n        [Fact]\n        [Obsolete(\"Old methods\")]\n        // ReSharper disable once InconsistentNaming\n        public void PushNS()\n        {\n            var root = Element.Create(\"div\");\n            var builder = new LaraBuilder(root);\n            builder.PushNS(\"abc\", \"svg\").Pop();\n            Assert.NotEmpty(root.Children);\n            var child = root.Children.FirstOrDefault() as Element;\n            Assert.NotNull(child);\n            Assert.Equal(\"abc\", child!.GetAttribute(\"xlmns\"));\n        }\n\n        [Fact]\n        [Obsolete(\"Old methods\")]\n        public void PushElementClass()\n        {\n            var root = Element.Create(\"root\");\n            var div = Element.Create(\"div\");\n            var builder = new LaraBuilder(root);\n            builder.Push(div, \"red\");\n            Assert.Equal(\"red\", div.Class);\n        }\n\n        [Fact]\n        public void SessionIdAvailable()\n        {\n            var guid = Guid.Parse(\"{0F9EE9CD-F9A0-40E6-A91B-FE4E3E2282F0}\");\n            var cnx = new Connection(guid, IPAddress.Loopback);\n            var x = new Session(cnx);\n            Assert.Equal(guid, x.SessionId);\n        }\n    }\n}\n"
  },
  {
    "path": "src/Tests/DOM/ClassEditorTesting.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 5/2019\nAuthor: Pablo Carbonell\n*/\n\nusing Xunit;\n\nnamespace Integrative.Lara.Tests.DOM\n{\n    public class ClassEditorTesting\n    {\n        [Fact]\n        public void HasEmptyClassTrue()\n        {\n            Assert.True(ClassEditor.HasClass(\"\", \"\"));\n        }\n\n        [Fact]\n        public void EmptyClassFalse()\n        {\n            Assert.False(ClassEditor.HasClass(\"\", \"lele\"));\n        }\n\n        [Fact]\n        public void HasClassTrue()\n        {\n            Assert.True(ClassEditor.HasClass(\"aaa\", \"aaa\"));\n            Assert.True(ClassEditor.HasClass(\"aaa b\", \"aaa\"));\n            Assert.True(ClassEditor.HasClass(\"b aaa\", \"aaa\"));\n            Assert.True(ClassEditor.HasClass(\" aaa\", \"aaa\"));\n            Assert.True(ClassEditor.HasClass(\"cc aaa ddd\", \"aaa\"));\n        }\n\n        [Fact]\n        public void RemoveClass()\n        {\n            Assert.Equal(\"lala\", ClassEditor.RemoveClass(\"lala\", \"\"));\n            Assert.Equal(\"\", ClassEditor.RemoveClass(\"lala\", \"lala\"));\n            Assert.Equal(\"\", ClassEditor.RemoveClass(\" lala \", \"lala\"));\n            Assert.Equal(\"\", ClassEditor.RemoveClass(\"lala\", \" lala \"));\n            Assert.Equal(\"blue\", ClassEditor.RemoveClass(\"lala blue\", \"lala\"));\n            Assert.Equal(\"blue\", ClassEditor.RemoveClass(\"blue lala\", \"lala\"));\n            Assert.Equal(\"red blue\", ClassEditor.RemoveClass(\"red lala blue\", \"lala\"));\n            Assert.Equal(\"orange\", ClassEditor.RemoveClass(\"orange\", \"lala\"));\n        }\n\n        [Fact]\n        public void AddClass()\n        {\n            Assert.Equal(\"lala\", ClassEditor.AddClass(\"lala\", \"lala\"));\n            Assert.Equal(\"lala\", ClassEditor.AddClass(\"  \", \"lala\"));\n            Assert.Equal(\" red lala\", ClassEditor.AddClass(\" red\", \"lala\"));\n        }\n    }\n}\n"
  },
  {
    "path": "src/Tests/DOM/DomOperationsTesting.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 5/2019\nAuthor: Pablo Carbonell\n*/\n\nusing Integrative.Lara.Tests.Main;\nusing Integrative.Lara.Tests.Middleware;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Threading.Tasks;\nusing Xunit;\n\nnamespace Integrative.Lara.Tests.DOM\n{\n    public class DomOperationsTesting : DummyContextTesting\n    {\n        private readonly Func<Task> _emptyHandler;\n\n        public DomOperationsTesting()\n        {\n            _emptyHandler = (() => Task.CompletedTask);\n        }\n        \n        [Fact]\n        public void AddElementWithId()\n        {\n            var button = Element.Create(\"button\", \"mybutton\");\n            var document = CreateDocument();\n            document.Body.AppendChild(button);\n            Assert.True(document.TryGetElementById(\"mybutton\", out var found));\n            Assert.Same(button, found);\n        }\n\n        internal static Document CreateDocument()\n        {\n            var guid = Connections.CreateCryptographicallySecureGuid();\n            var page = new MyPage();\n            return new Document(page, guid, BaseModeController.DefaultKeepAliveInterval);\n        }\n\n        [Fact]\n        public void AddBranchWithId()\n        {\n            var button = Element.Create(\"button\", \"mybutton\");\n            var span = Element.Create(\"span\", \"myspan\");\n            button.AppendChild(span);\n            var document = CreateDocument();\n            document.Body.AppendChild(button);\n            Assert.True(document.TryGetElementById(\"myspan\", out var found));\n            Assert.Same(span, found);\n        }\n\n        [Fact]\n        public void RemoveElementWithId()\n        {\n            var button = Element.Create(\"button\", \"mybutton\");\n            var document = CreateDocument();\n            document.Body.AppendChild(button);\n            button.Remove();\n            Assert.False(document.TryGetElementById(\"mybutton\", out _));\n        }\n\n        [Fact]\n        public void RemoveBranchWithIdInside()\n        {\n            var button = Element.Create(\"button\");\n            var span = Element.Create(\"span\", \"myspan\");\n            button.AppendChild(span);\n            var doc = CreateDocument();\n            doc.Body.AppendChild(button);\n            button.Remove();\n            Assert.False(doc.TryGetElementById(\"myspan\", out _));\n        }\n\n        [Fact]\n        public void CannotRemoveDocumentHead()\n        {\n            var doc = CreateDocument();\n            Assert.Throws<InvalidOperationException>(() => doc.Head.Remove());\n        }\n\n        [Fact]\n        public void CannotRemoveDocumentBody()\n        {\n            var doc = CreateDocument();\n            Assert.Throws<InvalidOperationException>(() => doc.Body.Remove());\n        }\n\n        [Fact]\n        public void CannotAddDuplicateId()\n        {\n            var button1 = Element.Create(\"button\", \"mybutton\");\n            var button2 = Element.Create(\"button\", \"mybutton\");\n            var div = Element.Create(\"div\");\n            div.AppendChild(button1);\n            div.AppendChild(button2);\n            var doc = CreateDocument();\n            Throws<InvalidOperationException>(() => doc.Body.AppendChild(div));\n        }\n\n        [Fact]\n        public void CannotInsertDuplicateId()\n        {\n            var button1 = Element.Create(\"button\", \"mybutton\");\n            var button2 = Element.Create(\"button\", \"mybutton\");\n            var div = Element.Create(\"div\");\n            div.AppendChild(button1);\n            div.AppendChild(button2);\n            var doc = CreateDocument();\n            var pane = Element.Create(\"div\");\n            doc.Body.AppendChild(pane);\n            Throws<InvalidOperationException>(() => doc.Body.InsertChildAfter(pane, div));\n        }\n\n        [Fact]\n        public void CannotAddNodeInsideItself()\n        {\n            var e1 = Element.Create(\"span\");\n            var e2 = Element.Create(\"span\");\n            e1.AppendChild(e2);\n            Throws<InvalidOperationException>(() => e2.AppendChild(e1));\n        }\n\n        internal static void Throws<T>(Action action) where T : Exception\n        {\n            var error = false;\n            try\n            {\n                action();\n            }\n            catch (T)\n            {\n                error = true;\n            }\n            Assert.True(error);\n        }\n\n        internal static async Task ThrowsAsync<T>(Func<Task> action) where T : Exception\n        {\n            var error = false;\n            try\n            {\n                await action();\n            }\n            catch (T)\n            {\n                error = true;\n            }\n            Assert.True(error);\n        }\n\n        [Fact]\n        public void TextNodeContent()\n        {\n            var node = new TextNode(\"hello\");\n            var x = node.GetContentNode();\n            Assert.Equal(NodeType.Text, node.NodeType);\n            Assert.Equal(ContentNodeType.Text, x.Type);\n            Assert.True(x is ContentTextNode);\n            Assert.Equal(\"hello\", ((ContentTextNode)x).Data);\n        }\n\n        [Fact]\n        public void InsertBeforeInserts()\n        {\n            var div = Element.Create(\"div\");\n            var span1 = Element.Create(\"span\");\n            var span2 = Element.Create(\"span\");\n            div.AppendChild(span2);\n            div.InsertChildBefore(span2, span1);\n            var list = new List<Node>(div.Children);\n            Assert.NotEmpty(list);\n            Assert.Equal(2, list.Count);\n            Assert.Equal(2, div.ChildCount);\n            Assert.Same(span1, list[0]);\n            Assert.Same(span2, list[1]);\n        }\n\n        [Fact]\n        public void GenerateIdsForEvents()\n        {\n            var span = Element.Create(\"span\");\n            span.On(\"click\", _emptyHandler);\n            var div = Element.Create(\"div\");\n            div.On(\"click\", _emptyHandler);\n            div.AppendChild(span);\n            var doc = CreateDocument();\n            doc.Body.AppendChild(div);\n            Assert.False(string.IsNullOrEmpty(span.Id));\n            Assert.False(string.IsNullOrEmpty(div.Id));\n        }\n\n\n        [Fact]\n        public void GenerateIdsForEventsInsert()\n        {\n            var span = Element.Create(\"span\");\n            span.On(\"click\", _emptyHandler);\n            var div = Element.Create(\"div\");\n            div.On(\"click\", _emptyHandler);\n            div.AppendChild(span);\n            var dummy = Element.Create(\"button\");\n            var doc = CreateDocument();\n            doc.Body.AppendChild(dummy);\n            doc.Body.InsertChildBefore(dummy, div);\n            Assert.False(string.IsNullOrEmpty(span.Id));\n            Assert.False(string.IsNullOrEmpty(div.Id));\n        }\n\n        [Fact]\n        public void TransferElementBetweenDocuments()\n        {\n            var button = Element.Create(\"button\", \"mybutton\");\n            var doc1 = CreateDocument();\n            var doc2 = CreateDocument();\n            doc1.Body.AppendChild(button);\n            doc2.Body.AppendChild(button);\n            Assert.False(doc1.TryGetElementById(\"mybutton\", out _));\n            Assert.True(doc2.TryGetElementById(\"mybutton\", out var found));\n            Assert.Same(button, found);\n        }\n\n        [Fact]\n        public void RemoveTextNode()\n        {\n            var div = Element.Create(\"div\", \"mydiv\");\n            var doc = CreateDocument();\n            doc.Body.AppendChild(new TextNode(\"hi\"));\n            doc.Body.AppendChild(div);\n            doc.Body.AppendChild(new TextNode(\"bye\"));\n            doc.OpenEventQueue();\n            doc.Body.RemoveAt(2);\n            var queue = doc.GetQueue();\n            Assert.NotEmpty(queue);\n            var step = queue.Peek() as NodeRemovedDelta;\n            Assert.NotNull(step);\n            Assert.Equal(doc.Body.Id, step!.ParentId);\n            Assert.Equal(2, step.ChildIndex);\n        }\n\n        [Fact]\n        public void RemoveElement()\n        {\n            var div = Element.Create(\"div\", \"mydiv\");\n            var doc = CreateDocument();\n            doc.Body.AppendChild(new TextNode(\"hi\"));\n            doc.Body.AppendChild(div);\n            doc.Body.AppendChild(new TextNode(\"bye\"));\n            doc.OpenEventQueue();\n            div.Remove();\n            var queue = doc.GetQueue();\n            Assert.NotEmpty(queue);\n            var step = queue.Peek() as RemoveElementDelta;\n            Assert.NotNull(step);\n            Assert.Equal(div.Id, step?.ElementId);\n        }\n\n        [Fact]\n        public void NodeAdded()\n        {\n            var div = Element.Create(\"div\", \"mydiv\");\n            var doc = CreateDocument();\n            doc.OpenEventQueue();\n            doc.Body.AppendChild(div);\n            var queue = doc.GetQueue();\n            Assert.NotEmpty(queue);\n            var step = queue.Peek() as NodeAddedDelta;\n            Assert.NotNull(step);\n            Assert.Equal(doc.Body.Id, step!.ParentId);\n            var content = step.Node as ContentElementNode;\n            Assert.NotNull(content);\n            Assert.Equal(\"div\", content!.TagName);\n            Assert.NotNull(content.Attributes?.FirstOrDefault());\n            var att = content.Attributes![0];\n            Assert.Equal(\"id\", att.Attribute);\n            Assert.Equal(\"mydiv\", att.Value);\n        }\n\n        [Fact]\n        public void NodeInsertedDelta()\n        {\n            var div = Element.Create(\"div\", \"mydiv\");\n            var doc = CreateDocument();\n            var text = new TextNode(\"lala\");\n            doc.Body.AppendChild(text);\n            doc.OpenEventQueue();\n            doc.Body.InsertChildAfter(text, div);\n            var queue = doc.GetQueue();\n            Assert.NotEmpty(queue);\n            var step = queue.Peek() as NodeInsertedDelta;\n            Assert.NotNull(step);\n            Assert.Equal(doc.Body.Id, step!.ParentElementId);\n            Assert.Equal(1, step.Index);\n            var content = step.Node as ContentElementNode;\n            Assert.NotNull(content);\n            Assert.Equal(\"div\", content!.TagName);\n            Assert.NotNull(content.Attributes?.FirstOrDefault());\n            var att = content.Attributes![0];\n            Assert.Equal(\"id\", att.Attribute);\n            Assert.Equal(\"mydiv\", att.Value);\n        }\n\n        [Fact]\n        public void FocusFailsOnGet()\n        {\n            var div = Element.Create(\"div\");\n            Throws<InvalidOperationException>(() => div.Focus());\n        }\n\n        [Fact]\n        public void RemoveOrphanThrows()\n        {\n            var div = Element.Create(\"div\");\n            Throws<InvalidOperationException>(() => div.Remove());\n        }\n\n        [Fact]\n        public void InsertBeforeUnknownThrows()\n        {\n            var div1 = Element.Create(\"div\");\n            var div2 = Element.Create(\"div\");\n            var div3 = Element.Create(\"div\");\n            Throws<InvalidOperationException>(() => div1.InsertChildBefore(div2, div3));\n        }\n\n        [Fact]\n        public void RemoveUnknownChildThrows()\n        {\n            var a = Element.Create(\"span\");\n            var b = Element.Create(\"span\");\n            Throws<InvalidOperationException>(() => a.RemoveChild(b));\n        }\n\n        [Fact]\n        public void ClearChildrenRemovesThem()\n        {\n            var div = Element.Create(\"div\");\n            var span1 = Element.Create(\"span\");\n            var span2 = Element.Create(\"span\");\n            div.AppendChild(span1);\n            div.AppendChild(span2);\n            div.ClearChildren();\n            Assert.Empty(div.Children);\n            Assert.Equal(0, div.ChildCount);\n        }\n\n        [Fact]\n        public void InsertAtSucceeds()\n        {\n            var x = Element.Create(\"div\");\n            var a = Element.Create(\"div\");\n            var b = Element.Create(\"div\");\n            var c = Element.Create(\"div\");\n            x.AppendChild(a);\n            x.AppendChild(c);\n            x.InsertChildAt(1, b);\n            Assert.Equal(3, x.ChildCount);\n            Assert.Same(b, x.GetChildAt(1));\n        }\n\n        [Fact]\n        public void RemoveAtSucceeds()\n        {\n            var x = Element.Create(\"div\");\n            var a = Element.Create(\"div\");\n            var b = Element.Create(\"div\");\n            x.AppendChild(a);\n            x.AppendChild(b);\n            x.RemoveAt(1);\n            Assert.Equal(1, x.ChildCount);\n            Assert.Same(a, x.GetChildAt(0));\n        }\n\n        [Fact]\n        public void MissingEventNameThrows()\n        {\n            var settings = new EventSettings();\n            var found = false;\n            try\n            {\n                settings.Verify();\n            }\n            catch (ArgumentException)\n            {\n                found = true;\n            }\n            Assert.True(found);\n        }\n\n        [Fact]\n        public void DocumentGetElementById()\n        {\n            var document = new Document(new MyPage(), BaseModeController.DefaultKeepAliveInterval);\n            var div = Element.Create(\"div\");\n            div.Id = \"lala\";\n            document.Body.AppendChild(div);\n            var found = document.GetElementById(\"lala\");\n            Assert.Same(div, found);\n        }\n\n        [Fact]\n        public async void DocumentOnUnloadExecutes()\n        {\n            var counter = 0;\n            var document = new Document(new MyPage(), BaseModeController.DefaultKeepAliveInterval);\n            document.OnUnload += (_, _) => counter++;\n            await document.NotifyUnload();\n            Assert.Equal(1, counter);\n        }\n\n        [Fact]\n        public void SwapChildrenSwaps()\n        {\n            var document = new Document(new MyPage(), BaseModeController.DefaultKeepAliveInterval);\n            var div = Element.Create(\"div\");\n            var n1 = new TextNode(\"n1\");\n            var n2 = new TextNode(\"n2\");\n            var n3 = new TextNode(\"n3\");\n            div.AppendChild(n1);\n            div.AppendChild(n2);\n            div.AppendChild(n3);\n            document.Body.AppendChild(div);\n            document.OpenEventQueue();\n            div.SwapChildren(1, 1);\n            div.SwapChildren(0, 2);\n            Assert.Equal(3, div.ChildCount);\n            Assert.Same(n3, div.GetChildAt(0));\n            Assert.Same(n1, div.GetChildAt(2));\n            var queue = document.GetQueue();\n            Assert.NotEmpty(queue);\n            var first = queue.Peek() as SwapChildrenDelta;\n            Assert.NotNull(first);\n            Assert.Equal(div.Id, first!.ParentId);\n            Assert.NotNull(div.Id);\n            Assert.Equal(0, first.Index1);\n            Assert.Equal(2, first.Index2);\n        }\n\n        [Fact]\n        public void InputNotifyValueUpdates()\n        {\n            var input = new HtmlInputElement();\n            input.NotifyValue(new ElementEventValue\n            {\n                Checked = true,\n                ElementId = input.Id,\n                Value = \"a\"\n            });\n            Assert.True(input.Checked);\n            Assert.Equal(\"a\", input.Value);\n        }\n\n        [Fact]\n        // ReSharper disable once InconsistentNaming\n        public void ElementGetChildPosition2nd()\n        {\n            var div = Element.Create(\"div\");\n            var x1 = Element.Create(\"div\");\n            var x2 = Element.Create(\"div\");\n            div.AppendChild(x1);\n            div.AppendChild(x2);\n            var index = div.GetChildElementPosition(x2);\n            Assert.Equal(1, index);\n        }\n\n        [Fact]\n        public void RemoveEventRemovesIt()\n        {\n            var div = Element.Create(\"div\");\n            div.On(\"click\", () => Task.CompletedTask);\n            div.On(\"click\", null);\n            Assert.Empty(div.Events);\n        }\n\n        [Fact]\n        public void ElementAppendDataWorks()\n        {\n            var div = Element.Create(\"div\");\n            div.AppendData(\"@@\");\n            Assert.Equal(1, div.ChildCount);\n            var child = div.GetChildAt(0) as TextNode;\n            Assert.NotNull(child);\n            Assert.Equal(\"@@\", child!.Data);\n        }\n\n        private class DummyAdoptable : WebComponent\n        {\n            public int AdoptedCount { get; private set; }\n\n            public DummyAdoptable() : base(\"x-adoptable\")\n            {\n            }\n\n            protected override void OnAdopted()\n            {\n                AdoptedCount++;\n            }\n        }\n\n        [Fact]\n        public void NotifyAdoptedPassedToChildren()\n        {\n            Context.Application.PublishComponent(new WebComponentOptions\n            {\n                ComponentTagName = \"x-adoptable\",\n                ComponentType = typeof(DummyAdoptable)\n            });\n            var div = Element.Create(\"div\");\n            var x = new DummyAdoptable();\n            div.AppendChild(x);\n            var doc1 = CreateDocument();\n            doc1.Body.AppendChild(div);\n            var doc2 = CreateDocument();\n            doc2.Body.AppendChild(div);\n            Assert.Equal(1, x.AdoptedCount);\n            Context.Application.UnPublishWebComponent(\"x-adoptable\");\n        }\n\n        [Fact]\n        public void SetInnerDataSetsData()\n        {\n            var div = Element.Create(\"div\");\n            div.SetInnerData(\"@@\");\n            Assert.Equal(1, div.ChildCount);\n            var child = div.GetChildAt(0) as TextNode;\n            Assert.NotNull(child);\n            Assert.Equal(\"@@\", child!.Data);\n        }\n\n        [Fact]\n        public void SetInnerTextReplacesText()\n        {\n            var div = Element.Create(\"div\");\n            div.InnerText = \"bb\";\n            div.InnerText = \"a<a\";\n            Assert.Equal(1, div.ChildCount);\n            var child = div.GetChildAt(0) as TextNode;\n            Assert.NotNull(child);\n            Assert.Equal(\"a&lt;a\", child!.Data);\n        }\n\n        [Fact]\n        public void SetInnerDataReplacesData()\n        {\n            var div = Element.Create(\"div\");\n            div.SetInnerData(\"a\");\n            div.SetInnerData(\"@@\");\n            Assert.Equal(1, div.ChildCount);\n            var child = div.GetChildAt(0) as TextNode;\n            Assert.NotNull(child);\n            Assert.Equal(\"@@\", child!.Data);\n        }\n\n        [Fact]\n        public void RemoveEventYieldsDelta()\n        {\n            var doc = CreateDocument();\n            var div = Element.Create(\"div\");\n            var counter = 0;\n            Task Handler()\n            {\n                counter++;\n                return Task.CompletedTask;\n            }\n            div.On(\"click\", Handler);\n            div.NotifyEvent(\"click\");\n            doc.Body.AppendChild(div);\n            doc.FlushQueue();\n            div.On(\"click\", null);\n            div.NotifyEvent(\"click\");\n            Assert.Equal(1, counter);\n            var queue = doc.GetQueue();\n            Assert.Single(queue);\n            var first = queue.Peek() as UnsubscribeDelta;\n            Assert.NotNull(first);\n            Assert.Equal(div.Id, first!.ElementId);\n            Assert.Equal(\"click\", first.EventName);\n        }\n\n        [Fact]\n        public void ElementGetHtml()\n        {\n            var div = Element.Create(\"div\");\n            var html = div.GetHtml().Replace('\\r', ' ').Replace('\\n', ' ').Trim();\n            Assert.Equal($\"<div id=\\\"{div.Id}\\\"></div>\", html);\n        }\n\n        [Fact]\n        public void FocusEnqueues()\n        {\n            var doc = CreateDocument();\n            var div = Element.Create(\"div\");\n            doc.Body.AppendChild(div);\n            div.Focus();\n            var q = doc.GetQueue();\n            Assert.Single(q);\n            var first = q.Peek() as FocusDelta;\n            Assert.NotNull(first);\n            Assert.Equal(div.Id, first!.ElementId);\n        }\n\n        [Fact]\n        public void ButtonNotifyValue()\n        {\n            var x = new HtmlButtonElement();\n            x.NotifyValue(new ElementEventValue\n            {\n                ElementId = x.Id,\n                Value = \"test\"\n            });\n            Assert.Equal(\"test\", x.GetAttribute(\"value\"));\n        }\n\n        [Fact]\n        public void TextNodeAppendData()\n        {\n            var x = new TextNode();\n            x.AppendData(\"a<a\");\n            Assert.Equal(\"a<a\", x.Data);\n        }\n\n        [Fact]\n        public void TextNodeAppendText()\n        {\n            var x = new TextNode();\n            x.AppendText(\"a<a\");\n            Assert.Equal(\"a&lt;a\", x.Data);\n        }\n    }\n}\n"
  },
  {
    "path": "src/Tests/DOM/ElementAttributes.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 5/2019\nAuthor: Pablo Carbonell\n*/\n\nusing Integrative.Lara.Tests.Middleware;\nusing Moq;\nusing System;\nusing System.Collections.Generic;\nusing System.Diagnostics.CodeAnalysis;\nusing System.Linq;\nusing System.Reflection;\nusing System.Threading.Tasks;\nusing Xunit;\n\nnamespace Integrative.Lara.Tests.DOM\n{\n    public class ElementAttributes : DummyContextTesting\n    {\n        private int _counter;\n\n        [Fact]\n        public void ElementProperties()\n        {\n            TestElement<HtmlAnchorElement>(\"a\");\n            TestElement<HtmlButtonElement>(\"button\");\n            TestElement<HtmlColGroupElement>(\"colgroup\");\n            TestElement<HtmlImageElement>(\"img\");\n            TestElement<HtmlInputElement>(\"input\");\n            TestElement<HtmlLabelElement>(\"label\");\n            TestElement<HtmlLinkElement>(\"link\");\n            TestElement<HtmlLiElement>(\"li\");\n            TestElement<HtmlMetaElement>(\"meta\");\n            TestElement<HtmlMeterElement>(\"meter\");\n            TestElement<HtmlOptionElement>(\"option\");\n            TestElement<HtmlOptionGroupElement>(\"optgroup\");\n            TestElement<HtmlOlElement>(\"ol\");\n            TestElement<HtmlScriptElement>(\"script\");\n            TestElement<HtmlSelectElement>(\"select\");\n            TestElement<HtmlTableElement>(\"table\");\n            TestElement<HtmlTableCellElement>(\"td\");\n            TestElement<HtmlTableHeaderElement>(\"th\");\n            TestElement<HtmlTextAreaElement>(\"textarea\");\n            Assert.NotNull(Document.CreateElement(\"tbody\") as HtmlTableSectionElement);\n            Assert.NotNull(Document.CreateElement(\"tfoot\") as HtmlTableSectionElement);\n            Assert.NotNull(Document.CreateElement(\"thead\") as HtmlTableSectionElement);\n        }\n\n        [Fact]\n        public void ElementNeedsTag()\n        {\n            DomOperationsTesting.Throws<ArgumentException>(() => Element.Create(\"\"));\n        }\n        \n        private void TestElement<T>(string tagName) where T : Element\n        {\n            var instance = Activator.CreateInstance(typeof(T)) as Element;\n            Assert.NotNull(instance);\n            Assert.Equal(tagName, instance!.TagName);\n            TestProperties(instance);\n        }\n\n        private void TestProperties(Element instance)\n        {\n            var type = instance.GetType();\n            foreach (var property in type.GetProperties())\n            {\n                if (property.SetMethod != null)\n                {\n                    TestProperty(instance, property);\n                }\n            }\n        }\n\n        private void TestProperty(Element instance, PropertyInfo property)\n        {\n            var type = property.PropertyType;\n            if (!GetTestValue(type, out var value))\n            {\n                return;\n            }\n            property.SetValue(instance, value);\n            var result = property.GetValue(instance);\n            Assert.Equal(value, result);\n        }\n\n        private bool GetTestValue(Type type, [NotNullWhen(true)] out object? value)\n        {\n            _counter++;\n            if (type == typeof(bool))\n            {\n                value = true;\n            }\n            else if (type == typeof(int))\n            {\n                value = _counter;\n            }\n            else if (type == typeof(string))\n            {\n                value = _counter.ToString();\n            }\n            else\n            {\n                value = null;\n                return false;\n            }\n            return true;\n        }\n\n        [Fact]\n        public void GetChildPositionNotFound()\n        {\n            var a = Element.Create(\"div\");\n            var b = Element.Create(\"div\");\n            var index = b.GetChildElementPosition(a);\n            Assert.Equal(-1, index);\n        }\n\n        [Fact]\n        public void ElementDescendsFromItself()\n        {\n            var x = Element.Create(\"div\");\n            Assert.True(x.DescendsFrom(x));\n        }\n\n        [Fact]\n        public void SetIntAttribute()\n        {\n            var input = new HtmlInputElement\n            {\n                Height = 10\n            };\n            Assert.Equal(10, input.Height);\n            input.Height = null;\n            Assert.Null(input.Height);\n        }\n\n        [Fact]\n        public async void ElementOnOptions()\n        {\n            var executed = false;\n            var div = Element.Create(\"div\");\n            div.On(new EventSettings\n            {\n                EventName = \"click\",\n                Handler = () =>\n                {\n                    executed = true;\n                    return Task.CompletedTask;\n                }\n            });\n            var context = new Mock<IPageContext>();\n            LaraUI.InternalContext.Value = context.Object;\n            await div.NotifyEvent(\"click\");\n            Assert.True(executed);\n        }\n\n        [Fact]\n        public void RemoveClassRemovesClass()\n        {\n            var button = new HtmlButtonElement\n            {\n                Class = \"red blue green\"\n            };\n            button.RemoveClass(\"blue\");\n            Assert.Equal(\"red green\", button.Class);\n        }\n\n        [Fact]\n        public void AddClassAddsClass()\n        {\n            var button = new HtmlButtonElement();\n            button.AddClass(\"red\");\n            Assert.Equal(\"red\", button.Class);\n        }\n\n        [Fact]\n        public void SetFlagAttributes()\n        {\n            var button = new HtmlButtonElement();\n            button.SetFlagAttribute(\"hidden\", true);\n            Assert.True(button.Hidden);\n        }\n\n        [Fact]\n        public void InputAttributes()\n        {\n            var input = new HtmlInputElement\n            {\n                MaxLength = 5,\n                Size = 3,\n                Width = 11\n            };\n            Assert.Equal(5, input.MaxLength);\n            Assert.Equal(3, input.Size);\n            Assert.Equal(11, input.Width);\n        }\n\n        [Fact]\n        public void EncodeTextNode()\n        {\n            var n1 = new TextNode(\"&lt;\");\n            var n2 = new TextNode(\"&lt;\", false);\n            Assert.Equal(\"&amp;lt;\", n1.Data);\n            Assert.Equal(\"&lt;\", n2.Data);\n        }\n\n        [Fact]\n        public void ImageProperties()\n        {\n            var image = new HtmlImageElement\n            {\n                Height = \"1\",\n                Width = \"2\"\n            };\n            Assert.Equal(\"1\", image.Height);\n            Assert.Equal(\"2\", image.Width);\n        }\n\n        [Fact]\n        public void OrderedListAttributes()\n        {\n            var ol = new HtmlOlElement\n            {\n                Start = 1\n            };\n            Assert.Equal(1, ol.Start);\n        }\n\n        [Fact]\n        public void TextAreaProperties()\n        {\n            var x = new HtmlTextAreaElement\n            {\n                Cols = 1,\n                MaxLength = 2,\n                Rows = 3\n            };\n            Assert.Equal(1, x.Cols);\n            Assert.Equal(2, x.MaxLength);\n            Assert.Equal(3, x.Rows);\n        }\n\n        [Fact]\n        public void NotifyValueTextArea()\n        {\n            var x = new HtmlTextAreaElement();\n            x.NotifyValue(new ElementEventValue\n            {\n                Value = \"lala\"\n            });\n            Assert.Equal(\"lala\", x.Value);\n        }\n\n        [Fact]\n        public void TableHeaderProperties()\n        {\n            var x = new HtmlTableHeaderElement\n            {\n                ColSpan = 1,\n                RowSpan = 2\n            };\n            Assert.Equal(1, x.ColSpan);\n            Assert.Equal(2, x.RowSpan);\n        }\n\n        [Fact]\n        public void EventSettingsAttributes()\n        {\n            var x = new EventSettings\n            {\n                LongRunning = true,\n                BlockOptions = new BlockOptions\n                {\n                    BlockedElementId = \"aaa\",\n                    ShowHtmlMessage = \"baa\",\n                    ShowElementId = \"xxx\"\n                }\n            };\n            Assert.True(x.Block);\n            Assert.Equal(\"aaa\", x.BlockOptions.BlockedElementId);\n            Assert.Equal(\"baa\", x.BlockOptions.ShowHtmlMessage);\n            Assert.Equal(\"xxx\", x.BlockOptions.ShowElementId);\n            Assert.True(x.LongRunning);\n        }\n\n        [Fact]\n        public void LaraOptionsProperties()\n        {\n            var x = new LaraOptions\n            {\n                AllowLocalhostOnly = true,\n                ShowNotFoundPage = false,\n                AddWebSocketsMiddleware = false,\n                Mode = ApplicationMode.BrowserApp,\n                PublishAssembliesOnStart = true\n            };\n            Assert.True(x.AllowLocalhostOnly);\n            Assert.False(x.ShowNotFoundPage);\n            Assert.False(x.AddWebSocketsMiddleware);\n            Assert.Equal(ApplicationMode.BrowserApp, x.Mode);\n            Assert.True(x.PublishAssembliesOnStart);\n        }\n\n        [Fact]\n        public void DuplicateElementEmptyConstructor()\n        {\n            var instance = Activator.CreateInstance<DuplicateElementIdException>();\n            Assert.NotNull(instance);\n        }\n\n        [Fact]\n        public void DuplicateElementInner()\n        {\n            var inner = new InvalidOperationException(\"lala\");\n            var instance = new DuplicateElementIdException(\"lele\", inner);\n            Assert.Same(inner, instance.InnerException);\n            Assert.Equal(\"lele\", instance.Message);\n        }\n\n        [Fact]\n        public void TableCellProperties()\n        {\n            var x = new HtmlTableCellElement\n            {\n                ColSpan = 1,\n                RowSpan = 2\n            };\n            Assert.Equal(1, x.ColSpan);\n            Assert.Equal(2, x.RowSpan);\n        }\n\n        [Fact]\n        public void ColGroupProperties()\n        {\n            var x = new HtmlColGroupElement\n            {\n                Span = 1\n            };\n            Assert.Equal(1, x.Span);\n        }\n\n        [Fact]\n        public void LoopSelectOptions()\n        {\n            var select = new HtmlSelectElement();\n            var option1 = new HtmlOptionElement();\n            var group = Element.Create(\"optgroup\");\n            var option2 = new HtmlOptionElement();\n            group.AppendChild(option2);\n            select.AppendChild(option1);\n            select.AppendChild(group);\n            Assert.Equal( new List<HtmlOptionElement>{ option1, option2 }, select.Options);\n        }\n\n        [Fact]\n        public void SelectProperties()\n        {\n            var select = new HtmlSelectElement\n            {\n                Size = 3\n            };\n            Assert.Equal(3, select.Size);\n        }\n\n        [Fact]\n        public void SelectNotifyValue()\n        {\n            var select = new HtmlSelectElement();\n            select.NotifyValue(new ElementEventValue\n            {\n                Value = \"lala\"\n            });\n            Assert.Equal(\"lala\", select.Value);\n        }\n\n        [Fact]\n        public void SelectAddOption()\n        {\n            var x = new HtmlSelectElement();\n            x.AddOption(\"myvalue\", \"this is the text\");\n            var option = x.Options.FirstOrDefault();\n            Assert.NotNull(option);\n            Assert.Equal(\"myvalue\", option?.Value);\n            var text = option?.Children.FirstOrDefault() as TextNode;\n            Assert.NotNull(text);\n            Assert.Equal(\"this is the text\", text!.Data);\n        }\n\n        [Fact]\n        public void OptionWithValueGetsSelected()\n        {\n            var select = new HtmlSelectElement\n            {\n                Value = \"lolo\"\n            };\n            var option = new HtmlOptionElement\n            {\n                Value = \"lolo\"\n            };\n            select.AppendChild(option);\n            Assert.True(option.Selected);\n        }\n\n        [Fact]\n        public void AddGroupWithSelectedOption()\n        {\n            var select = new HtmlSelectElement\n            {\n                Value = \"lolo\"\n            };\n            var option = new HtmlOptionElement\n            {\n                Value = \"lolo\"\n            };\n            var group = new HtmlOptionGroupElement();\n            group.AppendChild(option);\n            select.AppendChild(group);\n            Assert.True(option.Selected);\n        }\n\n        [Fact]\n        public void AddSelectedOptionInGroup()\n        {\n            var select = new HtmlSelectElement\n            {\n                Value = \"lolo\"\n            };\n            var option = new HtmlOptionElement\n            {\n                Value = \"lolo\"\n            };\n            var group = new HtmlOptionGroupElement();\n            select.AppendChild(group);\n            group.AppendChild(option);\n            Assert.True(option.Selected);\n        }\n\n        [Fact]\n        public void SelectValueChangeOnChildOptions()\n        {\n            var select = new HtmlSelectElement();\n            var opt1 = new HtmlOptionElement\n            {\n                Value = \"a\"\n            };\n            var opt2 = new HtmlOptionElement\n            {\n                Value = \"b\"\n            };\n            var group = new HtmlOptionGroupElement();\n            group.AppendChild(opt2);\n            select.AppendChild(opt1);\n            select.AppendChild(group);\n            select.Value = \"a\";\n            Assert.True(opt1.Selected);\n            Assert.False(opt2.Selected);\n            select.Multiple = true;\n            select.Value = \"b\";\n            Assert.True(opt1.Selected);\n            Assert.True(opt2.Selected);\n        }\n\n        [Fact]\n        public void MeterProperties()\n        {\n            var x = new HtmlMeterElement\n            {\n                High = 80,\n                Low = 20,\n                Max = 100,\n                Min = 1,\n                Optimum = 50,\n                Value = 55\n            };\n            Assert.Equal(80, x.High);\n            Assert.Equal(20, x.Low);\n            Assert.Equal(100, x.Max);\n            Assert.Equal(1, x.Min);\n            Assert.Equal(50, x.Optimum);\n            Assert.Equal(55, x.Value);\n        }\n\n        [Fact]\n        public void ElementToStringSuffix()\n        {\n            var div = Element.Create(\"div\");\n            div.Id = \"lolo\";\n            div.Class = \"red\";\n            Assert.Equal(\"div #lolo red\", div.ToString());\n        }\n\n        [Fact]\n        public void IgnoreNotificationsNotFound()\n        {\n            var x = Element.Create(\"div\");\n            var task = x.NotifyEvent(\"lala\");\n            Assert.Same(Task.CompletedTask, task);\n        }\n\n        [Fact]\n        public void AppendTextMergesNodes()\n        {\n            var x = Element.Create(\"div\");\n            x.AppendText(\"hi\");\n            x.AppendText(\" \");\n            x.AppendText(\"bye\");\n            Assert.Equal(1, x.ChildCount);\n            var node = x.GetChildAt(0) as TextNode;\n            Assert.NotNull(node);\n            Assert.Equal(\"hi bye\", node!.Data);\n        }\n    }\n}\n"
  },
  {
    "path": "src/Tests/DOM/EventsTesting.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 10/2019\nAuthor: Pablo Carbonell\n*/\n\nusing Integrative.Lara.Tests.Main;\nusing Integrative.Lara.Tests.Middleware;\nusing Moq;\nusing System;\nusing System.Globalization;\nusing System.Threading.Tasks;\nusing Xunit;\n\nnamespace Integrative.Lara.Tests.DOM\n{\n    public class EventsTesting : DummyContextTesting\n    {\n        [Fact]\n        public async void AddRemoveHandler()\n        {\n            var x = new MessageTypeRegistry();\n            var counter = 0;\n            Task Handler(MessageEventArgs args1)\n            {\n                Assert.Equal(\"test\", args1.Body);\n                counter++;\n                return Task.CompletedTask;\n            }\n            x.Add(Handler);\n            var args = new MessageEventArgs(\"test\");\n            await x.RunAll(args);\n            Assert.Equal(1, counter);\n            x.Remove(Handler);\n            await x.RunAll(args);\n            Assert.Equal(1, counter);\n        }\n\n        [Fact]\n        public async void AddRemoveHandlerRegistry()\n        {\n            CreateMessageContext();\n\n            var document = DomOperationsTesting.CreateDocument();\n            var x = new MessageRegistry(document);\n            var counter = 0;\n            Task Handler(MessageEventArgs args)\n            {\n                counter++;\n                return Task.CompletedTask;\n            }\n            x.Add(\"a\", Handler);\n            await document.Head.NotifyEvent(\"_a\");\n            Assert.Equal(1, counter);\n\n            x.Remove(\"b\", Handler);\n            await document.Head.NotifyEvent(\"_a\");\n            Assert.Equal(2, counter);\n\n            x.Remove(\"a\", Handler);\n            await document.Head.NotifyEvent(\"_a\");\n            Assert.Equal(2, counter);\n        }\n\n        private void CreateMessageContext()\n        {\n            var context = new Mock<IPageContext>();\n            LaraUI.InternalContext.Value = context.Object;\n            var bridge = new Mock<IJsBridge>();\n            context.Setup(x => x.JSBridge).Returns(bridge.Object);\n            bridge.Setup(x => x.EventData).Returns(\"test\");\n            context.Setup(x => x.Application).Returns(Context.Application);\n        }\n\n        [Fact]\n        public void DebounceStored()\n        {\n            var x = new EventSettings\n            {\n                DebounceInterval = 5\n            };\n            Assert.Equal(5, x.DebounceInterval);\n        }\n\n        [Fact]\n        public void DocumentProcessesMessageListeners()\n        {\n            CreateMessageContext();\n            var doc = DomOperationsTesting.CreateDocument();\n            var counter = 0;\n            Task Handler(MessageEventArgs args)\n            {\n                counter++;\n                return Task.CompletedTask;\n            }\n            doc.AddMessageListener(\"a\", Handler);\n            doc.Head.NotifyEvent(\"_a\");\n            Assert.Equal(1, counter);\n            doc.RemoveMessageListener(\"a\", Handler);\n            doc.Head.NotifyEvent(\"_a\");\n            Assert.Equal(1, counter);\n        }\n\n        [Fact]\n        [Obsolete(\"old methods\")]\n        public void DocumentOnMessageRuns()\n        {\n            CreateMessageContext();\n            var doc = DomOperationsTesting.CreateDocument();\n            var counter = 0;\n            Task Handler()\n            {\n                counter++;\n                return Task.CompletedTask;\n            }\n            doc.OnMessage(\"a\", Handler);\n            doc.Head.NotifyEvent(\"_a\");\n            Assert.Equal(1, counter);\n        }\n\n        [Fact]\n        public async void AsyncEventDispatches()\n        {\n            var counter = 0;\n            var ev = new AsyncEvent();\n            Task MyMethod()\n            {\n                counter++;\n                return Task.CompletedTask;\n            }\n            ev.Subscribe(MyMethod);\n            await ev.InvokeAsync(this, new EventArgs());\n            Assert.Equal(1, counter);\n            \n            ev.Unsubscribe(MyMethod);\n            await ev.InvokeAsync(this, new EventArgs());\n            Assert.Equal(1, counter);\n        }\n\n        [Fact]\n        public async void AsyncEventPassesAlong()\n        {\n            var counter = 0;\n            var ev = new AsyncEvent();\n            ev.Subscribe(() =>\n            {\n                counter++;\n                return Task.CompletedTask;\n            });\n            var ev2 = new AsyncEvent();\n            ev2.Subscribe(ev);\n            await ev2.InvokeAsync(this, new EventArgs());\n            Assert.Equal(1, counter);\n\n            ev2.Unsubscribe(ev);\n            await ev2.InvokeAsync(this, new EventArgs());\n            Assert.Equal(1, counter);\n        }\n\n        [Fact]\n        public async void AsyncEventSubscribeHandler()\n        {\n            var counter = 0;\n            Task MyMethod(object sender, EventArgs args)\n            {\n                counter++;\n                return Task.CompletedTask;\n            }\n            var handler = new AsyncEventHandler<EventArgs>(MyMethod);\n            var ev = new AsyncEvent();\n            ev.Subscribe(handler);\n            await ev.InvokeAsync(this, new EventArgs());\n            Assert.Equal(1, counter);\n\n            ev.Unsubscribe(handler);\n            await ev.InvokeAsync(this, new EventArgs());\n            Assert.Equal(1, counter);\n        }\n\n        [Fact]\n        public async void AsyncEventSyncHandler()\n        {\n            var counter = 0;\n            void MyMethod() => counter++;\n            var ev = new AsyncEvent<EventArgs>();\n            ev.Subscribe(MyMethod);\n            await ev.InvokeAsync(this, new EventArgs());\n            Assert.Equal(1, counter);\n\n            ev.Unsubscribe(MyMethod);\n            await ev.InvokeAsync(this, new EventArgs());\n            Assert.Equal(1, counter);\n        }\n\n        [Fact]\n        public void DocumentEvent()\n        {\n            var counter = 0;\n            var x = new Document(new MyPage(), 100);\n            x.On(\"keyup\", () =>\n            {\n                counter++;\n                return Task.CompletedTask;\n            });\n            x.NotifyEvent(\"keyup\");\n            Assert.Equal(1, counter);\n            x.NotifyEvent(\"keyup\");\n            Assert.Equal(2, counter);\n            x.On(\"keyup\", null);\n            x.NotifyEvent(\"keyup\");\n            Assert.Equal(2, counter);\n        }\n\n        [Fact]\n        public void DocumentGuidToString()\n        {\n            var x = new Document(new MyPage(), 100);\n            var text = x.VirtualId.ToString(GlobalConstants.GuidFormat, CultureInfo.InvariantCulture);\n            Assert.Equal(text, x.VirtualIdString);\n        }\n    }\n}\n"
  },
  {
    "path": "src/Tests/DOM/GlobalAttributesTesting.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 5/2019\nAuthor: Pablo Carbonell\n*/\n\nusing Integrative.Lara.Tests.Main;\nusing Integrative.Lara.Tests.Middleware;\nusing System.Collections;\nusing System.Collections.Generic;\nusing Xunit;\n\nnamespace Integrative.Lara.Tests.DOM\n{\n    public class GlobalAttributesTesting : DummyContextTesting\n    {\n        [Fact]\n        public void AccessKey()\n        {\n            var x = new HtmlButtonElement\n            {\n                AccessKey = \"1\",\n                Class = \"2\",\n                ContentEditable = \"3\",\n                Dir = \"4\",\n                Draggable = \"5\",\n                DropZone = \"6\",\n                Hidden = true,\n                Id = \"7\",\n                Lang = \"8\",\n                Spellcheck = \"9\",\n                Style = \"10\",\n                TabIndex = \"11\",\n                Title = \"12\",\n                Translate = \"13\"\n            };\n            Assert.Equal(\"1\", x.GetAttribute(\"accesskey\"));\n            Assert.Equal(\"2\", x.GetAttribute(\"class\"));\n            Assert.Equal(\"3\", x.GetAttribute(\"contenteditable\"));\n            Assert.Equal(\"4\", x.GetAttribute(\"dir\"));\n            Assert.Equal(\"5\", x.GetAttribute(\"draggable\"));\n            Assert.Equal(\"6\", x.GetAttribute(\"dropzone\"));\n            Assert.Equal(\"7\", x.GetAttribute(\"id\"));\n            Assert.Equal(\"8\", x.GetAttribute(\"lang\"));\n            Assert.Equal(\"9\", x.GetAttribute(\"spellcheck\"));\n            Assert.Equal(\"10\", x.GetAttribute(\"style\"));\n            Assert.Equal(\"11\", x.GetAttribute(\"tabindex\"));\n            Assert.Equal(\"12\", x.GetAttribute(\"title\"));\n            Assert.Equal(\"13\", x.GetAttribute(\"translate\"));\n            Assert.True(x.Hidden);\n            Assert.Equal(\"1\", x.AccessKey);\n            Assert.Equal(\"2\", x.Class);\n            Assert.Equal(\"3\", x.ContentEditable);\n            Assert.Equal(\"4\", x.Dir);\n            Assert.Equal(\"5\", x.Draggable);\n            Assert.Equal(\"6\", x.DropZone);\n            Assert.Equal(\"7\", x.Id);\n            Assert.Equal(\"8\", x.Lang);\n            Assert.Equal(\"9\", x.Spellcheck);\n            Assert.Equal(\"10\", x.Style);\n            Assert.Equal(\"11\", x.TabIndex);\n            Assert.Equal(\"12\", x.Title);\n            Assert.Equal(\"13\", x.Translate);\n        }\n\n        [Fact]\n        public void ElementToString()\n        {\n            var x = new HtmlButtonElement();\n            Assert.Equal($\"button #{x.Id}\", x.ToString());\n            x.Id = \"hi\";\n            Assert.Equal(\"button #hi\", x.ToString());\n        }\n\n        [Fact]\n        public void SetAttributeId()\n        {\n            var x = Element.Create(\"button\");\n            x.SetAttribute(\"ID\", \"x\");\n            Assert.Equal(\"x\", x.Id);\n        }\n\n        [Fact]\n        public void GetChildPositionNotFound()\n        {\n            var x1 = Element.Create(\"span\");\n            var x2 = Element.Create(\"span\");\n            var index = x1.GetChildNodePosition(x2);\n            Assert.Equal(-1, index);\n        }\n\n        [Fact]\n        public void GrandchildDescendsFromElement()\n        {\n            var x1 = Element.Create(\"span\");\n            var x2 = Element.Create(\"span\");\n            var x3 = Element.Create(\"span\");\n            x1.AppendChild(x2);\n            x2.AppendChild(x3);\n            Assert.True(x3.DescendsFrom(x1));\n        }\n\n        [Fact]\n        public void InsertChildAfter()\n        {\n            var div = Element.Create(\"div\");\n            var x1 = Element.Create(\"span\");\n            var x2 = Element.Create(\"span\");\n            var x3 = Element.Create(\"span\");\n            div.AppendChild(x1);\n            div.AppendChild(x3);\n            div.InsertChildAfter(x1, x2);\n            var list = new List<Node>(div.Children);\n            Assert.Equal(3, list.Count);\n            Assert.Same(x2, list[1]);\n        }\n\n        [Fact]\n        public void GetContentNodeElement()\n        {\n            var div = Element.Create(\"div\", \"mydiv\");\n            var span = Element.Create(\"span\");\n            var text = Element.Create(\"hello\");\n            div.AppendChild(span);\n            span.AppendChild(text);\n            var content = div.GetContentNode();\n            Assert.True(content is ContentElementNode);\n            var ce = (ContentElementNode)content;\n            Assert.Equal(\"div\", ce.TagName);\n            Assert.Single(ce.Children);\n            Assert.Single(ce.Attributes);\n            var att = ce.Attributes![0];\n            Assert.Equal(\"id\", att.Attribute);\n            Assert.Equal(\"mydiv\", att.Value);\n        }\n\n        [Fact]\n        public void InlineChildElementsPrintedInline()\n        {\n            var doc = new Document(new MyPage(), BaseModeController.DefaultKeepAliveInterval);\n            var b = Element.Create(\"span\");\n            doc.Body.AppendChild(b);\n            doc.Body.AppendChild(new TextNode(\"hello\"));\n            var writer = new DocumentWriter(doc);\n            writer.Print();\n            var result = writer.ToString();\n            Assert.Contains(\"/span>hello\", result);\n        }\n\n        [Fact]\n        public void AddDuplicateIdThrows()\n        {\n            var map = new DocumentIdMap();\n            var a1 = Element.Create(\"span\", \"a\");\n            var a2 = Element.Create(\"span\", \"a\");\n            map.NotifyAdded(a1);\n            DomOperationsTesting.Throws<DuplicateElementIdException>(() => map.NotifyAdded(a2));\n        }\n\n        [Fact]\n        public void CheckedFalseFlushed()\n        {\n            var doc = new Document(new MyPage(), BaseModeController.DefaultKeepAliveInterval);\n            var x = new HtmlInputElement\n            {\n                Id = \"x\"\n            };\n            doc.Body.AppendChild(x);\n            x.Checked = true;\n            doc.OpenEventQueue();\n            x.Checked = false;\n            var queue = doc.GetQueue();\n            Assert.NotEmpty(queue);\n            var top = queue.Peek() as SetCheckedDelta;\n            Assert.NotNull(top);\n            Assert.Equal(x.Id, top!.ElementId);\n            Assert.False(top.Checked);\n        }\n\n        [Fact]\n        public void CheckedTrueFlushed()\n        {\n            var doc = new Document(new MyPage(), BaseModeController.DefaultKeepAliveInterval);\n            var x = new HtmlInputElement\n            {\n                Id = \"x\"\n            };\n            doc.Body.AppendChild(x);\n            doc.OpenEventQueue();\n            x.Checked = true;\n            var queue = doc.GetQueue();\n            Assert.NotEmpty(queue);\n            var top = queue.Peek() as SetCheckedDelta;\n            Assert.NotNull(top);\n            Assert.Equal(x.Id, top!.ElementId);\n            Assert.True(top.Checked);\n        }\n\n        [Fact]\n        public void NotifyCheckedTrueAdds()\n        {\n            var div = Element.Create(\"div\");\n            var a = new Attributes(div);\n            a.NotifyChecked(true);\n            Assert.True(a.HasAttribute(\"checked\"));\n            IEnumerator e = ((IEnumerable)a).GetEnumerator();\n            Assert.True(e.MoveNext());\n            a.NotifyChecked(false);\n            Assert.False(a.HasAttribute(\"checked\"));\n        }\n    }\n}\n"
  },
  {
    "path": "src/Tests/DOM/LaraBuilderTesting.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 8/2019\nAuthor: Pablo Carbonell\n*/\n\nusing Integrative.Lara.Tests.Middleware;\nusing System;\nusing System.Collections.ObjectModel;\nusing Xunit;\n\nnamespace Integrative.Lara.Tests.DOM\n{\n    [Obsolete(\"old methods\")]\n    public class LaraBuilderTesting : DummyContextTesting\n    {\n        private readonly Element _root;\n        private readonly LaraBuilder _builder;\n\n        public LaraBuilderTesting()\n        {\n            _root = Element.Create(\"div\");\n            _builder = new LaraBuilder(_root);\n        }\n\n        [Fact]\n        public async void OnStringAction()\n        {\n            var counter = 0;\n            _builder.On(\"click\", () => counter++);\n            await _root.NotifyEvent(\"click\");\n            Assert.Equal(1, counter);\n        }\n\n        [Fact]\n        public void ToggleClassStringBool()\n        {\n            _builder.ToggleClass(\"red\", true);\n            Assert.Equal(\"red\", _root.Class);\n        }\n\n        [Fact]\n        public void ToggleClassString()\n        {\n            _builder.ToggleClass(\"blue\");\n            Assert.Equal(\"blue\", _root.Class);\n        }\n\n        [Fact]\n        public void RemoveClass()\n        {\n            _root.Class = \"very dark\";\n            _builder.RemoveClass(\"very\");\n            Assert.Equal(\"dark\", _root.Class);\n        }\n\n        [Fact]\n        public void AddClass()\n        {\n            _builder.AddClass(\"green\");\n            Assert.Equal(\"green\", _root.Class);\n        }\n\n        [Fact]\n        public void GetCurrent()\n        {\n            _builder.GetCurrent(out var element);\n            Assert.Same(_root, element);\n        }\n\n        [Fact]\n        public void BindOptions()\n        {\n            var data = new MyData();\n            var found = false;\n            _builder.Bind(new BindHandlerOptions<MyData>\n            {\n                ModifiedHandler = (_, _) => found = true,\n                BindObject = data\n            });\n            data.Counter = 15;\n            Assert.True(found);\n        }\n\n        [Fact]\n        public void BindValueActions()\n        {\n            var data = new MyData();\n            var found = false;\n            _builder.Bind(data, () => found = true);\n            data.Counter = 15;\n            Assert.True(found);\n        }\n\n        [Fact]\n        public void BindChildrenOptions()\n        {\n            var list = new ObservableCollection<MyData>();\n            _builder.BindChildren(new BindChildrenOptions<MyData>(list, _ => Element.Create(\"span\")));\n            list.Add(new MyData());\n            Assert.NotEmpty(_root.Children);\n        }\n\n        [Fact]\n        public void BindChildrenCollectionElement()\n        {\n            var list = new ObservableCollection<MyData>();\n            _builder.BindChildren(list, () => Element.Create(\"span\"));\n            list.Add(new MyData());\n            Assert.NotEmpty(_root.Children);\n        }\n\n        [Fact]\n        public void BindChildenCollectionElement()\n        {\n            var list = new ObservableCollection<MyData>();\n            _builder.BindChildren(list, _ => Element.Create(\"div\"));\n            list.Add(new MyData());\n            Assert.NotEmpty(_root.Children);\n        }\n\n        [Fact]\n        public void BindInnerTextOptions()\n        {\n            var data = new MyData();\n            _builder.BindInnerText(new BindInnerTextOptions<MyData>\n            {\n                BindObject = data,\n                Property = x => x.Counter.ToString()\n            });\n            data.Counter = 3;\n            VerifyInnerText(_root, \"3\");\n        }\n\n        private static void VerifyInnerText(Element element, string data)\n        {\n            Assert.NotEmpty(element.Children);\n            var node = element.GetChildAt(0) as TextNode;\n            Assert.NotNull(node);\n            Assert.Equal(data, node!.Data);\n        }\n\n        [Fact]\n        public void BindInnerTextExpanded()\n        {\n            var data = new MyData();\n            _builder.BindInnerText(data, () => data.Counter.ToString());\n            data.Counter = 3;\n            VerifyInnerText(_root, \"3\");\n        }\n\n        [Fact]\n        public void BindInnerTextValueFuncString()\n        {\n            var data = new MyData();\n            _builder.BindInnerText(data, x => x.Counter.ToString());\n            data.Counter = 3;\n            VerifyInnerText(_root, \"3\");\n        }\n\n        [Fact]\n        public void BindAttributeOptions()\n        {\n            var data = new MyData();\n            _builder.BindAttribute(new BindAttributeOptions<MyData>\n            {\n                Attribute = \"data-counter\",\n                BindObject = data,\n                Property = x => x.Counter.ToString()\n            });\n            data.Counter = 2;\n            Assert.Equal(\"2\", _root.GetAttribute(\"data-counter\"));\n        }\n\n        [Fact]\n        public void BindAttributeExpanded()\n        {\n            var data = new MyData();\n            _builder.BindAttribute(\"data-counter\", data, () => data.Counter.ToString());\n            data.Counter = 2;\n            Assert.Equal(\"2\", _root.GetAttribute(\"data-counter\"));\n        }\n\n        [Fact]\n        public void BindAttributeStringFuncString()\n        {\n            var data = new MyData();\n            _builder.BindAttribute(\"data-counter\", data, x => x.Counter.ToString());\n            data.Counter = 2;\n            Assert.Equal(\"2\", _root.GetAttribute(\"data-counter\"));\n        }\n\n        [Fact]\n        public void BindActionElement()\n        {\n            var data = new MyData();\n            _builder.Bind(data, (x, y) => y.InnerText = x.Counter.ToString());\n            data.Counter = 14;\n            VerifyInnerText(_root, \"14\");\n        }\n\n        private class MyData : BindableBase\n        {\n            private int _counter;\n\n            public int Counter\n            {\n                get => _counter;\n                set => SetProperty(ref _counter, value);\n            }\n        }\n\n        [Fact]\n        [Obsolete(\"old method\")]\n        public void AddTextNode1()\n        {\n            var div = Element.Create(\"div\");\n            var builder = new LaraBuilder(div);\n            builder.AddTextNode(\"@@\", false);\n            var node = div.GetChildAt(0) as TextNode;\n            Assert.NotNull(node);\n            Assert.Equal(\"@@\", node!.Data);\n        }\n\n        [Fact]\n        [Obsolete(\"old method\")]\n        public void AddTextNode2()\n        {\n            var div = Element.Create(\"div\");\n            var builder = new LaraBuilder(div);\n            var node = new TextNode(\"test\");\n            builder.AddTextNode(node);\n            var x = div.GetChildAt(0) as TextNode;\n            Assert.Same(node, x);\n        }\n\n        [Fact]\n        public void InnerText()\n        {\n            var div = Element.Create(\"div\");\n            var builder = new LaraBuilder(div);\n            builder.InnerText(\"a<a\");\n            var node = div.GetChildAt(0) as TextNode;\n            Assert.NotNull(node);\n            Assert.Equal(\"a&lt;a\", node!.Data);\n        }\n\n        [Fact]\n        public void InnerData()\n        {\n            var div = Element.Create(\"div\");\n            var builder = new LaraBuilder(div);\n            builder.InnerData(\"a<a\");\n            var node = div.GetChildAt(0) as TextNode;\n            Assert.NotNull(node);\n            Assert.Equal(\"a<a\", node!.Data);\n        }\n\n        [Fact]\n        public void AppendData()\n        {\n            var div = Element.Create(\"div\");\n            var builder = new LaraBuilder(div);\n            builder.AppendData(\"a<a\");\n            var node = div.GetChildAt(0) as TextNode;\n            Assert.NotNull(node);\n            Assert.Equal(\"a<a\", node!.Data);\n        }\n\n        [Fact]\n        public void EnsureElementId()\n        {\n            var div = Element.Create(\"div\");\n            var builder = new LaraBuilder(div);\n            builder.EnsureElementId();\n            Assert.False(string.IsNullOrEmpty(div.Id));\n        }\n\n        [Fact]\n        public void ToggleClass1()\n        {\n            var data = new MyData();\n            _builder.BindToggleClass(new BindToggleClassOptions<MyData>\n            {\n                BindObject = data,\n                ClassName = \"red\",\n                Property = x => x.Counter > 0\n            });\n            Assert.False(_root.HasClass(\"red\"));\n            data.Counter = 1;\n            Assert.True(_root.HasClass(\"red\"));\n            data.Counter = 0;\n            Assert.False(_root.HasClass(\"red\"));\n        }\n\n        [Fact]\n        public void ToggleClass2()\n        {\n            var data = new MyData();\n            _builder.BindToggleClass(\"red\", data, () => data.Counter > 0);\n            Assert.False(_root.HasClass(\"red\"));\n            data.Counter = 1;\n            Assert.True(_root.HasClass(\"red\"));\n            data.Counter = 0;\n            Assert.False(_root.HasClass(\"red\"));\n        }\n\n        [Fact]\n        public void ToggleClass3()\n        {\n            var data = new MyData();\n            _builder.BindToggleClass(\"red\", data, x => x.Counter > 0);\n            Assert.False(_root.HasClass(\"red\"));\n            data.Counter = 1;\n            Assert.True(_root.HasClass(\"red\"));\n            data.Counter = 0;\n            Assert.False(_root.HasClass(\"red\"));\n        }\n    }\n}\n"
  },
  {
    "path": "src/Tests/Delta/AttributeEditTesting.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 5/2019\nAuthor: Pablo Carbonell\n*/\n\nusing Integrative.Lara.Tests.Main;\nusing Integrative.Lara.Tests.Middleware;\nusing System;\nusing Xunit;\n\nnamespace Integrative.Lara.Tests.Delta\n{\n    public class AttributeEditTesting : DummyContextTesting\n    {\n        [Fact]\n        public void AttributeEdited()\n        {\n            var doc = CreateDocument();\n            var div = Element.Create(\"div\", \"mydiv\");\n            doc.Body.AppendChild(div);\n            doc.OpenEventQueue();\n            div.SetAttribute(\"data-test\", \"x\");\n            var queue = doc.GetQueue();\n            Assert.Single(queue);\n            var step = queue.Peek() as AttributeEditedDelta;\n            Assert.Equal(\"data-test\", step!.Attribute);\n            Assert.Equal(\"x\", step.Value);\n            Assert.Equal(\"mydiv\", step.ElementId);\n        }\n\n        [Fact]\n        public void UnchangedIdNoSteps()\n        {\n            var doc = CreateDocument();\n            var div = Element.Create(\"div\", \"mydiv\");\n            doc.Body.AppendChild(div);\n            doc.OpenEventQueue();\n            div.Id = \"mydiv\";\n            var queue = doc.GetQueue();\n            Assert.Empty(queue);\n        }\n\n        [Fact]\n        public void SetValueDeltaProperties()\n        {\n            var div = Element.Create(\"div\", \"mydiv\");\n            var doc = CreateDocument();\n            doc.Body.AppendChild(div);\n            doc.OpenEventQueue();\n            div.SetAttribute(\"value\", \"x\");\n            var queue = doc.GetQueue();\n            Assert.NotEmpty(queue);\n            var step = queue.Peek() as SetValueDelta;\n            Assert.NotNull(step);\n            Assert.Equal(\"mydiv\", step!.ElementId);\n            Assert.Equal(\"x\", step.Value);\n        }\n\n        private static Document CreateDocument()\n        {\n            var page = new MyPage();\n            var doc = new Document(page,\n                Connections.CreateCryptographicallySecureGuid(),\n                BaseModeController.DefaultKeepAliveInterval);\n            return doc;\n        }\n\n        [Fact]\n        public void PlugOptionsHasEmptyConstructor()\n        {\n            var instance = Activator.CreateInstance<PlugOptions>();\n            Assert.NotNull(instance);\n        }\n\n        [Fact]\n        public void ClearChildrenOnEvent()\n        {\n            var div = Element.Create(\"div\");\n            var document = new Document(new MyPage(), BaseModeController.DefaultKeepAliveInterval);\n            document.Body.AppendChild(div);\n            document.OpenEventQueue();\n            document.Body.ClearChildren();\n            var queue = document.GetQueue();\n            Assert.NotEmpty(queue);\n            var first = queue.Peek() as ClearChildrenDelta;\n            Assert.NotNull(first);\n        }\n\n        [Fact]\n        public void ToggleClassToggles()\n        {\n            var button = new HtmlButtonElement();\n            button.ToggleClass(\"red\", true);\n            Assert.True(button.HasClass(\"red\"));\n            button.ToggleClass(\"red\", false);\n            Assert.False(button.HasClass(\"red\"));\n        }\n\n        [Fact]\n        public void DocumentCreatesElements()\n        {\n            var button = Document.CreateElement(\"button\");\n            Assert.NotNull(button);\n        }\n\n        [Fact]\n        public void DocumentCreatesText()\n        {\n            var text = Document.CreateTextNode(\"hello\");\n            Assert.NotNull(text);\n            Assert.Equal(\"hello\", text.Data);\n        }\n\n        [Fact]\n        public void ClearChildrenElement()\n        {\n            var x = new ClearChildrenDelta\n            {\n                ElementId = \"x\"\n            };\n            Assert.Equal(\"x\", x.ElementId);\n        }\n\n        [Fact]\n        public void PlugOptionsBlocking()\n        {\n            var settings = new EventSettings\n            {\n                BlockOptions = new BlockOptions\n                {\n                    BlockedElementId = \"a\",\n                    ShowElementId = \"b\",\n                    ShowHtmlMessage = \"c\"\n                }\n            };\n            var x = new PlugOptions(settings);\n            Assert.Equal(\"a\", x.BlockElementId);\n            Assert.Equal(\"b\", x.BlockShownId);\n            Assert.Equal(\"c\", x.BlockHTML);\n        }\n\n        [Fact]\n        public void ServerEventsDeltaCorrectType()\n        {\n            var delta = new ServerEventsDelta();\n            Assert.Equal(DeltaType.ServerEvents, delta.Type);\n        }\n    }\n}\n"
  },
  {
    "path": "src/Tests/Delta/DeltaTesting.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 10/2019\nAuthor: Pablo Carbonell\n*/\n\nusing Integrative.Lara.Tests.DOM;\nusing Integrative.Lara.Tests.Main;\nusing Integrative.Lara.Tests.Middleware;\nusing Xunit;\n\nnamespace Integrative.Lara.Tests.Delta\n{\n    public class DeltaTesting : DummyContextTesting\n    {\n        [Fact]\n        public void SubmitJsStores()\n        {\n            var x = new SubmitJsDelta\n            {\n                Code = \"code\"\n            };\n            Assert.Equal(\"code\", x.Code);\n        }\n\n        [Fact]\n        public void ReplaceDeltaEnqueues()\n        {\n            var doc = DomOperationsTesting.CreateDocument();\n            doc.OpenEventQueue();\n            ReplaceDelta.Enqueue(doc, \"test\");\n            var q = doc.GetQueue();\n            Assert.Single(q);\n            var first = q.Peek() as ReplaceDelta;\n            Assert.NotNull(first);\n            Assert.Equal(\"test\", first!.Location);\n        }\n\n        [Fact]\n        public void TextModifiedGenerated()\n        {\n            var doc = DomOperationsTesting.CreateDocument();\n            var span = Element.Create(\"span\");\n            span.InnerText = \"a\";\n            doc.Body.AppendChild(span);\n            doc.OpenEventQueue();\n            span.InnerText = \"test\";\n            var q = doc.GetQueue();\n            Assert.Single(q);\n            var first = q.Peek() as TextModifiedDelta;\n            Assert.NotNull(first);\n            Assert.Equal(\"test\", first!.Text);\n            Assert.Equal(span.Id, first.ParentElementId);\n            Assert.Equal(0, first.ChildNodeIndex);\n        }\n\n        [Fact]\n        public void ElementEventValueData()\n        {\n            var x = new ElementEventValue\n            {\n                Checked = true,\n                ElementId = \"a\",\n                Value = \"text\"\n            };\n            Assert.Equal(\"#a='text' checked\", x.ToString());\n            x.Checked = false;\n            Assert.Equal(\"#a='text'\", x.ToString());\n        }\n\n        [Fact]\n        public void PlugOptionsLongRunning()\n        {\n            var x = new PlugOptions\n            {\n                LongRunning = true,\n                Block = true\n            };\n            Assert.True(x.LongRunning);\n            Assert.True(x.Block);\n        }\n\n        [Fact]\n        public void PlugOptionsSerialize()\n        {\n            var x = new PlugOptions\n            {\n                Block = true\n            };\n            var json1 = x.ToJSON();\n            var json2 = LaraUI.JSON.Stringify(x);\n            Assert.Equal(json1, json2);\n        }\n\n        [Fact]\n        public void ClientEventFromSettings()\n        {\n            var x = new EventSettings\n            {\n                BlockOptions = new BlockOptions\n                {\n                    BlockedElementId = \"a\",\n                    ShowElementId = \"b\",\n                    ShowHtmlMessage = \"c\"\n                },\n                EventName = \"click\",\n                LongRunning = true,\n                Propagation = PropagationType.StopImmediatePropagation,\n                DebounceInterval = 400,\n                EvalFilter = \"true\",\n                UploadFiles = true\n            };\n            var client = ClientEventSettings.CreateFrom(x);\n            client.ExtraData = \"xx\";\n            Assert.Equal(x.Block, client.Block);\n            Assert.Equal(x.BlockOptions.ShowElementId, client.BlockShownId);\n            Assert.Equal(x.BlockOptions.BlockedElementId, client.BlockElementId);\n            Assert.Equal(x.BlockOptions.ShowHtmlMessage, client.BlockHTML);\n            Assert.Equal(x.EventName, client.EventName);\n            Assert.Equal(x.LongRunning, client.LongRunning);\n            Assert.Equal(x.Propagation, client.Propagation);\n            Assert.Equal(x.UploadFiles, client.UploadFiles);\n            Assert.Equal(\"true\", x.EvalFilter);\n            Assert.Equal(\"xx\", client.ExtraData);\n        }\n\n        [Fact]\n        public void SubscribeDocumentEventEnqueues()\n        {\n            var doc = new Document(new MyPage(), 100);\n            var settings = new EventSettings();\n            Assert.True(doc.CanDiscard);\n            SubscribeDelta.Enqueue(doc, settings);\n            Assert.False(doc.CanDiscard);\n            var q = doc.GetQueue();\n            Assert.NotEmpty(q);\n        }\n\n        [Fact]\n        public void DeltaPayload()\n        {\n            var x = new SubmitJsDelta\n            {\n                Payload = \"abc\"\n            };\n            Assert.Equal(\"abc\", x.Payload);\n        }\n\n        [Fact]\n        public void DeltaRenderClass()\n        {\n            var document = new Document(new MyPage(), 100);\n            RenderDelta.Enqueue(document, new []{document.Body});\n            var queue = document.GetQueue();\n            Assert.Single(queue);\n            var delta = queue.Peek();\n            Assert.Equal(DeltaType.Render, delta.Type);\n            var render = delta as RenderDelta;\n            Assert.NotNull(render);\n            Assert.NotNull(render?.Locator);\n            Assert.NotNull(render?.Node);\n            Assert.Equal(document.Body.Id, render?.Locator?.StartingId);\n        }\n    }\n}\n"
  },
  {
    "path": "src/Tests/Delta/LocatorTesting.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 5/2019\nAuthor: Pablo Carbonell\n*/\n\nusing Integrative.Lara.Tests.Middleware;\nusing Xunit;\n\nnamespace Integrative.Lara.Tests.Delta\n{\n    public class LocatorTesting : DummyContextTesting\n    {\n        [Fact]\n        public void LocateElementWithId()\n        {\n            var x = Element.Create(\"span\", \"x\");\n            var locator = NodeLocator.FromNode(x);\n            Assert.Equal(x.Id, locator.StartingId);\n        }\n    }\n}\n"
  },
  {
    "path": "src/Tests/Main/ButtonCounterPage.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 5/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System.Threading.Tasks;\n\nnamespace Integrative.Lara.Tests.Main\n{\n    internal class ButtonCounterPage : IPage\n    {\n        private const string ButtonId = \"MyCounterButton\";\n\n        private readonly bool _useSockets;\n\n        public ButtonCounterPage(bool useSockets)\n        {\n            _useSockets = useSockets;\n        }\n\n        private int _counter;\n\n        public Task OnGet()\n        {\n            var document = LaraUI.Page.Document;\n            var span = Element.Create(\"span\");\n            var text = new TextNode(\"Click me\");\n            var button = Element.Create(\"button\", ButtonId);\n            button.AppendChild(text);\n            document.Body.AppendChild(span);\n            document.Body.AppendChild(button);\n            button.On(new EventSettings\n            {\n                EventName = \"click\",\n                LongRunning = _useSockets,\n                Handler = () =>\n                {\n                    _counter++;\n                    text.Data = $\"Clicked {_counter} times\";\n                    return Task.CompletedTask;\n                }\n            });\n            return Task.CompletedTask;\n        }\n    }\n}\n"
  },
  {
    "path": "src/Tests/Main/ConnectionTesting.cs",
    "content": "/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 5/2019\nAuthor: Pablo Carbonell\n*/\n\nusing Integrative.Lara.Tests.Middleware;\nusing System.Net;\nusing System.Threading.Tasks;\nusing Xunit;\n\nnamespace Integrative.Lara.Tests.Main\n{\n    public class ConnectionTesting : DummyContextTesting\n    {\n        [Fact]\n        public void NonExistingDocument()\n        {\n            var guid = Connections.CreateCryptographicallySecureGuid();\n            var connection = new Connection(guid, IPAddress.Loopback);\n            Assert.True(IPAddress.Loopback.Equals(connection.RemoteIp));\n            Assert.True(connection.IsEmpty);\n            Assert.False(connection.TryGetDocument(guid, out _));\n        }\n\n        [Fact]\n        public void CreateDocumentPresent()\n        {\n            var connectionId = Connections.CreateCryptographicallySecureGuid();\n            var connection = new Connection(connectionId, IPAddress.Loopback);\n            var document = connection.CreateDocument(new MyPage(), BaseModeController.DefaultKeepAliveInterval);\n            var count = 0;\n            foreach (var pair in connection.GetDocuments())\n            {\n                Assert.Same(document, pair.Value);\n                count++;\n            }\n            Assert.Equal(1, count);\n            Assert.True(connection.TryGetDocument(document.VirtualId, out var found));\n            Assert.Same(document, found);\n            Assert.False(connection.IsEmpty);\n        }\n\n        [Fact]\n        public async void DiscardRemovesDocument()\n        {\n            var connectionId = Connections.CreateCryptographicallySecureGuid();\n            var connection = new Connection(connectionId, IPAddress.Loopback);\n            var page = new MyPage();\n            var document = connection.CreateDocument(page, BaseModeController.DefaultKeepAliveInterval);\n            await connection.Discard(document.VirtualId);\n            Assert.False(connection.TryGetDocument(document.VirtualId, out _));\n            Assert.True(page.Disposed);\n        }\n\n        [Fact]\n        public void CanDiscardStartsFalse()\n        {\n            var x = CreateDocument();\n            Assert.True(x.CanDiscard);\n        }\n\n        [Fact]\n        public void CannotDiscardAfterServerEventsOn()\n        {\n            var x = CreateDocument();\n            x.ServerEventsOn();\n            Assert.False(x.CanDiscard);\n        }\n\n        [Fact]\n        public void CannotDiscardAfterEvent()\n        {\n            var x = CreateDocument();\n            var div = Element.Create(\"div\");\n            div.On(\"click\", () => Task.CompletedTask);\n            x.Body.AppendChild(div);\n            Assert.False(x.CanDiscard);\n        }\n\n        private static Document CreateDocument()\n        {\n            var connectionId = Connections.CreateCryptographicallySecureGuid();\n            var connection = new Connection(connectionId, IPAddress.Loopback);\n            return connection.CreateDocument(new MyPage(), 100);\n        }\n    }\n}\n"
  },
  {
    "path": "src/Tests/Main/ConnectionsTesting.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 5/2019\nAuthor: Pablo Carbonell\n*/\n\nusing Integrative.Lara.Tests.Middleware;\nusing System;\nusing System.Net;\nusing System.Threading.Tasks;\nusing Xunit;\n\nnamespace Integrative.Lara.Tests.Main\n{\n    public class ConnectionsTesting : DummyContextTesting\n    {\n        [Fact]\n        public void ConnectionFound()\n        {\n            using var connections = new Connections();\n            var cnx = connections.CreateConnection(IPAddress.Loopback);\n            var count = 0;\n            foreach (var unused in connections.GetConnections())\n            {\n                count++;\n            }\n            Assert.Equal(1, count);\n            Assert.True(connections.TryGetConnection(cnx.Id, out var found));\n            Assert.Same(cnx, found);\n        }\n\n        [Fact]\n        public async void DiscardRemovesConnection()\n        {\n            using var connections = new Connections();\n            var cnx = connections.CreateConnection(IPAddress.Loopback);\n            await connections.Discard(cnx.Id);\n            Assert.False(connections.TryGetConnection(cnx.Id, out _));\n        }\n\n        [Fact]\n        public void DisposeRemovesConnection()\n        {\n            var connections = new Connections();\n            var cnx = connections.CreateConnection(IPAddress.Loopback);\n            connections.Dispose();\n            Assert.False(connections.TryGetConnection(cnx.Id, out _));\n        }\n\n        [Fact]\n        public async void TimerCleansUpDocuments()\n        {\n            using var connections = new Connections();\n            var connection = connections.CreateConnection(IPAddress.Loopback);\n            connection.CreateDocument(new MyPage(), BaseModeController.DefaultKeepAliveInterval);\n            var required = DateTime.UtcNow.AddSeconds(10);\n            Assert.NotEmpty(connection.GetDocuments());\n            await StaleConnectionsCollector.CleanupExpired(connection, required);\n            Assert.Empty(connection.GetDocuments());\n        }\n\n        [Fact]\n        public async void TimerCleansUp()\n        {\n            using var connections = new Connections(200, 100);\n            var cnx = connections.CreateConnection(IPAddress.Loopback);\n            var document = cnx.CreateDocument(new MyPage(), BaseModeController.DefaultKeepAliveInterval);\n            Assert.NotEmpty(connections.GetConnections());\n            document.ModifyLastUtcForTesting(DateTime.UtcNow.AddDays(-1));\n            await Task.Delay(400);\n            Assert.Empty(connections.GetConnections());\n        }\n    }\n}\n"
  },
  {
    "path": "src/Tests/Main/MyPage.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 5/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System;\nusing System.Threading.Tasks;\n\nnamespace Integrative.Lara.Tests.Main\n{\n    internal class MyPage : IPage, IDisposable\n    {\n        public bool Disposed { get; private set; }\n\n        public void Dispose()\n        {\n            Disposed = true;\n        }\n\n        public Task OnGet()\n        {\n            throw new NotImplementedException();\n        }\n    }\n}\n"
  },
  {
    "path": "src/Tests/Main/PublishedTesting.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 5/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System;\nusing System.Net;\nusing System.Runtime.Serialization;\nusing System.Threading.Tasks;\nusing Integrative.Lara.Tests.Middleware;\nusing Microsoft.AspNetCore.Http;\nusing Moq;\nusing Xunit;\n\nnamespace Integrative.Lara.Tests.Main\n{\n    public class PublishedTesting : DummyContextTesting\n    {\n        [Fact]\n        public void UnpublishRemoves()\n        {\n            using var app = new Application();\n            using var published = app.GetPublished();\n            published.Publish(\"/coco\", new StaticContent(new byte[0]));\n            published.Publish(\"/lala\", new StaticContent(new byte[0]));\n            app.UnPublish(\"/coco\");\n            Assert.True(published.TryGetNode(\"/lala\", out _));\n            Assert.False(published.TryGetNode(\"/coco\", out _));\n        }\n\n        [Fact]\n        public async void RedirectExecutes()\n        {\n            var http = new Mock<HttpContext>();\n            var response = new Mock<HttpResponse>();\n            var request = new Mock<HttpRequest>();\n            http.Setup(x => x.Response).Returns(response.Object);\n            http.Setup(x => x.Request).Returns(request.Object);\n            request.Setup(x => x.Method).Returns(\"GET\");\n            var page = new MyRedirectPage();\n            var document = new Document(page, BaseModeController.DefaultKeepAliveInterval);\n            var cx = new Connection(Guid.NewGuid(), IPAddress.Loopback);\n            var context = new PageContext(Context.Application, http.Object, cx);\n            await page.OnGet();\n            await PagePublished.ProcessGetResult(http.Object, document, context, HttpStatusCode.OK);\n            response.Verify(x => x.Redirect(\"https://www.google.com\"));\n        }\n\n        private class MyRedirectPage : IPage\n        {\n            public Task OnGet()\n            {\n                LaraUI.Page.Navigation.Replace(\"https://www.google.com\");\n                return Task.CompletedTask;\n            }\n        }\n\n        [Fact]\n        public void WebServiceContentType()\n        {\n            var x = new WebServiceContent();\n            Assert.Equal(\"application/json\", x.ContentType);\n            Assert.Equal(\"POST\", x.Method);\n            x.ContentType = \"text/html\";\n            x.Method = \"PUT\";\n            Assert.Equal(\"text/html\", x.ContentType);\n            Assert.Equal(\"PUT\", x.Method);\n        }\n\n        [Fact]\n        public void LaraStringify()\n        {\n            var start = new MyClass\n            {\n                Value = 5\n            };\n            var json = LaraUI.JSON.Stringify(start);\n            var ok = LaraUI.JSON.TryParse<MyClass>(json, out var result);\n            Assert.True(ok);\n            Assert.Equal(start.Value, result!.Value);\n        }\n\n        [DataContract]\n        private class MyClass\n        {\n            [DataMember]\n            public int Value { get; set; }\n        }\n\n        [Fact]\n        public void SessionRemoveValue()\n        {\n            var guid = Guid.Parse(\"{A11072B8-7CD4-4D70-821D-C8934ACCD270}\");\n            var connection = new Connection(guid, IPAddress.Loopback);\n            var session = new Session(connection);\n            session.SaveValue(\"mykey\", \"myvalue\");\n            session.RemoveValue(\"mykey\");\n            Assert.False(session.TryGetValue(\"mykey\", out _));\n        }\n\n    }\n}\n"
  },
  {
    "path": "src/Tests/Main/StaleTesting.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 5/2019\nAuthor: Pablo Carbonell\n*/\n\nusing Integrative.Lara.Tests.Middleware;\nusing System;\nusing System.Net;\nusing System.Threading.Tasks;\nusing Xunit;\n\nnamespace Integrative.Lara.Tests.Main\n{\n    public class StaleTesting : DummyContextTesting\n    {\n        [Fact]\n        public async void CleanupLeavesUnexpiredDocument()\n        {\n            var connections = new Connections();\n            var cnx = connections.CreateConnection(IPAddress.Loopback);\n            var doc1 = cnx.CreateDocument(new MyPage(), BaseModeController.DefaultKeepAliveInterval);\n            var doc2 = cnx.CreateDocument(new MyPage(), BaseModeController.DefaultKeepAliveInterval);\n            doc2.ModifyLastUtcForTesting(DateTime.UtcNow.AddHours(-10));\n            await Task.Delay(200);\n            using (var collector = new StaleConnectionsCollector(connections))\n            {\n                await collector.CleanupExpiredHandler();\n            }\n            Assert.True(cnx.TryGetDocument(doc1.VirtualId, out _));\n            Assert.False(cnx.TryGetDocument(doc2.VirtualId, out _));\n        }\n\n        [Fact]\n        public async void EmptyConnectionGetsCollected()\n        {\n            var connections = new Connections();\n            var cnx = connections.CreateConnection(IPAddress.Loopback);\n            using (var collector = new StaleConnectionsCollector(connections))\n            {\n                await collector.CleanupExpiredHandler();\n            }\n            Assert.False(connections.TryGetConnection(cnx.Id, out _));\n        }\n\n        [Fact]\n        public void TimerInterval()\n        {\n            using var x = new Connections\n            {\n                StaleCollectionInterval = 100,\n                StaleExpirationInterval = 200\n            };\n            Assert.Equal(100, x.StaleCollectionInterval);\n            Assert.Equal(200, x.StaleExpirationInterval);\n        }\n    }\n}\n"
  },
  {
    "path": "src/Tests/Main/StaticContentTesting.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 5/2019\nAuthor: Pablo Carbonell\n*/\n\nusing Integrative.Lara.Tests.Middleware;\nusing System;\nusing System.IO;\nusing System.Linq;\nusing System.Net;\nusing System.Net.Http;\nusing System.Reflection;\nusing Xunit;\n\nnamespace Integrative.Lara.Tests.Main\n{\n    public class StaticContentTesting : DummyContextTesting\n    {\n        private static readonly HttpClient _Client;\n\n        static StaticContentTesting()\n        {\n            var handler = new HttpClientHandler\n            {\n                AutomaticDecompression = DecompressionMethods.Deflate\n            };\n            _Client = new HttpClient(handler);\n        }            \n\n        [Fact]\n        public void AreadyCompressedFileDoesNotGetCompressed()\n        {\n            var bytes = LoadSampleJpeg();\n            var content = new StaticContent(bytes, ContentTypes.ImageJpeg);\n            Assert.Same(bytes, content.GetBytes());\n            Assert.Equal(ContentTypes.ImageJpeg, content.ContentType);\n            Assert.False(content.Compressed);\n            Assert.False(string.IsNullOrEmpty(content.ETag));\n        }\n\n        private static byte[] LoadSampleJpeg()\n        {\n            return LoadAsset(\"pexels-photo-248673.jpeg\");\n        }\n\n        private static byte[] LoadCompressibleBmp()\n        {\n            return LoadAsset(\"Compressible.bmp\");\n        }\n\n        private static byte[] LoadAsset(string filename)\n        {\n            return LoadResource($\"Integrative.Lara.Tests.Assets.{filename}\");\n        }\n\n        private static byte[] LoadResource(string filename)\n        {\n            var assembly = Assembly.GetAssembly(typeof(StaticContentTesting));\n            using Stream? resFilestream = assembly!.GetManifestResourceStream(filename);\n            if (resFilestream == null) return Array.Empty<byte>();\n            byte[] ba = new byte[resFilestream.Length];\n            resFilestream.Read(ba, 0, ba.Length);\n            return ba;\n        }\n\n        [Fact]\n        public async void RequestWithoutETagReceivesFile()\n        {\n            var bytes = LoadSampleJpeg();\n            var content = new StaticContent(bytes, ContentTypes.ImageJpeg);\n\n            await Context.Application.Start();\n            var address = LaraUI.GetFirstURL(Context.Application.GetHost());\n            Context.Application.PublishFile(\"/\", content);\n            using var response = await _Client.GetAsync(address);\n            var downloaded = await response.Content.ReadAsByteArrayAsync();\n            Assert.Equal(HttpStatusCode.OK, response.StatusCode);\n            Assert.True(response.Headers.TryGetValues(\"ETag\", out var values));\n            Assert.Equal(content.ETag, values?.FirstOrDefault());\n            Assert.Equal(bytes, downloaded);\n        }\n\n        [Fact]\n        public async void RequestWrongETagReceivesFile()\n        {\n            var bytes = LoadSampleJpeg();\n            var content = new StaticContent(bytes, ContentTypes.ImageJpeg);\n\n            await Context.Application.Start(new StartServerOptions\n            {\n                AllowLocalhostOnly = true\n            });\n            var address = LaraUI.GetFirstURL(Context.Application.GetHost());\n            Context.Application.PublishFile(\"/\", content);\n\n            var request = new HttpRequestMessage\n            {\n                Method = new HttpMethod(\"GET\"),\n                RequestUri = new Uri(address)\n            };\n            request.Headers.TryAddWithoutValidation(\"If-None-Match\", \"lalalalala\");\n\n            using var response = await _Client.SendAsync(request);\n            var downloaded = await response.Content.ReadAsByteArrayAsync();\n            Assert.Equal(HttpStatusCode.OK, response.StatusCode);\n            Assert.True(response.Headers.TryGetValues(\"ETag\", out var values));\n            Assert.Equal(content.ETag, values?.FirstOrDefault());\n            Assert.Equal(bytes, downloaded);\n        }\n\n        [Fact]\n        public async void RequestCorrectETagReceivesNotModified()\n        {\n            var bytes = LoadSampleJpeg();\n            var content = new StaticContent(bytes, ContentTypes.ImageJpeg);\n\n            await Context.Application.Start();\n            var address = LaraUI.GetFirstURL(Context.Application.GetHost());\n            Context.Application.PublishFile(\"/\", content);\n\n            var request = new HttpRequestMessage\n            {\n                Method = new HttpMethod(\"GET\"),\n                RequestUri = new Uri(address)\n            };\n            request.Headers.TryAddWithoutValidation(\"If-None-Match\", content.ETag);\n\n            using var response = await _Client.SendAsync(request);\n            var downloaded = await response.Content.ReadAsByteArrayAsync();\n            Assert.Equal(HttpStatusCode.NotModified, response.StatusCode);\n            Assert.False(response.Headers.Contains(\"ETag\"));\n            Assert.Empty(downloaded);\n        }\n\n        [Fact]\n        public async void CompressibleFileIsSentCompressed()\n        {\n            var bytes = LoadCompressibleBmp();\n            var content = new StaticContent(bytes, \"image\");\n\n            await Context.Application.Start();\n            var address = LaraUI.GetFirstURL(Context.Application.GetHost());\n            Context.Application.PublishFile(\"/\", content);\n            using var response = await _Client.GetAsync(address);\n            var downloaded = await response.Content.ReadAsByteArrayAsync();\n            Assert.Equal(HttpStatusCode.OK, response.StatusCode);\n            Assert.True(response.Headers.TryGetValues(\"ETag\", out var values));\n            Assert.Equal(content.ETag, values?.FirstOrDefault());\n            Assert.Equal(bytes, downloaded);\n        }\n\n        [Fact]\n        public async void ContentNotFound()\n        {\n            await Context.Application.Start();\n            var address = LaraUI.GetFirstURL(Context.Application.GetHost());\n            using var response = await _Client.GetAsync(address + \"/lalala\");\n            Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);\n        }\n    }\n}\n"
  },
  {
    "path": "src/Tests/Middleware/DummyContext.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 11/2019\nAuthor: Pablo Carbonell\n*/\n\nusing Microsoft.AspNetCore.Http;\nusing Moq;\nusing System;\nusing System.Diagnostics.CodeAnalysis;\nusing System.Net;\n\nnamespace Integrative.Lara.Tests.Middleware\n{\n    internal class DummyContext : BaseContext, IPageContext, IWebServiceContext\n    {\n        private DummyContext(Application app, Mock<HttpContext> http)\n            : base(app, http.Object)\n        {\n            var request = new Mock<HttpRequest>();\n            http.Setup(x => x.Request).Returns(request.Object);\n            request.Setup(x => x.Path).Returns(\"/abc\");\n            var guid = Connections.CreateCryptographicallySecureGuid();\n            var cnx = new Connection(guid, IPAddress.Loopback);\n            Session = new Session(cnx);\n            JSBridge = _bridge.Object;\n        }\n\n        public Document Document => throw new NotImplementedException();\n\n        private readonly Mock<IJsBridge> _bridge = new Mock<IJsBridge>();\n\n        public IJsBridge JSBridge { get; set; }\n\n        public INavigation Navigation => throw new NotImplementedException();\n\n        public Session Session { get; }\n\n        public string RequestBody { get; set; } = string.Empty;\n\n        public HttpStatusCode StatusCode { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }\n\n        public static DummyContext Create()\n        {\n            var app = new Application();\n            var http = new Mock<HttpContext>();\n            return new DummyContext(app, http);\n        }\n\n        public void Dispose()\n        {\n            Application.Dispose();\n        }\n\n        public bool TryGetSession([NotNullWhen(true)] out Session? session)\n        {\n            throw new NotImplementedException();\n        }\n    }\n}\n"
  },
  {
    "path": "src/Tests/Middleware/DummyContextTesting.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 11/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System;\n\nnamespace Integrative.Lara.Tests.Middleware\n{\n    public class DummyContextTesting : IDisposable\n    {\n        internal readonly DummyContext Context = DummyContext.Create();\n\n        public void Dispose()\n        {\n            Context.Dispose();\n        }\n    }\n}\n"
  },
  {
    "path": "src/Tests/Middleware/ErrorPagesTesting.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 9/2019\nAuthor: Pablo Carbonell\n*/\n\nusing Xunit;\n\nnamespace Integrative.Lara.Tests.Middleware\n{\n    public class ErrorPagesTesting : DummyContextTesting\n    {\n        [Fact]\n        public void DefaultNotFoundRuns()\n        {\n            var pages = new ErrorPages(Context.Application.GetPublished());\n            var page = pages.GetPage(System.Net.HttpStatusCode.NotFound);\n            Assert.NotNull(page);\n        }\n\n        [Fact]\n        public void DefaultServerErrorRuns()\n        {\n            var pages = new ErrorPages(Context.Application.GetPublished());\n            var found = pages.TryGetPage(System.Net.HttpStatusCode.InternalServerError, out var page);\n            Assert.True(found);\n            Assert.NotNull(page);\n        }\n\n    }\n}\n"
  },
  {
    "path": "src/Tests/Middleware/EventParametersTesting.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 12/2019\nAuthor: Pablo Carbonell\n*/\n\nusing Microsoft.AspNetCore.Http;\nusing System;\nusing System.Collections.Generic;\nusing Xunit;\n\nnamespace Integrative.Lara.Tests.Middleware\n{\n    public class EventParametersTesting\n    {\n        private readonly Guid _id = Guid.Parse(\"{2F97BE9D-1CC3-4EBD-BC93-EF748B774F1D}\");\n\n        [Fact]\n        public void RoundTripMinimum()\n        {\n            var x = new EventParameters\n            {\n                DocumentId = _id,\n                ElementId = \"abc\",\n                EventName = \"click\",\n                EventNumber = 1,\n            };\n            var json = LaraUI.JSON.Stringify(x);\n            var result = LaraUI.JSON.Parse<EventParameters>(json);\n            Assert.Equal(x.DocumentId, result.DocumentId);\n            Assert.Equal(x.ElementId, result.ElementId);\n            Assert.Equal(x.EventName, result.EventName);\n            Assert.Equal(x.EventNumber, result.EventNumber);\n        }\n\n        [Fact]\n        public void RoundTripEmptyFiles()\n        {\n            var x = new SocketEventParameters\n            {\n                DocumentId = _id,\n                SocketFiles = new FormFileCollection()\n            };\n            var json = LaraUI.JSON.Stringify(x);\n            var result = LaraUI.JSON.Parse<SocketEventParameters>(json);\n            Assert.NotNull(result.SocketFiles);\n            Assert.Empty(result.SocketFiles);\n        }\n\n        [Fact]\n        public void RoundTripFile()\n        {\n            var x = new FormFile\n            {\n                Content = \"hello\",\n                ContentDisposition = \"a\",\n                ContentType = \"b\",\n                FileName = \"c\",\n                Name = \"d\"\n            };\n            var json = LaraUI.JSON.Stringify(x);\n            var result = LaraUI.JSON.Parse<FormFile>(json);\n            Assert.Equal(x.Content, result.Content);\n            Assert.Equal(x.ContentDisposition, result.ContentDisposition);\n            Assert.Equal(x.ContentType, result.ContentType);\n            Assert.Equal(x.FileName, result.FileName);\n            Assert.Equal(x.Length, result.Length);\n            Assert.Equal(x.Name, result.Name);\n        }\n\n        [Fact]\n        public void SerializeBytes()\n        {\n            var bytes = BuildBytes();\n            var x = new FormFile\n            {\n                Content = Convert.ToBase64String(bytes)\n            };\n            var json = LaraUI.JSON.Stringify(x);\n            var result = LaraUI.JSON.Parse<FormFile>(json);\n            var output = Convert.FromBase64String(result.Content);\n            Assert.Equal(256, bytes.Length);\n            for (var index = 0; index < 256; index++)\n            {\n                Assert.Equal(index, output[index]);\n            }\n        }\n\n        private static byte[] BuildBytes()\n        {\n            var bytes = new byte[256];\n            for (var index = 0; index <= 255; index++)\n            {\n                bytes[index] = (byte)index;\n            }\n            return bytes;\n        }\n\n        [Fact]\n        public void FileCollectionIterates()\n        {\n            var f1 = new FormFile();\n            var f2 = new FormFile();\n            var list = new FormFileCollection\n            {\n                InnerList = new List<FormFile>()\n            };\n            list.InnerList.Add(f1);\n            list.InnerList.Add(f2);\n            var other = new List<FormFile>();\n            foreach (FormFile? f in list)\n            {\n                Assert.NotNull(f);\n                other.Add(f!);\n            }\n            Assert.Equal(2, other.Count);\n            Assert.Same(f1, other[0]);\n            Assert.Same(f2, other[1]);\n        }\n\n        [Fact]\n        public void NullCollectionEmpty()\n        {\n            var x = new FormFileCollection();\n            var size = x.Count;\n            Assert.Equal(0, size);\n            x.InnerList = new List<FormFile>();\n            size = x.Count;\n            Assert.Equal(0, size);\n        }\n\n        [Fact]\n        public void FindFileByName()\n        {\n            var f1 = new FormFile();\n            var f2 = new FormFile\n            {\n                Name = \"lala\"\n            };\n            var x = new FormFileCollection\n            {\n                InnerList = new List<FormFile>\n                {\n                    f1,\n                    f2\n                }\n            };\n            var found = x.GetFile(\"lala\");\n            Assert.Same(f2, found);\n        }\n\n        [Fact]\n        public void GetFileReadonlyList()\n        {\n            var f1 = new FormFile();\n            var x = new FormFileCollection\n            {\n                InnerList = new List<FormFile>\n                {\n                    f1\n                }\n            };\n            var list = x as IReadOnlyList<IFormFile>;\n            var found = list[0];\n            Assert.Same(f1, found);\n        }\n\n        [Fact]\n        public void GetFilesByName()\n        {\n            var f1 = new FormFile\n            {\n                Name = \"a\"\n            };\n            var f2 = new FormFile\n            {\n                Name = \"b\"\n            };\n            var f3 = new FormFile\n            {\n                Name = \"a\"\n            };\n            var x = new FormFileCollection\n            {\n                InnerList = new List<FormFile>\n                {\n                    f1, f2, f3\n                }\n            };\n            var found = x.GetFiles(\"a\");\n            Assert.Equal(2, found.Count);\n            Assert.Same(f1, found[0]);\n            Assert.Same(f3, found[1]);\n        }\n\n        [Fact]\n        public void EnumrableInterface()\n        {\n            var x = new FormFileCollection();\n            var y = (IEnumerable<IFormFile>) x;\n            using var enumerator = y.GetEnumerator();\n            Assert.False(enumerator.MoveNext());\n        }\n    }\n}\n"
  },
  {
    "path": "src/Tests/Middleware/MiddlewareTesting.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 5/2019\nAuthor: Pablo Carbonell\n*/\n\nusing Integrative.Lara.Tests.DOM;\nusing Integrative.Lara.Tests.Main;\nusing Microsoft.AspNetCore.Builder;\nusing Microsoft.AspNetCore.Hosting;\nusing Microsoft.AspNetCore.Http;\nusing Microsoft.Extensions.Logging;\nusing Microsoft.Extensions.Primitives;\nusing Moq;\nusing System;\nusing System.Collections;\nusing System.Collections.Generic;\nusing System.Globalization;\nusing System.IO;\nusing System.Net;\nusing System.Net.WebSockets;\nusing System.Text;\nusing System.Threading;\nusing System.Threading.Tasks;\nusing Xunit;\n\nnamespace Integrative.Lara.Tests.Middleware\n{\n    internal class MyStatusPage : IPage\n    {\n        public Task OnGet()\n        {\n            throw new StatusCodeException(HttpStatusCode.Unauthorized);\n        }\n    }\n\n\n    public class MiddlewareTesting : DummyContextTesting\n    {\n        [Fact]\n        public void TryParseMissingDocFails()\n        {\n            StringValues values;\n            var mock = new Mock<IQueryCollection>();\n            mock.Setup(x => x.TryGetValue(\"doc\", out values)).Returns(false);\n            Assert.False(DiscardParameters.TryParse(mock.Object, out _));\n            Assert.False(EventParameters.TryParse(mock.Object, out _));\n        }\n\n        [Fact]\n        public async void LocalhostFilterTesting()\n        {\n            var logger = new Mock<ILogger<LocalhostFilter>>();\n            var next = new Mock<RequestDelegate>();\n            var filter = new LocalhostFilter(next.Object, logger.Object);\n\n            var connectionInfo = new Mock<ConnectionInfo>();\n            connectionInfo.Setup(x => x.RemoteIpAddress).Returns(IPAddress.Parse(\"172.217.4.206\"));\n            var http = new Mock<HttpContext>();\n            http.Setup(x => x.Connection).Returns(connectionInfo.Object);\n            var response = new Mock<HttpResponse>();\n            http.Setup(x => x.Response).Returns(response.Object);\n            response.SetupProperty(x => x.StatusCode);\n            var headers = new Mock<IHeaderDictionary>();\n            response.Setup(x => x.Headers).Returns(headers.Object);\n            var body = new Mock<Stream>();\n            response.Setup(x => x.Body).Returns(body.Object);\n            response.SetupProperty(x => x.ContentLength);\n\n            await filter.Invoke(http.Object);\n\n            Assert.Equal(403, response.Object.StatusCode);\n        }\n\n        [Fact]\n        public async void NullBodyReturnsEmptyString()\n        {\n            var http = new Mock<HttpContext>();\n            var request = new Mock<HttpRequest>();\n            http.Setup(x => x.Request).Returns(request.Object);\n            var result = await MiddlewareCommon.ReadBody(http.Object);\n            Assert.Equal(string.Empty, result);\n        }\n\n        [Fact]\n        public void AddHeaderNeverExpires()\n        {\n            var http = new Mock<HttpContext>();\n            var response = new Mock<HttpResponse>();\n            http.Setup(x => x.Response).Returns(response.Object);\n            var headers = new Mock<IHeaderDictionary>();\n            response.Setup(x => x.Headers).Returns(headers.Object);\n            MiddlewareCommon.AddHeaderNeverExpires(http.Object);\n            headers.Verify(x => x.Add(\"Cache-Control\", \"max-age=31556926\"), Times.Exactly(1));\n        }\n\n        [Fact]\n        public void ProcessMessageSkipsEmptyMessage()\n        {\n            var http = new Mock<HttpContext>();\n            var cx = new Connection(Guid.NewGuid(), IPAddress.Loopback);\n            var context = new PageContext(Context.Application, http.Object, cx);\n            var parameters = new EventParameters();\n            PostEventHandler.ProcessMessageIfNeeded(context, parameters);\n        }\n\n        [Fact]\n        public async void ConnectionNotFoundSendsReload()\n        {\n            var http = new Mock<HttpContext>();\n            var request = new Mock<HttpRequest>();\n            var cookies = new Mock<IRequestCookieCollection>();\n            var response = new Mock<HttpResponse>();\n            var headers = new Mock<IHeaderDictionary>();\n            var body = new Mock<Stream>();\n            var sockets = new Mock<WebSocketManager>();\n            http.Setup(x => x.Request).Returns(request.Object);\n            http.Setup(x => x.Response).Returns(response.Object);\n            http.Setup(x => x.WebSockets).Returns(sockets.Object);\n            request.Setup(x => x.Method).Returns(\"POST\");\n            request.Setup(x => x.Path).Returns(\"/_event\");\n            request.Setup(x => x.Cookies).Returns(cookies.Object);\n            response.Setup(x => x.Headers).Returns(headers.Object);\n            response.Setup(x => x.Body).Returns(body.Object);\n            sockets.Setup(x => x.IsWebSocketRequest).Returns(false);\n            var query = new MyQueryCollection();\n            request.Setup(x => x.Query).Returns(query);\n            query.Add(\"doc\", \"EF2FF98720E34A2EA29E619977A5F04A\");\n            query.Add(\"el\", \"lala\");\n            query.Add(\"ev\", \"lala\");\n            var next = new Mock<RequestDelegate>();\n            var handler = new PostEventHandler(Context.Application, next.Object);\n            var result = await handler.ProcessRequest(http.Object);\n            Assert.True(result);\n        }\n\n        private class MyQueryCollection : IQueryCollection\n        {\n            private readonly Dictionary<string, StringValues> _map = new Dictionary<string, StringValues>();\n\n            public StringValues this[string key] => _map[key];\n\n            public int Count => _map.Count;\n\n            public ICollection<string> Keys => _map.Keys;\n\n            public bool ContainsKey(string key) => _map.ContainsKey(key);\n\n            public IEnumerator<KeyValuePair<string, StringValues>> GetEnumerator()\n            {\n                return _map.GetEnumerator();\n            }\n\n            public bool TryGetValue(string key, out StringValues value)\n            {\n                return _map.TryGetValue(key, out value);\n            }\n\n            IEnumerator IEnumerable.GetEnumerator()\n            {\n                return _map.GetEnumerator();\n            }\n\n            public void Add(string key, string value)\n            {\n                _map.Add(key, new StringValues(value));\n            }\n        }\n\n        [Fact]\n        public void ClientEventMessageExtraData()\n        {\n            var x = new ClientEventMessage\n            {\n                ExtraData = \"lala\"\n            };\n            Assert.Equal(\"lala\", x.ExtraData);\n        }\n\n        [Fact]\n        [Obsolete]\n        public void UseLaraEmpty()\n        {\n            var app = new Mock<IApplicationBuilder>();\n            Assert.Same(app.Object, app.Object.UseLara());\n        }\n\n        [Fact]\n        public void PageContextSocket()\n        {\n            var http = new Mock<HttpContext>();\n            var cx = new Connection(Guid.NewGuid(), IPAddress.Loopback);\n            var page = new PageContext(Context.Application, http.Object, cx);\n            var socket = new Mock<WebSocket>();\n            page.Socket = socket.Object;\n            Assert.Same(socket.Object, page.Socket);\n        }\n\n        [Fact]\n        public async void CannotFlushAjax()\n        {\n            var http = new Mock<HttpContext>();\n            var cx = new Connection(Guid.NewGuid(), IPAddress.Loopback);\n            var page = new PageContext(Context.Application, http.Object, cx);\n            await DomOperationsTesting.ThrowsAsync<InvalidOperationException>(async ()\n                => await page.Navigation.FlushPartialChanges());\n        }\n\n        [Fact]\n        public async void FlushSendsMessage()\n        {\n            var http = new Mock<HttpContext>();\n            var document = new Document(new MyPage(), BaseModeController.DefaultKeepAliveInterval);\n            var socket = new Mock<WebSocket>();\n            var cx = new Connection(Guid.NewGuid(), IPAddress.Loopback);\n            var page = new PageContext(Context.Application, http.Object, cx)\n            {\n                Socket = socket.Object,\n                DocumentInternal = document\n            };\n            var button = new HtmlButtonElement();\n            document.OpenEventQueue();\n            document.Body.AppendChild(button);\n            await page.Navigation.FlushPartialChanges();\n            Assert.Empty(document.GetQueue());\n            Assert.Same(socket.Object, page.Socket);\n        }\n\n        [Fact]\n        public void EmptyArraySegment()\n        {\n            var x = PostEventHandler.BuildArraySegment(\"\");\n            Assert.NotNull(x.Array);\n            Assert.Empty(x.Array);\n        }\n\n        [Fact]\n        public void ValidateAddress()\n        {\n            Published.ValidateAddress(\"test\");\n            Assert.ThrowsAny<ArgumentException>(() => Published.ValidateAddress(\"\"));\n            Assert.ThrowsAny<ArgumentException>(() => Published.ValidateAddress(null));\n        }\n\n        [Fact]\n        public void ValidateMethod()\n        {\n            Published.ValidateMethod(\"test\");\n            Assert.ThrowsAny<ArgumentException>(() => Published.ValidateMethod(\"\"));\n            Assert.ThrowsAny<ArgumentException>(() => Published.ValidateMethod(null));\n        }\n\n        [Fact]\n        public void UnpublishMethod()\n        {\n            var service = new Mock<IWebService>();\n            using var x = new Published();\n            x.Publish(new WebServiceContent\n            {\n                Address = \"/myws\",\n                Method = \"PUT\",\n                Factory = () => service.Object\n            });\n            var combined = Published.CombinePathMethod(\"/myws\", \"PUT\");\n            Assert.True(x.TryGetNode(combined, out _));\n            x.UnPublish(\"/myws\", \"PUT\");\n            Assert.False(x.TryGetNode(combined, out _));\n        }\n\n        [Fact]\n        public void WebServiceSessionNotFound()\n        {\n            var http = new Mock<HttpContext>();\n            var context = new WebServiceContext(Context.Application, http.Object);\n            var request = new Mock<HttpRequest>();\n            http.Setup(x => x.Request).Returns(request.Object);\n            var cookies = new Mock<IRequestCookieCollection>();\n            request.Setup(x => x.Cookies).Returns(cookies.Object);\n            string temp;\n            cookies.Setup(x => x.TryGetValue(GlobalConstants.CookieSessionId, out temp)).Returns(false);\n            Assert.False(context.TryGetSession(out _));\n        }\n\n        [Fact]\n        public void WebServiceCustomCode()\n        {\n            var http = new Mock<HttpContext>();\n            var x = new WebServiceContext(Context.Application, http.Object)\n            {\n                StatusCode = HttpStatusCode.BadRequest\n            };\n            Assert.Equal(HttpStatusCode.BadRequest, x.StatusCode);\n        }\n\n        [Fact]\n        public async void PostEventHandlerSkipsRequests()\n        {\n            var http = new Mock<HttpContext>();\n            var next = new Mock<RequestDelegate>();\n            var post = new PostEventHandler(Context.Application, next.Object);\n            var request = new Mock<HttpRequest>();\n            http.Setup(x => x.Request).Returns(request.Object);\n            request.Setup(x => x.Path).Returns(PostEventHandler.EventPrefix);\n            request.Setup(x => x.Method).Returns(\"LALA\");\n            var websockets = new Mock<WebSocketManager>();\n            http.Setup(x => x.WebSockets).Returns(websockets.Object);\n            Assert.False(await post.ProcessRequest(http.Object));\n        }\n\n        [Fact]\n        public async void PostEventHandlerNoElement()\n        {\n            var http = new Mock<HttpContext>();\n            var page = new MyPage();\n            var document = new Document(page, BaseModeController.DefaultKeepAliveInterval);\n            var context = new PostEventContext(Context.Application, http.Object)\n            {\n                Document = document,\n                Parameters = new EventParameters\n                {\n                    ElementId = \"aaa\"\n                }\n            };\n            var sockets = new Mock<WebSocketManager>();\n            http.Setup(x => x.WebSockets).Returns(sockets.Object);\n            var response = new Mock<HttpResponse>();\n            http.Setup(x => x.Response).Returns(response.Object);\n            response.SetupProperty(x => x.StatusCode);\n            var headers = new Mock<IHeaderDictionary>();\n            response.Setup(x => x.Headers).Returns(headers.Object);\n            var body = new Mock<Stream>();\n            response.Setup(x => x.Body).Returns(body.Object);\n            await PostEventHandler.ProcessRequestDocument(context);\n            var code = response.Object.StatusCode;\n            Assert.True(code == 200 || code == 0);\n        }\n\n        [Fact]\n        public async void SendReplyLeavesSocketOpen()\n        {\n            var page = new MyPage();\n            var document = new Document(page, BaseModeController.DefaultKeepAliveInterval);\n            document.ServerEventsOn();\n            var post = new Mock<PostEventContext>(Context.Application, Context.Http);\n            post.Object.Document = document;\n            var parameters = new EventParameters\n            {\n                EventName = GlobalConstants.ServerSideEvent,\n            };\n            post.Object.Parameters = parameters;\n            var http = new Mock<HttpContext>();\n            var ws = new Mock<WebSocketManager>();\n            http.Setup(x => x.WebSockets).Returns(ws.Object);\n            ws.Setup(x => x.IsWebSocketRequest).Returns(true);\n            post.Object.Http = http.Object;\n            post.Setup(x => x.GetSocketCompletion()).Returns(CompletionResult);\n            await PostEventHandler.SendReply(post.Object, \"lala\");\n            post.Verify(x => x.GetSocketCompletion());\n        }\n\n        private static Task<TaskCompletionSource<bool>> CompletionResult()\n        {\n            var mock = new Mock<TaskCompletionSource<bool>>();\n            return Task.FromResult(mock.Object);\n        }\n\n        [Fact]\n        public async void StatusCodeExceptionProcessed()\n        {\n            var element = Element.Create(\"div\");\n            element.On(\"click\", () => throw new StatusCodeException(HttpStatusCode.Forbidden));\n            var http = new Mock<HttpContext>();\n            var post = new PostEventContext(Context.Application, http.Object)\n            {\n                Element = element,\n                Parameters = new EventParameters\n                {\n                    EventName = \"click\"\n                }\n            };\n            var response = new Mock<HttpResponse>();\n            response.SetupProperty(x => x.StatusCode);\n            http.Setup(x => x.Response).Returns(response.Object);\n            post.Http = http.Object;\n            var headers = new Mock<IHeaderDictionary>();\n            response.Setup(x => x.Headers).Returns(headers.Object);\n            var body = new Mock<Stream>();\n            response.Setup(x => x.Body).Returns(body.Object);\n            await PostEventHandler.RunEventHandler(post);\n            Assert.Equal((int)HttpStatusCode.Forbidden, response.Object.StatusCode);\n        }\n\n        [Fact]\n        public void ProcessSocketMessageWrongType()\n        {\n            var ms = new Mock<MemoryStream>();\n            var sr = new WebSocketReceiveResult(1, WebSocketMessageType.Close, true);\n            var result = MiddlewareCommon.ProcessWebSocketMessage<Element>(100, ms.Object, sr);\n            Assert.False(result.Item1);\n        }\n\n        [Fact]\n        public void ProcessSocketMessageWrongCount()\n        {\n            var ms = new Mock<MemoryStream>();\n            var sr = new WebSocketReceiveResult(101, WebSocketMessageType.Text, true);\n            var result = MiddlewareCommon.ProcessWebSocketMessage<Element>(100, ms.Object, sr);\n            Assert.False(result.Item1);\n        }\n\n        [Fact]\n        public void ProcessSocketMessageFailDeserialize()\n        {\n            var bytes = Encoding.UTF8.GetBytes(\"hello\");\n            using var ms = new MemoryStream(bytes);\n            var sr = new WebSocketReceiveResult(1, WebSocketMessageType.Text, true);\n            var result = MiddlewareCommon.ProcessWebSocketMessage<Element>(100, ms, sr);\n            Assert.False(result.Item1);\n        }\n\n        [Fact]\n        public async void ProcessAjaxBadParameters()\n        {\n            var http = new Mock<HttpContext>();\n            var request = new Mock<HttpRequest>();\n            var response = new Mock<HttpResponse>();\n            var query = new Mock<IQueryCollection>();\n            var headers = new Mock<IHeaderDictionary>();\n            http.Setup(x => x.Request).Returns(request.Object);\n            request.Setup(x => x.Query).Returns(query.Object);\n            StringValues values;\n            query.Setup(x => x.TryGetValue(\"doc\", out values)).Returns(false);\n            response.SetupProperty(x => x.StatusCode);\n            http.Setup(x => x.Response).Returns(response.Object);\n            response.Setup(x => x.Headers).Returns(headers.Object);\n            var body = new Mock<Stream>();\n            response.Setup(x => x.Body).Returns(body.Object);\n            await PostEventHandler.ProcessAjaxRequest(Context.Application, http.Object);\n        }\n\n        [Fact]\n        public void PostGetCompletionRuns()\n        {\n            var page = new MyPage();\n            var document = new Mock<Document>(page, 200);\n            var socket = new Mock<WebSocket>();\n            var post = new PostEventContext(Context.Application, Context.Http)\n            {\n                Document = document.Object,\n                Socket = socket.Object\n            };\n            var task = post.GetSocketCompletion();\n            Assert.NotNull(task);\n            document.Verify(x => x.GetSocketCompletion(socket.Object));\n        }\n\n        [Fact]\n        public void ServerEventCases()\n        {\n            var socket = new Mock<WebSocket>();\n            var status = ServerEventsController.CalculateServerEventsStatus(false, socket.Object);\n            Assert.Equal(ServerEventsStatus.Disabled, status);\n            status = ServerEventsController.CalculateServerEventsStatus(true, null);\n            Assert.Equal(ServerEventsStatus.Connecting, status);\n            status = ServerEventsController.CalculateServerEventsStatus(true, socket.Object);\n            Assert.Equal(ServerEventsStatus.Enabled, status);\n        }\n\n        [Fact]\n        public void SessionCloseIgnoresErrors()\n        {\n            using var connections = new Connections();\n            var connection = connections.CreateConnection(IPAddress.Loopback);\n            var session = new Session(connection);\n            session.Closing += Session_Closing;\n            session.Close();\n        }\n\n        private static void Session_Closing(object? sender, EventArgs e)\n        {\n            throw new InvalidOperationException();\n        }\n\n        [Fact]\n        public void LaraPageDefaultConstructor()\n        {\n            var page = new LaraPageAttribute();\n            Assert.True(string.IsNullOrEmpty(page.Address));\n        }\n\n        [Fact]\n        public void ServerLauncherUseDeveloperPage()\n        {\n            var lara = new Application();\n            var app = new Mock<IApplicationBuilder>();\n            ServerLauncher.ConfigureExceptions(app.Object, lara, new StartServerOptions\n            {\n                ShowExceptions = true\n            });\n            app.Verify(x => x.ApplicationServices, Times.Once);\n        }\n\n        [Fact]\n        public void LaraCreateConnection()\n        {\n            using var app = new Application();\n            app.CreateModeController(ApplicationMode.Default);\n            var x = app.CreateConnection(IPAddress.Loopback);\n            var ok = app.TryGetConnection(x.Id, out var y);\n            Assert.True(ok);\n            Assert.Same(x, y);\n            app.ClearEmptyConnection(x);\n            Assert.False(app.TryGetConnection(x.Id, out _));\n        }\n\n        [Fact]\n        public void SetDefaultErrorPage()\n        {\n            var published = Context.Application.GetPublished();\n            var x = new ErrorPages(published);\n            var page = new MyPage();\n            x.SetDefaultPage(HttpStatusCode.ExpectationFailed, () => page);\n            var result = x.GetPage(HttpStatusCode.ExpectationFailed);\n            var show = result.CreateInstance();\n            Assert.Same(page, show);\n\n            x.Remove(HttpStatusCode.ExpectationFailed);\n            result = x.GetPage(HttpStatusCode.ExpectationFailed);\n            Assert.Null(result);\n        }\n\n        [Fact]\n        public void DefaultErrorPageReturned()\n        {\n            var published = Context.Application.GetPublished();\n            var x = new ErrorPages(published);\n            var page = x.DefaultServerError();\n            Assert.True(page is DefaultErrorPage);\n        }\n\n        [Fact]\n        public void DefaultNotFoundReturned()\n        {\n            var x = new ErrorPages(Context.Application.GetPublished());\n            var page = x.DefaultNotFound();\n            Assert.True(page is DefaultErrorPage);\n        }\n\n        [Fact]\n        public void SequencerReorders()\n        {\n            var builder = new StringBuilder();\n            var x = new Sequencer();\n            Task.Run(async () =>\n            {\n                var ok = await x.WaitForTurn(2);\n                Assert.True(ok);\n                AddChars(builder, \"a\");\n            });\n            Task.Run(async () =>\n            {\n                var ok = await x.WaitForTurn(1);\n                Assert.True(ok);\n                AddChars(builder, \"b\");\n            });\n            Task.Run(async () =>\n            {\n                var ok = await x.WaitForTurn(3);\n                Assert.True(ok);\n                Assert.Equal(\"ba\", builder.ToString());\n            });\n        }\n\n        [Fact]\n        public void SequencerAborts()\n        {\n            var x = new Sequencer();\n            Task.Run(async () =>\n            {\n                var ok = await x.WaitForTurn(3);\n                Assert.False(ok);\n            });\n            Task.Run(async () =>\n            {\n                var ok = await x.WaitForTurn(1);\n                Assert.True(ok);\n                x.AbortAll();\n            });\n        }\n\n        private static void AddChars(StringBuilder builder, string text)\n        {\n            builder.Append(text);\n        }\n\n        [Fact]\n        public void ApplicationModeControllerProperties()\n        {\n            using var app = new Application();\n            app.CreateModeController(ApplicationMode.BrowserApp);\n            Assert.Equal(BrowserAppController.BrowserAppKeepAliveInterval, app.KeepAliveInterval);\n            Assert.True(app.AllowLocalhostOnly);\n        }\n\n        [Fact]\n        public async void LocalhostFilterPass()\n        {\n            var http = new Mock<HttpContext>();\n            var cnx = new Mock<ConnectionInfo>();\n            http.Setup(x => x.Connection).Returns(cnx.Object);\n            cnx.Setup(x => x.RemoteIpAddress).Returns(IPAddress.Loopback);\n            var next = new Mock<RequestDelegate>();\n            var logger = new Mock<ILogger<LocalhostFilter>>();\n            var filter = new LocalhostFilter(next.Object, logger.Object);\n            await filter.Invoke(http.Object);\n            next.Verify(x => x.Invoke(http.Object), Times.Once);\n        }\n\n        [Fact]\n        public void StartServerOptionsSettings()\n        {\n            var x = new StartServerOptions\n            {\n                Port = 12345,\n                IPAddress = IPAddress.Loopback\n            };\n            Assert.Equal(12345, x.Port);\n            Assert.Equal(IPAddress.Loopback, x.IPAddress);\n        }\n\n        private static readonly object _MyLock = new object();\n\n        [Fact]\n        [Obsolete]\n        public void LaraUiDefaultStatic()\n        {\n            lock (_MyLock)\n            {\n                LaraUI.Publish(\"/a\", new StaticContent(Encoding.UTF8.GetBytes(\"hello\"), \"text\"));\n                Assert.True(LaraUI.DefaultApplication.TryGetNode(\"/a\", out _));\n            }\n        }\n\n        [Fact]\n        [Obsolete]\n        public void LaraUiDefaultPage()\n        {\n            lock (_MyLock)\n            {\n                LaraUI.Publish(\"/b\", () => new MyPage());\n                Assert.True(LaraUI.DefaultApplication.TryGetNode(\"/b\", out _));\n                LaraUI.UnPublish(\"/b\");\n                Assert.False(LaraUI.DefaultApplication.TryGetNode(\"/b\", out _));\n            }\n        }\n\n        [Fact]\n        [Obsolete]\n        public void LaraUiDefaultService()\n        {\n            lock (_MyLock)\n            {\n                LaraUI.Publish(new WebComponentOptions\n                {\n                    ComponentTagName = \"lala-lala\",\n                    ComponentType = typeof(RemovableComponent)\n                });\n                Assert.True(LaraUI.DefaultApplication.TryGetComponent(\"lala-lala\", out _));\n                LaraUI.UnPublishWebComponent(\"lala-lala\");\n                Assert.False(LaraUI.DefaultApplication.TryGetComponent(\"lala-lala\", out _));\n            }\n        }\n\n        [Fact]\n        public void NoCurrentSessionParameters()\n        {\n            var inner = new InvalidOperationException(\"x\");\n            var x = new NoCurrentSessionException(\"a\", inner);\n            Assert.Same(inner, x.InnerException);\n            Assert.Equal(\"a\", x.Message);\n        }\n\n        [Fact]\n        public void NoCurrentSessionBase()\n        {\n            var x = new NoCurrentSessionException();\n            Assert.Null(x.InnerException);\n        }\n\n        [Fact]\n        public void LaraUiDocument()\n        {\n            var x = new Mock<IPageContext>();\n            var doc = new Document(new MyPage(), 100);\n            x.Setup(x1 => x1.Document).Returns(doc);\n            LaraUI.InternalContext.Value = x.Object;\n            Assert.Same(doc, LaraUI.Document);\n            Assert.Null(LaraUI.GetContextDocument(null));\n        }\n\n        [Fact]\n        public async Task SingleComponentPageTest()\n        {\n            var x = new Mock<IPageContext>();\n            var doc = new Document(new MyPage(), 100);\n            x.Setup(x1 => x1.Document).Returns(doc);\n            LaraUI.InternalContext.Value = x.Object;\n            var page = new SingleElementPage(() => new HtmlAnchorElement());\n            await page.OnGet();\n            Assert.Equal(1, doc.Body.ChildCount);\n            var child = doc.Body.GetChildAt(0);\n            Assert.NotNull(child as HtmlAnchorElement);\n        }\n\n        [Fact]\n        public void SetExtraData()\n        {\n            var app = new Application();\n            var http = new Mock<HttpContext>();\n            using var connections = new Connections();\n            var connection = connections.CreateConnection(IPAddress.Loopback);\n            var x = new PageContext(app, http.Object, connection);\n            x.SetExtraData(\"abc\");\n            Assert.Equal(\"abc\", x.JSBridge.EventData);\n            Assert.Same(connection.Session, x.Session);\n        }\n\n        [Fact]\n        public async void StopStops()\n        {\n            var counter = 0;\n            using var x = new Application();\n            var host = new Mock<IWebHost>();\n            x.SetHost(host.Object);\n            var token = CancellationToken.None;\n            host.Setup(x1 => x1.StopAsync(token)).Callback(() => counter++);\n\n            await x.Stop(token);\n            Assert.Equal(1, counter);\n        }\n\n        [Fact]\n        public void ReuseConnection()\n        {\n            var app = Context.Application;\n            var http = new Mock<HttpContext>();\n            var request = new Mock<HttpRequest>();\n            var cookies = new Mock<IRequestCookieCollection>();\n            http.Setup(x => x.Request).Returns(request.Object);\n            request.Setup(x => x.Cookies).Returns(cookies.Object);\n            var info = new Mock<ConnectionInfo>();\n            http.Setup(x => x.Connection).Returns(info.Object);\n            info.Setup(x => x.RemoteIpAddress).Returns(IPAddress.Loopback);\n\n            app.CreateModeController(ApplicationMode.Default);\n            var current = app.CreateConnection(IPAddress.Loopback);\n            var id = current.Id.ToString(GlobalConstants.GuidFormat, CultureInfo.InvariantCulture);\n            cookies.Setup(x => x.TryGetValue(GlobalConstants.CookieSessionId, out id)).Returns(true);\n            \n            var connection = PagePublished.GetConnection(app, http.Object);\n            Assert.Equal(current, connection);\n        }\n\n        [Fact]\n        public async void PageStatusCodeReturned()\n        {\n            var page = new MyStatusPage();\n            var http = new Mock<HttpContext>();\n            var response = new Mock<HttpResponse>();\n            var headers = new Mock<IHeaderDictionary>();\n            var body = new Mock<Stream>();\n            response.Setup(x => x.Headers).Returns(headers.Object);\n            http.Setup(x => x.Response).Returns(response.Object);\n            response.SetupProperty(x => x.StatusCode);\n            response.Setup(x => x.Body).Returns(body.Object);\n            var ok = await PagePublished.RunPage(Context.Application, http.Object, page, new LaraOptions());\n            Assert.False(ok);\n            Assert.Equal((int)HttpStatusCode.Unauthorized, http.Object.Response.StatusCode);\n        }\n        \n        [Fact]\n        public void ETagFormatCorrect()\n        {\n            const int hash = 12345;\n            var expected = \"\\\"\" + hash.ToString(CultureInfo.InvariantCulture) + \"\\\"\";\n            var etag = StaticContent.FormatETag(hash);\n            Assert.Equal(expected, etag);\n        }\n    }\n}\n"
  },
  {
    "path": "src/Tests/Middleware/ServerEventsTesting.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 8/2019\nAuthor: Pablo Carbonell\n*/\n\nusing Integrative.Lara.Tests.Main;\nusing Moq;\nusing System;\nusing System.Net.WebSockets;\nusing System.Threading;\nusing System.Threading.Tasks;\nusing Xunit;\n\nnamespace Integrative.Lara.Tests.Middleware\n{\n    public class ServerEventsTesting : DummyContextTesting\n    {\n        private readonly Document _document;\n        private readonly ServerEventsController _controller;\n        private readonly Mock<WebSocket> _socket;\n\n        public ServerEventsTesting()\n        {\n            var page = new MyPage();\n            _document = new Document(page, BaseModeController.DefaultKeepAliveInterval);\n            _controller = _document.GetServerEventsController();\n            _controller.ServerEventsOn();\n            _socket = new Mock<WebSocket>();\n        }\n\n        private async Task Initialize()\n        {\n            await _controller.GetSocketCompletion(_socket.Object);\n        }\n\n        [Fact]\n        public async void DiscardSocketDiscards()\n        {\n            await Initialize();\n            await _controller.ServerEventsOff();\n            Assert.Equal(ServerEventsStatus.Disabled, _controller.ServerEventsStatus);\n        }\n\n        [Fact]\n        public async void ServerEventRemainsOpen()\n        {\n            await Initialize();\n            Assert.True(_controller.SocketRemainsOpen(GlobalConstants.ServerSideEvent));\n        }\n\n        [Fact]\n        public async void ServerEventFlushFlushes()\n        {\n            await Initialize();\n            _document.OpenEventQueue();\n            _document.Body.AppendText(\"hello\");\n            Assert.NotEmpty(_document.GetQueue());\n            await _controller.ServerEventFlush();\n            Assert.Empty(_document.GetQueue());\n        }\n\n        [Fact]\n        public async void FlushWhenDisabledRejected()\n        {\n            await Initialize();\n            await _document.ServerEventsOff();\n            _document.OpenEventQueue();\n            _document.Body.AppendText(\"hi\");\n            await _controller.ServerEventsOff();\n            var found = false;\n            try\n            {\n                await _controller.ServerEventFlush();\n            }\n            catch (InvalidOperationException)\n            {\n                found = true;\n            }\n            Assert.True(found);\n        }\n\n        [Fact]\n        public async void NoFlushWhenQueueEmpty()\n        {\n            await Initialize();\n            _document.OpenEventQueue();\n            await _controller.ServerEventFlush();\n            _socket.Verify(x => x.SendAsync(It.IsAny<ArraySegment<byte>>(),\n                It.IsAny<WebSocketMessageType>(), It.IsAny<bool>(), It.IsAny<CancellationToken>()), Times.Never);\n        }\n\n        [Fact]\n        public async void NoFlushWhenWaitingConnection()\n        {\n            await Initialize();\n            await _controller.ServerEventsOff();\n            _controller.ServerEventsOn();\n            _document.OpenEventQueue();\n            _document.Body.AppendText(\"hi\");\n            _controller.ServerEventsOn();\n            await _controller.ServerEventFlush();\n            _socket.Verify(x => x.SendAsync(It.IsAny<ArraySegment<byte>>(),\n                It.IsAny<WebSocketMessageType>(), It.IsAny<bool>(), It.IsAny<CancellationToken>()), Times.Never);\n        }\n\n        [Fact]\n        public async void AcceptingSocketFlushesPending()\n        {\n            _document.OpenEventQueue();\n            _document.Body.AppendText(\"hello\");\n            _controller.ServerEventsOn();\n            await _controller.ServerEventFlush();\n            Assert.NotEmpty(_document.GetQueue());\n            await _document.GetSocketCompletion(_socket.Object);\n            Assert.Empty(_document.GetQueue());\n        }\n\n        [Fact]\n        public async void ServerEventFlushes()\n        {\n            _document.OpenEventQueue();\n            _controller.ServerEventsOn();\n            await _controller.GetSocketCompletion(_socket.Object);\n            using (_document.StartServerEvent())\n            {\n                _document.Body.AppendText(\"hello\");\n                Assert.NotEmpty(_document.GetQueue());\n            }\n            Assert.Empty(_document.GetQueue());\n        }\n\n        [Fact]\n        public async void ServerEventsFlushesPartialChanges()\n        {\n            _document.OpenEventQueue();\n            _controller.ServerEventsOn();\n            await _controller.GetSocketCompletion(_socket.Object);\n            using var access = _document.StartServerEvent();\n            _document.Body.AppendText(\"hello\");\n            Assert.NotEmpty(_document.GetQueue());\n            await access.FlushPartialChanges();\n            Assert.Empty(_document.GetQueue());\n        }\n\n        [Fact]\n        public async void ServerEventFailDisposed()\n        {\n            _document.OpenEventQueue();\n            _controller.ServerEventsOn();\n            await _controller.GetSocketCompletion(_socket.Object);\n            var access = _document.StartServerEvent();\n            access.Dispose();\n            var found = false;\n            try\n            {\n                await access.FlushPartialChanges();\n            }\n            catch (InvalidOperationException)\n            {\n                found = true;\n            }\n            Assert.True(found);\n        }\n    }\n}\n"
  },
  {
    "path": "src/Tests/Middleware/ToolsTesting.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 9/2019\nAuthor: Pablo Carbonell\n*/\n\nusing Integrative.Lara.Tests.Main;\nusing Moq;\nusing System;\nusing System.Net;\nusing Xunit;\n\nnamespace Integrative.Lara.Tests.Middleware\n{\n    public class ToolsTesting : DummyContextTesting\n    {\n        [Fact]\n        public void DocumentLocalException()\n        {\n#pragma warning disable CS8625 // Cannot convert null literal to non-nullable reference type.\n            LaraUI.InternalContext.Value = null;  // on purpose for testing purposes\n#pragma warning restore CS8625 // Cannot convert null literal to non-nullable reference type.\n            var error = false;\n            try\n            {\n                // ReSharper disable once ObjectCreationAsStatement\n#pragma warning disable CA1806 // Do not ignore method results\n                new DocumentLocal<int>\n                {\n                    Value = 5\n                };\n#pragma warning restore CA1806 // Do not ignore method results\n            }\n            catch (InvalidOperationException)\n            {\n                error = true;\n            }\n            Assert.True(error);\n        }\n\n        [Fact]\n        public void SessionLocalException()\n        {\n#pragma warning disable CS8625 // Cannot convert null literal to non-nullable reference type.\n            LaraUI.InternalContext.Value = null;\n#pragma warning restore CS8625 // Cannot convert null literal to non-nullable reference type.\n            var error = false;\n            try\n            {\n                // ReSharper disable once ObjectCreationAsStatement\n#pragma warning disable CA1806 // Do not ignore method results\n                new SessionLocal<int>\n                {\n                    Value = 5\n                };\n#pragma warning restore CA1806 // Do not ignore method results\n            }\n            catch (InvalidOperationException)\n            {\n                error = true;\n            }\n            Assert.True(error);\n        }\n\n        [Fact]\n        public void DocumentLocalDefaultValue()\n        {\n            var local = BuildLocal();\n            Assert.Equal(0, local.Value);\n        }\n\n        [Fact]\n        public void DocumentLocalWrites()\n        {\n            var local = BuildLocal();\n            local.Value = 3;\n            Assert.Equal(3, local.Value);\n        }\n\n        [Fact]\n        public void DocumentLocalSkipsReplacement()\n        {\n            var local = BuildLocal();\n            local.Value = 6;\n            local.Value = 6;\n            Assert.Equal(6, local.Value);\n        }\n\n        [Fact]\n        public void DocumentLocalReplaces()\n        {\n            var local = BuildLocal();\n            local.Value = 7;\n            local.Value = 8;\n            Assert.Equal(8, local.Value);\n        }\n\n        [Fact]\n        public async void DocumentLocalUnloads()\n        {\n            var local = BuildLocal(out var document);\n            local.Value = 5;\n            await document.NotifyUnload();\n            Assert.Equal(0, local.Value);\n        }\n\n        private static DocumentLocal<int> BuildLocal()\n        {\n            return BuildLocal(out _);\n        }\n\n        private static DocumentLocal<int> BuildLocal(out Document document)\n        {\n            var context = new Mock<IPageContext>();\n            document = new Document(new MyPage(), BaseModeController.DefaultKeepAliveInterval);\n            context.Setup(x => x.Document).Returns(document);\n            LaraUI.InternalContext.Value = context.Object;\n            return new DocumentLocal<int>();\n        }\n\n        private static SessionLocal<int> GetSessionLocal(out Session session)\n        {\n            var context = new Mock<IPageContext>();\n            var connection = new Connection(Guid.Parse(\"{B064124D-154D-4F49-89CF-CFC117509807}\"), IPAddress.Loopback);\n            session = new Session(connection);\n            context.Setup(x => x.Session).Returns(session);\n            LaraUI.InternalContext.Value = context.Object;\n            return new SessionLocal<int>();\n        }\n\n        private static SessionLocal<int> GetSessionLocal()\n        {\n            return GetSessionLocal(out _);\n        }\n\n        [Fact]\n        public void SessionLocalDefaultValue()\n        {\n            var local = GetSessionLocal();\n            Assert.Equal(0, local.Value);\n        }\n\n        [Fact]\n        public void SessionLocalWrites()\n        {\n            var local = GetSessionLocal();\n            local.Value = 3;\n            Assert.Equal(3, local.Value);\n        }\n\n        [Fact]\n        public void SessionLocalSkipsReplacement()\n        {\n            var local = GetSessionLocal();\n            local.Value = 6;\n            local.Value = 6;\n            Assert.Equal(6, local.Value);\n        }\n\n        [Fact]\n        public void SessionLocalReplaces()\n        {\n            var local = GetSessionLocal();\n            local.Value = 7;\n            local.Value = 8;\n            Assert.Equal(8, local.Value);\n        }\n\n        [Fact]\n        public void SessionLocalUnloads()\n        {\n            var local = GetSessionLocal(out var session);\n            local.Value = 5;\n            session.Close();\n            Assert.Equal(0, local.Value);\n        }\n\n        [Fact]\n        public void SessionLocalService()\n        {\n            var context = new Mock<IWebServiceContext>();\n            var connection = new Connection(Guid.Parse(\"{B064124D-154D-4F49-89CF-CFC117509807}\"), IPAddress.Loopback);\n            Session? session = new Session(connection);\n            context.Setup(x => x.TryGetSession(out session)).Returns(true);\n            LaraUI.InternalContext.Value = context.Object;\n            var local = new SessionLocal<int>();\n            Assert.Equal(0, local.Value);\n            local.Value = 11;\n            Assert.Equal(11, local.Value);\n        }\n\n        [Fact]\n        public void SmaValueNullTrue()\n        {\n            Assert.True(LaraTools.SameValue((string?) null, null));\n        }\n\n        [Fact]\n        [Obsolete(\"Old methods\")]\n        public void LaraFlushEvent()\n        {\n            var x = new HtmlInputElement();\n            var builder = new LaraBuilder(x);\n            builder.FlushEvent(\"click\");\n            x.NotifyEvent(\"click\");\n            Assert.True(x.Events.TryGetValue(\"click\", out var ev));\n            Assert.Equal(\"click\", ev!.EventName);\n        }\n    }\n}\n"
  },
  {
    "path": "src/Tests/Middleware/WebServicesTesting.cs",
    "content": "﻿/*\nCopyright (c) 2019-2021 Integrative Software LLC\nCreated: 8/2019\nAuthor: Pablo Carbonell\n*/\n\nusing System;\nusing System.Net;\nusing System.Runtime.Serialization;\nusing System.Text;\nusing System.Threading.Tasks;\nusing Xunit;\n\nnamespace Integrative.Lara.Tests.Middleware\n{\n    internal class RemovablePage : IPage\n    {\n        public Task OnGet() => Task.CompletedTask;\n    }\n\n    internal class RemovableComponent : WebComponent\n    {\n        public RemovableComponent() : base(\"x-removable\")\n        {\n        }\n    }\n\n    internal class RemovableService : IWebService\n    {\n        public Task<string> Execute() => Task.FromResult(string.Empty);\n    }\n\n    internal class MyBinary : IBinaryService\n    {\n        public Task<byte[]> Execute()\n        {\n            throw new NotImplementedException();\n        }\n    }\n\n    public class WebServicesTesting : DummyContextTesting\n    {\n        [Fact]\n        public void LaraJsonSerializeType()\n        {\n            var tool = new LaraJson();\n            var data = new MyData\n            {\n                Counter = 5\n            };\n            var json = tool.Stringify(data, typeof(MyData));\n            var back = tool.Parse<MyData>(json);\n            Assert.NotNull(back);\n            Assert.Equal(data.Counter, back!.Counter);\n        }\n\n        [DataContract]\n        private class MyData\n        {\n            [DataMember]\n            public int Counter { get; set; }\n        }\n\n        [Fact]\n        public void TryParseCatchesSerializationErrors()\n        {\n            var tool = new LaraJson();\n            var ok = tool.TryParse<MyData>(\"caca\", out _);\n            Assert.False(ok);\n        }\n\n        [Fact]\n        public void ParseThrowsBadRequestException()\n        {\n            var tool = new LaraJson();\n            var found = false;\n            try\n            {\n                tool.Parse<MyData>(\"caca\");\n            }\n            catch (StatusCodeException e)\n            {\n                found = true;\n                Assert.Equal(HttpStatusCode.BadRequest, e.StatusCode);\n            }\n            Assert.True(found);\n        }\n\n        [Fact]\n        public void StatusCodeExceptionDefaultCode()\n        {\n            var e = new StatusCodeException();\n            Assert.Equal(HttpStatusCode.InternalServerError, e.StatusCode);\n        }\n\n        [Fact]\n        public void StatusCodeExceptionMessageConstructor()\n        {\n            var e = new StatusCodeException(\"hello\");\n            Assert.Equal(\"hello\", e.Message);\n        }\n\n        [Fact]\n        public void StatusCodeExceptionCodeAndMessage()\n        {\n            var e = new StatusCodeException(HttpStatusCode.Conflict, \"lala\");\n            Assert.Equal(HttpStatusCode.Conflict, e.StatusCode);\n            Assert.Equal(\"lala\", e.Message);\n        }\n\n        [Fact]\n        public void StatusForbiddenExceptionDefault()\n        {\n            var e = new StatusForbiddenException();\n            Assert.Equal(HttpStatusCode.Forbidden, e.StatusCode);\n        }\n\n        [Fact]\n        public void StatusForbiddenMessage()\n        {\n            var e = new StatusForbiddenException(\"lala\");\n            Assert.Equal(\"lala\", e.Message);\n            Assert.Equal(HttpStatusCode.Forbidden, e.StatusCode);\n        }\n\n        [Fact]\n        public void StatusForbiddenMessageInner()\n        {\n            var inner = new InvalidDataContractException();\n            var e = new StatusForbiddenException(\"lala\", inner);\n            Assert.Equal(\"lala\", e.Message);\n            Assert.Equal(HttpStatusCode.Forbidden, e.StatusCode);\n            Assert.Same(inner, e.InnerException);\n        }\n\n        [Fact]\n        public void LaraWebServiceAttributeDefaults()\n        {\n            var x = new LaraWebServiceAttribute();\n            Assert.Equal(\"POST\", x.Method);\n            Assert.Equal(\"application/json\", x.ContentType);\n        }\n\n        [Fact]\n        public void LaraWebServiceAttributeProperties()\n        {\n            var x = new LaraWebServiceAttribute\n            {\n                Address = \"/\",\n                ContentType = \"lala\",\n                Method = \"PUT\"\n            };\n            Assert.Equal(\"/\", x.Address);\n            Assert.Equal(\"lala\", x.ContentType);\n            Assert.Equal(\"PUT\", x.Method);\n        }\n\n        [LaraWebServiceAttribute(Address = \"/myWS\")]\n        private class MyWebService : IWebService\n        {\n            public Task<string> Execute()\n            {\n                return Task.FromResult(string.Empty);\n            }\n        }\n\n        [LaraPageAttribute(\"/myPage\")]\n        private class MyPage : IPage\n        {\n            public Task OnGet()\n            {\n                return Task.CompletedTask;\n            }\n        }\n\n        [Fact]\n        public void PublishAssembliesService()\n        {\n            const string address = \"/mydummy1\";\n            using var app = new Application();\n            app.PublishService(new WebServiceContent\n            {\n                Address = address,\n                Method = \"POST\",\n                Factory = () => new DummyWs()\n            });\n            var combined = Published.CombinePathMethod(address, \"POST\");\n            var found = app.TryGetNode(combined, out var item);\n            Assert.True(found);\n            var service = item as WebServicePublished;\n            Assert.NotNull(service);\n            Assert.NotNull(service!.Factory());\n        }\n\n        private class DummyWs : IWebService\n        {\n            public Task<string> Execute() => Task.FromResult(string.Empty);\n        }\n\n        [Fact]\n        public void PublishAssembliesBinaryService()\n        {\n            const string address = \"/mydummy2\";\n            using var app = new Application();\n            app.PublishService(new BinaryServiceContent\n            {\n                Address = address,\n                Method = \"POST\",\n                Factory = () => new DummyBinaryWs()\n            });\n            var combined = Published.CombinePathMethod(address, \"POST\");\n            var found = app.TryGetNode(combined, out var item);\n            Assert.True(found);\n            var service = item as BinaryServicePublished;\n            Assert.NotNull(service);\n            Assert.NotNull(service!.Factory());\n        }\n\n        private class DummyBinaryWs : IBinaryService\n        {\n            public Task<byte[]> Execute() => Task.FromResult(Array.Empty<byte>());\n        }\n\n        [Fact]\n        public void PublishAssembliesPage()\n        {\n            const string address = \"/mypapapapa\";\n            using var app = new Application();\n            app.PublishPage(address, () => new MyPage());\n            var found = app.TryGetNode(address, out var item);\n            Assert.True(found);\n            var page = item as PagePublished;\n            Assert.NotNull(page);\n            Assert.NotNull(page!.CreateInstance());\n        }\n\n        [Fact]\n        [Obsolete]\n        public void UnpublishWebservice()\n        {\n            const string address = \"/mylalala\";\n            using var app = new Application();\n            LaraUI.Publish(new WebServiceContent\n            {\n                Address = address,\n                Factory = () => new MyWebService()\n            });\n            LaraUI.UnPublish(\"/mylalala\", \"POST\");\n            var combined = Published.CombinePathMethod(address, \"POST\");\n            Assert.False(app.TryGetNode(combined, out _));\n        }\n\n        [Fact]\n        public void VerifyTypeException()\n        {\n            var found = false;\n            try\n            {\n                AssembliesReader.VerifyType(typeof(MyPage), \"a\", typeof(Element));\n            }\n            catch (InvalidOperationException)\n            {\n                found = true;\n            }\n            Assert.True(found);\n        }\n\n        [Fact]\n        public void ClearAllRemovesPublished()\n        {\n            using var app = new Application();\n            app.PublishPage(_pageName, () => new RemovablePage());\n            app.PublishService(new WebServiceContent\n            {\n                Address = _serviceName,\n                Factory = () => new RemovableService(),\n                Method = \"GET\"\n            });\n            var bytes = Encoding.UTF8.GetBytes(\"hello\");\n            app.PublishFile(_fileName, new StaticContent(bytes));\n            app.PublishComponent(new WebComponentOptions\n            {\n                ComponentTagName = _componentName,\n                ComponentType = typeof(RemovableComponent)\n            });\n            VerifyFound(app, true);\n            app.ClearAllPublished();\n            VerifyFound(app, false);\n            app.PublishAssemblies();\n        }\n\n        private readonly string _serviceName = \"/removableService\" + GetRandom();\n        private readonly string _componentName = \"x-removable\" + GetRandom();\n        private readonly string _pageName = \"/removablePage\" + GetRandom();\n        private readonly string _fileName = \"/removableFile\" + GetRandom();\n\n        private static string GetRandom()\n        {\n            var random = new Random();\n            return random.Next(0, int.MaxValue).ToString();\n        }\n\n        // ReSharper disable once ParameterOnlyUsedForPreconditionCheck.Local\n        private void VerifyFound(Application app, bool found)\n        {\n            Assert.Equal(found, app.TryGetNode(_pageName, out _));\n            Assert.Equal(found, app.TryGetNode(_fileName, out _));\n            Assert.Equal(found, app.TryGetNode(_serviceName, out _));\n            Assert.Equal(found, app.TryGetComponent(_componentName, out _));\n        }\n\n        [Fact]\n        public void BinaryServiceContentProperties()\n        {\n            var x = new BinaryServiceContent\n            {\n                Address = \"a\",\n                ContentType = \"b\",\n                Method = \"c\",\n                Factory = () => new MyBinary()\n            };\n            Assert.Equal(\"a\", x.Address);\n            Assert.Equal(\"b\", x.ContentType);\n            Assert.Equal(\"c\", x.Method);\n            var result = x.Factory() as MyBinary;\n            Assert.NotNull(result);\n        }\n\n        [Fact]\n        public void BinaryServiceAttribute()\n        {\n            var x = new LaraBinaryServiceAttribute\n            {\n                Address = \"a\",\n                ContentType = \"b\",\n                Method = \"c\"\n            };\n            Assert.Equal(\"a\", x.Address);\n            Assert.Equal(\"b\", x.ContentType);\n            Assert.Equal(\"c\", x.Method);\n        }\n    }\n}\n"
  },
  {
    "path": "src/Tests/Tests.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <TargetFramework>net5.0</TargetFramework>\n\n    <IsPackable>false</IsPackable>\n\n    <RootNamespace>Integrative.Lara.Tests</RootNamespace>\n\n    <Platforms>x64;AnyCPU</Platforms>\n\n    <LangVersion>latest</LangVersion>\n    <Nullable>enable</Nullable>\n  </PropertyGroup>\n\n  <ItemGroup>\n    <None Remove=\"Assets\\Compressible.bmp\" />\n    <None Remove=\"Assets\\pexels-photo-248673.jpeg\" />\n  </ItemGroup>\n\n  <ItemGroup>\n    <EmbeddedResource Include=\"Assets\\Compressible.bmp\" />\n    <EmbeddedResource Include=\"Assets\\pexels-photo-248673.jpeg\" />\n  </ItemGroup>\n\n  <ItemGroup>\n    <PackageReference Include=\"coverlet.msbuild\" Version=\"3.1.0\">\n      <PrivateAssets>all</PrivateAssets>\n      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>\n    </PackageReference>\n    <PackageReference Include=\"Microsoft.AspNetCore\" Version=\"2.2.0\" />\n    <PackageReference Include=\"Microsoft.NET.Test.Sdk\" Version=\"17.0.0\" />\n    <PackageReference Include=\"Moq\" Version=\"4.16.1\" />\n    <PackageReference Include=\"xunit\" Version=\"2.4.1\" />\n    <PackageReference Include=\"xunit.runner.visualstudio\" Version=\"2.4.3\">\n      <PrivateAssets>all</PrivateAssets>\n      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>\n    </PackageReference>\n  </ItemGroup>\n\n  <ItemGroup>\n    <ProjectReference Include=\"..\\LaraUI\\LaraUI.csproj\" />\n  </ItemGroup>\n\n  <ItemGroup>\n    <Reference Include=\"System.Net.Http\" />\n  </ItemGroup>\n\n</Project>\n"
  }
]