diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json new file mode 100644 index 0000000..6a8c8a1 --- /dev/null +++ b/.config/dotnet-tools.json @@ -0,0 +1,12 @@ +{ + "version": 1, + "isRoot": true, + "tools": { + "fallout.cli": { + "version": "11.0.18", + "commands": [ + "fallout" + ] + } + } +} diff --git a/.fallout/build.schema.json b/.fallout/build.schema.json new file mode 100644 index 0000000..82690d1 --- /dev/null +++ b/.fallout/build.schema.json @@ -0,0 +1,145 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "definitions": { + "Host": { + "type": "string", + "enum": [ + "AppVeyor", + "AzurePipelines", + "Bamboo", + "Bitbucket", + "Bitrise", + "GitHubActions", + "GitLab", + "Jenkins", + "Rider", + "SpaceAutomation", + "TeamCity", + "Terminal", + "TravisCI", + "VisualStudio", + "VSCode" + ] + }, + "ExecutableTarget": { + "type": "string", + "enum": [ + "Clean", + "Compile", + "CreatePackage", + "Default", + "Package", + "PrePublish", + "Publish", + "PublishPackage", + "PublishPreRelease", + "PublishRelease", + "Restore", + "RunUnitTests" + ] + }, + "Verbosity": { + "type": "string", + "description": "", + "enum": [ + "Verbose", + "Normal", + "Minimal", + "Quiet" + ] + }, + "FalloutBuild": { + "properties": { + "Continue": { + "type": "boolean", + "description": "Indicates to continue a previously failed build attempt" + }, + "Help": { + "type": "boolean", + "description": "Shows the help text for this build assembly" + }, + "Host": { + "description": "Host for execution. Default is 'automatic'", + "$ref": "#/definitions/Host" + }, + "NoLogo": { + "type": "boolean", + "description": "Disables displaying the NUKE logo" + }, + "Partition": { + "type": "string", + "description": "Partition to use on CI" + }, + "Plan": { + "type": "boolean", + "description": "Shows the execution plan (HTML)" + }, + "Profile": { + "type": "array", + "description": "Defines the profiles to load", + "items": { + "type": "string" + } + }, + "Root": { + "type": "string", + "description": "Root directory during build execution" + }, + "Skip": { + "type": "array", + "description": "List of targets to be skipped. Empty list skips all dependencies", + "items": { + "$ref": "#/definitions/ExecutableTarget" + } + }, + "Target": { + "type": "array", + "description": "List of targets to be invoked. Default is '{default_target}'", + "items": { + "$ref": "#/definitions/ExecutableTarget" + } + }, + "Verbosity": { + "description": "Logging verbosity during build execution. Default is 'Normal'", + "$ref": "#/definitions/Verbosity" + }, + "BuildProjectFile": { + "type": [ + "null", + "string" + ], + "description": "Path to the build project (.csproj) relative to the repository root. Defaults to 'build/_build.csproj' when unset. Read by the Fallout global tool's in-tool runner." + } + } + } + }, + "allOf": [ + { + "properties": { + "AngleSharpVersion": { + "type": "string", + "description": "AngleSharp package version override (e.g. 1.0.0 for compatibility checks)" + }, + "Configuration": { + "type": "string", + "enum": [ + "Debug", + "Release" + ], + "description": "Configuration to build - Default is 'Debug' (local) or 'Release' (server)" + }, + "ReleaseNotesFilePath": { + "type": "string", + "description": "ReleaseNotesFilePath - To determine the SemanticVersion" + }, + "Solution": { + "type": "string", + "description": "Path to a solution file that is automatically loaded" + } + } + }, + { + "$ref": "#/definitions/FalloutBuild" + } + ] +} diff --git a/.fallout/parameters.json b/.fallout/parameters.json new file mode 100644 index 0000000..4dcccaa --- /dev/null +++ b/.fallout/parameters.json @@ -0,0 +1,4 @@ +{ + "$schema": "./build.schema.json", + "Solution": "src/AngleSharp.Js.sln" +} \ No newline at end of file diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index 1f114d6..b74e64f 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1,4 +1,4 @@ # These are supported funding model platforms github: FlorianRappl -custom: https://salt.bountysource.com/teams/anglesharp +custom: ['https://www.paypal.me/FlorianRappl', 'https://buymeacoffee.com/florianrappl'] diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e224ca8..7841368 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,7 +18,7 @@ jobs: id: check_job run: | echo "value: ${{ env.DOCS_PATH != null && github.ref == env.DOCS_BRANCH }}" - echo "::set-output name=value::${{ env.DOCS_PATH != null && github.ref == env.DOCS_BRANCH }}" + echo "value=${{ env.DOCS_PATH != null && github.ref == env.DOCS_BRANCH }}" >> "$GITHUB_OUTPUT" documentation: needs: [can_document] @@ -26,12 +26,12 @@ jobs: if: needs.can_document.outputs.value == 'true' steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v7 - name: Use Node.js - uses: actions/setup-node@v1 + uses: actions/setup-node@v7 with: - node-version: "14.x" + node-version: "20.x" registry-url: 'https://registry.npmjs.org' - name: Install Dependencies @@ -48,23 +48,33 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v7 + + - uses: actions/setup-dotnet@v6 + with: + dotnet-version: | + 10.0.x - name: Build - run: ./build.sh + run: ./build.sh -AngleSharpVersion 1.5.0 windows: runs-on: windows-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v7 + + - uses: actions/setup-dotnet@v6 + with: + dotnet-version: | + 10.0.x - name: Build run: | if ($env:GITHUB_REF -eq "refs/heads/main") { - .\build.ps1 -Target Publish + .\build.ps1 -Target Publish -AngleSharpVersion 1.5.0 } elseif ($env:GITHUB_REF -eq "refs/heads/devel") { - .\build.ps1 -Target PrePublish + .\build.ps1 -Target PrePublish -AngleSharpVersion 1.5.0 } else { - .\build.ps1 + .\build.ps1 -AngleSharpVersion 1.5.0 } diff --git a/.gitignore b/.gitignore index 39a64c7..1079ab1 100644 --- a/.gitignore +++ b/.gitignore @@ -194,3 +194,6 @@ pip-log.txt # Mac crap .DS_Store + +# Fallout build tool +.fallout/temp \ No newline at end of file diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..907e072 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,223 @@ +# AGENTS.md + +Guidance for AI coding agents working in this repository. `CLAUDE.md` imports this file, so +keep it the single source of truth and every agent reads the same instructions. + +AngleSharp.Js is an AngleSharp plugin that exposes the AngleSharp DOM to the +[Jint](https://github.com/sebastienros/jint) JavaScript engine. There is no code generation +and no hand-written binding layer: the whole JS surface is derived at runtime by reflecting +over AngleSharp's `[DomName]`-style attributes. + +## Commands + +The orchestrator is [Fallout](https://fallout.build) (`build/Build.cs`), the maintained hard fork +of NUKE. `build.ps1` / `build.sh` (`build.cmd` forwards to either) provision the SDK, then +`dotnet tool restore` + `dotnet fallout`; the CLI itself is pinned in +`.config/dotnet-tools.json` and resolves `build/_build.csproj` by convention. Default target is +`RunUnitTests`. + +```powershell +.\build.ps1 # restore, compile, run the full test suite +.\build.ps1 -Target Compile # other targets: Clean Restore Compile RunUnitTests +.\build.ps1 -Target Package # CreatePackage Package PrePublish Publish +``` + +For the normal edit/test loop use the SDK directly — much faster than the Fallout bootstrap: + +```powershell +dotnet build src/AngleSharp.Js.sln +dotnet test src/AngleSharp.Js.Tests/AngleSharp.Js.Tests.csproj -f net10.0 +dotnet test src/AngleSharp.Js.Tests/AngleSharp.Js.Tests.csproj -f net10.0 --filter "FullyQualifiedName~InstanceOfTests" +dotnet test src/AngleSharp.Js.Tests/AngleSharp.Js.Tests.csproj -f net10.0 --filter "Name=WindowIsAnInstanceOfWindow" +``` + +- Always pass `-f net10.0` when iterating. On Windows the test project also targets `net462` + and `net472`, so omitting it runs everything three times. +- `TreatWarningsAsErrors` is on (`src/Directory.Build.props`) — a warning breaks the build. + There is no separate lint step; the compiler is it. +- Package versions are centralized in `src/Directory.Packages.props` (CPM is on), so a + `PackageReference` in a csproj carries no `Version`. AngleSharp and Jint are deliberately + stated as ranges: `dotnet pack` publishes them verbatim as the package's dependency ranges. +- The package version is parsed from the top entry of `CHANGELOG.md` (`ReleaseNotesParser`) + and passed to the build as `-p:Version`. Release-worthy changes get a `CHANGELOG.md` line. + `AssemblyVersion` is pinned to `1.0.0.0` so the strong-name identity survives that. +- `RunUnitTests` runs the suite twice, differing only in a `prefetched` environment variable + that nothing in this repo currently reads — a single run is equivalent locally. +- There is deliberately no `global.json` — the repository does not restrict the SDK version. The + bootstrap scripts use the STS channel, CI installs 10.0.x. + +## Architecture + +### Wiring into AngleSharp + +`WithJs()` (`JsConfigurationExtensions`) registers four things: `JsScriptingService`, an +`EventAttributeObserver` (inline `onclick="…"` attributes), a `JsNavigationHandler` +(`javascript:` URLs), and a default `INavigator`. `WithEventLoop()` adds `JsEventLoop`, a +dedicated background thread that serializes script tasks — most DOM-mutating tests need it. + +`JsScriptingService` is AngleSharp's `IScriptingService`. It keeps one `EngineInstance` per +`IWindow` in a `ConditionalWeakTable`, and accepts `text/javascript`, `module` and +`importmap`. **The set of assemblies whose types get exposed is derived from the services +registered in the browsing context** (every `AngleSharp*` assembly behind a registered +service), so adding `WithCss()` or `WithIo()` widens the JS DOM surface. + +`EngineInstance` owns the Jint `Engine` and the per-engine caches. On construction it wraps +the window, walks each library's exported types to publish constructors, constructor +functions and instances, then copies the window's own properties onto the Jint global and +points the global's prototype at the window prototype. `RunScript` locks on the engine. + +### The proxy objects (`src/AngleSharp.Js/Proxies`) + +- **`DomNodeInstance`** — the JS object standing for one CLR DOM object. Identity is + preserved by `ReferenceCache` (a `ConditionalWeakTable`), so the same node always maps to + the same JS object. Only indexers become own properties; interface members live on the + prototype. Writes to the window instance are mirrored onto the Jint global. +- **`DomPrototypeInstance`** — one per DOM type. Members are registered lazily in + `Initialize()`: reflecting the full type tree is the expensive part and most prototypes + are never touched. Also owns the numeric/string indexers and extension members pulled from + `[DomExposed]` types. +- **`DomConstructorInstance`** — the exposed constructor (`HTMLDivElement`, …). The + prototype holds it, so the object script reads off the window and the one an instance + reports as its `constructor` are the same. It answers `Symbol.hasInstance` itself, because + the prototype chain cannot cover mixins (`ParentNode`) or generic collections + (`IHtmlCollection`). +- **`DomConstructorDescriptor`** — the property a constructor is published under. Every + exported type gets one, so the constructor object behind it is built only on first read. + +### Type → prototype canonicalization + +This is the subtlety most changes trip over. Instances are created from internal concrete +classes (`HtmlDivElement`), constructors are built from exported interfaces +(`IHtmlDivElement`), and many element classes carry no `[DomName]` of their own (a `b` +element is just an `HTMLElement`). `PrototypeTypeCache` folds all of these onto the topmost +class that defines a given DOM name, which is what makes `instanceof` and +`Object.getPrototypeOf(div) === HTMLDivElement.prototype` hold. Enums and generic types are +deliberately excluded from folding. + +### Caching rules (`src/AngleSharp.Js/Cache`) + +Split by what the cached value depends on: + +- Process-wide statics — `CreatorCache`, `PrototypeTypeCache`, `MethodCache`, `ScriptCache` + (capped at 32 prepared scripts) — hold values determined by a type, method or source alone. +- Per-engine — `PrototypeCache`, `ReferenceCache` — hold anything that is a `JsValue` or + otherwise bound to one engine, plus anything depending on the engine's library set. + +Anything shared across engines must be thread-safe; the existing caches all use +`Concurrent*` collections. + +### Marshalling + +`EngineExtensions.ToJsValue` and `JsValueExtensions.FromJsValue` / `.As(type, …)` convert +values. `EngineExtensions.BuildArgs` maps JS arguments onto a CLR signature and handles the +DOM-specific cases: an implicit leading `IWindow` parameter, optional parameters, `params` +arrays, and `[DomInitDict]` option objects expanded into positional arguments. + +## Performance + +**Performance is a primary concern in this repository, not an afterthought.** Jint is an +interpreter and every DOM access from script crosses this binding layer, so the binding must +never be the bottleneck. Reflection is the whole mechanism here, and reflection is slow — the +work of the last release cycle was largely making it happen once instead of once per call +(`perf/cache-interop-reflection`, `perf/cache-parsed-scripts`, `perf/lazy-dom-prototypes`, +`perf/lazy-dom-constructors`). Hold new code to that standard. + +The two techniques that carry most of the win: + +- **Cache anything derived from a type, method or source string.** It cannot change for the + lifetime of the process. `MethodDescription.Of` resolves a signature once; + `CreatorCache.GetConstructorDefinition` caches even the *null* answer, because most + exported types are not constructors; `ScriptCache` keeps Jint's `Prepared")); +``` + +JavaScript can call delegates and access the public members of objects exposed this way. For +advanced integration, `GetOrCreateJint(document)` returns the document's Jint `Engine`, which +lets host code inspect JavaScript values or invoke JavaScript functions directly. + +Registering a `JsScriptingService` directly does not add the auxiliary integration that +`WithJs()` supplies: inline event-handler attributes and `javascript:` URL navigation. + +### Capture `console.log` + +`console.log` forwards its arguments to an `IConsoleLogger` registered for the browsing +context. Provide one with `WithConsoleLogger`: + +```cs +var configuration = Configuration.Default .WithJs() - .WithConsoleLogger(ctx => new MyConsoleLogger(ctx)); + .WithConsoleLogger(_ => new ApplicationConsoleLogger()); +``` + +Implement `IConsoleLogger.Log(Object[] values)` to send the values to your application's +logging system. Without a logger, calls to `console.log` do not produce output. + +For example, a minimal logger can forward values to `System.Diagnostics`: + +```cs +sealed class ApplicationConsoleLogger : IConsoleLogger +{ + public void Log(Object[] values) + { + Debug.WriteLine(String.Join(" ", values)); + } +} ``` -in the previous example `MyConsoleLogger` refers to a class implementing the `IConsoleLogger` interface. Examples of classes implementing this interface are available in our [samples repository](https://github.com/AngleSharp/AngleSharp.Samples). +## DOM APIs and integration points + +AngleSharp.Js exposes AngleSharp DOM interfaces to JavaScript dynamically. The available +surface therefore follows the AngleSharp services registered in the browsing context. For +example, adding CSS support also makes its AngleSharp DOM types available to scripts. + +In addition to the DOM provided by AngleSharp, the package supplies: + +- `console.log`, `atob`, `btoa`, `DOMParser`, `Image`, `screen`, and `XMLHttpRequest`. +- `javascript:` URL navigation. +- Inline event-handler attributes and DOM event callbacks. +- ES modules and import maps through Jint's module loader. + +### Built-in browser facades + +The additional browser-like APIs are deliberately small. Use this table when deciding whether +they meet a script's needs: + +| API | Available behavior | +| --- | --- | +| `DOMParser` | `parseFromString` creates a document through the configured `IDocumentFactory`. The requested MIME type must be supported by that configuration. | +| `Image` | Creates an AngleSharp `` element; optional width and height become its display dimensions. | +| `window.postMessage` | Queues a `message` event on the current window. It requires an event loop; it does not transfer objects, deliver to another browsing context, or enforce `targetOrigin`. | +| `XMLHttpRequest` | Supports `open`, `send`, request headers, status, text responses, and lifecycle events through the configured document loader. | +| `console` | Supports `console.log` only. | +| `screen` | Exposes fixed 1920-by-1080 dimensions and 24-bit color depth for compatibility. | + +To replace the supplied behavior, register your own compatible AngleSharp service before +calling `WithJs()`. In particular, `WithJs()` preserves an existing `INavigator`, and the +`WithEventLoop` overloads accept either an existing `IEventLoop` or a factory for one. + +## Supported behavior and limitations + +AngleSharp.Js is a DOM and scripting integration, not a browser runtime. Script behavior +depends on the installed Jint and AngleSharp versions, plus the services that your +configuration supplies. Test the browser APIs your application relies on instead of assuming +complete browser parity. + +Notable limitations include: + +- The package does not calculate layout. Its `scroll*`, `client*`, and `offset*` element + properties return `0`. +- The default `navigator` is intentionally minimal. Its platform is empty, registration + methods are no-ops, and its user-agent value is a fixed compatibility string. +- Network-backed features such as external scripts and `XMLHttpRequest` require suitable + AngleSharp requesters and resource loading configuration. +- `XMLHttpRequest` currently provides text responses only. Its `response`, `responseXML`, and + `upload` properties are unavailable, `responseType` always has its empty value, and its + `timeout` and `withCredentials` settings do not affect requests. +- The JavaScript engine executes application-provided or page-provided code in your process. + Treat untrusted scripts as untrusted code and apply the constraints appropriate to your + application. diff --git a/src/AngleSharp.Js.Tests/AngleSharp.Js.Tests.csproj b/src/AngleSharp.Js.Tests/AngleSharp.Js.Tests.csproj index 167bca3..3081f6f 100644 --- a/src/AngleSharp.Js.Tests/AngleSharp.Js.Tests.csproj +++ b/src/AngleSharp.Js.Tests/AngleSharp.Js.Tests.csproj @@ -1,28 +1,30 @@ - AngleSharp.Js.Tests - netcoreapp2.1 - true - Key.snk + net10.0 + $(TargetFrameworks);net462;net472 false - 7.1 - AngleSharp.Js.Tests true - - netstandard2.0 - + - - - - - - - + - \ No newline at end of file + + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + diff --git a/src/AngleSharp.Js.Tests/BootstrapTests.cs b/src/AngleSharp.Js.Tests/BootstrapTests.cs new file mode 100644 index 0000000..b5136af --- /dev/null +++ b/src/AngleSharp.Js.Tests/BootstrapTests.cs @@ -0,0 +1,205 @@ +namespace AngleSharp.Js.Tests +{ + using NUnit.Framework; + using System; + using System.Collections.Generic; + using System.Threading.Tasks; + + [TestFixture] + public class BootstrapTests + { + private static Task EvaluateScriptWithBootstrapAsync(params String[] sources) + { + var list = new List(sources); + list.Insert(0, Constants.Bootstrap_5_3_3); + return list.EvalScriptsAsync(); + } + + [Test] + public async Task BootstrapCanInstantiateAlertOncePerElement() + { + var result = await EvaluateScriptWithBootstrapAsync(@" +var el = document.createElement('div'); +el.className = 'alert'; +document.body.appendChild(el); +var first = bootstrap.Alert.getOrCreateInstance(el); +var second = bootstrap.Alert.getOrCreateInstance(el); +document.querySelector('#result').textContent = (first === second).toString();") + .ConfigureAwait(false); + Assert.AreEqual("true", result); + } + + [Test] + public async Task BootstrapExposesVersionString() + { + var result = await EvaluateScriptWithBootstrapAsync(@" +document.querySelector('#result').textContent = bootstrap.Tooltip.VERSION;") + .ConfigureAwait(false); + Assert.AreEqual("5.3.3", result); + } + + [Test] + public async Task BootstrapButtonToggleSetsActiveAndAriaPressed() + { + var result = await EvaluateScriptWithBootstrapAsync(@" +var button = document.createElement('button'); +button.className = 'btn'; +document.body.appendChild(button); +var instance = new bootstrap.Button(button); +instance.toggle(); +document.querySelector('#result').textContent = button.classList.contains('active').toString() + ':' + button.getAttribute('aria-pressed');") + .ConfigureAwait(false); + Assert.AreEqual("true:true", result); + } + + [Test] + public async Task BootstrapCollapseShowAndHideUpdatesExpandedState() + { + var result = await EvaluateScriptWithBootstrapAsync(@" +var panel = document.createElement('div'); +panel.id = 'panel'; +panel.className = 'collapse'; +document.body.appendChild(panel); + +var collapse = new bootstrap.Collapse(panel, { toggle: false }); +var sameInstance = (bootstrap.Collapse.getInstance(panel) === collapse).toString(); +var state = 'ok'; +try { + collapse.show(); + collapse.hide(); +} +catch (e) { + state = 'fail'; +} +document.querySelector('#result').textContent = sameInstance + ':' + state;") + .ConfigureAwait(false); + Assert.AreEqual("true:ok", result); + } + + [Test] + public async Task BootstrapTabCanBeInstantiatedAndTracked() + { + var result = await EvaluateScriptWithBootstrapAsync(@" +var nav = document.createElement('div'); +nav.innerHTML = '' + + ''; +document.body.appendChild(nav); + +var content = document.createElement('div'); +content.innerHTML = '
One
' + + '
Two
'; +document.body.appendChild(content); + +var tab = new bootstrap.Tab(document.getElementById('tab2')); +var sameInstance = (bootstrap.Tab.getInstance(document.getElementById('tab2')) === tab).toString(); +document.querySelector('#result').textContent = sameInstance;") + .ConfigureAwait(false); + Assert.AreEqual("true", result); + } + + [Test] + public async Task BootstrapToastShowAndHideFiresLifecycleEvents() + { + var result = await EvaluateScriptWithBootstrapAsync(@" +var el = document.createElement('div'); +el.className = 'toast'; +document.body.appendChild(el); +var toast = new bootstrap.Toast(el, { autohide: false }); +var sameInstance = (bootstrap.Toast.getInstance(el) === toast).toString(); +var state = 'ok'; +try { + toast.show(); + toast.hide(); +} +catch (e) { + state = 'fail'; +} +document.querySelector('#result').textContent = sameInstance + ':' + state;") + .ConfigureAwait(false); + Assert.AreEqual("true:ok", result); + } + + [Test] + public async Task BootstrapModalShowAndHideTogglesBodyClass() + { + var result = await EvaluateScriptWithBootstrapAsync(@" +var modalEl = document.createElement('div'); +modalEl.className = 'modal'; +modalEl.innerHTML = '
'; +document.body.appendChild(modalEl); + +var modal = new bootstrap.Modal(modalEl, { backdrop: false, keyboard: false }); +modal.show(); +var duringShow = document.body.classList.contains('modal-open').toString(); + +modalEl.addEventListener('hidden.bs.modal', function () { + var afterHide = document.body.classList.contains('modal-open').toString(); + document.querySelector('#result').textContent = duringShow + ':' + afterHide; +}); + +modal.hide();") + .ConfigureAwait(false); + Assert.AreEqual("true:false", result); + } + + [Test] + public async Task BootstrapExportsExpectedComponentConstructors() + { + var result = await EvaluateScriptWithBootstrapAsync(@" +var checks = [ + typeof bootstrap.Alert, + typeof bootstrap.Button, + typeof bootstrap.Collapse, + typeof bootstrap.Modal, + typeof bootstrap.Carousel, + typeof bootstrap.Toast +]; +document.querySelector('#result').textContent = checks.join(':');") + .ConfigureAwait(false); + Assert.AreEqual("function:function:function:function:function:function", result); + } + + [Test] + public async Task BootstrapCarouselCanMoveToSpecificSlide() + { + var result = await EvaluateScriptWithBootstrapAsync(@" +var el = document.createElement('div'); +el.className = 'carousel slide'; +el.innerHTML = '
' + + '
1
' + + '
2
' + + '
3
' + + '
'; +document.body.appendChild(el); + +var carousel = new bootstrap.Carousel(el, { interval: false, ride: false, wrap: true }); +var sameInstance = (bootstrap.Carousel.getInstance(el) === carousel).toString(); +var state = 'ok'; +try { + carousel.next(); + carousel.prev(); + carousel.to(2); +} +catch (e) { + state = 'fail'; +} +document.querySelector('#result').textContent = sameInstance + ':' + state;") + .ConfigureAwait(false); + Assert.AreEqual("true:ok", result); + } + + [Test] + public async Task BootstrapOffcanvasCanBeInstantiatedAndTracked() + { + var result = await EvaluateScriptWithBootstrapAsync(@" +var el = document.createElement('div'); +el.className = 'offcanvas offcanvas-start'; +document.body.appendChild(el); + +var offcanvas = new bootstrap.Offcanvas(el, { backdrop: false }); +document.querySelector('#result').textContent = (bootstrap.Offcanvas.getInstance(el) === offcanvas).toString();") + .ConfigureAwait(false); + Assert.AreEqual("true", result); + } + } +} diff --git a/src/AngleSharp.Js.Tests/ComponentTests.cs b/src/AngleSharp.Js.Tests/ComponentTests.cs index 5ba0c4a..0e0072d 100644 --- a/src/AngleSharp.Js.Tests/ComponentTests.cs +++ b/src/AngleSharp.Js.Tests/ComponentTests.cs @@ -1,7 +1,7 @@ namespace AngleSharp.Js.Tests { using AngleSharp.Scripting; - using AngleSharp.Xml; + using Jint; using NUnit.Framework; using System; using System.Threading.Tasks; diff --git a/src/AngleSharp.Js.Tests/Constants.cs b/src/AngleSharp.Js.Tests/Constants.cs index 5dc8d04..81fa93d 100644 --- a/src/AngleSharp.Js.Tests/Constants.cs +++ b/src/AngleSharp.Js.Tests/Constants.cs @@ -265,5 +265,320 @@ static class Constants b,c,d){ob(c)?void 0:n(""200"");null==a||void 0===a._reactInternalFiber?n(""38""):void 0;return Wc(a,b,c,!1,d)},unmountComponentAtNode:function(a){ob(a)?void 0:n(""40"");return a._reactRootContainer?($g(function(){Wc(null,null,a,!1,function(){a._reactRootContainer=null})}),!0):!1},unstable_createPortal:function(){return ch.apply(void 0,arguments)},unstable_batchedUpdates:Zg,unstable_interactiveUpdates:ah,flushSync:function(a,b){w?n(""187""):void 0;var c=z;z=!0;try{return Tg(a,b)}finally{z=c,Z(1073741823,!1)}}, unstable_createRoot:function(a,b){ob(a)?void 0:n(""299"",""unstable_createRoot"");return new nb(a,!0,null!=b&&!0===b.hydrate)},unstable_flushControlled:function(a){var b=z;z=!0;try{Tg(a)}finally{(z=b)||w||Z(1073741823,!1)}},__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED:{Events:[Je,Da,dd,Ae.injectEventPluginsByName,Zc,Qa,function(a){ad(a,xh)},Ve,We,oc,cd]}};(function(a){var b=a.findFiberByHostInstance;return ai(B({},a,{overrideProps:null,currentDispatcherRef:Ma.ReactCurrentDispatcher,findHostInstanceByFiber:function(a){a= tf(a);return null===a?null:a.stateNode},findFiberByHostInstance:function(a){return b?b(a):null}}))})({findFiberByHostInstance:dc,bundleType:0,version:""16.8.6"",rendererPackageName:""react-dom""});var ph={default:oh},qh=ph&&oh||ph;return qh.default||qh});"; + + public static readonly String Bootstrap_5_3_3 = @"/*! * Bootstrap v5.3.3 (https://getbootstrap.com/) * Copyright 2011-2024 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors) * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) */ !function(t,e){""object""==typeof exports&&""undefined""!=typeof module?module.exports=e():""function""==typeof define&&define.amd?define(e):(t=""undefined""!=typeof globalThis?globalThis:t||self).bootstrap=e()}(this,(function(){""use strict"";const t=new Map,e={set(e,i,n){t.has(e)||t.set(e +,new Map);const s=t.get(e);s.has(i)||0===s.size?s.set(i,n):console.error(`Bootstrap doesn't allow more than one instance per element. Bound instance: ${Array.from(s.keys())[0]}.`)},get:(e,i)=>t.has(e)&&t.get(e).get(i)||null,remove(e,i){if(!t.has(e))return;const n=t.get(e);n.delete(i),0===n.size&&t.delete(e)}},i=""transitionend"",n=t=>(t&&window.CSS&&window.CSS.escape&&(t=t.replace(/#([^\s""#']+)/g,((t,e)=>`#${CSS.escape(e)}`))),t),s=t=>{t.dispatchEvent(new Event(i))},o=t=>!(!t||""object""!=typeof t)&&(void + 0!==t.jquery&&(t=t[0]),void 0!==t.nodeType),r=t=>o(t)?t.jquery?t[0]:t:""string""==typeof t&&t.length>0?document.querySelector(n(t)):null,a=t=>{if(!o(t)||0===t.getClientRects().length)return!1;const e=""visible""===getComputedStyle(t).getPropertyValue(""visibility""),i=t.closest(""details:not([open])"");if(!i)return e;if(i!==t){const e=t.closest(""summary"");if(e&&e.parentNode!==i)return!1;if(null===e)return!1}return e},l=t=>!t||t.nodeType!==Node.ELEMENT_NODE||!!t.classList.contains(""disabled"")||(void 0!==t.disabled?t.disabled:t.hasAttribute(""disabled"")&&""false""!==t.getAttribute(""disabled"")) +,c=t=>{if(!document.documentElement.attachShadow)return null;if(""function""==typeof t.getRootNode){const e=t.getRootNode();return e instanceof ShadowRoot?e:null}return t instanceof ShadowRoot?t:t.parentNode?c(t.parentNode):null},h=()=>{},d=t=>{t.offsetHeight},u=()=>window.jQuery&&!document.body.hasAttribute(""data-bs-no-jquery"")?window.jQuery:null,f=[],p=()=>""rtl""===document.documentElement.dir,m=t=>{var e;e=()=>{const e=u();if(e){const i=t.NAME,n=e.fn[i];e.fn[i]=t.jQueryInterface,e.fn[i].Constructor=t +,e.fn[i].noConflict=()=>(e.fn[i]=n,t.jQueryInterface)}},""loading""===document.readyState?(f.length||document.addEventListener(""DOMContentLoaded"",(()=>{for(const t of f)t()})),f.push(e)):e()},g=(t,e=[],i=t)=>""function""==typeof t?t(...e):i,_=(t,e,n=!0)=>{if(!n)return void g(t);const o=(t=>{if(!t)return 0;let{transitionDuration:e,transitionDelay:i}=window.getComputedStyle(t);const n=Number.parseFloat(e),s=Number.parseFloat(i);return n||s?(e=e.split("","")[0],i=i.split("","")[0],1e3*(Number.parseFloat(e)+Number.parseFloat(i))):0})(e)+5 +;let r=!1;const a=({target:n})=>{n===e&&(r=!0,e.removeEventListener(i,a),g(t))};e.addEventListener(i,a),setTimeout((()=>{r||s(e)}),o)},b=(t,e,i,n)=>{const s=t.length;let o=t.indexOf(e);return-1===o?!i&&n?t[s-1]:t[0]:(o+=i?1:-1,n&&(o=(o+s)%s),t[Math.max(0,Math.min(o,s-1))])},v=/[^.]*(?=\..*)\.|.*/,y=/\..*/,w=/::\d+$/,A={};let E=1;const T={mouseenter:""mouseover"",mouseleave:""mouseout""},C=new Set([""click"",""dblclick"",""mouseup"",""mousedown"",""contextmenu"",""mousewheel"",""DOMMouseScroll"",""mouseover"",""mouseout"" +,""mousemove"",""selectstart"",""selectend"",""keydown"",""keypress"",""keyup"",""orientationchange"",""touchstart"",""touchmove"",""touchend"",""touchcancel"",""pointerdown"",""pointermove"",""pointerup"",""pointerleave"",""pointercancel"",""gesturestart"",""gesturechange"",""gestureend"",""focus"",""blur"",""change"",""reset"",""select"",""submit"",""focusin"",""focusout"",""load"",""unload"",""beforeunload"",""resize"",""move"",""DOMContentLoaded"",""readystatechange"",""error"",""abort"",""scroll""]);function O(t,e){return e&&`${e}::${E++}`||t.uidEvent||E++}function + x(t){const e=O(t);return t.uidEvent=e,A[e]=A[e]||{},A[e]}function k(t,e,i=null){return Object.values(t).find((t=>t.callable===e&&t.delegationSelector===i))}function L(t,e,i){const n=""string""==typeof e,s=n?i:e||i;let o=I(t);return C.has(o)||(o=t),[n,s,o]}function S(t,e,i,n,s){if(""string""!=typeof e||!t)return;let[o,r,a]=L(e,i,n);if(e in T){const t=t=>function(e){if(!e.relatedTarget||e.relatedTarget!==e.delegateTarget&&!e.delegateTarget.contains(e.relatedTarget))return t.call(this,e)};r=t(r)}const + l=x(t),c=l[a]||(l[a]={}),h=k(c,r,o?i:null);if(h)return void(h.oneOff=h.oneOff&&s);const d=O(r,e.replace(v,"""")),u=o?function(t,e,i){return function n(s){const o=t.querySelectorAll(e);for(let{target:r}=s;r&&r!==this;r=r.parentNode)for(const a of o)if(a===r)return P(s,{delegateTarget:r}),n.oneOff&&N.off(t,s.type,e,i),i.apply(r,[s])}}(t,i,r):function(t,e){return function i(n){return P(n,{delegateTarget:t}),i.oneOff&&N.off(t,n.type,e),e.apply(t,[n])}}(t,r);u.delegationSelector=o?i:null,u.callable=r,u.oneOff=s +,u.uidEvent=d,c[d]=u,t.addEventListener(a,u,o)}function D(t,e,i,n,s){const o=k(e[i],n,s);o&&(t.removeEventListener(i,o,Boolean(s)),delete e[i][o.uidEvent])}function $(t,e,i,n){const s=e[i]||{};for(const[o,r]of Object.entries(s))o.includes(n)&&D(t,e,i,r.callable,r.delegationSelector)}function I(t){return t=t.replace(y,""""),T[t]||t}const N={on(t,e,i,n){S(t,e,i,n,!1)},one(t,e,i,n){S(t,e,i,n,!0)},off(t,e,i,n){if(""string""!=typeof e||!t)return;const[s,o,r]=L(e,i,n),a=r!==e,l=x(t),c=l[r]||{},h=e.startsWith(""."") +;if(void 0===o){if(h)for(const i of Object.keys(l))$(t,l,i,e.slice(1));for(const[i,n]of Object.entries(c)){const s=i.replace(w,"""");a&&!e.includes(s)||D(t,l,r,n.callable,n.delegationSelector)}}else{if(!Object.keys(c).length)return;D(t,l,r,o,s?i:null)}},trigger(t,e,i){if(""string""!=typeof e||!t)return null;const n=u();let s=null,o=!0,r=!0,a=!1;e!==I(e)&&n&&(s=n.Event(e,i),n(t).trigger(s),o=!s.isPropagationStopped(),r=!s.isImmediatePropagationStopped(),a=s.isDefaultPrevented());const l=P(new Event(e +,{bubbles:o,cancelable:!0}),i);return a&&l.preventDefault(),r&&t.dispatchEvent(l),l.defaultPrevented&&s&&s.preventDefault(),l}};function P(t,e={}){for(const[i,n]of Object.entries(e))try{t[i]=n}catch(e){Object.defineProperty(t,i,{configurable:!0,get:()=>n})}return t}function j(t){if(""true""===t)return!0;if(""false""===t)return!1;if(t===Number(t).toString())return Number(t);if(""""===t||""null""===t)return null;if(""string""!=typeof t)return t;try{return JSON.parse(decodeURIComponent(t))}catch(e){return t}}function + M(t){return t.replace(/[A-Z]/g,(t=>`-${t.toLowerCase()}`))}const F={setDataAttribute(t,e,i){t.setAttribute(`data-bs-${M(e)}`,i)},removeDataAttribute(t,e){t.removeAttribute(`data-bs-${M(e)}`)},getDataAttributes(t){if(!t)return{};const e={},i=Object.keys(t.dataset).filter((t=>t.startsWith(""bs"")&&!t.startsWith(""bsConfig"")));for(const n of i){let i=n.replace(/^bs/,"""");i=i.charAt(0).toLowerCase()+i.slice(1,i.length),e[i]=j(t.dataset[n])}return e},getDataAttribute:(t,e)=>j(t.getAttribute(`data-bs-${M(e)}`))} +;class H{static get Default(){return{}}static get DefaultType(){return{}}static get NAME(){throw new Error('You have to implement the static method ""NAME"", for each component!')}_getConfig(t){return t=this._mergeConfigObj(t),t=this._configAfterMerge(t),this._typeCheckConfig(t),t}_configAfterMerge(t){return t}_mergeConfigObj(t,e){const i=o(e)?F.getDataAttribute(e,""config""):{};return{...this.constructor.Default,...""object""==typeof i?i:{},...o(e)?F.getDataAttributes(e):{},...""object""==typeof t?t:{}}}_typeCheckConfig(t +,e=this.constructor.DefaultType){for(const[n,s]of Object.entries(e)){const e=t[n],r=o(e)?""element"":null==(i=e)?`${i}`:Object.prototype.toString.call(i).match(/\s([a-z]+)/i)[1].toLowerCase();if(!new RegExp(s).test(r))throw new TypeError(`${this.constructor.NAME.toUpperCase()}: Option ""${n}"" provided type ""${r}"" but expected type ""${s}"".`)}var i}}class W extends H{constructor(t,i){super(),(t=r(t))&&(this._element=t,this._config=this._getConfig(i),e.set(this._element,this.constructor.DATA_KEY,this))}dispose(){e.remove(this._element +,this.constructor.DATA_KEY),N.off(this._element,this.constructor.EVENT_KEY);for(const t of Object.getOwnPropertyNames(this))this[t]=null}_queueCallback(t,e,i=!0){_(t,e,i)}_getConfig(t){return t=this._mergeConfigObj(t,this._element),t=this._configAfterMerge(t),this._typeCheckConfig(t),t}static getInstance(t){return e.get(r(t),this.DATA_KEY)}static getOrCreateInstance(t,e={}){return this.getInstance(t)||new this(t,""object""==typeof e?e:null)}static get VERSION(){return""5.3.3""}static get DATA_KEY(){return`bs.${this.NAME}`}static + get EVENT_KEY(){return`.${this.DATA_KEY}`}static eventName(t){return`${t}${this.EVENT_KEY}`}}const B=t=>{let e=t.getAttribute(""data-bs-target"");if(!e||""#""===e){let i=t.getAttribute(""href"");if(!i||!i.includes(""#"")&&!i.startsWith("".""))return null;i.includes(""#"")&&!i.startsWith(""#"")&&(i=`#${i.split(""#"")[1]}`),e=i&&""#""!==i?i.trim():null}return e?e.split("","").map((t=>n(t))).join("",""):null},z={find:(t,e=document.documentElement)=>[].concat(...Element.prototype.querySelectorAll.call(e,t)),findOne:(t,e=document.documentElement)=>Element.prototype.querySelector.call(e +,t),children:(t,e)=>[].concat(...t.children).filter((t=>t.matches(e))),parents(t,e){const i=[];let n=t.parentNode.closest(e);for(;n;)i.push(n),n=n.parentNode.closest(e);return i},prev(t,e){let i=t.previousElementSibling;for(;i;){if(i.matches(e))return[i];i=i.previousElementSibling}return[]},next(t,e){let i=t.nextElementSibling;for(;i;){if(i.matches(e))return[i];i=i.nextElementSibling}return[]},focusableChildren(t){const e=[""a"",""button"",""input"",""textarea"",""select"",""details"",""[tabindex]"",'[contenteditable=""true""]'].map((t=>`${t}:not([tabindex^=""-""])`)) +.join("","");return this.find(e,t).filter((t=>!l(t)&&a(t)))},getSelectorFromElement(t){const e=B(t);return e&&z.findOne(e)?e:null},getElementFromSelector(t){const e=B(t);return e?z.findOne(e):null},getMultipleElementsFromSelector(t){const e=B(t);return e?z.find(e):[]}},R=(t,e=""hide"")=>{const i=`click.dismiss${t.EVENT_KEY}`,n=t.NAME;N.on(document,i,`[data-bs-dismiss=""${n}""]`,(function(i){if([""A"",""AREA""].includes(this.tagName)&&i.preventDefault(),l(this))return;const s=z.getElementFromSelector(this)||this.closest(`.${n}`) +;t.getOrCreateInstance(s)[e]()}))},q="".bs.alert"",V=`close${q}`,K=`closed${q}`;class Q extends W{static get NAME(){return""alert""}close(){if(N.trigger(this._element,V).defaultPrevented)return;this._element.classList.remove(""show"");const t=this._element.classList.contains(""fade"");this._queueCallback((()=>this._destroyElement()),this._element,t)}_destroyElement(){this._element.remove(),N.trigger(this._element,K),this.dispose()}static jQueryInterface(t){return this.each((function(){const e=Q.getOrCreateInstance(this) +;if(""string""==typeof t){if(void 0===e[t]||t.startsWith(""_"")||""constructor""===t)throw new TypeError(`No method named ""${t}""`);e[t](this)}}))}}R(Q,""close""),m(Q);const X='[data-bs-toggle=""button""]';class Y extends W{static get NAME(){return""button""}toggle(){this._element.setAttribute(""aria-pressed"",this._element.classList.toggle(""active""))}static jQueryInterface(t){return this.each((function(){const e=Y.getOrCreateInstance(this);""toggle""===t&&e[t]()}))}}N.on(document,""click.bs.button.data-api"",X,(t=>{t.preventDefault() +;const e=t.target.closest(X);Y.getOrCreateInstance(e).toggle()})),m(Y);const U="".bs.swipe"",G=`touchstart${U}`,J=`touchmove${U}`,Z=`touchend${U}`,tt=`pointerdown${U}`,et=`pointerup${U}`,it={endCallback:null,leftCallback:null,rightCallback:null},nt={endCallback:""(function|null)"",leftCallback:""(function|null)"",rightCallback:""(function|null)""};class st extends H{constructor(t,e){super(),this._element=t,t&&st.isSupported()&&(this._config=this._getConfig(e),this._deltaX=0,this._supportPointerEvents=Boolean(window.PointerEvent) +,this._initEvents())}static get Default(){return it}static get DefaultType(){return nt}static get NAME(){return""swipe""}dispose(){N.off(this._element,U)}_start(t){this._supportPointerEvents?this._eventIsPointerPenTouch(t)&&(this._deltaX=t.clientX):this._deltaX=t.touches[0].clientX}_end(t){this._eventIsPointerPenTouch(t)&&(this._deltaX=t.clientX-this._deltaX),this._handleSwipe(),g(this._config.endCallback)}_move(t){this._deltaX=t.touches&&t.touches.length>1?0:t.touches[0].clientX-this._deltaX}_handleSwipe(){const + t=Math.abs(this._deltaX);if(t<=40)return;const e=t/this._deltaX;this._deltaX=0,e&&g(e>0?this._config.rightCallback:this._config.leftCallback)}_initEvents(){this._supportPointerEvents?(N.on(this._element,tt,(t=>this._start(t))),N.on(this._element,et,(t=>this._end(t))),this._element.classList.add(""pointer-event"")):(N.on(this._element,G,(t=>this._start(t))),N.on(this._element,J,(t=>this._move(t))),N.on(this._element,Z,(t=>this._end(t))))}_eventIsPointerPenTouch(t){return this._supportPointerEvents&&(""pen""===t.pointerType||""touch""===t.pointerType)}static + isSupported(){return""ontouchstart""in document.documentElement||navigator.maxTouchPoints>0}}const ot="".bs.carousel"",rt="".data-api"",at=""next"",lt=""prev"",ct=""left"",ht=""right"",dt=`slide${ot}`,ut=`slid${ot}`,ft=`keydown${ot}`,pt=`mouseenter${ot}`,mt=`mouseleave${ot}`,gt=`dragstart${ot}`,_t=`load${ot}${rt}`,bt=`click${ot}${rt}`,vt=""carousel"",yt=""active"",wt="".active"",At="".carousel-item"",Et=wt+At,Tt={ArrowLeft:ht,ArrowRight:ct},Ct={interval:5e3,keyboard:!0,pause:""hover"",ride:!1,touch:!0,wrap:!0},Ot={interval:""(number|boolean)"" +,keyboard:""boolean"",pause:""(string|boolean)"",ride:""(boolean|string)"",touch:""boolean"",wrap:""boolean""};class xt extends W{constructor(t,e){super(t,e),this._interval=null,this._activeElement=null,this._isSliding=!1,this.touchTimeout=null,this._swipeHelper=null,this._indicatorsElement=z.findOne("".carousel-indicators"",this._element),this._addEventListeners(),this._config.ride===vt&&this.cycle()}static get Default(){return Ct}static get DefaultType(){return Ot}static get NAME(){return""carousel""}next(){this._slide(at)}nextWhenVisible(){!document.hidden&&a(this._element)&&this.next()}prev(){this._slide(lt)}pause(){this._isSliding&&s(this._element) +,this._clearInterval()}cycle(){this._clearInterval(),this._updateInterval(),this._interval=setInterval((()=>this.nextWhenVisible()),this._config.interval)}_maybeEnableCycle(){this._config.ride&&(this._isSliding?N.one(this._element,ut,(()=>this.cycle())):this.cycle())}to(t){const e=this._getItems();if(t>e.length-1||t<0)return;if(this._isSliding)return void N.one(this._element,ut,(()=>this.to(t)));const i=this._getItemIndex(this._getActive());if(i===t)return;const n=t>i?at:lt;this._slide(n,e[t])}dispose(){this._swipeHelper&&this._swipeHelper.dispose() +,super.dispose()}_configAfterMerge(t){return t.defaultInterval=t.interval,t}_addEventListeners(){this._config.keyboard&&N.on(this._element,ft,(t=>this._keydown(t))),""hover""===this._config.pause&&(N.on(this._element,pt,(()=>this.pause())),N.on(this._element,mt,(()=>this._maybeEnableCycle()))),this._config.touch&&st.isSupported()&&this._addTouchEventListeners()}_addTouchEventListeners(){for(const t of z.find("".carousel-item img"",this._element))N.on(t,gt,(t=>t.preventDefault()));const t={leftCallback:()=>this._slide(this._directionToOrder(ct)) +,rightCallback:()=>this._slide(this._directionToOrder(ht)),endCallback:()=>{""hover""===this._config.pause&&(this.pause(),this.touchTimeout&&clearTimeout(this.touchTimeout),this.touchTimeout=setTimeout((()=>this._maybeEnableCycle()),500+this._config.interval))}};this._swipeHelper=new st(this._element,t)}_keydown(t){if(/input|textarea/i.test(t.target.tagName))return;const e=Tt[t.key];e&&(t.preventDefault(),this._slide(this._directionToOrder(e)))}_getItemIndex(t){return this._getItems().indexOf(t)}_setActiveIndicatorElement(t){if(!this._indicatorsElement)return +;const e=z.findOne(wt,this._indicatorsElement);e.classList.remove(yt),e.removeAttribute(""aria-current"");const i=z.findOne(`[data-bs-slide-to=""${t}""]`,this._indicatorsElement);i&&(i.classList.add(yt),i.setAttribute(""aria-current"",""true""))}_updateInterval(){const t=this._activeElement||this._getActive();if(!t)return;const e=Number.parseInt(t.getAttribute(""data-bs-interval""),10);this._config.interval=e||this._config.defaultInterval}_slide(t,e=null){if(this._isSliding)return;const i=this._getActive() +,n=t===at,s=e||b(this._getItems(),i,n,this._config.wrap);if(s===i)return;const o=this._getItemIndex(s),r=e=>N.trigger(this._element,e,{relatedTarget:s,direction:this._orderToDirection(t),from:this._getItemIndex(i),to:o});if(r(dt).defaultPrevented)return;if(!i||!s)return;const a=Boolean(this._interval);this.pause(),this._isSliding=!0,this._setActiveIndicatorElement(o),this._activeElement=s;const l=n?""carousel-item-start"":""carousel-item-end"",c=n?""carousel-item-next"":""carousel-item-prev"";s.classList.add(c) +,d(s),i.classList.add(l),s.classList.add(l),this._queueCallback((()=>{s.classList.remove(l,c),s.classList.add(yt),i.classList.remove(yt,c,l),this._isSliding=!1,r(ut)}),i,this._isAnimated()),a&&this.cycle()}_isAnimated(){return this._element.classList.contains(""slide"")}_getActive(){return z.findOne(Et,this._element)}_getItems(){return z.find(At,this._element)}_clearInterval(){this._interval&&(clearInterval(this._interval),this._interval=null)}_directionToOrder(t){return p()?t===ct?lt:at:t===ct?at:lt}_orderToDirection(t){return + p()?t===lt?ct:ht:t===lt?ht:ct}static jQueryInterface(t){return this.each((function(){const e=xt.getOrCreateInstance(this,t);if(""number""!=typeof t){if(""string""==typeof t){if(void 0===e[t]||t.startsWith(""_"")||""constructor""===t)throw new TypeError(`No method named ""${t}""`);e[t]()}}else e.to(t)}))}}N.on(document,bt,""[data-bs-slide], [data-bs-slide-to]"",(function(t){const e=z.getElementFromSelector(this);if(!e||!e.classList.contains(vt))return;t.preventDefault();const i=xt.getOrCreateInstance(e),n=this.getAttribute(""data-bs-slide-to"") +;return n?(i.to(n),void i._maybeEnableCycle()):""next""===F.getDataAttribute(this,""slide"")?(i.next(),void i._maybeEnableCycle()):(i.prev(),void i._maybeEnableCycle())})),N.on(window,_t,(()=>{const t=z.find('[data-bs-ride=""carousel""]');for(const e of t)xt.getOrCreateInstance(e)})),m(xt);const kt="".bs.collapse"",Lt=`show${kt}`,St=`shown${kt}`,Dt=`hide${kt}`,$t=`hidden${kt}`,It=`click${kt}.data-api`,Nt=""show"",Pt=""collapse"",jt=""collapsing"",Mt=`:scope .${Pt} .${Pt}`,Ft='[data-bs-toggle=""collapse""]',Ht={parent:null +,toggle:!0},Wt={parent:""(null|element)"",toggle:""boolean""};class Bt extends W{constructor(t,e){super(t,e),this._isTransitioning=!1,this._triggerArray=[];const i=z.find(Ft);for(const t of i){const e=z.getSelectorFromElement(t),i=z.find(e).filter((t=>t===this._element));null!==e&&i.length&&this._triggerArray.push(t)}this._initializeChildren(),this._config.parent||this._addAriaAndCollapsedClass(this._triggerArray,this._isShown()),this._config.toggle&&this.toggle()}static get Default(){return Ht}static + get DefaultType(){return Wt}static get NAME(){return""collapse""}toggle(){this._isShown()?this.hide():this.show()}show(){if(this._isTransitioning||this._isShown())return;let t=[];if(this._config.parent&&(t=this._getFirstLevelChildren("".collapse.show, .collapse.collapsing"").filter((t=>t!==this._element)).map((t=>Bt.getOrCreateInstance(t,{toggle:!1})))),t.length&&t[0]._isTransitioning)return;if(N.trigger(this._element,Lt).defaultPrevented)return;for(const e of t)e.hide();const e=this._getDimension() +;this._element.classList.remove(Pt),this._element.classList.add(jt),this._element.style[e]=0,this._addAriaAndCollapsedClass(this._triggerArray,!0),this._isTransitioning=!0;const i=`scroll${e[0].toUpperCase()+e.slice(1)}`;this._queueCallback((()=>{this._isTransitioning=!1,this._element.classList.remove(jt),this._element.classList.add(Pt,Nt),this._element.style[e]="""",N.trigger(this._element,St)}),this._element,!0),this._element.style[e]=`${this._element[i]}px`}hide(){if(this._isTransitioning||!this._isShown())return +;if(N.trigger(this._element,Dt).defaultPrevented)return;const t=this._getDimension();this._element.style[t]=`${this._element.getBoundingClientRect()[t]}px`,d(this._element),this._element.classList.add(jt),this._element.classList.remove(Pt,Nt);for(const t of this._triggerArray){const e=z.getElementFromSelector(t);e&&!this._isShown(e)&&this._addAriaAndCollapsedClass([t],!1)}this._isTransitioning=!0,this._element.style[t]="""",this._queueCallback((()=>{this._isTransitioning=!1,this._element.classList.remove(jt) +,this._element.classList.add(Pt),N.trigger(this._element,$t)}),this._element,!0)}_isShown(t=this._element){return t.classList.contains(Nt)}_configAfterMerge(t){return t.toggle=Boolean(t.toggle),t.parent=r(t.parent),t}_getDimension(){return this._element.classList.contains(""collapse-horizontal"")?""width"":""height""}_initializeChildren(){if(!this._config.parent)return;const t=this._getFirstLevelChildren(Ft);for(const e of t){const t=z.getElementFromSelector(e);t&&this._addAriaAndCollapsedClass([e],this._isShown(t))}}_getFirstLevelChildren(t){const + e=z.find(Mt,this._config.parent);return z.find(t,this._config.parent).filter((t=>!e.includes(t)))}_addAriaAndCollapsedClass(t,e){if(t.length)for(const i of t)i.classList.toggle(""collapsed"",!e),i.setAttribute(""aria-expanded"",e)}static jQueryInterface(t){const e={};return""string""==typeof t&&/show|hide/.test(t)&&(e.toggle=!1),this.each((function(){const i=Bt.getOrCreateInstance(this,e);if(""string""==typeof t){if(void 0===i[t])throw new TypeError(`No method named ""${t}""`);i[t]()}}))}}N.on(document,It +,Ft,(function(t){(""A""===t.target.tagName||t.delegateTarget&&""A""===t.delegateTarget.tagName)&&t.preventDefault();for(const t of z.getMultipleElementsFromSelector(this))Bt.getOrCreateInstance(t,{toggle:!1}).toggle()})),m(Bt);var zt=""top"",Rt=""bottom"",qt=""right"",Vt=""left"",Kt=""auto"",Qt=[zt,Rt,qt,Vt],Xt=""start"",Yt=""end"",Ut=""clippingParents"",Gt=""viewport"",Jt=""popper"",Zt=""reference"",te=Qt.reduce((function(t,e){return t.concat([e+""-""+Xt,e+""-""+Yt])}),[]),ee=[].concat(Qt,[Kt]).reduce((function(t,e){return t.concat([e +,e+""-""+Xt,e+""-""+Yt])}),[]),ie=""beforeRead"",ne=""read"",se=""afterRead"",oe=""beforeMain"",re=""main"",ae=""afterMain"",le=""beforeWrite"",ce=""write"",he=""afterWrite"",de=[ie,ne,se,oe,re,ae,le,ce,he];function ue(t){return t?(t.nodeName||"""").toLowerCase():null}function fe(t){if(null==t)return window;if(""[object Window]""!==t.toString()){var e=t.ownerDocument;return e&&e.defaultView||window}return t}function pe(t){return t instanceof fe(t).Element||t instanceof Element}function me(t){return t instanceof fe(t).HTMLElement||t + instanceof HTMLElement}function ge(t){return""undefined""!=typeof ShadowRoot&&(t instanceof fe(t).ShadowRoot||t instanceof ShadowRoot)}const _e={name:""applyStyles"",enabled:!0,phase:""write"",fn:function(t){var e=t.state;Object.keys(e.elements).forEach((function(t){var i=e.styles[t]||{},n=e.attributes[t]||{},s=e.elements[t];me(s)&&ue(s)&&(Object.assign(s.style,i),Object.keys(n).forEach((function(t){var e=n[t];!1===e?s.removeAttribute(t):s.setAttribute(t,!0===e?"""":e)})))}))},effect:function(t){var e=t.state +,i={popper:{position:e.options.strategy,left:""0"",top:""0"",margin:""0""},arrow:{position:""absolute""},reference:{}};return Object.assign(e.elements.popper.style,i.popper),e.styles=i,e.elements.arrow&&Object.assign(e.elements.arrow.style,i.arrow),function(){Object.keys(e.elements).forEach((function(t){var n=e.elements[t],s=e.attributes[t]||{},o=Object.keys(e.styles.hasOwnProperty(t)?e.styles[t]:i[t]).reduce((function(t,e){return t[e]="""",t}),{});me(n)&&ue(n)&&(Object.assign(n.style,o),Object.keys(s).forEach((function(t){n.removeAttribute(t)})))}))}} +,requires:[""computeStyles""]};function be(t){return t.split(""-"")[0]}var ve=Math.max,ye=Math.min,we=Math.round;function Ae(){var t=navigator.userAgentData;return null!=t&&t.brands&&Array.isArray(t.brands)?t.brands.map((function(t){return t.brand+""/""+t.version})).join("" ""):navigator.userAgent}function Ee(){return!/^((?!chrome|android).)*safari/i.test(Ae())}function Te(t,e,i){void 0===e&&(e=!1),void 0===i&&(i=!1);var n=t.getBoundingClientRect(),s=1,o=1;e&&me(t)&&(s=t.offsetWidth>0&&we(n.width)/t.offsetWidth||1 +,o=t.offsetHeight>0&&we(n.height)/t.offsetHeight||1);var r=(pe(t)?fe(t):window).visualViewport,a=!Ee()&&i,l=(n.left+(a&&r?r.offsetLeft:0))/s,c=(n.top+(a&&r?r.offsetTop:0))/o,h=n.width/s,d=n.height/o;return{width:h,height:d,top:c,right:l+h,bottom:c+d,left:l,x:l,y:c}}function Ce(t){var e=Te(t),i=t.offsetWidth,n=t.offsetHeight;return Math.abs(e.width-i)<=1&&(i=e.width),Math.abs(e.height-n)<=1&&(n=e.height),{x:t.offsetLeft,y:t.offsetTop,width:i,height:n}}function Oe(t,e){var i=e.getRootNode&&e.getRootNode() +;if(t.contains(e))return!0;if(i&&ge(i)){var n=e;do{if(n&&t.isSameNode(n))return!0;n=n.parentNode||n.host}while(n)}return!1}function xe(t){return fe(t).getComputedStyle(t)}function ke(t){return[""table"",""td"",""th""].indexOf(ue(t))>=0}function Le(t){return((pe(t)?t.ownerDocument:t.document)||window.document).documentElement}function Se(t){return""html""===ue(t)?t:t.assignedSlot||t.parentNode||(ge(t)?t.host:null)||Le(t)}function De(t){return me(t)&&""fixed""!==xe(t).position?t.offsetParent:null}function $e(t){for(var + e=fe(t),i=De(t);i&&ke(i)&&""static""===xe(i).position;)i=De(i);return i&&(""html""===ue(i)||""body""===ue(i)&&""static""===xe(i).position)?e:i||function(t){var e=/firefox/i.test(Ae());if(/Trident/i.test(Ae())&&me(t)&&""fixed""===xe(t).position)return null;var i=Se(t);for(ge(i)&&(i=i.host);me(i)&&[""html"",""body""].indexOf(ue(i))<0;){var n=xe(i);if(""none""!==n.transform||""none""!==n.perspective||""paint""===n.contain||-1!==[""transform"",""perspective""].indexOf(n.willChange)||e&&""filter""===n.willChange||e&&n.filter&&""none""!==n.filter)return + i;i=i.parentNode}return null}(t)||e}function Ie(t){return[""top"",""bottom""].indexOf(t)>=0?""x"":""y""}function Ne(t,e,i){return ve(t,ye(e,i))}function Pe(t){return Object.assign({},{top:0,right:0,bottom:0,left:0},t)}function je(t,e){return e.reduce((function(e,i){return e[i]=t,e}),{})}const Me={name:""arrow"",enabled:!0,phase:""main"",fn:function(t){var e,i=t.state,n=t.name,s=t.options,o=i.elements.arrow,r=i.modifiersData.popperOffsets,a=be(i.placement),l=Ie(a),c=[Vt,qt].indexOf(a)>=0?""height"":""width"";if(o&&r){var + h=function(t,e){return Pe(""number""!=typeof(t=""function""==typeof t?t(Object.assign({},e.rects,{placement:e.placement})):t)?t:je(t,Qt))}(s.padding,i),d=Ce(o),u=""y""===l?zt:Vt,f=""y""===l?Rt:qt,p=i.rects.reference[c]+i.rects.reference[l]-r[l]-i.rects.popper[c],m=r[l]-i.rects.reference[l],g=$e(o),_=g?""y""===l?g.clientHeight||0:g.clientWidth||0:0,b=p/2-m/2,v=h[u],y=_-d[c]-h[f],w=_/2-d[c]/2+b,A=Ne(v,w,y),E=l;i.modifiersData[n]=((e={})[E]=A,e.centerOffset=A-w,e)}},effect:function(t){var e=t.state,i=t.options.element +,n=void 0===i?""[data-popper-arrow]"":i;null!=n&&(""string""!=typeof n||(n=e.elements.popper.querySelector(n)))&&Oe(e.elements.popper,n)&&(e.elements.arrow=n)},requires:[""popperOffsets""],requiresIfExists:[""preventOverflow""]};function Fe(t){return t.split(""-"")[1]}var He={top:""auto"",right:""auto"",bottom:""auto"",left:""auto""};function We(t){var e,i=t.popper,n=t.popperRect,s=t.placement,o=t.variation,r=t.offsets,a=t.position,l=t.gpuAcceleration,c=t.adaptive,h=t.roundOffsets,d=t.isFixed,u=r.x,f=void 0===u?0:u +,p=r.y,m=void 0===p?0:p,g=""function""==typeof h?h({x:f,y:m}):{x:f,y:m};f=g.x,m=g.y;var _=r.hasOwnProperty(""x""),b=r.hasOwnProperty(""y""),v=Vt,y=zt,w=window;if(c){var A=$e(i),E=""clientHeight"",T=""clientWidth"";A===fe(i)&&""static""!==xe(A=Le(i)).position&&""absolute""===a&&(E=""scrollHeight"",T=""scrollWidth""),(s===zt||(s===Vt||s===qt)&&o===Yt)&&(y=Rt,m-=(d&&A===w&&w.visualViewport?w.visualViewport.height:A[E])-n.height,m*=l?1:-1),s!==Vt&&(s!==zt&&s!==Rt||o!==Yt)||(v=qt,f-=(d&&A===w&&w.visualViewport?w.visualViewport.width:A[T])-n.width +,f*=l?1:-1)}var C,O=Object.assign({position:a},c&&He),x=!0===h?function(t,e){var i=t.x,n=t.y,s=e.devicePixelRatio||1;return{x:we(i*s)/s||0,y:we(n*s)/s||0}}({x:f,y:m},fe(i)):{x:f,y:m};return f=x.x,m=x.y,l?Object.assign({},O,((C={})[y]=b?""0"":"""",C[v]=_?""0"":"""",C.transform=(w.devicePixelRatio||1)<=1?""translate(""+f+""px, ""+m+""px)"":""translate3d(""+f+""px, ""+m+""px, 0)"",C)):Object.assign({},O,((e={})[y]=b?m+""px"":"""",e[v]=_?f+""px"":"""",e.transform="""",e))}const Be={name:""computeStyles"",enabled:!0,phase:""beforeWrite"" +,fn:function(t){var e=t.state,i=t.options,n=i.gpuAcceleration,s=void 0===n||n,o=i.adaptive,r=void 0===o||o,a=i.roundOffsets,l=void 0===a||a,c={placement:be(e.placement),variation:Fe(e.placement),popper:e.elements.popper,popperRect:e.rects.popper,gpuAcceleration:s,isFixed:""fixed""===e.options.strategy};null!=e.modifiersData.popperOffsets&&(e.styles.popper=Object.assign({},e.styles.popper,We(Object.assign({},c,{offsets:e.modifiersData.popperOffsets,position:e.options.strategy,adaptive:r,roundOffsets:l})))) +,null!=e.modifiersData.arrow&&(e.styles.arrow=Object.assign({},e.styles.arrow,We(Object.assign({},c,{offsets:e.modifiersData.arrow,position:""absolute"",adaptive:!1,roundOffsets:l})))),e.attributes.popper=Object.assign({},e.attributes.popper,{""data-popper-placement"":e.placement})},data:{}};var ze={passive:!0};const Re={name:""eventListeners"",enabled:!0,phase:""write"",fn:function(){},effect:function(t){var e=t.state,i=t.instance,n=t.options,s=n.scroll,o=void 0===s||s,r=n.resize,a=void 0===r||r,l=fe(e.elements.popper) +,c=[].concat(e.scrollParents.reference,e.scrollParents.popper);return o&&c.forEach((function(t){t.addEventListener(""scroll"",i.update,ze)})),a&&l.addEventListener(""resize"",i.update,ze),function(){o&&c.forEach((function(t){t.removeEventListener(""scroll"",i.update,ze)})),a&&l.removeEventListener(""resize"",i.update,ze)}},data:{}};var qe={left:""right"",right:""left"",bottom:""top"",top:""bottom""};function Ve(t){return t.replace(/left|right|bottom|top/g,(function(t){return qe[t]}))}var Ke={start:""end"",end:""start""} +;function Qe(t){return t.replace(/start|end/g,(function(t){return Ke[t]}))}function Xe(t){var e=fe(t);return{scrollLeft:e.pageXOffset,scrollTop:e.pageYOffset}}function Ye(t){return Te(Le(t)).left+Xe(t).scrollLeft}function Ue(t){var e=xe(t),i=e.overflow,n=e.overflowX,s=e.overflowY;return/auto|scroll|overlay|hidden/.test(i+s+n)}function Ge(t){return[""html"",""body"",""#document""].indexOf(ue(t))>=0?t.ownerDocument.body:me(t)&&Ue(t)?t:Ge(Se(t))}function Je(t,e){var i;void 0===e&&(e=[]);var n=Ge(t),s=n===(null==(i=t.ownerDocument)?void + 0:i.body),o=fe(n),r=s?[o].concat(o.visualViewport||[],Ue(n)?n:[]):n,a=e.concat(r);return s?a:a.concat(Je(Se(r)))}function Ze(t){return Object.assign({},t,{left:t.x,top:t.y,right:t.x+t.width,bottom:t.y+t.height})}function ti(t,e,i){return e===Gt?Ze(function(t,e){var i=fe(t),n=Le(t),s=i.visualViewport,o=n.clientWidth,r=n.clientHeight,a=0,l=0;if(s){o=s.width,r=s.height;var c=Ee();(c||!c&&""fixed""===e)&&(a=s.offsetLeft,l=s.offsetTop)}return{width:o,height:r,x:a+Ye(t),y:l}}(t,i)):pe(e)?function(t,e){var + i=Te(t,!1,""fixed""===e);return i.top=i.top+t.clientTop,i.left=i.left+t.clientLeft,i.bottom=i.top+t.clientHeight,i.right=i.left+t.clientWidth,i.width=t.clientWidth,i.height=t.clientHeight,i.x=i.left,i.y=i.top,i}(e,i):Ze(function(t){var e,i=Le(t),n=Xe(t),s=null==(e=t.ownerDocument)?void 0:e.body,o=ve(i.scrollWidth,i.clientWidth,s?s.scrollWidth:0,s?s.clientWidth:0),r=ve(i.scrollHeight,i.clientHeight,s?s.scrollHeight:0,s?s.clientHeight:0),a=-n.scrollLeft+Ye(t),l=-n.scrollTop;return""rtl""===xe(s||i).direction&&(a+=ve(i.clientWidth +,s?s.clientWidth:0)-o),{width:o,height:r,x:a,y:l}}(Le(t)))}function ei(t){var e,i=t.reference,n=t.element,s=t.placement,o=s?be(s):null,r=s?Fe(s):null,a=i.x+i.width/2-n.width/2,l=i.y+i.height/2-n.height/2;switch(o){case zt:e={x:a,y:i.y-n.height};break;case Rt:e={x:a,y:i.y+i.height};break;case qt:e={x:i.x+i.width,y:l};break;case Vt:e={x:i.x-n.width,y:l};break;default:e={x:i.x,y:i.y}}var c=o?Ie(o):null;if(null!=c){var h=""y""===c?""height"":""width"";switch(r){case Xt:e[c]=e[c]-(i[h]/2-n[h]/2);break;case + Yt:e[c]=e[c]+(i[h]/2-n[h]/2)}}return e}function ii(t,e){void 0===e&&(e={});var i=e,n=i.placement,s=void 0===n?t.placement:n,o=i.strategy,r=void 0===o?t.strategy:o,a=i.boundary,l=void 0===a?Ut:a,c=i.rootBoundary,h=void 0===c?Gt:c,d=i.elementContext,u=void 0===d?Jt:d,f=i.altBoundary,p=void 0!==f&&f,m=i.padding,g=void 0===m?0:m,_=Pe(""number""!=typeof g?g:je(g,Qt)),b=u===Jt?Zt:Jt,v=t.rects.popper,y=t.elements[p?b:u],w=function(t,e,i,n){var s=""clippingParents""===e?function(t){var e=Je(Se(t)),i=[""absolute"" +,""fixed""].indexOf(xe(t).position)>=0&&me(t)?$e(t):t;return pe(i)?e.filter((function(t){return pe(t)&&Oe(t,i)&&""body""!==ue(t)})):[]}(t):[].concat(e),o=[].concat(s,[i]),r=o[0],a=o.reduce((function(e,i){var s=ti(t,i,n);return e.top=ve(s.top,e.top),e.right=ye(s.right,e.right),e.bottom=ye(s.bottom,e.bottom),e.left=ve(s.left,e.left),e}),ti(t,r,n));return a.width=a.right-a.left,a.height=a.bottom-a.top,a.x=a.left,a.y=a.top,a}(pe(y)?y:y.contextElement||Le(t.elements.popper),l,h,r),A=Te(t.elements.reference) +,E=ei({reference:A,element:v,strategy:""absolute"",placement:s}),T=Ze(Object.assign({},v,E)),C=u===Jt?T:A,O={top:w.top-C.top+_.top,bottom:C.bottom-w.bottom+_.bottom,left:w.left-C.left+_.left,right:C.right-w.right+_.right},x=t.modifiersData.offset;if(u===Jt&&x){var k=x[s];Object.keys(O).forEach((function(t){var e=[qt,Rt].indexOf(t)>=0?1:-1,i=[zt,Rt].indexOf(t)>=0?""y"":""x"";O[t]+=k[i]*e}))}return O}function ni(t,e){void 0===e&&(e={});var i=e,n=i.placement,s=i.boundary,o=i.rootBoundary,r=i.padding,a=i.flipVariations +,l=i.allowedAutoPlacements,c=void 0===l?ee:l,h=Fe(n),d=h?a?te:te.filter((function(t){return Fe(t)===h})):Qt,u=d.filter((function(t){return c.indexOf(t)>=0}));0===u.length&&(u=d);var f=u.reduce((function(e,i){return e[i]=ii(t,{placement:i,boundary:s,rootBoundary:o,padding:r})[be(i)],e}),{});return Object.keys(f).sort((function(t,e){return f[t]-f[e]}))}const si={name:""flip"",enabled:!0,phase:""main"",fn:function(t){var e=t.state,i=t.options,n=t.name;if(!e.modifiersData[n]._skip){for(var s=i.mainAxis,o=void + 0===s||s,r=i.altAxis,a=void 0===r||r,l=i.fallbackPlacements,c=i.padding,h=i.boundary,d=i.rootBoundary,u=i.altBoundary,f=i.flipVariations,p=void 0===f||f,m=i.allowedAutoPlacements,g=e.options.placement,_=be(g),b=l||(_!==g&&p?function(t){if(be(t)===Kt)return[];var e=Ve(t);return[Qe(t),e,Qe(e)]}(g):[Ve(g)]),v=[g].concat(b).reduce((function(t,i){return t.concat(be(i)===Kt?ni(e,{placement:i,boundary:h,rootBoundary:d,padding:c,flipVariations:p,allowedAutoPlacements:m}):i)}),[]),y=e.rects.reference,w=e.rects.popper +,A=new Map,E=!0,T=v[0],C=0;C=0,S=L?""width"":""height"",D=ii(e,{placement:O,boundary:h,rootBoundary:d,altBoundary:u,padding:c}),$=L?k?qt:Vt:k?Rt:zt;y[S]>w[S]&&($=Ve($));var I=Ve($),N=[];if(o&&N.push(D[x]<=0),a&&N.push(D[$]<=0,D[I]<=0),N.every((function(t){return t}))){T=O,E=!1;break}A.set(O,N)}if(E)for(var P=function(t){var e=v.find((function(e){var i=A.get(e);if(i)return i.slice(0,t).every((function(t){return t}))}));if(e)return T=e +,""break""},j=p?3:1;j>0&&""break""!==P(j);j--);e.placement!==T&&(e.modifiersData[n]._skip=!0,e.placement=T,e.reset=!0)}},requiresIfExists:[""offset""],data:{_skip:!1}};function oi(t,e,i){return void 0===i&&(i={x:0,y:0}),{top:t.top-e.height-i.y,right:t.right-e.width+i.x,bottom:t.bottom-e.height+i.y,left:t.left-e.width-i.x}}function ri(t){return[zt,qt,Rt,Vt].some((function(e){return t[e]>=0}))}const ai={name:""hide"",enabled:!0,phase:""main"",requiresIfExists:[""preventOverflow""],fn:function(t){var e=t.state +,i=t.name,n=e.rects.reference,s=e.rects.popper,o=e.modifiersData.preventOverflow,r=ii(e,{elementContext:""reference""}),a=ii(e,{altBoundary:!0}),l=oi(r,n),c=oi(a,s,o),h=ri(l),d=ri(c);e.modifiersData[i]={referenceClippingOffsets:l,popperEscapeOffsets:c,isReferenceHidden:h,hasPopperEscaped:d},e.attributes.popper=Object.assign({},e.attributes.popper,{""data-popper-reference-hidden"":h,""data-popper-escaped"":d})}},li={name:""offset"",enabled:!0,phase:""main"",requires:[""popperOffsets""],fn:function(t){var e=t.state +,i=t.options,n=t.name,s=i.offset,o=void 0===s?[0,0]:s,r=ee.reduce((function(t,i){return t[i]=function(t,e,i){var n=be(t),s=[Vt,zt].indexOf(n)>=0?-1:1,o=""function""==typeof i?i(Object.assign({},e,{placement:t})):i,r=o[0],a=o[1];return r=r||0,a=(a||0)*s,[Vt,qt].indexOf(n)>=0?{x:a,y:r}:{x:r,y:a}}(i,e.rects,o),t}),{}),a=r[e.placement],l=a.x,c=a.y;null!=e.modifiersData.popperOffsets&&(e.modifiersData.popperOffsets.x+=l,e.modifiersData.popperOffsets.y+=c),e.modifiersData[n]=r}},ci={name:""popperOffsets"" +,enabled:!0,phase:""read"",fn:function(t){var e=t.state,i=t.name;e.modifiersData[i]=ei({reference:e.rects.reference,element:e.rects.popper,strategy:""absolute"",placement:e.placement})},data:{}},hi={name:""preventOverflow"",enabled:!0,phase:""main"",fn:function(t){var e=t.state,i=t.options,n=t.name,s=i.mainAxis,o=void 0===s||s,r=i.altAxis,a=void 0!==r&&r,l=i.boundary,c=i.rootBoundary,h=i.altBoundary,d=i.padding,u=i.tether,f=void 0===u||u,p=i.tetherOffset,m=void 0===p?0:p,g=ii(e,{boundary:l,rootBoundary:c +,padding:d,altBoundary:h}),_=be(e.placement),b=Fe(e.placement),v=!b,y=Ie(_),w=""x""===y?""y"":""x"",A=e.modifiersData.popperOffsets,E=e.rects.reference,T=e.rects.popper,C=""function""==typeof m?m(Object.assign({},e.rects,{placement:e.placement})):m,O=""number""==typeof C?{mainAxis:C,altAxis:C}:Object.assign({mainAxis:0,altAxis:0},C),x=e.modifiersData.offset?e.modifiersData.offset[e.placement]:null,k={x:0,y:0};if(A){if(o){var L,S=""y""===y?zt:Vt,D=""y""===y?Rt:qt,$=""y""===y?""height"":""width"",I=A[y],N=I+g[S],P=I-g[D] +,j=f?-T[$]/2:0,M=b===Xt?E[$]:T[$],F=b===Xt?-T[$]:-E[$],H=e.elements.arrow,W=f&&H?Ce(H):{width:0,height:0},B=e.modifiersData[""arrow#persistent""]?e.modifiersData[""arrow#persistent""].padding:{top:0,right:0,bottom:0,left:0},z=B[S],R=B[D],q=Ne(0,E[$],W[$]),V=v?E[$]/2-j-q-z-O.mainAxis:M-q-z-O.mainAxis,K=v?-E[$]/2+j+q+R+O.mainAxis:F+q+R+O.mainAxis,Q=e.elements.arrow&&$e(e.elements.arrow),X=Q?""y""===y?Q.clientTop||0:Q.clientLeft||0:0,Y=null!=(L=null==x?void 0:x[y])?L:0,U=I+K-Y,G=Ne(f?ye(N,I+V-Y-X):N,I,f?ve(P +,U):P);A[y]=G,k[y]=G-I}if(a){var J,Z=""x""===y?zt:Vt,tt=""x""===y?Rt:qt,et=A[w],it=""y""===w?""height"":""width"",nt=et+g[Z],st=et-g[tt],ot=-1!==[zt,Vt].indexOf(_),rt=null!=(J=null==x?void 0:x[w])?J:0,at=ot?nt:et-E[it]-T[it]-rt+O.altAxis,lt=ot?et+E[it]+T[it]-rt-O.altAxis:st,ct=f&&ot?function(t,e,i){var n=Ne(t,e,i);return n>i?i:n}(at,et,lt):Ne(f?at:nt,et,f?lt:st);A[w]=ct,k[w]=ct-et}e.modifiersData[n]=k}},requiresIfExists:[""offset""]};function di(t,e,i){void 0===i&&(i=!1);var n,s,o=me(e),r=me(e)&&function(t){var + e=t.getBoundingClientRect(),i=we(e.width)/t.offsetWidth||1,n=we(e.height)/t.offsetHeight||1;return 1!==i||1!==n}(e),a=Le(e),l=Te(t,r,i),c={scrollLeft:0,scrollTop:0},h={x:0,y:0};return(o||!o&&!i)&&((""body""!==ue(e)||Ue(a))&&(c=(n=e)!==fe(n)&&me(n)?{scrollLeft:(s=n).scrollLeft,scrollTop:s.scrollTop}:Xe(n)),me(e)?((h=Te(e,!0)).x+=e.clientLeft,h.y+=e.clientTop):a&&(h.x=Ye(a))),{x:l.left+c.scrollLeft-h.x,y:l.top+c.scrollTop-h.y,width:l.width,height:l.height}}function ui(t){var e=new Map,i=new Set,n=[] +;function s(t){i.add(t.name),[].concat(t.requires||[],t.requiresIfExists||[]).forEach((function(t){if(!i.has(t)){var n=e.get(t);n&&s(n)}})),n.push(t)}return t.forEach((function(t){e.set(t.name,t)})),t.forEach((function(t){i.has(t.name)||s(t)})),n}var fi={placement:""bottom"",modifiers:[],strategy:""absolute""};function pi(){for(var t=arguments.length,e=new Array(t),i=0;iNumber.parseInt(t,10))):""function""==typeof t?e=>t(e,this._element):t}_getPopperConfig(){const t={placement:this._getPlacement(),modifiers:[{name:""preventOverflow"",options:{boundary:this._config.boundary}},{name:""offset"",options:{offset:this._getOffset()}}]};return(this._inNavbar||""static""===this._config.display)&&(F.setDataAttribute(this._menu +,""popper"",""static""),t.modifiers=[{name:""applyStyles"",enabled:!1}]),{...t,...g(this._config.popperConfig,[t])}}_selectMenuItem({key:t,target:e}){const i=z.find("".dropdown-menu .dropdown-item:not(.disabled):not(:disabled)"",this._menu).filter((t=>a(t)));i.length&&b(i,e,t===Ti,!i.includes(e)).focus()}static jQueryInterface(t){return this.each((function(){const e=qi.getOrCreateInstance(this,t);if(""string""==typeof t){if(void 0===e[t])throw new TypeError(`No method named ""${t}""`);e[t]()}}))}static clearMenus(t){if(2===t.button||""keyup""===t.type&&""Tab""!==t.key)return +;const e=z.find(Ni);for(const i of e){const e=qi.getInstance(i);if(!e||!1===e._config.autoClose)continue;const n=t.composedPath(),s=n.includes(e._menu);if(n.includes(e._element)||""inside""===e._config.autoClose&&!s||""outside""===e._config.autoClose&&s)continue;if(e._menu.contains(t.target)&&(""keyup""===t.type&&""Tab""===t.key||/input|select|option|textarea|form/i.test(t.target.tagName)))continue;const o={relatedTarget:e._element};""click""===t.type&&(o.clickEvent=t),e._completeHide(o)}}static dataApiKeydownHandler(t){const + e=/input|textarea/i.test(t.target.tagName),i=""Escape""===t.key,n=[Ei,Ti].includes(t.key);if(!n&&!i)return;if(e&&!i)return;t.preventDefault();const s=this.matches(Ii)?this:z.prev(this,Ii)[0]||z.next(this,Ii)[0]||z.findOne(Ii,t.delegateTarget.parentNode),o=qi.getOrCreateInstance(s);if(n)return t.stopPropagation(),o.show(),void o._selectMenuItem(t);o._isShown()&&(t.stopPropagation(),o.hide(),s.focus())}}N.on(document,Si,Ii,qi.dataApiKeydownHandler),N.on(document,Si,Pi,qi.dataApiKeydownHandler),N.on(document +,Li,qi.clearMenus),N.on(document,Di,qi.clearMenus),N.on(document,Li,Ii,(function(t){t.preventDefault(),qi.getOrCreateInstance(this).toggle()})),m(qi);const Vi=""backdrop"",Ki=""show"",Qi=`mousedown.bs.${Vi}`,Xi={className:""modal-backdrop"",clickCallback:null,isAnimated:!1,isVisible:!0,rootElement:""body""},Yi={className:""string"",clickCallback:""(function|null)"",isAnimated:""boolean"",isVisible:""boolean"",rootElement:""(element|string)""};class Ui extends H{constructor(t){super(),this._config=this._getConfig(t) +,this._isAppended=!1,this._element=null}static get Default(){return Xi}static get DefaultType(){return Yi}static get NAME(){return Vi}show(t){if(!this._config.isVisible)return void g(t);this._append();const e=this._getElement();this._config.isAnimated&&d(e),e.classList.add(Ki),this._emulateAnimation((()=>{g(t)}))}hide(t){this._config.isVisible?(this._getElement().classList.remove(Ki),this._emulateAnimation((()=>{this.dispose(),g(t)}))):g(t)}dispose(){this._isAppended&&(N.off(this._element,Qi),this._element.remove() +,this._isAppended=!1)}_getElement(){if(!this._element){const t=document.createElement(""div"");t.className=this._config.className,this._config.isAnimated&&t.classList.add(""fade""),this._element=t}return this._element}_configAfterMerge(t){return t.rootElement=r(t.rootElement),t}_append(){if(this._isAppended)return;const t=this._getElement();this._config.rootElement.append(t),N.on(t,Qi,(()=>{g(this._config.clickCallback)})),this._isAppended=!0}_emulateAnimation(t){_(t,this._getElement(),this._config.isAnimated)}}const + Gi="".bs.focustrap"",Ji=`focusin${Gi}`,Zi=`keydown.tab${Gi}`,tn=""backward"",en={autofocus:!0,trapElement:null},nn={autofocus:""boolean"",trapElement:""element""};class sn extends H{constructor(t){super(),this._config=this._getConfig(t),this._isActive=!1,this._lastTabNavDirection=null}static get Default(){return en}static get DefaultType(){return nn}static get NAME(){return""focustrap""}activate(){this._isActive||(this._config.autofocus&&this._config.trapElement.focus(),N.off(document,Gi),N.on(document,Ji +,(t=>this._handleFocusin(t))),N.on(document,Zi,(t=>this._handleKeydown(t))),this._isActive=!0)}deactivate(){this._isActive&&(this._isActive=!1,N.off(document,Gi))}_handleFocusin(t){const{trapElement:e}=this._config;if(t.target===document||t.target===e||e.contains(t.target))return;const i=z.focusableChildren(e);0===i.length?e.focus():this._lastTabNavDirection===tn?i[i.length-1].focus():i[0].focus()}_handleKeydown(t){""Tab""===t.key&&(this._lastTabNavDirection=t.shiftKey?tn:""forward"")}}const +on="".fixed-top, .fixed-bottom, .is-fixed, .sticky-top"",rn="".sticky-top"",an=""padding-right"",ln=""margin-right"";class cn{constructor(){this._element=document.body}getWidth(){const t=document.documentElement.clientWidth;return Math.abs(window.innerWidth-t)}hide(){const t=this.getWidth();this._disableOverFlow(),this._setElementAttributes(this._element,an,(e=>e+t)),this._setElementAttributes(on,an,(e=>e+t)),this._setElementAttributes(rn,ln,(e=>e-t))}reset(){this._resetElementAttributes(this._element,""overflow""),this._resetElementAttributes(this._element +,an),this._resetElementAttributes(on,an),this._resetElementAttributes(rn,ln)}isOverflowing(){return this.getWidth()>0}_disableOverFlow(){this._saveInitialAttribute(this._element,""overflow""),this._element.style.overflow=""hidden""}_setElementAttributes(t,e,i){const n=this.getWidth();this._applyManipulationCallback(t,(t=>{if(t!==this._element&&window.innerWidth>t.clientWidth+n)return;this._saveInitialAttribute(t,e);const s=window.getComputedStyle(t).getPropertyValue(e);t.style.setProperty(e,`${i(Number.parseFloat(s))}px`)}))}_saveInitialAttribute(t +,e){const i=t.style.getPropertyValue(e);i&&F.setDataAttribute(t,e,i)}_resetElementAttributes(t,e){this._applyManipulationCallback(t,(t=>{const i=F.getDataAttribute(t,e);null!==i?(F.removeDataAttribute(t,e),t.style.setProperty(e,i)):t.style.removeProperty(e)}))}_applyManipulationCallback(t,e){if(o(t))e(t);else for(const i of z.find(t,this._element))e(i)}}const hn="".bs.modal"",dn=`hide${hn}`,un=`hidePrevented${hn}`,fn=`hidden${hn}`,pn=`show${hn}`,mn=`shown${hn}`,gn=`resize${hn}`,_n=`click.dismiss${hn}` +,bn=`mousedown.dismiss${hn}`,vn=`keydown.dismiss${hn}`,yn=`click${hn}.data-api`,wn=""modal-open"",An=""show"",En=""modal-static"",Tn={backdrop:!0,focus:!0,keyboard:!0},Cn={backdrop:""(boolean|string)"",focus:""boolean"",keyboard:""boolean""};class On extends W{constructor(t,e){super(t,e),this._dialog=z.findOne("".modal-dialog"",this._element),this._backdrop=this._initializeBackDrop(),this._focustrap=this._initializeFocusTrap(),this._isShown=!1,this._isTransitioning=!1,this._scrollBar=new cn,this._addEventListeners()}static + get Default(){return Tn}static get DefaultType(){return Cn}static get NAME(){return""modal""}toggle(t){return this._isShown?this.hide():this.show(t)}show(t){this._isShown||this._isTransitioning||N.trigger(this._element,pn,{relatedTarget:t}).defaultPrevented||(this._isShown=!0,this._isTransitioning=!0,this._scrollBar.hide(),document.body.classList.add(wn),this._adjustDialog(),this._backdrop.show((()=>this._showElement(t))))}hide(){this._isShown&&!this._isTransitioning&&(N.trigger(this._element,dn).defaultPrevented||(this._isShown=!1 +,this._isTransitioning=!0,this._focustrap.deactivate(),this._element.classList.remove(An),this._queueCallback((()=>this._hideModal()),this._element,this._isAnimated())))}dispose(){N.off(window,hn),N.off(this._dialog,hn),this._backdrop.dispose(),this._focustrap.deactivate(),super.dispose()}handleUpdate(){this._adjustDialog()}_initializeBackDrop(){return new Ui({isVisible:Boolean(this._config.backdrop),isAnimated:this._isAnimated()})}_initializeFocusTrap(){return new sn({trapElement:this._element})}_showElement(t){document.body.contains(this._element)||document.body.append(this._element) +,this._element.style.display=""block"",this._element.removeAttribute(""aria-hidden""),this._element.setAttribute(""aria-modal"",!0),this._element.setAttribute(""role"",""dialog""),this._element.scrollTop=0;const e=z.findOne("".modal-body"",this._dialog);e&&(e.scrollTop=0),d(this._element),this._element.classList.add(An),this._queueCallback((()=>{this._config.focus&&this._focustrap.activate(),this._isTransitioning=!1,N.trigger(this._element,mn,{relatedTarget:t})}),this._dialog,this._isAnimated())}_addEventListeners(){N.on(this._element +,vn,(t=>{""Escape""===t.key&&(this._config.keyboard?this.hide():this._triggerBackdropTransition())})),N.on(window,gn,(()=>{this._isShown&&!this._isTransitioning&&this._adjustDialog()})),N.on(this._element,bn,(t=>{N.one(this._element,_n,(e=>{this._element===t.target&&this._element===e.target&&(""static""!==this._config.backdrop?this._config.backdrop&&this.hide():this._triggerBackdropTransition())}))}))}_hideModal(){this._element.style.display=""none"",this._element.setAttribute(""aria-hidden"",!0),this._element.removeAttribute(""aria-modal"") +,this._element.removeAttribute(""role""),this._isTransitioning=!1,this._backdrop.hide((()=>{document.body.classList.remove(wn),this._resetAdjustments(),this._scrollBar.reset(),N.trigger(this._element,fn)}))}_isAnimated(){return this._element.classList.contains(""fade"")}_triggerBackdropTransition(){if(N.trigger(this._element,un).defaultPrevented)return;const t=this._element.scrollHeight>document.documentElement.clientHeight,e=this._element.style.overflowY;""hidden""===e||this._element.classList.contains(En)||(t||(this._element.style.overflowY=""hidden"") +,this._element.classList.add(En),this._queueCallback((()=>{this._element.classList.remove(En),this._queueCallback((()=>{this._element.style.overflowY=e}),this._dialog)}),this._dialog),this._element.focus())}_adjustDialog(){const t=this._element.scrollHeight>document.documentElement.clientHeight,e=this._scrollBar.getWidth(),i=e>0;if(i&&!t){const t=p()?""paddingLeft"":""paddingRight"";this._element.style[t]=`${e}px`}if(!i&&t){const t=p()?""paddingRight"":""paddingLeft"";this._element.style[t]=`${e}px`}}_resetAdjustments(){this._element.style.paddingLeft="""" +,this._element.style.paddingRight=""""}static jQueryInterface(t,e){return this.each((function(){const i=On.getOrCreateInstance(this,t);if(""string""==typeof t){if(void 0===i[t])throw new TypeError(`No method named ""${t}""`);i[t](e)}}))}}N.on(document,yn,'[data-bs-toggle=""modal""]',(function(t){const e=z.getElementFromSelector(this);[""A"",""AREA""].includes(this.tagName)&&t.preventDefault(),N.one(e,pn,(t=>{t.defaultPrevented||N.one(e,fn,(()=>{a(this)&&this.focus()}))}));const i=z.findOne("".modal.show"");i&&On.getInstance(i).hide() +,On.getOrCreateInstance(e).toggle(this)})),R(On),m(On);const xn="".bs.offcanvas"",kn="".data-api"",Ln=`load${xn}${kn}`,Sn=""show"",Dn=""showing"",$n=""hiding"",In="".offcanvas.show"",Nn=`show${xn}`,Pn=`shown${xn}`,jn=`hide${xn}`,Mn=`hidePrevented${xn}`,Fn=`hidden${xn}`,Hn=`resize${xn}`,Wn=`click${xn}${kn}`,Bn=`keydown.dismiss${xn}`,zn={backdrop:!0,keyboard:!0,scroll:!1},Rn={backdrop:""(boolean|string)"",keyboard:""boolean"",scroll:""boolean""};class qn extends W{constructor(t,e){super(t,e),this._isShown=!1,this._backdrop=this._initializeBackDrop() +,this._focustrap=this._initializeFocusTrap(),this._addEventListeners()}static get Default(){return zn}static get DefaultType(){return Rn}static get NAME(){return""offcanvas""}toggle(t){return this._isShown?this.hide():this.show(t)}show(t){this._isShown||N.trigger(this._element,Nn,{relatedTarget:t}).defaultPrevented||(this._isShown=!0,this._backdrop.show(),this._config.scroll||(new cn).hide(),this._element.setAttribute(""aria-modal"",!0),this._element.setAttribute(""role"",""dialog""),this._element.classList.add(Dn) +,this._queueCallback((()=>{this._config.scroll&&!this._config.backdrop||this._focustrap.activate(),this._element.classList.add(Sn),this._element.classList.remove(Dn),N.trigger(this._element,Pn,{relatedTarget:t})}),this._element,!0))}hide(){this._isShown&&(N.trigger(this._element,jn).defaultPrevented||(this._focustrap.deactivate(),this._element.blur(),this._isShown=!1,this._element.classList.add($n),this._backdrop.hide(),this._queueCallback((()=>{this._element.classList.remove(Sn,$n),this._element.removeAttribute(""aria-modal"") +,this._element.removeAttribute(""role""),this._config.scroll||(new cn).reset(),N.trigger(this._element,Fn)}),this._element,!0)))}dispose(){this._backdrop.dispose(),this._focustrap.deactivate(),super.dispose()}_initializeBackDrop(){const t=Boolean(this._config.backdrop);return new Ui({className:""offcanvas-backdrop"",isVisible:t,isAnimated:!0,rootElement:this._element.parentNode,clickCallback:t?()=>{""static""!==this._config.backdrop?this.hide():N.trigger(this._element,Mn)}:null})}_initializeFocusTrap(){return + new sn({trapElement:this._element})}_addEventListeners(){N.on(this._element,Bn,(t=>{""Escape""===t.key&&(this._config.keyboard?this.hide():N.trigger(this._element,Mn))}))}static jQueryInterface(t){return this.each((function(){const e=qn.getOrCreateInstance(this,t);if(""string""==typeof t){if(void 0===e[t]||t.startsWith(""_"")||""constructor""===t)throw new TypeError(`No method named ""${t}""`);e[t](this)}}))}}N.on(document,Wn,'[data-bs-toggle=""offcanvas""]',(function(t){const e=z.getElementFromSelector(this) +;if([""A"",""AREA""].includes(this.tagName)&&t.preventDefault(),l(this))return;N.one(e,Fn,(()=>{a(this)&&this.focus()}));const i=z.findOne(In);i&&i!==e&&qn.getInstance(i).hide(),qn.getOrCreateInstance(e).toggle(this)})),N.on(window,Ln,(()=>{for(const t of z.find(In))qn.getOrCreateInstance(t).show()})),N.on(window,Hn,(()=>{for(const t of z.find(""[aria-modal][class*=show][class*=offcanvas-]""))""fixed""!==getComputedStyle(t).position&&qn.getOrCreateInstance(t).hide()})),R(qn),m(qn);const Vn={""*"":[""class"" +,""dir"",""id"",""lang"",""role"",/^aria-[\w-]*$/i],a:[""target"",""href"",""title"",""rel""],area:[],b:[],br:[],col:[],code:[],dd:[],div:[],dl:[],dt:[],em:[],hr:[],h1:[],h2:[],h3:[],h4:[],h5:[],h6:[],i:[],img:[""src"",""srcset"",""alt"",""title"",""width"",""height""],li:[],ol:[],p:[],pre:[],s:[],small:[],span:[],sub:[],sup:[],strong:[],u:[],ul:[]},Kn=new Set([""background"",""cite"",""href"",""itemtype"",""longdesc"",""poster"",""src"",""xlink:href""]),Qn=/^(?!javascript:)(?:[a-z0-9+.-]+:|[^&:/?#]*(?:[/?#]|$))/i,Xn=(t,e)=>{const i=t.nodeName.toLowerCase() +;return e.includes(i)?!Kn.has(i)||Boolean(Qn.test(t.nodeValue)):e.filter((t=>t instanceof RegExp)).some((t=>t.test(i)))},Yn={allowList:Vn,content:{},extraClass:"""",html:!1,sanitize:!0,sanitizeFn:null,template:""
""},Un={allowList:""object"",content:""object"",extraClass:""(string|function)"",html:""boolean"",sanitize:""boolean"",sanitizeFn:""(null|function)"",template:""string""},Gn={entry:""(string|element|function|null)"",selector:""(string|element)""};class Jn extends H{constructor(t){super(),this._config=this._getConfig(t)}static + get Default(){return Yn}static get DefaultType(){return Un}static get NAME(){return""TemplateFactory""}getContent(){return Object.values(this._config.content).map((t=>this._resolvePossibleFunction(t))).filter(Boolean)}hasContent(){return this.getContent().length>0}changeContent(t){return this._checkContent(t),this._config.content={...this._config.content,...t},this}toHtml(){const t=document.createElement(""div"");t.innerHTML=this._maybeSanitize(this._config.template);for(const[e,i]of Object.entries(this._config.content))this._setContent(t +,i,e);const e=t.children[0],i=this._resolvePossibleFunction(this._config.extraClass);return i&&e.classList.add(...i.split("" "")),e}_typeCheckConfig(t){super._typeCheckConfig(t),this._checkContent(t.content)}_checkContent(t){for(const[e,i]of Object.entries(t))super._typeCheckConfig({selector:e,entry:i},Gn)}_setContent(t,e,i){const n=z.findOne(i,t);n&&((e=this._resolvePossibleFunction(e))?o(e)?this._putElementInTemplate(r(e),n):this._config.html?n.innerHTML=this._maybeSanitize(e):n.textContent=e:n.remove())}_maybeSanitize(t){return + this._config.sanitize?function(t,e,i){if(!t.length)return t;if(i&&""function""==typeof i)return i(t);const n=(new window.DOMParser).parseFromString(t,""text/html""),s=[].concat(...n.body.querySelectorAll(""*""));for(const t of s){const i=t.nodeName.toLowerCase();if(!Object.keys(e).includes(i)){t.remove();continue}const n=[].concat(...t.attributes),s=[].concat(e[""*""]||[],e[i]||[]);for(const e of n)Xn(e,s)||t.removeAttribute(e.nodeName)}return n.body.innerHTML}(t,this._config.allowList,this._config.sanitizeFn):t}_resolvePossibleFunction(t){return + g(t,[this])}_putElementInTemplate(t,e){if(this._config.html)return e.innerHTML="""",void e.append(t);e.textContent=t.textContent}}const Zn=new Set([""sanitize"",""allowList"",""sanitizeFn""]),ts=""fade"",es=""show"",is="".modal"",ns=""hide.bs.modal"",ss=""hover"",os=""focus"",rs={AUTO:""auto"",TOP:""top"",RIGHT:p()?""left"":""right"",BOTTOM:""bottom"",LEFT:p()?""right"":""left""},as={allowList:Vn,animation:!0,boundary:""clippingParents"",container:!1,customClass:"""",delay:0,fallbackPlacements:[""top"",""right"",""bottom"",""left""],html:!1 +,offset:[0,6],placement:""top"",popperConfig:null,sanitize:!0,sanitizeFn:null,selector:!1,template:'
',title:"""",trigger:""hover focus""},ls={allowList:""object"",animation:""boolean"",boundary:""(string|element)"",container:""(string|element|boolean)"",customClass:""(string|function)"",delay:""(number|object)"",fallbackPlacements:""array"",html:""boolean"",offset:""(array|string|function)"",placement:""(string|function)"" +,popperConfig:""(null|object|function)"",sanitize:""boolean"",sanitizeFn:""(null|function)"",selector:""(string|boolean)"",template:""string"",title:""(string|element|function)"",trigger:""string""};class cs extends W{constructor(t,e){if(void 0===vi)throw new TypeError(""Bootstrap's tooltips require Popper (https://popper.js.org)"");super(t,e),this._isEnabled=!0,this._timeout=0,this._isHovered=null,this._activeTrigger={},this._popper=null,this._templateFactory=null,this._newContent=null,this.tip=null,this._setListeners() +,this._config.selector||this._fixTitle()}static get Default(){return as}static get DefaultType(){return ls}static get NAME(){return""tooltip""}enable(){this._isEnabled=!0}disable(){this._isEnabled=!1}toggleEnabled(){this._isEnabled=!this._isEnabled}toggle(){this._isEnabled&&(this._activeTrigger.click=!this._activeTrigger.click,this._isShown()?this._leave():this._enter())}dispose(){clearTimeout(this._timeout),N.off(this._element.closest(is),ns,this._hideModalHandler),this._element.getAttribute(""data-bs-original-title"")&&this._element.setAttribute(""title"" +,this._element.getAttribute(""data-bs-original-title"")),this._disposePopper(),super.dispose()}show(){if(""none""===this._element.style.display)throw new Error(""Please use show on visible elements"");if(!this._isWithContent()||!this._isEnabled)return;const t=N.trigger(this._element,this.constructor.eventName(""show"")),e=(c(this._element)||this._element.ownerDocument.documentElement).contains(this._element);if(t.defaultPrevented||!e)return;this._disposePopper();const i=this._getTipElement();this._element.setAttribute(""aria-describedby"" +,i.getAttribute(""id""));const{container:n}=this._config;if(this._element.ownerDocument.documentElement.contains(this.tip)||(n.append(i),N.trigger(this._element,this.constructor.eventName(""inserted""))),this._popper=this._createPopper(i),i.classList.add(es),""ontouchstart""in document.documentElement)for(const t of[].concat(...document.body.children))N.on(t,""mouseover"",h);this._queueCallback((()=>{N.trigger(this._element,this.constructor.eventName(""shown"")),!1===this._isHovered&&this._leave(),this._isHovered=!1}) +,this.tip,this._isAnimated())}hide(){if(this._isShown()&&!N.trigger(this._element,this.constructor.eventName(""hide"")).defaultPrevented){if(this._getTipElement().classList.remove(es),""ontouchstart""in document.documentElement)for(const t of[].concat(...document.body.children))N.off(t,""mouseover"",h);this._activeTrigger.click=!1,this._activeTrigger[os]=!1,this._activeTrigger[ss]=!1,this._isHovered=null,this._queueCallback((()=>{this._isWithActiveTrigger()||(this._isHovered||this._disposePopper(),this._element.removeAttribute(""aria-describedby"") +,N.trigger(this._element,this.constructor.eventName(""hidden"")))}),this.tip,this._isAnimated())}}update(){this._popper&&this._popper.update()}_isWithContent(){return Boolean(this._getTitle())}_getTipElement(){return this.tip||(this.tip=this._createTipElement(this._newContent||this._getContentForTemplate())),this.tip}_createTipElement(t){const e=this._getTemplateFactory(t).toHtml();if(!e)return null;e.classList.remove(ts,es),e.classList.add(`bs-${this.constructor.NAME}-auto`);const i=(t=>{do{t+=Math.floor(1e6*Math.random())}while(document.getElementById(t)) +;return t})(this.constructor.NAME).toString();return e.setAttribute(""id"",i),this._isAnimated()&&e.classList.add(ts),e}setContent(t){this._newContent=t,this._isShown()&&(this._disposePopper(),this.show())}_getTemplateFactory(t){return this._templateFactory?this._templateFactory.changeContent(t):this._templateFactory=new Jn({...this._config,content:t,extraClass:this._resolvePossibleFunction(this._config.customClass)}),this._templateFactory}_getContentForTemplate(){return{"".tooltip-inner"":this._getTitle()}}_getTitle(){return + this._resolvePossibleFunction(this._config.title)||this._element.getAttribute(""data-bs-original-title"")}_initializeOnDelegatedTarget(t){return this.constructor.getOrCreateInstance(t.delegateTarget,this._getDelegateConfig())}_isAnimated(){return this._config.animation||this.tip&&this.tip.classList.contains(ts)}_isShown(){return this.tip&&this.tip.classList.contains(es)}_createPopper(t){const e=g(this._config.placement,[this,t,this._element]),i=rs[e.toUpperCase()];return bi(this._element,t,this._getPopperConfig(i))}_getOffset(){const{offset:t}=this._config +;return""string""==typeof t?t.split("","").map((t=>Number.parseInt(t,10))):""function""==typeof t?e=>t(e,this._element):t}_resolvePossibleFunction(t){return g(t,[this._element])}_getPopperConfig(t){const e={placement:t,modifiers:[{name:""flip"",options:{fallbackPlacements:this._config.fallbackPlacements}},{name:""offset"",options:{offset:this._getOffset()}},{name:""preventOverflow"",options:{boundary:this._config.boundary}},{name:""arrow"",options:{element:`.${this.constructor.NAME}-arrow`}},{name:""preSetPlacement"" +,enabled:!0,phase:""beforeMain"",fn:t=>{this._getTipElement().setAttribute(""data-popper-placement"",t.state.placement)}}]};return{...e,...g(this._config.popperConfig,[e])}}_setListeners(){const t=this._config.trigger.split("" "");for(const e of t)if(""click""===e)N.on(this._element,this.constructor.eventName(""click""),this._config.selector,(t=>{this._initializeOnDelegatedTarget(t).toggle()}));else if(""manual""!==e){const t=e===ss?this.constructor.eventName(""mouseenter""):this.constructor.eventName(""focusin"") +,i=e===ss?this.constructor.eventName(""mouseleave""):this.constructor.eventName(""focusout"");N.on(this._element,t,this._config.selector,(t=>{const e=this._initializeOnDelegatedTarget(t);e._activeTrigger[""focusin""===t.type?os:ss]=!0,e._enter()})),N.on(this._element,i,this._config.selector,(t=>{const e=this._initializeOnDelegatedTarget(t);e._activeTrigger[""focusout""===t.type?os:ss]=e._element.contains(t.relatedTarget),e._leave()}))}this._hideModalHandler=()=>{this._element&&this.hide()},N.on(this._element.closest(is) +,ns,this._hideModalHandler)}_fixTitle(){const t=this._element.getAttribute(""title"");t&&(this._element.getAttribute(""aria-label"")||this._element.textContent.trim()||this._element.setAttribute(""aria-label"",t),this._element.setAttribute(""data-bs-original-title"",t),this._element.removeAttribute(""title""))}_enter(){this._isShown()||this._isHovered?this._isHovered=!0:(this._isHovered=!0,this._setTimeout((()=>{this._isHovered&&this.show()}),this._config.delay.show))}_leave(){this._isWithActiveTrigger()||(this._isHovered=!1 +,this._setTimeout((()=>{this._isHovered||this.hide()}),this._config.delay.hide))}_setTimeout(t,e){clearTimeout(this._timeout),this._timeout=setTimeout(t,e)}_isWithActiveTrigger(){return Object.values(this._activeTrigger).includes(!0)}_getConfig(t){const e=F.getDataAttributes(this._element);for(const t of Object.keys(e))Zn.has(t)&&delete e[t];return t={...e,...""object""==typeof t&&t?t:{}},t=this._mergeConfigObj(t),t=this._configAfterMerge(t),this._typeCheckConfig(t),t}_configAfterMerge(t){return t.container=!1===t.container?document.body:r(t.container) +,""number""==typeof t.delay&&(t.delay={show:t.delay,hide:t.delay}),""number""==typeof t.title&&(t.title=t.title.toString()),""number""==typeof t.content&&(t.content=t.content.toString()),t}_getDelegateConfig(){const t={};for(const[e,i]of Object.entries(this._config))this.constructor.Default[e]!==i&&(t[e]=i);return t.selector=!1,t.trigger=""manual"",t}_disposePopper(){this._popper&&(this._popper.destroy(),this._popper=null),this.tip&&(this.tip.remove(),this.tip=null)}static jQueryInterface(t){return this.each((function(){const + e=cs.getOrCreateInstance(this,t);if(""string""==typeof t){if(void 0===e[t])throw new TypeError(`No method named ""${t}""`);e[t]()}}))}}m(cs);const hs={...cs.Default,content:"""",offset:[0,8],placement:""right"",template:'

',trigger:""click""},ds={...cs.DefaultType,content:""(null|string|element|function)""};class us extends cs{static get Default(){return hs}static get DefaultType(){return + ds}static get NAME(){return""popover""}_isWithContent(){return this._getTitle()||this._getContent()}_getContentForTemplate(){return{"".popover-header"":this._getTitle(),"".popover-body"":this._getContent()}}_getContent(){return this._resolvePossibleFunction(this._config.content)}static jQueryInterface(t){return this.each((function(){const e=us.getOrCreateInstance(this,t);if(""string""==typeof t){if(void 0===e[t])throw new TypeError(`No method named ""${t}""`);e[t]()}}))}}m(us);const fs="".bs.scrollspy"",ps=`activate${fs}` +,ms=`click${fs}`,gs=`load${fs}.data-api`,_s=""active"",bs=""[href]"",vs="".nav-link"",ys=`${vs}, .nav-item > ${vs}, .list-group-item`,ws={offset:null,rootMargin:""0px 0px -25%"",smoothScroll:!1,target:null,threshold:[.1,.5,1]},As={offset:""(number|null)"",rootMargin:""string"",smoothScroll:""boolean"",target:""element"",threshold:""array""};class Es extends W{constructor(t,e){super(t,e),this._targetLinks=new Map,this._observableSections=new Map,this._rootElement=""visible""===getComputedStyle(this._element).overflowY?null:this._element +,this._activeTarget=null,this._observer=null,this._previousScrollData={visibleEntryTop:0,parentScrollTop:0},this.refresh()}static get Default(){return ws}static get DefaultType(){return As}static get NAME(){return""scrollspy""}refresh(){this._initializeTargetsAndObservables(),this._maybeEnableSmoothScroll(),this._observer?this._observer.disconnect():this._observer=this._getNewObserver();for(const t of this._observableSections.values())this._observer.observe(t)}dispose(){this._observer.disconnect() +,super.dispose()}_configAfterMerge(t){return t.target=r(t.target)||document.body,t.rootMargin=t.offset?`${t.offset}px 0px -30%`:t.rootMargin,""string""==typeof t.threshold&&(t.threshold=t.threshold.split("","").map((t=>Number.parseFloat(t)))),t}_maybeEnableSmoothScroll(){this._config.smoothScroll&&(N.off(this._config.target,ms),N.on(this._config.target,ms,bs,(t=>{const e=this._observableSections.get(t.target.hash);if(e){t.preventDefault();const i=this._rootElement||window,n=e.offsetTop-this._element.offsetTop +;if(i.scrollTo)return void i.scrollTo({top:n,behavior:""smooth""});i.scrollTop=n}})))}_getNewObserver(){const t={root:this._rootElement,threshold:this._config.threshold,rootMargin:this._config.rootMargin};return new IntersectionObserver((t=>this._observerCallback(t)),t)}_observerCallback(t){const e=t=>this._targetLinks.get(`#${t.target.id}`),i=t=>{this._previousScrollData.visibleEntryTop=t.target.offsetTop,this._process(e(t))},n=(this._rootElement||document.documentElement).scrollTop,s=n>=this._previousScrollData.parentScrollTop +;this._previousScrollData.parentScrollTop=n;for(const o of t){if(!o.isIntersecting){this._activeTarget=null,this._clearActiveClass(e(o));continue}const t=o.target.offsetTop>=this._previousScrollData.visibleEntryTop;if(s&&t){if(i(o),!n)return}else s||t||i(o)}}_initializeTargetsAndObservables(){this._targetLinks=new Map,this._observableSections=new Map;const t=z.find(bs,this._config.target);for(const e of t){if(!e.hash||l(e))continue;const t=z.findOne(decodeURI(e.hash),this._element);a(t)&&(this._targetLinks.set(decodeURI(e.hash) +,e),this._observableSections.set(e.hash,t))}}_process(t){this._activeTarget!==t&&(this._clearActiveClass(this._config.target),this._activeTarget=t,t.classList.add(_s),this._activateParents(t),N.trigger(this._element,ps,{relatedTarget:t}))}_activateParents(t){if(t.classList.contains(""dropdown-item""))z.findOne("".dropdown-toggle"",t.closest("".dropdown"")).classList.add(_s);else for(const e of z.parents(t,"".nav, .list-group""))for(const t of z.prev(e,ys))t.classList.add(_s)}_clearActiveClass(t){t.classList.remove(_s) +;const e=z.find(`${bs}.${_s}`,t);for(const t of e)t.classList.remove(_s)}static jQueryInterface(t){return this.each((function(){const e=Es.getOrCreateInstance(this,t);if(""string""==typeof t){if(void 0===e[t]||t.startsWith(""_"")||""constructor""===t)throw new TypeError(`No method named ""${t}""`);e[t]()}}))}}N.on(window,gs,(()=>{for(const t of z.find('[data-bs-spy=""scroll""]'))Es.getOrCreateInstance(t)})),m(Es);const Ts="".bs.tab"",Cs=`hide${Ts}`,Os=`hidden${Ts}`,xs=`show${Ts}`,ks=`shown${Ts}`,Ls=`click${Ts}` +,Ss=`keydown${Ts}`,Ds=`load${Ts}`,$s=""ArrowLeft"",Is=""ArrowRight"",Ns=""ArrowUp"",Ps=""ArrowDown"",js=""Home"",Ms=""End"",Fs=""active"",Hs=""fade"",Ws=""show"",Bs="".dropdown-toggle"",zs=`:not(${Bs})`,Rs='[data-bs-toggle=""tab""], [data-bs-toggle=""pill""], [data-bs-toggle=""list""]',qs=`.nav-link${zs}, .list-group-item${zs}, [role=""tab""]${zs}, ${Rs}`,Vs=`.${Fs}[data-bs-toggle=""tab""], .${Fs}[data-bs-toggle=""pill""], .${Fs}[data-bs-toggle=""list""]`;class Ks extends W{constructor(t){super(t),this._parent= +this._element.closest('.list-group, .nav, [role=""tablist""]'),this._parent&&(this._setInitialAttributes(this._parent,this._getChildren()),N.on(this._element,Ss,(t=>this._keydown(t))))}static get NAME(){return""tab""}show(){const t=this._element;if(this._elemIsActive(t))return;const e=this._getActiveElem(),i=e?N.trigger(e,Cs,{relatedTarget:t}):null;N.trigger(t,xs,{relatedTarget:e}).defaultPrevented||i&&i.defaultPrevented||(this._deactivate(e,t),this._activate(t,e))}_activate(t,e){t&&(t.classList.add(Fs),this._activate(z.getElementFromSelector(t)) +,this._queueCallback((()=>{""tab""===t.getAttribute(""role"")?(t.removeAttribute(""tabindex""),t.setAttribute(""aria-selected"",!0),this._toggleDropDown(t,!0),N.trigger(t,ks,{relatedTarget:e})):t.classList.add(Ws)}),t,t.classList.contains(Hs)))}_deactivate(t,e){t&&(t.classList.remove(Fs),t.blur(),this._deactivate(z.getElementFromSelector(t)),this._queueCallback((()=>{""tab""===t.getAttribute(""role"")?(t.setAttribute(""aria-selected"",!1),t.setAttribute(""tabindex"",""-1""),this._toggleDropDown(t,!1),N.trigger(t,Os +,{relatedTarget:e})):t.classList.remove(Ws)}),t,t.classList.contains(Hs)))}_keydown(t){if(![$s,Is,Ns,Ps,js,Ms].includes(t.key))return;t.stopPropagation(),t.preventDefault();const e=this._getChildren().filter((t=>!l(t)));let i;if([js,Ms].includes(t.key))i=e[t.key===js?0:e.length-1];else{const n=[Is,Ps].includes(t.key);i=b(e,t.target,n,!0)}i&&(i.focus({preventScroll:!0}),Ks.getOrCreateInstance(i).show())}_getChildren(){return z.find(qs,this._parent)}_getActiveElem(){return this._getChildren().find((t=>this._elemIsActive(t)))||null}_setInitialAttributes(t +,e){this._setAttributeIfNotExists(t,""role"",""tablist"");for(const t of e)this._setInitialAttributesOnChild(t)}_setInitialAttributesOnChild(t){t=this._getInnerElement(t);const e=this._elemIsActive(t),i=this._getOuterElement(t);t.setAttribute(""aria-selected"",e),i!==t&&this._setAttributeIfNotExists(i,""role"",""presentation""),e||t.setAttribute(""tabindex"",""-1""),this._setAttributeIfNotExists(t,""role"",""tab""),this._setInitialAttributesOnTargetPanel(t)}_setInitialAttributesOnTargetPanel(t){const e=z.getElementFromSelector(t) +;e&&(this._setAttributeIfNotExists(e,""role"",""tabpanel""),t.id&&this._setAttributeIfNotExists(e,""aria-labelledby"",`${t.id}`))}_toggleDropDown(t,e){const i=this._getOuterElement(t);if(!i.classList.contains(""dropdown""))return;const n=(t,n)=>{const s=z.findOne(t,i);s&&s.classList.toggle(n,e)};n(Bs,Fs),n("".dropdown-menu"",Ws),i.setAttribute(""aria-expanded"",e)}_setAttributeIfNotExists(t,e,i){t.hasAttribute(e)||t.setAttribute(e,i)}_elemIsActive(t){return t.classList.contains(Fs)}_getInnerElement(t){return + t.matches(qs)?t:z.findOne(qs,t)}_getOuterElement(t){return t.closest("".nav-item, .list-group-item"")||t}static jQueryInterface(t){return this.each((function(){const e=Ks.getOrCreateInstance(this);if(""string""==typeof t){if(void 0===e[t]||t.startsWith(""_"")||""constructor""===t)throw new TypeError(`No method named ""${t}""`);e[t]()}}))}}N.on(document,Ls,Rs,(function(t){[""A"",""AREA""].includes(this.tagName)&&t.preventDefault(),l(this)||Ks.getOrCreateInstance(this).show()})),N.on(window,Ds,(()=>{for(const t + of z.find(Vs))Ks.getOrCreateInstance(t)})),m(Ks);const Qs="".bs.toast"",Xs=`mouseover${Qs}`,Ys=`mouseout${Qs}`,Us=`focusin${Qs}`,Gs=`focusout${Qs}`,Js=`hide${Qs}`,Zs=`hidden${Qs}`,to=`show${Qs}`,eo=`shown${Qs}`,io=""hide"",no=""show"",so=""showing"",oo={animation:""boolean"",autohide:""boolean"",delay:""number""},ro={animation:!0,autohide:!0,delay:5e3};class ao extends W{constructor(t,e){super(t,e),this._timeout=null,this._hasMouseInteraction=!1,this._hasKeyboardInteraction=!1,this._setListeners()}static get + Default(){return ro}static get DefaultType(){return oo}static get NAME(){return""toast""}show(){N.trigger(this._element,to).defaultPrevented||(this._clearTimeout(),this._config.animation&&this._element.classList.add(""fade""),this._element.classList.remove(io),d(this._element),this._element.classList.add(no,so),this._queueCallback((()=>{this._element.classList.remove(so),N.trigger(this._element,eo),this._maybeScheduleHide()}),this._element,this._config.animation))}hide(){this.isShown()&&(N.trigger(this._element +,Js).defaultPrevented||(this._element.classList.add(so),this._queueCallback((()=>{this._element.classList.add(io),this._element.classList.remove(so,no),N.trigger(this._element,Zs)}),this._element,this._config.animation)))}dispose(){this._clearTimeout(),this.isShown()&&this._element.classList.remove(no),super.dispose()}isShown(){return this._element.classList.contains(no)}_maybeScheduleHide(){this._config.autohide&&(this._hasMouseInteraction||this._hasKeyboardInteraction||(this._timeout=setTimeout((()=>{this.hide()}) +,this._config.delay)))}_onInteraction(t,e){switch(t.type){case""mouseover"":case""mouseout"":this._hasMouseInteraction=e;break;case""focusin"":case""focusout"":this._hasKeyboardInteraction=e}if(e)return void this._clearTimeout();const i=t.relatedTarget;this._element===i||this._element.contains(i)||this._maybeScheduleHide()}_setListeners(){N.on(this._element,Xs,(t=>this._onInteraction(t,!0))),N.on(this._element,Ys,(t=>this._onInteraction(t,!1))),N.on(this._element,Us,(t=>this._onInteraction(t,!0))),N.on(this._element +,Gs,(t=>this._onInteraction(t,!1)))}_clearTimeout(){clearTimeout(this._timeout),this._timeout=null}static jQueryInterface(t){return this.each((function(){const e=ao.getOrCreateInstance(this,t);if(""string""==typeof t){if(void 0===e[t])throw new TypeError(`No method named ""${t}""`);e[t](this)}}))}}return R(ao),m(ao),{Alert:Q,Button:Y,Carousel:xt,Collapse:Bt,Dropdown:qi,Modal:On,Offcanvas:qn,Popover:us,ScrollSpy:Es,Tab:Ks,Toast:ao,Tooltip:cs}})); //# sourceMappingURL=bootstrap.bundle.min.js.map"; + + public static readonly string Jquery4_0_0_ESM = @"/*! jQuery v4.0.0-beta | (c) OpenJS Foundation and other contributors | jquery.org/license */function e(e,t){if(void 0===e||!e.document)throw Error(""jQuery requires a window with a document"");var n=[],r=Object.getPrototypeOf,i=n.slice,o=n.flat?function(e){return n.flat.call(e)}:function(e){return n.concat.apply([],e)},a=n.push,s=n.indexOf,u={},l=u.toString,c=u.hasOwnProperty,f=c.toString,p=f.call(Object),d={};function h(e){return null==e?e+"""":""object""==typeof e?u[l.call(e)]||""object"":typeof e} +function g(e){return null!=e&&e===e.window}function v(e){var t=!!e&&e.length,n=h(e);return!(""function""==typeof e||g(e))&&(""array""===n||0===t||""number""==typeof t&&t>0&&t-1 in e)}var y=e.document,m={type:!0,src:!0,nonce:!0,noModule:!0};function x(e,t,n){var r,i=(n=n||y).createElement(""script"");for(r in i.text=e,m)t&&t[r]&&(i[r]=t[r]);n.head.appendChild(i).parentNode&&i.parentNode.removeChild(i)}var b=""4.0.0-beta"",w=/HTML$/i,T=function(e,t){return new T.fn.init(e,t)};function C(e,t){return + e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()}T.fn=T.prototype={jquery:b,constructor:T,length:0,toArray:function(){return i.call(this)},get:function(e){return null==e?i.call(this):e<0?this[e+this.length]:this[e]},pushStack:function(e){var t=T.merge(this.constructor(),e);return t.prevObject=this,t},each:function(e){return T.each(this,e)},map:function(e){return this.pushStack(T.map(this,(function(t,n){return e.call(t,n,t)})))},slice:function(){return this.pushStack(i.apply(this, + arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},even:function(){return this.pushStack(T.grep(this,(function(e,t){return(t+1)%2})))},odd:function(){return this.pushStack(T.grep(this,(function(e,t){return t%2})))},eq:function(e){var t=this.length,n=+e+(e<0?t:0);return this.pushStack(n>=0&&n+~]|""+E+"")""+E+""*""),N=RegExp(E+""|>""),O=/[+~]/,H=y.documentElement,L=H.matches||H.msMatchesSelector;function P(){var e=[];function t(n,r){return e.push(n+"" "")>T.expr.cacheLength&&delete t[e.shift()],t[n+"" ""]=r}return t}function R(e){return e&&void 0!==e.getElementsByTagName&&e}var M=""\\[""+E+""*(""+A+"")(?:""+E+""*([*^$|!~]?=)""+E+""*(?:'((?:\\\\.|[^\\\\'])*)'|\""((?:\\\\.|[^\\\\\""])*)\""|(""+A+""))|)""+E+ + ""*\\]"",W="":(""+A+"")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\""((?:\\\\.|[^\\\\\""])*)\"")|((?:\\\\.|[^\\\\()[\\]]|""+M+"")*)|.*)\\)|)"",I={ID:RegExp(""^#(""+A+"")""),CLASS:RegExp(""^\\.(""+A+"")""),TAG:RegExp(""^(""+A+""|[*])""),ATTR:RegExp(""^""+M),PSEUDO:RegExp(""^""+W),CHILD:RegExp(""^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(""+E+""*(even|odd|(([+-]|)(\\d*)n|)""+E+""*(?:([+-]|)""+E+""*(\\d+)|))""+E+""*\\)|)"",""i"")},$=new RegExp(W),F=RegExp(""\\\\[\\da-fA-F]{1,6}""+E+""?|\\\\([^\\r\\n\\f])"",""g""),B=function(e,t){var n=""0x""+ + e.slice(1)-65536;return t||(n<0?String.fromCharCode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320))};function _(e){return e.replace(F,B)}function U(e){T.error(""Syntax error, unrecognized expression: ""+e)}var X=RegExp(""^""+E+""*,""+E+""*""),z=P();function V(e,t){var n,r,i,o,a,s,u,l=z[e+"" ""];if(l)return t?0:l.slice(0);a=e,s=[],u=T.expr.preFilter;while(a){for(o in(!n||(r=X.exec(a)))&&(r&&(a=a.slice(r[0].length)||a),s.push(i=[])),n=!1,(r=q.exec(a))&&(n=r.shift(),i.push({value:n,type:r[0].replace( + D,"" "")}),a=a.slice(n.length)),I)(r=T.expr.match[o].exec(a))&&(!u[o]||(r=u[o](r)))&&(n=r.shift(),i.push({value:n,type:o,matches:r}),a=a.slice(n.length));if(!n)break}return t?a.length:a?U(e):z(e,s).slice(0)}function Y(e){for(var t=0,n=e.length,r="""";t1)},removeAttr:function(e){return this.each((function(){T.removeAttr(this,e)}))}}),T.extend({attr:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o){if(void 0===e.getAttribute)return T.prop(e,t,n);if(1===o&&T.isXMLDoc(e)||(i=T.attrHooks[t.toLowerCase()]),void 0!==n){if( + null===n){T.removeAttr(e,t);return}return i&&""set""in i&&void 0!==(r=i.set(e,n,t))?r:(e.setAttribute(t,n),n)}return i&&""get""in i&&null!==(r=i.get(e,t))?r:null==(r=e.getAttribute(t))?void 0:r}},attrHooks:{},removeAttr:function(e,t){var n,r=0,i=t&&t.match(Q);if(i&&1===e.nodeType)while(n=i[r++])e.removeAttribute(n)}}),k&&(T.attrHooks.type={set:function(e,t){if(""radio""===t&&C(e,""input"")){var n=e.value;return e.setAttribute(""type"",t),n&&(e.value=n),t}}}),T.each(""checked selected async autofocus autoplay controls defer disabled hidden ismap loop multiple open readonly required scoped"".split("" ""),(function(e,t){T.attrHooks[t]={get:function(e){return null!=e.getAttribute(t)?t.toLowerCase():null},set:function(e,t,n){return!1===t?T.removeAttr(e,n):e.setAttribute(n,n),n}}}));var J=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\x80-\uFFFF\w-]/g;function K(e,t){return t?""\0""===e?"" "":e.slice(0,-1)+""\\""+e.charCodeAt(e.length-1).toString(16)+"" "":""\\""+e}T.escapeSelector=function(e){return(e+"""").replace(J,K)};var + Z=n.sort,ee=n.splice;function te(e,t){if(e===t)return ne=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)?e==y||e.ownerDocument==y&&T.contains(y,e)?-1:t==y||t.ownerDocument==y&&T.contains(y,t)?1:0:4&n?-1:1)}T.uniqueSort=function(e){var t,n=[],r=0,i=0;if(ne=!1,Z.call(e,te),ne){while(t=e[i++])t===e[i]&&(r=n.push(i));while(r--)ee.call(e,n[r],1)}return e},T.fn.uniqueSort=function(){return + this.pushStack(T.uniqueSort(i.apply(this)))};var ne,re,ie,oe,ae,se,ue=0,le=0,ce=P(),fe=P(),pe=P(),de=RegExp(E+""+"",""g""),he=RegExp(""^""+A+""$""),ge=T.extend({needsContext:RegExp(""^""+E+""*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(""+E+""*((?:-\\d)?\\d*)""+E+""*\\)|)(?=[^-]|$)"",""i"")},I),ve=/^(?:input|select|textarea|button)$/i,ye=/^h\d$/i,me=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,xe=function(){Ee()},be=Se((function(e){return!0===e.disabled&&C(e,""fieldset"")}),{dir:""parentNode"",next:""legend""});function we(e, + t,n,r){var i,o,s,u,l,c,f,p=t&&t.ownerDocument,d=t?t.nodeType:9;if(n=n||[],""string""!=typeof e||!e||1!==d&&9!==d&&11!==d)return n;if(!r&&(Ee(t),t=t||oe,se)){if(11!==d&&(l=me.exec(e))){if(i=l[1]){if(9===d)return(s=t.getElementById(i))&&a.call(n,s),n;if(p&&(s=p.getElementById(i))&&T.contains(t,s))return a.call(n,s),n}else if(l[2])return a.apply(n,t.getElementsByTagName(e)),n;else if((i=l[3])&&t.getElementsByClassName)return a.apply(n,t.getElementsByClassName(i)),n}if(!pe[e+"" ""]&&(!S||!S.test(e))){ + if(f=e,p=t,1===d&&(N.test(e)||q.test(e))){((p=O.test(e)&&R(t.parentNode)||t)!=t||k)&&((u=t.getAttribute(""id""))?u=T.escapeSelector(u):t.setAttribute(""id"",u=T.expando)),o=(c=V(e)).length;while(o--)c[o]=(u?""#""+u:"":scope"")+"" ""+Y(c[o]);f=c.join("","")}try{return a.apply(n,p.querySelectorAll(f)),n}catch(t){pe(e,!0)}finally{u===T.expando&&t.removeAttribute(""id"")}}}return Ne(e.replace(D,""$1""),t,n,r)}function Te(e){return e[T.expando]=!0,e}function Ce(e){return function(t){if(""form""in t)return + t.parentNode&&!1===t.disabled?""label""in t?""label""in t.parentNode?t.parentNode.disabled===e:t.disabled===e:t.isDisabled===e||!e!==t.isDisabled&&be(t)===e:t.disabled===e;return""label""in t&&t.disabled===e}}function je(e){return Te((function(t){return t=+t,Te((function(n,r){var i,o=e([],n.length,t),a=o.length;while(a--)n[i=o[a]]&&(n[i]=!(r[i]=n[i]))}))}))}function Ee(e){var t,n=e?e.ownerDocument||e:y;n!=oe&&9===n.nodeType&&(ae=(oe=n).documentElement,se=!T.isXMLDoc(oe),k&&y!=oe&&(t=oe.defaultView) + &&t.top!==t&&t.addEventListener(""unload"",xe))}for(re in we.matches=function(e,t){return we(e,null,null,t)},we.matchesSelector=function(e,t){if(Ee(e),se&&!pe[t+"" ""]&&(!S||!S.test(t)))try{return L.call(e,t)}catch(e){pe(t,!0)}return we(t,oe,null,[e]).length>0},T.expr={cacheLength:50,createPseudo:Te,match:ge,find:{ID:function(e,t){if(void 0!==t.getElementById&&se){var n=t.getElementById(e);return n?[n]:[]}},TAG:function(e,t){return void 0!==t.getElementsByTagName?t.getElementsByTagName(e): + t.querySelectorAll(e)},CLASS:function(e,t){if(void 0!==t.getElementsByClassName&&se)return t.getElementsByClassName(e)}},relative:{"">"":{dir:""parentNode"",first:!0},"" "":{dir:""parentNode""},""+"":{dir:""previousSibling"",first:!0},""~"":{dir:""previousSibling""}},preFilter:{ATTR:function(e){return e[1]=_(e[1]),e[3]=_(e[3]||e[4]||e[5]||""""),""~=""===e[2]&&(e[3]="" ""+e[3]+"" ""),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),""nth""===e[1].slice(0,3)?(e[3]||U(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*( + ""even""===e[3]||""odd""===e[3])),e[5]=+(e[7]+e[8]||""odd""===e[3])):e[3]&&U(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return I.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"""":n&&$.test(n)&&(t=V(n,!0))&&(t=n.indexOf("")"",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{ID:function(e){var t=_(e);return function(e){return e.getAttribute(""id"")===t}},TAG:function(e){var t=_(e).toLowerCase();return""*""===e?function(){return!0}:function(e){return C(e,t)}},CLASS: + function(e){var t=ce[e+"" ""];return t||(t=RegExp(""(^|""+E+"")""+e+""(""+E+""|$)""),ce(e,(function(e){return t.test(""string""==typeof e.className&&e.className||void 0!==e.getAttribute&&e.getAttribute(""class"")||"""")})))},ATTR:function(e,t,n){return function(r){var i=T.attr(r,e);return null==i?""!=""===t:!t||((i+="""",""=""===t)?i===n:""!=""===t?i!==n:""^=""===t?n&&0===i.indexOf(n):""*=""===t?n&&i.indexOf(n)>-1:""$=""===t?n&&i.slice(-n.length)===n:""~=""===t?("" ""+i.replace(de,"" "")+"" "").indexOf(n)>-1:""|=""===t&&(i===n|| + i.slice(0,n.length+1)===n+""-""))}},CHILD:function(e,t,n,r,i){var o=""nth""!==e.slice(0,3),a=""last""!==e.slice(-4),s=""of-type""===t;return 1===r&&0===i?function(e){return!!e.parentNode}:function(t,n,u){var l,c,f,p,d,h=o!==a?""nextSibling"":""previousSibling"",g=t.parentNode,v=s&&t.nodeName.toLowerCase(),y=!u&&!s,m=!1;if(g){if(o){while(h){f=t;while(f=f[h])if(s?C(f,v):1===f.nodeType)return!1;d=h=""only""===e&&!d&&""nextSibling""}return!0}if(d=[a?g.firstChild:g.lastChild],a&&y){m=(p=(l=(c=g[T.expando]||(g[ + T.expando]={}))[e]||[])[0]===ue&&l[1])&&l[2],f=p&&g.childNodes[p];while(f=++p&&f&&f[h]||(m=p=0)||d.pop())if(1===f.nodeType&&++m&&f===t){c[e]=[ue,p,m];break}}else if(y&&(m=p=(l=(c=t[T.expando]||(t[T.expando]={}))[e]||[])[0]===ue&&l[1]),!1===m){while(f=++p&&f&&f[h]||(m=p=0)||d.pop())if((s?C(f,v):1===f.nodeType)&&++m&&(y&&((c=f[T.expando]||(f[T.expando]={}))[e]=[ue,m]),f===t))break}return(m-=i)===r||m%r==0&&m/r>=0}}},PSEUDO:function(e,t){var n=T.expr.pseudos[e]||T.expr.setFilters[e.toLowerCase()]|| + U(""unsupported pseudo: ""+e);return n[T.expando]?n(t):n}},pseudos:{not:Te((function(e){var t=[],n=[],r=qe(e.replace(D,""$1""));return r[T.expando]?Te((function(e,t,n,i){var o,a=r(e,null,i,[]),s=e.length;while(s--)(o=a[s])&&(e[s]=!(t[s]=o))})):function(e,i,o){return t[0]=e,r(t,null,o,n),t[0]=null,!n.pop()}})),has:Te((function(e){return function(t){return we(e,t).length>0}})),contains:Te((function(e){return e=_(e),function(t){return(t.textContent||T.text(t)).indexOf(e)>-1}})),lang:Te((function(e){ + return he.test(e||"""")||U(""unsupported lang: ""+e),e=_(e).toLowerCase(),function(t){var n;do{if(n=se?t.lang:t.getAttribute(""xml:lang"")||t.getAttribute(""lang""))return(n=n.toLowerCase())===e||0===n.indexOf(e+""-"")}while((t=t.parentNode)&&1===t.nodeType);return!1}})),target:function(t){var n=e.location&&e.location.hash;return n&&n.slice(1)===t.id},root:function(e){return e===ae},focus:function(e){return e===oe.activeElement&&oe.hasFocus()&&!!(e.type||e.href||~e.tabIndex)},enabled:Ce(!1),disabled:Ce( + !0),checked:function(e){return C(e,""input"")&&!!e.checked||C(e,""option"")&&!!e.selected},selected:function(e){return k&&e.parentNode&&e.parentNode.selectedIndex,!0===e.selected},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeType<6)return!1;return!0},parent:function(e){return!T.expr.pseudos.empty(e)},header:function(e){return ye.test(e.nodeName)},input:function(e){return ve.test(e.nodeName)},button:function(e){return C(e,""input"")&&""button""===e.type||C(e,""button"")},text:function( + e){return C(e,""input"")&&""text""===e.type},first:je((function(){return[0]})),last:je((function(e,t){return[t-1]})),eq:je((function(e,t,n){return[n<0?n+t:n]})),even:je((function(e,t){for(var n=0;nt?t:n;--r>=0;)e.push(r);return e})),gt:je((function(e,t,n){for(var r=n<0?n+t:n;++r1?function(t,n,r){var i=e.length;while(i--)if(!e[i](t,n,r))return!1;return!0}:e[0]}function Ae(e,t,n,r,i){for(var o,a=[],s=0,u=e.length,l=null!=t;s-1}),l,!0),d=[function(e,t,r){var i=!u&&(r||t!=ie)||((n=t).nodeType?f(e,t,r):p(e,t,r));return n=null,i}];c-1&&(e[f]=!(u[f]=d))}}else h=Ae(h===u?h.splice(y,h.length):h),o?o(null,u,h,c):a.apply(u,h)}))}(c>1&&De(d),c>1&&Y(t.slice(0,c-1).concat({value:"" ""===t[c-2].type?""*"":""""})).replace(D,""$1""),r,c0,r=l.length>0,i=function(e,t,i,o,s){var c,f,p,d=0,h=""0"",g=e&&[],v=[],y=ie,m=e|| + r&&T.expr.find.TAG(""*"",s),x=ue+=null==y?1:Math.random()||.1;for(s&&(ie=t==oe||t||s);null!=(c=m[h]);h++){if(r&&c){f=0,t||c.ownerDocument==oe||(Ee(c),i=!se);while(p=l[f++])if(p(c,t||oe,i)){a.call(o,c);break}s&&(ue=x)}n&&((c=!p&&c)&&d--,e&&g.push(c))}if(d+=h,n&&h!==d){f=0;while(p=u[f++])p(g,v,t,i);if(e){if(d>0)while(h--)g[h]||v[h]||(v[h]=j.call(o));v=Ae(v)}a.apply(o,v),s&&!e&&v.length>0&&d+u.length>1&&T.uniqueSort(o)}return s&&(ue=x,ie=y),g},n?Te(i):i))).selector=e}return c}function Ne(e,t,n,r){ + var i,o,s,u,l,c=""function""==typeof e&&e,f=!r&&V(e=c.selector||e);if(n=n||[],1===f.length){if((o=f[0]=f[0].slice(0)).length>2&&""ID""===(s=o[0]).type&&9===t.nodeType&&se&&T.expr.relative[o[1].type]){if(!(t=(T.expr.find.ID(_(s.matches[0]),t)||[])[0]))return n;c&&(t=t.parentNode),e=e.slice(o.shift().value.length)}i=ge.needsContext.test(e)?0:o.length;while(i--){if(s=o[i],T.expr.relative[u=s.type])break;if((l=T.expr.find[u])&&(r=l(_(s.matches[0]),O.test(o[0].type)&&R(t.parentNode)||t))){if(o.splice(i, + 1),!(e=r.length&&Y(o)))return a.apply(n,r),n;break}}}return(c||qe(e,f))(r,t,!se,n,!t||O.test(e)&&R(t.parentNode)||t),n}function Oe(e,t,n){var r=[],i=void 0!==n;while((e=e[t])&&9!==e.nodeType)if(1===e.nodeType){if(i&&T(e).is(n))break;r.push(e)}return r}function He(e,t){for(var n=[];e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n}ke.prototype=T.expr.filters=T.expr.pseudos,T.expr.setFilters=new ke,Ee(),T.find=we,we.compile=qe,we.select=Ne,we.setDocument=Ee,we.tokenize=V;var + Le=T.expr.match.needsContext,Pe=/^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function Re(e){return""<""===e[0]&&"">""===e[e.length-1]&&e.length>=3}function Me(e,t,n){return""function""==typeof t?T.grep(e,(function(e,r){return!!t.call(e,r,e)!==n})):t.nodeType?T.grep(e,(function(e){return e===t!==n})):""string""!=typeof t?T.grep(e,(function(e){return s.call(t,e)>-1!==n})):T.filter(t,e,n)}T.filter=function(e,t,n){var r=t[0];return(n&&(e="":not(""+e+"")""),1===t.length&&1===r.nodeType) + ?T.find.matchesSelector(r,e)?[r]:[]:T.find.matches(e,T.grep(t,(function(e){return 1===e.nodeType})))},T.fn.extend({find:function(e){var t,n,r=this.length,i=this;if(""string""!=typeof e)return this.pushStack(T(e).filter((function(){for(t=0;t1?T.uniqueSort(n):n},filter:function(e){return this.pushStack(Me(this,e||[],!1))},not:function(e){return this.pushStack(Me(this,e||[],!0))},is:function( + e){return!!Me(this,""string""==typeof e&&Le.test(e)?T(e):e||[],!1).length}});var We,Ie=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/;(T.fn.init=function(e,t){var n,r;if(!e)return this;if(e.nodeType)return this[0]=e,this.length=1,this;if(""function""==typeof e)return void 0!==We.ready?We.ready(e):e(T);if(Re(n=e+""""))n=[null,e,null];else{if(""string""!=typeof e)return T.makeArray(e,this);n=Ie.exec(e)}if(n&&(n[1]||!t)){if(!n[1])return(r=y.getElementById(n[2]))&&(this[0]=r,this.length=1),this;if(t=t instanceof + T?t[0]:t,T.merge(this,T.parseHTML(n[1],t&&t.nodeType?t.ownerDocument||t:y,!0)),Pe.test(n[1])&&T.isPlainObject(t))for(n in t)""function""==typeof this[n]?this[n](t[n]):this.attr(n,t[n]);return this}return!t||t.jquery?(t||We).find(e):this.constructor(t).find(e)}).prototype=T.fn,We=T(y);var $e=/^(?:parents|prev(?:Until|All))/,Fe={children:!0,contents:!0,next:!0,prev:!0};function Be(e,t){while((e=e[t])&&1!==e.nodeType);return e}function _e(e){return e}function Ue(e){throw e}function Xe(e,t,n,r){var i; + try{e&&""function""==typeof(i=e.promise)?i.call(e).done(t).fail(n):e&&""function""==typeof(i=e.then)?i.call(e,t,n):t.apply(void 0,[e].slice(r))}catch(e){n(e)}}T.fn.extend({has:function(e){var t=T(e,this),n=t.length;return this.filter((function(){for(var e=0;e-1: + 1===n.nodeType&&T.find.matchesSelector(n,e))){o.push(n);break}}return this.pushStack(o.length>1?T.uniqueSort(o):o)},index:function(e){return e?""string""==typeof e?s.call(T(e),this[0]):s.call(this,e.jquery?e[0]:e):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){return this.pushStack(T.uniqueSort(T.merge(this.get(),T(e,t))))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}}),T.each({parent:function(e){var t=e.parentNode;return + t&&11!==t.nodeType?t:null},parents:function(e){return Oe(e,""parentNode"")},parentsUntil:function(e,t,n){return Oe(e,""parentNode"",n)},next:function(e){return Be(e,""nextSibling"")},prev:function(e){return Be(e,""previousSibling"")},nextAll:function(e){return Oe(e,""nextSibling"")},prevAll:function(e){return Oe(e,""previousSibling"")},nextUntil:function(e,t,n){return Oe(e,""nextSibling"",n)},prevUntil:function(e,t,n){return Oe(e,""previousSibling"",n)},siblings:function(e){return He((e.parentNode||{}) + .firstChild,e)},children:function(e){return He(e.firstChild)},contents:function(e){return null!=e.contentDocument&&r(e.contentDocument)?e.contentDocument:(C(e,""template"")&&(e=e.content||e),T.merge([],e.childNodes))}},(function(e,t){T.fn[e]=function(n,r){var i=T.map(this,t,n);return""Until""!==e.slice(-5)&&(r=n),r&&""string""==typeof r&&(i=T.filter(r,i)),this.length>1&&(Fe[e]||T.uniqueSort(i),$e.test(e)&&i.reverse()),this.pushStack(i)}})),T.Callbacks=function(e){e=""string""==typeof e?(t=e,n={},T.each( + t.match(Q)||[],(function(e,t){n[t]=!0})),n):T.extend({},e);var t,n,r,i,o,a,s=[],u=[],l=-1,c=function(){for(a=a||e.once,o=r=!0;u.length;l=-1){i=u.shift();while(++l-1)s.splice(n,1),n<=l&&l--})),this},has:function(e){return e?T.inArray(e,s)>-1:s.length>0},empty:function(){return s&&(s=[]),this},disable:function(){return a=u=[],s=i="""",this},disabled:function(){return!s},lock:function(){return a=u=[],i||r||(s=i=""""),this},locked:function(){return!!a},fireWith:function(e,t){return a||(t=[e,(t=t||[]).slice?t.slice():t],u.push(t),r||c()),this},fire:function(){return f.fireWith( + this,arguments),this},fired:function(){return!!o}};return f},T.extend({Deferred:function(t){var n=[[""notify"",""progress"",T.Callbacks(""memory""),T.Callbacks(""memory""),2],[""resolve"",""done"",T.Callbacks(""once memory""),T.Callbacks(""once memory""),0,""resolved""],[""reject"",""fail"",T.Callbacks(""once memory""),T.Callbacks(""once memory""),1,""rejected""]],r=""pending"",i={state:function(){return r},always:function(){return o.done(arguments).fail(arguments),this},catch:function(e){return i.then(null,e)},pipe: + function(){var e=arguments;return T.Deferred((function(t){T.each(n,(function(n,r){var i=""function""==typeof e[r[4]]&&e[r[4]];o[r[1]]((function(){var e=i&&i.apply(this,arguments);e&&""function""==typeof e.promise?e.promise().progress(t.notify).done(t.resolve).fail(t.reject):t[r[0]+""With""](this,i?[e]:arguments)}))})),e=null})).promise()},then:function(t,r,i){var o=0;function a(t,n,r,i){return function(){var s=this,u=arguments,l=function(){var e,l;if(!(t=o&&(r!==Ue&&(s=void 0,u=[e]),n.rejectWith(s,u))}};t?c():(T.Deferred.getErrorHook&&(c.error=T.Deferred.getErrorHook()),e.setTimeout( + c))}}return T.Deferred((function(e){n[0][3].add(a(0,e,""function""==typeof i?i:_e,e.notifyWith)),n[1][3].add(a(0,e,""function""==typeof t?t:_e)),n[2][3].add(a(0,e,""function""==typeof r?r:Ue))})).promise()},promise:function(e){return null!=e?T.extend(e,i):i}},o={};return T.each(n,(function(e,t){var a=t[2],s=t[5];i[t[1]]=a.add,s&&a.add((function(){r=s}),n[3-e][2].disable,n[3-e][3].disable,n[0][2].lock,n[0][3].lock),a.add(t[3].fire),o[t[0]]=function(){return o[t[0]+""With""](this===o?void 0:this, + arguments),this},o[t[0]+""With""]=a.fireWith})),i.promise(o),t&&t.call(o,o),o},when:function(e){var t=arguments.length,n=t,r=Array(n),o=i.call(arguments),a=T.Deferred(),s=function(e){return function(n){r[e]=this,o[e]=arguments.length>1?i.call(arguments):n,--t||a.resolveWith(r,o)}};if(t<=1&&(Xe(e,a.done(s(n)).resolve,a.reject,!t),""pending""===a.state()||""function""==typeof(o[n]&&o[n].then)))return a.then();while(n--)Xe(o[n],s(n),a.reject);return a.promise()}});var ze= + /^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;T.Deferred.exceptionHook=function(t,n){t&&ze.test(t.name)&&e.console.warn(""jQuery.Deferred exception"",t,n)},T.readyException=function(t){e.setTimeout((function(){throw t}))};var Ve=T.Deferred();function Ye(){y.removeEventListener(""DOMContentLoaded"",Ye),e.removeEventListener(""load"",Ye),T.ready()}T.fn.ready=function(e){return Ve.then(e).catch((function(e){T.readyException(e)})),this},T.extend({isReady:!1,readyWait:1,ready:function(e){!(!0===e?--T.readyWait:T.isReady)&&( + T.isReady=!0,!0!==e&&--T.readyWait>0||Ve.resolveWith(y,[T]))}}),T.ready.then=Ve.then,""loading""!==y.readyState?e.setTimeout(T.ready):(y.addEventListener(""DOMContentLoaded"",Ye),e.addEventListener(""load"",Ye));var Ge=/-([a-z])/g;function Qe(e,t){return t.toUpperCase()}function Je(e){return e.replace(Ge,Qe)}function Ke(e){return 1===e.nodeType||9===e.nodeType||!+e.nodeType}function Ze(){this.expando=T.expando+Ze.uid++}Ze.uid=1,Ze.prototype={cache:function(e){var t=e[this.expando];return!t&&( + t=Object.create(null),Ke(e)&&(e.nodeType?e[this.expando]=t:Object.defineProperty(e,this.expando,{value:t,configurable:!0}))),t},set:function(e,t,n){var r,i=this.cache(e);if(""string""==typeof t)i[Je(t)]=n;else for(r in t)i[Je(r)]=t[r];return n},get:function(e,t){return void 0===t?this.cache(e):e[this.expando]&&e[this.expando][Je(t)]},access:function(e,t,n){return void 0===t||t&&""string""==typeof t&&void 0===n?this.get(e,t):(this.set(e,t,n),void 0!==n?n:t)},remove:function(e,t){var n,r=e[ + this.expando];if(void 0!==r){if(void 0!==t){n=(t=Array.isArray(t)?t.map(Je):(t=Je(t))in r?[t]:t.match(Q)||[]).length;while(n--)delete r[t[n]]}(void 0===t||T.isEmptyObject(r))&&(e.nodeType?e[this.expando]=void 0:delete e[this.expando])}},hasData:function(e){var t=e[this.expando];return void 0!==t&&!T.isEmptyObject(t)}};var et=new Ze,tt=new Ze,nt=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,rt=/[A-Z]/g;function it(e,t,n){var r,i;if(void 0===n&&1===e.nodeType){if(r=""data-""+t.replace(rt,""-$&"").toLowerCase(), + ""string""==typeof(n=e.getAttribute(r))){try{i=n,n=""true""===i||""false""!==i&&(""null""===i?null:i===+i+""""?+i:nt.test(i)?JSON.parse(i):i)}catch(e){}tt.set(e,t,n)}else n=void 0}return n}T.extend({hasData:function(e){return tt.hasData(e)||et.hasData(e)},data:function(e,t,n){return tt.access(e,t,n)},removeData:function(e,t){tt.remove(e,t)},_data:function(e,t,n){return et.access(e,t,n)},_removeData:function(e,t){et.remove(e,t)}}),T.fn.extend({data:function(e,t){var n,r,i,o=this[0],a=o&&o.attributes;if( + void 0===e){if(this.length&&(i=tt.get(o),1===o.nodeType&&!et.get(o,""hasDataAttrs""))){n=a.length;while(n--)a[n]&&0===(r=a[n].name).indexOf(""data-"")&&it(o,r=Je(r.slice(5)),i[r]);et.set(o,""hasDataAttrs"",!0)}return i}return""object""==typeof e?this.each((function(){tt.set(this,e)})):G(this,(function(t){var n;if(o&&void 0===t)return void 0!==(n=tt.get(o,e))||void 0!==(n=it(o,e))?n:void 0;this.each((function(){tt.set(this,e,t)}))}),null,t,arguments.length>1,null,!0)},removeData:function(e){return + this.each((function(){tt.remove(this,e)}))}}),T.extend({queue:function(e,t,n){var r;if(e)return t=(t||""fx"")+""queue"",r=et.get(e,t),n&&(!r||Array.isArray(n)?r=et.set(e,t,T.makeArray(n)):r.push(n)),r||[]},dequeue:function(e,t){t=t||""fx"";var n=T.queue(e,t),r=n.length,i=n.shift(),o=T._queueHooks(e,t);""inprogress""===i&&(i=n.shift(),r--),i&&(""fx""===t&&n.unshift(""inprogress""),delete o.stop,i.call(e,(function(){T.dequeue(e,t)}),o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+""queueHooks""; + return et.get(e,n)||et.set(e,n,{empty:T.Callbacks(""once memory"").add((function(){et.remove(e,[t+""queue"",n])}))})}}),T.fn.extend({queue:function(e,t){var n=2;return(""string""!=typeof e&&(t=e,e=""fx"",n--),arguments.length\x20\t\r\n\f]*)/i,bt={thead:[""table""],col:[""colgroup"",""table""],tr:[""tbody"",""table""],td:[""tr"",""tbody"",""table""]};function wt(e,t){var n;return(n=void 0!==e.getElementsByTagName?e.getElementsByTagName(t||""*""):void 0!==e.querySelectorAll?e.querySelectorAll(t||""*""):[],void 0===t||t&&C(e,t))?T.merge([e],n):n} + bt.tbody=bt.tfoot=bt.colgroup=bt.caption=bt.thead,bt.th=bt.td;var Tt=/^$|^module$|\/(?:java|ecma)script/i;function Ct(e,t){for(var n=0,r=e.length;n-1)s=s.appendChild(t.createElement(u[c]));s.innerHTML=T.htmlPrefilter(a),T.merge(p,s.childNodes),(s=f.firstChild).textContent=""""}else p.push(t.createTextNode(a))}f.textContent="""",d=0;while(a=p[d++]){if(i&&T.inArray(a,i)>-1){o&&o.push(a);continue}if(l=yt(a),s=wt(f.appendChild(a),""script""),l&&Ct(s),r){c=0;while(a=s[c++])Tt.test(a.type||"""")&&r.push(a)}}return f}function kt(e){return e.type=(null!==e.getAttribute(""type""))+""/""+e.type,e}function St(e){ + return""true/""===(e.type||"""").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute(""type""),e}function Dt(e,t,n,r){t=o(t);var i,a,s,u,l,c,f=0,p=e.length,d=p-1,h=t[0];if(""function""==typeof h)return e.each((function(i){var o=e.eq(i);t[0]=h.call(this,i,o.html()),Dt(o,t,n,r)}));if(p&&(a=(i=Et(t,e[0].ownerDocument,!1,e,r)).firstChild,1===i.childNodes.length&&(i=a),a||r)){for(u=(s=T.map(wt(i,""script""),kt)).length;f=1)){for(;l!==this; + l=l.parentNode||this)if(1===l.nodeType&&!(""click""===e.type&&!0===l.disabled)){for(n=0,o=[],a={};n-1:T.find(i,this,null,[l]).length),a[i]&&o.push(r);o.length&&s.push({elem:l,handlers:o})}}return l=this,u0&&Ct(a,!u&&wt(e,""script"")),s},cleanData:function(e){for(var t,n,r,i=T.event.special,o=0;void 0!==(n=e[o]);o++)if(Ke(n)){if(t=n[et.expando]){ + if(t.events)for(r in t.events)i[r]?T.event.remove(n,r):T.removeEvent(n,r,t.handle);n[et.expando]=void 0}n[tt.expando]&&(n[tt.expando]=void 0)}}}),T.fn.extend({detach:function(e){return Wt(this,e,!0)},remove:function(e){return Wt(this,e)},text:function(e){return G(this,(function(e){return void 0===e?T.text(this):this.empty().each((function(){(1===this.nodeType||11===this.nodeType||9===this.nodeType)&&(this.textContent=e)}))}),null,e,arguments.length)},append:function(){return Dt(this,arguments,( + function(e){(1===this.nodeType||11===this.nodeType||9===this.nodeType)&&Rt(this,e).appendChild(e)}))},prepend:function(){return Dt(this,arguments,(function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=Rt(this,e);t.insertBefore(e,t.firstChild)}}))},before:function(){return Dt(this,arguments,(function(e){this.parentNode&&this.parentNode.insertBefore(e,this)}))},after:function(){return Dt(this,arguments,(function(e){this.parentNode&&this.parentNode.insertBefore(e, + this.nextSibling)}))},empty:function(){for(var e,t=0;null!=(e=this[t]);t++)1===e.nodeType&&(T.cleanData(wt(e,!1)),e.textContent="""");return this},clone:function(e,t){return e=null!=e&&e,t=null==t?e:t,this.map((function(){return T.clone(this,e,t)}))},html:function(e){return G(this,(function(e){var t=this[0]||{},n=0,r=this.length;if(void 0===e&&1===t.nodeType)return t.innerHTML;if(""string""==typeof e&&!Pt.test(e)&&!bt[(xt.exec(e)||["""",""""])[1].toLowerCase()]){e=T.htmlPrefilter(e);try{for(;nT.inArray(this,e)&&(T.cleanData(wt(this)),n&&n.replaceChild(t,this))}),e)}}),T.each({appendTo:""append"",prependTo:""prepend"",insertBefore:""before"",insertAfter:""after"",replaceAll:""replaceWith""},(function(e,t){T.fn[e]=function(e){for(var n,r=[],i=T(e),o=i.length-1,s=0;s<=o; + s++)n=s===o?this:this.clone(!0),T(i[s])[t](n),a.apply(r,n);return this.pushStack(r)}}));var It=RegExp(""^(""+ot+"")(?!px)[a-z%]+$"",""i""),$t=/^--/;function Ft(t){var n=t.ownerDocument.defaultView;return n||(n=e),n.getComputedStyle(t)}function Bt(e,t,n){var r,i=$t.test(t);return(n=n||Ft(e))&&(r=n.getPropertyValue(t)||n[t],i&&r&&(r=r.replace(D,""$1"")||void 0),""""!==r||yt(e)||(r=T.style(e,t))),void 0!==r?r+"""":r}var _t=[""Webkit"",""Moz"",""ms""],Ut=y.createElement(""div"").style,Xt={};function zt(e){return Xt[ + e]||(e in Ut?e:Xt[e]=function(e){var t=e[0].toUpperCase()+e.slice(1),n=_t.length;while(n--)if((e=_t[n]+t)in Ut)return e}(e)||e)}(tn=y.createElement(""div"")).style&&(d.reliableTrDimensions=function(){var t,n,r;if(null==en){if(t=y.createElement(""table""),n=y.createElement(""tr""),t.style.cssText=""position:absolute;left:-11111px;border-collapse:separate"",n.style.cssText=""box-sizing:content-box;border:1px solid"",n.style.height=""1px"",tn.style.height=""9px"",tn.style.display=""block"",H.appendChild(t) + .appendChild(n).appendChild(tn),0===t.offsetWidth){H.removeChild(t);return}en=parseInt((r=e.getComputedStyle(n)).height,10)+parseInt(r.borderTopWidth,10)+parseInt(r.borderBottomWidth,10)===n.offsetHeight,H.removeChild(t)}return en});var Vt=/^(none|table(?!-c[ea]).+)/,Yt={position:""absolute"",visibility:""hidden"",display:""block""},Gt={letterSpacing:""0"",fontWeight:""400""};function Qt(e,t,n){var r=at.exec(t);return r?Math.max(0,r[2]-(n||0))+(r[3]||""px""):t}function Jt(e,t,n,r,i,o){var a=""width""===t?1:0, + s=0,u=0,l=0;if(n===(r?""border"":""content""))return 0;for(;a<4;a+=2)""margin""===n&&(l+=T.css(e,n+st[a],!0,i)),r?(""content""===n&&(u-=T.css(e,""padding""+st[a],!0,i)),""margin""!==n&&(u-=T.css(e,""border""+st[a]+""Width"",!0,i))):(u+=T.css(e,""padding""+st[a],!0,i),""padding""!==n?u+=T.css(e,""border""+st[a]+""Width"",!0,i):s+=T.css(e,""border""+st[a]+""Width"",!0,i));return!r&&o>=0&&(u+=Math.max(0,Math.ceil(e[""offset""+t[0].toUpperCase()+t.slice(1)]-o-u-s-.5))||0),u+l}function Kt(e,t,n){var r=Ft(e),i=(k||n)&&""border-box""=== + T.css(e,""boxSizing"",!1,r),o=i,a=Bt(e,t,r),s=""offset""+t[0].toUpperCase()+t.slice(1);if(It.test(a)){if(!n)return a;a=""auto""}return(""auto""===a||k&&i||!d.reliableTrDimensions()&&C(e,""tr""))&&e.getClientRects().length&&(i=""border-box""===T.css(e,""boxSizing"",!1,r),(o=s in e)&&(a=e[s])),(a=parseFloat(a)||0)+Jt(e,t,n||(i?""border"":""content""),o,r,a)+""px""}function Zt(e,t,n,r,i){return new Zt.prototype.init(e,t,n,r,i)}T.extend({cssHooks:{},style:function(e,t,n,r){if( + e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var i,o,a,s=ht(t),u=$t.test(t),l=e.style;if(u||(t=zt(s)),a=T.cssHooks[t]||T.cssHooks[s],void 0===n)return a&&""get""in a&&void 0!==(i=a.get(e,!1,r))?i:l[t];""string""==(o=typeof n)&&(i=at.exec(n))&&i[1]&&(n=pt(e,t,i),o=""number""),null!=n&&n==n&&(""number""===o&&(n+=i&&i[3]||(ft(s)?""px"":"""")),k&&""""===n&&0===t.indexOf(""background"")&&(l[t]=""inherit""),a&&""set""in a&&void 0===(n=a.set(e,n,r))||(u?l.setProperty(t,n):l[t]=n))}},css:function(e,t,n,r){var i,o,a,s=ht(t); + return($t.test(t)||(t=zt(s)),(a=T.cssHooks[t]||T.cssHooks[s])&&""get""in a&&(i=a.get(e,!0,n)),void 0===i&&(i=Bt(e,t,r)),""normal""===i&&t in Gt&&(i=Gt[t]),""""===n||n)?(o=parseFloat(i),!0===n||isFinite(o)?o||0:i):i}}),T.each([""height"",""width""],(function(e,t){T.cssHooks[t]={get:function(e,n,r){if(n)return!Vt.test(T.css(e,""display""))||e.getClientRects().length&&e.getBoundingClientRect().width?Kt(e,t,r):function(e,t,n){var r,i,o={};for(i in t)o[i]=e.style[i],e.style[i]=t[i];for(i in r=n.call(e),t) + e.style[i]=o[i];return r}(e,Yt,(function(){return Kt(e,t,r)}))},set:function(e,n,r){var i,o=Ft(e),a=r&&""border-box""===T.css(e,""boxSizing"",!1,o),s=r?Jt(e,t,r,a,o):0;return s&&(i=at.exec(n))&&""px""!==(i[3]||""px"")&&(e.style[t]=n,n=T.css(e,t)),Qt(e,n,s)}}})),T.each({margin:"""",padding:"""",border:""Width""},(function(e,t){T.cssHooks[e+t]={expand:function(n){for(var r=0,i={},o=""string""==typeof n?n.split("" ""):[n];r<4;r++)i[e+st[r]+t]=o[r]||o[r-2]||o[0];return i}},""margin""!==e&&(T.cssHooks[e+t].set=Qt)})), + T.fn.extend({css:function(e,t){return G(this,(function(e,t,n){var r,i,o={},a=0;if(Array.isArray(t)){for(r=Ft(e),i=t.length;a1)}}),T.Tween=Zt,Zt.prototype={constructor:Zt,init:function(e,t,n,r,i,o){this.elem=e,this.prop=n,this.easing=i||T.easing._default,this.options=t,this.start=this.now=this.cur(),this.end=r,this.unit=o||(ft(n)?""px"":"""")},cur:function(){var e=Zt.propHooks[this.prop]; + return e&&e.get?e.get(this):Zt.propHooks._default.get(this)},run:function(e){var t,n=Zt.propHooks[this.prop];return this.options.duration?this.pos=t=T.easing[this.easing](e,this.options.duration*e,0,1,this.options.duration):this.pos=t=e,this.now=(this.end-this.start)*t+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),n&&n.set?n.set(this):Zt.propHooks._default.set(this),this}},Zt.prototype.init.prototype=Zt.prototype,Zt.propHooks={_default:{get:function(e){var t; + return 1!==e.elem.nodeType||null!=e.elem[e.prop]&&null==e.elem.style[e.prop]?e.elem[e.prop]:(t=T.css(e.elem,e.prop,""""))&&""auto""!==t?t:0},set:function(e){T.fx.step[e.prop]?T.fx.step[e.prop](e):1===e.elem.nodeType&&(T.cssHooks[e.prop]||null!=e.elem.style[zt(e.prop)])?T.style(e.elem,e.prop,e.now+e.unit):e.elem[e.prop]=e.now}}},T.easing={linear:function(e){return e},swing:function(e){return.5-Math.cos(e*Math.PI)/2},_default:""swing""},T.fx=Zt.prototype.init,T.fx.step={};var en,tn,nn,rn,on= + /^(?:toggle|show|hide)$/,an=/queueHooks$/;function sn(){return e.setTimeout((function(){nn=void 0})),nn=Date.now()}function un(e,t){var n,r=0,i={height:e};for(t=t?1:0;r<4;r+=2-t)i[""margin""+(n=st[r])]=i[""padding""+n]=e;return t&&(i.opacity=i.width=e),i}function ln(e,t,n){for(var r,i=(cn.tweeners[t]||[]).concat(cn.tweeners[""*""]),o=0,a=i.length;o1)},removeProp:function(e){return this.each((function(){delete this[T.propFix[e]||e]}))}}),T.extend({prop:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return(1===o&&T.isXMLDoc(e)||(t=T.propFix[t]||t,i=T.propHooks[t]),void 0!==n)?i&&""set""in i&&void 0!==(r=i.set(e,n,t))?r:e[t]=n:i&&""get""in + i&&null!==(r=i.get(e,t))?r:e[t]},propHooks:{tabIndex:{get:function(e){var t=e.getAttribute(""tabindex"");return t?parseInt(t,10):fn.test(e.nodeName)||pn.test(e.nodeName)&&e.href?0:-1}}},propFix:{for:""htmlFor"",class:""className""}}),k&&(T.propHooks.selected={get:function(e){var t=e.parentNode;return t&&t.parentNode&&t.parentNode.selectedIndex,null},set:function(e){var t=e.parentNode;t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex)}}),T.each([""tabIndex"",""readOnly"",""maxLength"", + ""cellSpacing"",""cellPadding"",""rowSpan"",""colSpan"",""useMap"",""frameBorder"",""contentEditable""],(function(){T.propFix[this.toLowerCase()]=this})),T.fn.extend({addClass:function(e){var t,n,r,i,o,a;return""function""==typeof e?this.each((function(t){T(this).addClass(e.call(this,t,hn(this)))})):(t=gn(e)).length?this.each((function(){if(r=hn(this),n=1===this.nodeType&&"" ""+dn(r)+"" ""){for(o=0;on.indexOf("" ""+i+"" "")&&(n+=i+"" "");r!==(a=dn(n))&&this.setAttribute(""class"",a)}})):this}, + removeClass:function(e){var t,n,r,i,o,a;return""function""==typeof e?this.each((function(t){T(this).removeClass(e.call(this,t,hn(this)))})):arguments.length?(t=gn(e)).length?this.each((function(){if(r=hn(this),n=1===this.nodeType&&"" ""+dn(r)+"" ""){for(o=0;o-1)n=n.replace("" ""+i+"" "","" "")}r!==(a=dn(n))&&this.setAttribute(""class"",a)}})):this:this.attr(""class"","""")},toggleClass:function(e,t){var n,r,i,o;return""function""==typeof e?this.each((function(n){T( + this).toggleClass(e.call(this,n,hn(this),t),t)})):""boolean""==typeof t?t?this.addClass(e):this.removeClass(e):(n=gn(e)).length?this.each((function(){for(i=0,o=T(this);i-1)return!0;return!1}}),T.fn.extend({val:function(e){var t,n,r,i=this[0];return arguments.length?(r=""function""==typeof e,this.each((function(n){ + var i;1===this.nodeType&&(null==(i=r?e.call(this,n,T(this).val()):e)?i="""":""number""==typeof i?i+="""":Array.isArray(i)&&(i=T.map(i,(function(e){return null==e?"""":e+""""}))),(t=T.valHooks[this.type]||T.valHooks[this.nodeName.toLowerCase()])&&""set""in t&&void 0!==t.set(this,i,""value"")||(this.value=i))}))):i?(t=T.valHooks[i.type]||T.valHooks[i.nodeName.toLowerCase()])&&""get""in t&&void 0!==(n=t.get(i,""value""))?n:null==(n=i.value)?"""":n:void 0}}),T.extend({valHooks:{select:{get:function(e){var t,n,r, + i=e.options,o=e.selectedIndex,a=""select-one""===e.type,s=a?null:[],u=a?o+1:i.length;for(r=o<0?u:a?o:0;r-1)&&(n=!0);return n||(e.selectedIndex=-1),o}}}}),k&&(T.valHooks.option={get:function(e){var t=e.getAttribute(""value"");return + null!=t?t:dn(T.text(e))}}),T.each([""radio"",""checkbox""],(function(){T.valHooks[this]={set:function(e,t){if(Array.isArray(t))return e.checked=T.inArray(T(e).val(),t)>-1}}}));var vn=/^(?:focusinfocus|focusoutblur)$/,yn=function(e){e.stopPropagation()};T.extend(T.event,{trigger:function(t,n,r,i){var o,a,s,u,l,f,p,d,h=[r||y],v=c.call(t,""type"")?t.type:t,m=c.call(t,""namespace"")?t.namespace.split("".""):[];if(a=d=s=r=r||y,!(3===r.nodeType||8===r.nodeType||vn.test(v+T.event.triggered))&&(v.indexOf(""."")>- + 1&&(v=(m=v.split(""."")).shift(),m.sort()),l=0>v.indexOf("":"")&&""on""+v,(t=t[T.expando]?t:new T.Event(v,""object""==typeof t&&t)).isTrigger=i?2:3,t.namespace=m.join("".""),t.rnamespace=t.namespace?RegExp(""(^|\\.)""+m.join(""\\.(?:.*\\.|)"")+""(\\.|$)""):null,t.result=void 0,t.target||(t.target=r),n=null==n?[t]:T.makeArray(n,[t]),p=T.event.special[v]||{},i||!p.trigger||!1!==p.trigger.apply(r,n))){if(!i&&!p.noBubble&&!g(r)){for(u=p.delegateType||v,vn.test(u+v)||(a=a.parentNode);a;a=a.parentNode)h.push(a),s=a; + s===(r.ownerDocument||y)&&h.push(s.defaultView||s.parentWindow||e)}o=0;while((a=h[o++])&&!t.isPropagationStopped())d=a,t.type=o>1?u:p.bindType||v,(f=(et.get(a,""events"")||Object.create(null))[t.type]&&et.get(a,""handle""))&&f.apply(a,n),(f=l&&a[l])&&f.apply&&Ke(a)&&(t.result=f.apply(a,n),!1===t.result&&t.preventDefault());return t.type=v,!i&&!t.isDefaultPrevented()&&(!p._default||!1===p._default.apply(h.pop(),n))&&Ke(r)&&l&&""function""==typeof r[v]&&!g(r)&&((s=r[l])&&(r[l]=null),T.event.triggered=v, + t.isPropagationStopped()&&d.addEventListener(v,yn),r[v](),t.isPropagationStopped()&&d.removeEventListener(v,yn),T.event.triggered=void 0,s&&(r[l]=s)),t.result}},simulate:function(e,t,n){var r=T.extend(new T.Event,n,{type:e,isSimulated:!0});T.event.trigger(r,null,t)}}),T.fn.extend({trigger:function(e,t){return this.each((function(){T.event.trigger(e,t,this)}))},triggerHandler:function(e,t){var n=this[0];if(n)return T.event.trigger(e,t,n,!0)}});var mn=e.location,xn={guid:Date.now()},bn=/\?/; + T.parseXML=function(t){var n,r;if(!t||""string""!=typeof t)return null;try{n=(new e.DOMParser).parseFromString(t,""text/xml"")}catch(e){}return r=n&&n.getElementsByTagName(""parsererror"")[0],(!n||r)&&T.error(""Invalid XML: ""+(r?T.map(r.childNodes,(function(e){return e.textContent})).join(""\n""):t)),n};var wn=/\[\]$/,Tn=/\r?\n/g,Cn=/^(?:submit|button|image|reset|file)$/i,jn=/^(?:input|select|textarea|keygen)/i;T.param=function(e,t){var n,r=[],i=function(e,t){var n=""function""==typeof t?t():t;r[r.length] + =encodeURIComponent(e)+""=""+encodeURIComponent(null==n?"""":n)};if(null==e)return"""";if(Array.isArray(e)||e.jquery&&!T.isPlainObject(e))T.each(e,(function(){i(this.name,this.value)}));else for(n in e)!function e(t,n,r,i){var o;if(Array.isArray(n))T.each(n,(function(n,o){r||wn.test(t)?i(t,o):e(t+""[""+(""object""==typeof o&&null!=o?n:"""")+""]"",o,r,i)}));else if(r||""object""!==h(n))i(t,n);else for(o in n)e(t+""[""+o+""]"",n[o],r,i)}(n,e[n],t,i);return r.join(""&"")},T.fn.extend({serialize:function(){return + T.param(this.serializeArray())},serializeArray:function(){return this.map((function(){var e=T.prop(this,""elements"");return e?T.makeArray(e):this})).filter((function(){var e=this.type;return this.name&&!T(this).is("":disabled"")&&jn.test(this.nodeName)&&!Cn.test(e)&&(this.checked||!At.test(e))})).map((function(e,t){var n=T(this).val();return null==n?null:Array.isArray(n)?T.map(n,(function(e){return{name:t.name,value:e.replace(Tn,""\r\n"")}})):{name:t.name,value:n.replace(Tn,""\r\n"")}})).get()}});var + En=/%20/g,kn=/#.*$/,Sn=/([?&])_=[^&]*/,Dn=/^(.*?):[ \t]*([^\r\n]*)$/gm,An=/^(?:GET|HEAD)$/,qn=/^\/\//,Nn={},On={},Hn=""*/"".concat(""*""),Ln=y.createElement(""a"");function Pn(e){return function(t,n){""string""!=typeof t&&(n=t,t=""*"");var r,i=0,o=t.toLowerCase().match(Q)||[];if(""function""==typeof n)while(r=o[i++])""+""===r[0]?(e[r=r.slice(1)||""*""]=e[r]||[]).unshift(n):(e[r]=e[r]||[]).push(n)}}function Rn(e,t,n,r){var i={},o=e===On;function a(s){var u;return i[s]=!0,T.each(e[s]||[],(function(e,s){var l=s(t, + n,r);return""string""!=typeof l||o||i[l]?o?!(u=l):void 0:(t.dataTypes.unshift(l),a(l),!1)})),u}return a(t.dataTypes[0])||!i[""*""]&&a(""*"")}function Mn(e,t){var n,r,i=T.ajaxSettings.flatOptions||{};for(n in t)void 0!==t[n]&&((i[n]?e:r||(r={}))[n]=t[n]);return r&&T.extend(!0,e,r),e}Ln.href=mn.href,T.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:mn.href,type:""GET"",isLocal:/^(?:about|app|app-storage|.+-extension|file|res|widget):$/.test(mn.protocol),global:!0,processData:!0,async:!0, + contentType:""application/x-www-form-urlencoded; charset=UTF-8"",accepts:{""*"":Hn,text:""text/plain"",html:""text/html"",xml:""application/xml, text/xml"",json:""application/json, text/javascript""},contents:{xml:/\bxml\b/,html:/\bhtml/,json:/\bjson\b/},responseFields:{xml:""responseXML"",text:""responseText"",json:""responseJSON""},converters:{""* text"":String,""text html"":!0,""text json"":JSON.parse,""text xml"":T.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(e,t){return t?Mn(Mn(e,T.ajaxSettings),t): + Mn(T.ajaxSettings,e)},ajaxPrefilter:Pn(Nn),ajaxTransport:Pn(On),ajax:function(t,n){""object""==typeof t&&(n=t,t=void 0),n=n||{};var r,i,o,a,s,u,l,c,f,p,d=T.ajaxSetup({},n),h=d.context||d,g=d.context&&(h.nodeType||h.jquery)?T(h):T.event,v=T.Deferred(),m=T.Callbacks(""once memory""),x=d.statusCode||{},b={},w={},C=""canceled"",j={readyState:0,getResponseHeader:function(e){var t;if(l){if(!a){a={};while(t=Dn.exec(o))a[t[1].toLowerCase()+"" ""]=(a[t[1].toLowerCase()+"" ""]||[]).concat(t[2])}t=a[e.toLowerCase()+ + "" ""]}return null==t?null:t.join("", "")},getAllResponseHeaders:function(){return l?o:null},setRequestHeader:function(e,t){return null==l&&(b[e=w[e.toLowerCase()]=w[e.toLowerCase()]||e]=t),this},overrideMimeType:function(e){return null==l&&(d.mimeType=e),this},statusCode:function(e){var t;if(e){if(l)j.always(e[j.status]);else for(t in e)x[t]=[x[t],e[t]]}return this},abort:function(e){var t=e||C;return r&&r.abort(t),E(0,t),this}};if(v.promise(j),d.url=((t||d.url||mn.href)+"""").replace(qn,mn.protocol+ + ""//""),d.type=n.method||n.type||d.method||d.type,d.dataTypes=(d.dataType||""*"").toLowerCase().match(Q)||[""""],null==d.crossDomain){u=y.createElement(""a"");try{u.href=d.url,u.href=u.href,d.crossDomain=Ln.protocol+""//""+Ln.host!=u.protocol+""//""+u.host}catch(e){d.crossDomain=!0}}if(Rn(Nn,d,n,j),d.data&&d.processData&&""string""!=typeof d.data&&(d.data=T.param(d.data,d.traditional)),l)return j;for(f in(c=T.event&&d.global)&&0==T.active++&&T.event.trigger(""ajaxStart""),d.type=d.type.toUpperCase(), + d.hasContent=!An.test(d.type),i=d.url.replace(kn,""""),d.hasContent?d.data&&d.processData&&0===(d.contentType||"""").indexOf(""application/x-www-form-urlencoded"")&&(d.data=d.data.replace(En,""+"")):(p=d.url.slice(i.length),d.data&&(d.processData||""string""==typeof d.data)&&(i+=(bn.test(i)?""&"":""?"")+d.data,delete d.data),!1===d.cache&&(i=i.replace(Sn,""$1""),p=(bn.test(i)?""&"":""?"")+""_=""+xn.guid+++p),d.url=i+p),d.ifModified&&(T.lastModified[i]&&j.setRequestHeader(""If-Modified-Since"",T.lastModified[i]),T.etag[ + i]&&j.setRequestHeader(""If-None-Match"",T.etag[i])),(d.data&&d.hasContent&&!1!==d.contentType||n.contentType)&&j.setRequestHeader(""Content-Type"",d.contentType),j.setRequestHeader(""Accept"",d.dataTypes[0]&&d.accepts[d.dataTypes[0]]?d.accepts[d.dataTypes[0]]+(""*""!==d.dataTypes[0]?"", ""+Hn+""; q=0.01"":""""):d.accepts[""*""]),d.headers)j.setRequestHeader(f,d.headers[f]);if(d.beforeSend&&(!1===d.beforeSend.call(h,j,d)||l))return j.abort();if(C=""abort"",m.add(d.complete),j.done(d.success),j.fail(d.error),r=Rn( + On,d,n,j)){if(j.readyState=1,c&&g.trigger(""ajaxSend"",[j,d]),l)return j;d.async&&d.timeout>0&&(s=e.setTimeout((function(){j.abort(""timeout"")}),d.timeout));try{l=!1,r.send(b,E)}catch(e){if(l)throw e;E(-1,e)}}else E(-1,""No Transport"");function E(t,n,a,u){var f,p,y,b,w,C=n;!l&&(l=!0,s&&e.clearTimeout(s),r=void 0,o=u||"""",j.readyState=t>0?4:0,f=t>=200&&t<300||304===t,a&&(b=function(e,t,n){var r,i,o,a,s=e.contents,u=e.dataTypes;while(""*""===u[0])u.shift(),void 0===r&&(r=e.mimeType||t.getResponseHeader( + ""Content-Type""));if(r){for(i in s)if(s[i]&&s[i].test(r)){u.unshift(i);break}}if(u[0]in n)o=u[0];else{for(i in n){if(!u[0]||e.converters[i+"" ""+u[0]]){o=i;break}a||(a=i)}o=o||a}if(o)return o!==u[0]&&u.unshift(o),n[o]}(d,j,a)),!f&&T.inArray(""script"",d.dataTypes)>-1&&0>T.inArray(""json"",d.dataTypes)&&(d.converters[""text script""]=function(){}),b=function(e,t,n,r){var i,o,a,s,u,l={},c=e.dataTypes.slice();if(c[1])for(a in e.converters)l[a.toLowerCase()]=e.converters[a];o=c.shift();while(o)if( + e.responseFields[o]&&(n[e.responseFields[o]]=t),!u&&r&&e.dataFilter&&(t=e.dataFilter(t,e.dataType)),u=o,o=c.shift()){if(""*""===o)o=u;else if(""*""!==u&&u!==o){if(!(a=l[u+"" ""+o]||l[""* ""+o])){for(i in l)if((s=i.split("" ""))[1]===o&&(a=l[u+"" ""+s[0]]||l[""* ""+s[0]])){!0===a?a=l[i]:!0!==l[i]&&(o=s[0],c.unshift(s[1]));break}}if(!0!==a){if(a&&e.throws)t=a(t);else try{t=a(t)}catch(e){return{state:""parsererror"",error:a?e:""No conversion from ""+u+"" to ""+o}}}}}return{state:""success"",data:t}}(d,b,j,f),f?( + d.ifModified&&((w=j.getResponseHeader(""Last-Modified""))&&(T.lastModified[i]=w),(w=j.getResponseHeader(""etag""))&&(T.etag[i]=w)),204===t||""HEAD""===d.type?C=""nocontent"":304===t?C=""notmodified"":(C=b.state,p=b.data,f=!(y=b.error))):(y=C,(t||!C)&&(C=""error"",t<0&&(t=0))),j.status=t,j.statusText=(n||C)+"""",f?v.resolveWith(h,[p,C,j]):v.rejectWith(h,[j,C,y]),j.statusCode(x),x=void 0,c&&g.trigger(f?""ajaxSuccess"":""ajaxError"",[j,d,f?p:y]),m.fireWith(h,[j,C]),!c||(g.trigger(""ajaxComplete"",[j,d]),--T.active|| + T.event.trigger(""ajaxStop"")))}return j},getJSON:function(e,t,n){return T.get(e,t,n,""json"")},getScript:function(e,t){return T.get(e,void 0,t,""script"")}}),T.each([""get"",""post""],(function(e,t){T[t]=function(e,n,r,i){return(""function""==typeof n||null===n)&&(i=i||r,r=n,n=void 0),T.ajax(T.extend({url:e,type:t,dataType:i,data:n,success:r},T.isPlainObject(e)&&e))}})),T.ajaxPrefilter((function(e){var t;for(t in e.headers)""content-type""===t.toLowerCase()&&(e.contentType=e.headers[t]||"""")})), + T._evalUrl=function(e,t,n){return T.ajax({url:e,type:""GET"",dataType:""script"",cache:!0,async:!1,global:!1,scriptAttrs:t.crossOrigin?{crossOrigin:t.crossOrigin}:void 0,converters:{""text script"":function(){}},dataFilter:function(e){T.globalEval(e,t,n)}})},T.fn.extend({wrapAll:function(e){var t;return this[0]&&(""function""==typeof e&&(e=e.call(this[0])),t=T(e,this[0].ownerDocument).eq(0).clone(!0),this[0].parentNode&&t.insertBefore(this[0]),t.map((function(){var e=this;while(e.firstElementChild) + e=e.firstElementChild;return e})).append(this)),this},wrapInner:function(e){return""function""==typeof e?this.each((function(t){T(this).wrapInner(e.call(this,t))})):this.each((function(){var t=T(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)}))},wrap:function(e){var t=""function""==typeof e;return this.each((function(n){T(this).wrapAll(t?e.call(this,n):e)}))},unwrap:function(e){return this.parent(e).not(""body"").each((function(){T(this).replaceWith(this.childNodes)})),this}}), + T.expr.pseudos.hidden=function(e){return!T.expr.pseudos.visible(e)},T.expr.pseudos.visible=function(e){return!!(e.offsetWidth||e.offsetHeight||e.getClientRects().length)},T.ajaxSettings.xhr=function(){return new e.XMLHttpRequest};var Wn={0:200};function In(e){return e.scriptAttrs||!e.headers&&(e.crossDomain||e.async&&0>T.inArray(""json"",e.dataTypes))}T.ajaxTransport((function(e){var t;return{send:function(n,r){var i,o=e.xhr();if(o.open(e.type,e.url,e.async,e.username,e.password),e.xhrFields)for( + i in e.xhrFields)o[i]=e.xhrFields[i];for(i in e.mimeType&&o.overrideMimeType&&o.overrideMimeType(e.mimeType),e.crossDomain||n[""X-Requested-With""]||(n[""X-Requested-With""]=""XMLHttpRequest""),n)o.setRequestHeader(i,n[i]);t=function(e){return function(){t&&(t=o.onload=o.onerror=o.onabort=o.ontimeout=null,""abort""===e?o.abort():""error""===e?r(o.status,o.statusText):r(Wn[o.status]||o.status,o.statusText,""text""===(o.responseType||""text"")?{text:o.responseText}:{binary:o.response}, + o.getAllResponseHeaders()))}},o.onload=t(),o.onabort=o.onerror=o.ontimeout=t(""error""),t=t(""abort"");try{o.send(e.hasContent&&e.data||null)}catch(e){if(t)throw e}},abort:function(){t&&t()}}})),T.ajaxSetup({accepts:{script:""text/javascript, application/javascript, application/ecmascript, application/x-ecmascript""},converters:{""text script"":function(e){return T.globalEval(e),e}}}),T.ajaxPrefilter(""script"",(function(e){void 0===e.cache&&(e.cache=!1),In(e)&&(e.type=""GET"")})),T.ajaxTransport(""script"",( + function(e){if(In(e)){var t,n;return{send:function(r,i){t=T("""; + Parallel.Invoke(() => Assert(ctx1), () => Assert(ctx2)); + return; + + void Assert(IBrowsingContext context) => Task + .Run(async () => + { + var document = await context.OpenAsync(m => m.Content(html)); + var result = document.QuerySelector("#result").TextContent; + NUnit.Framework.Assert.AreEqual("foo", result); + }) + .Wait(); + } } } diff --git a/src/AngleSharp.Js.Tests/DeferredConstructorTests.cs b/src/AngleSharp.Js.Tests/DeferredConstructorTests.cs new file mode 100644 index 0000000..4cb9155 --- /dev/null +++ b/src/AngleSharp.Js.Tests/DeferredConstructorTests.cs @@ -0,0 +1,141 @@ +namespace AngleSharp.Js.Tests +{ + using NUnit.Framework; + using System.Threading.Tasks; + + /// + /// The constructor of an exposed type is only built once script reads the property it + /// is published under, so these cover what a reader is entitled to see either way. + /// + [TestFixture] + public class DeferredConstructorTests + { + [Test] + public async Task ConstructorIsSameObjectOnWindowAndGlobal() + { + var result = await "String(window.HTMLDivElement === HTMLDivElement)".EvalScriptAsync(); + Assert.AreEqual("true", result); + } + + [Test] + public async Task ConstructorIsSameObjectOnEveryRead() + { + var result = await "String((function () { var a = HTMLDivElement; var b = window.HTMLDivElement; return a === b && a === HTMLDivElement; })())".EvalScriptAsync(); + Assert.AreEqual("true", result); + } + + [Test] + public async Task ConstructorIsFunction() + { + var result = await "typeof HTMLDivElement".EvalScriptAsync(); + Assert.AreEqual("function", result); + } + + [Test] + public async Task UnreadConstructorIsStillOwnPropertyOfWindow() + { + var result = await "String(window.hasOwnProperty('HTMLTableColElement'))".EvalScriptAsync(); + Assert.AreEqual("true", result); + } + + [Test] + public async Task UnreadConstructorIsStillEnumerable() + { + var result = await "String(Object.keys(window).indexOf('HTMLTableColElement') !== -1)".EvalScriptAsync(); + Assert.AreEqual("true", result); + } + + [Test] + public async Task UnreadConstructorIsStillFoundByForIn() + { + var result = await "String((function () { for (var k in window) { if (k === 'HTMLTableColElement') { return true; } } return false; })())".EvalScriptAsync(); + Assert.AreEqual("true", result); + } + + [Test] + public async Task ConstructorKeepsItsAttributes() + { + var result = await "(function () { var d = Object.getOwnPropertyDescriptor(window, 'HTMLDivElement'); return d.writable + ',' + d.enumerable + ',' + d.configurable; })()".EvalScriptAsync(); + Assert.AreEqual("false,true,false", result); + } + + [Test] + public async Task DescriptorValueIsTheConstructor() + { + var result = await "String(Object.getOwnPropertyDescriptor(window, 'HTMLDivElement').value === HTMLDivElement)".EvalScriptAsync(); + Assert.AreEqual("true", result); + } + + [Test] + public async Task ReadingAConstructorDoesNotChangeTheKeysOfWindow() + { + var result = await "String((function () { var before = Object.keys(window).length; var c = HTMLDivElement; return before === Object.keys(window).length; })())".EvalScriptAsync(); + Assert.AreEqual("true", result); + } + + [Test] + public async Task ConstructedInstanceIsInstanceOfItsConstructor() + { + var result = await "String(new CustomEvent('foo') instanceof CustomEvent)".EvalScriptAsync(); + Assert.AreEqual("true", result); + } + + [Test] + public async Task ConstructorBuildsInstances() + { + var result = await "new CustomEvent('foo').type".EvalScriptAsync(); + Assert.AreEqual("foo", result); + } + + [Test] + public async Task PrototypePointsBackAtItsConstructor() + { + var result = await "String(HTMLDivElement.prototype.constructor === HTMLDivElement)".EvalScriptAsync(); + Assert.AreEqual("true", result); + } + + // Reaching a prototype through an instance is the one path that never names the + // type, so it is the one that has to pull the constructor in by itself. + [Test] + public async Task InstanceReportsItsConstructorWhenTheNameWasNeverRead() + { + var result = await "screen.constructor.name".EvalScriptAsync(); + Assert.AreEqual("Screen", result); + } + + [Test] + public async Task InstanceReportsTheSameConstructorTheWindowPublishes() + { + var result = await "String(screen.constructor === Screen)".EvalScriptAsync(); + Assert.AreEqual("true", result); + } + + [Test] + public async Task ConstructorIsNotWritable() + { + var result = await "String((function () { var before = HTMLDivElement; window.HTMLDivElement = 5; return window.HTMLDivElement === before; })())".EvalScriptAsync(); + Assert.AreEqual("true", result); + } + + [Test] + public async Task ConstructorIsNotConfigurable() + { + var result = await "String((delete window.HTMLDivElement) === false && typeof HTMLDivElement === 'function')".EvalScriptAsync(); + Assert.AreEqual("true", result); + } + + [Test] + public async Task ConstructorStringifiesAsNativeCode() + { + var result = await "String(HTMLDivElement)".EvalScriptAsync(); + Assert.AreEqual("function HTMLDivElement() { [native code] }", result); + } + + [Test] + public async Task NonConstructableTypeStillRejectsNew() + { + var result = await "(function () { try { new Node(); return 'no throw'; } catch (e) { return 'threw'; } })()".EvalScriptAsync(); + Assert.AreEqual("threw", result); + } + } +} diff --git a/src/AngleSharp.Js.Tests/DomTests.cs b/src/AngleSharp.Js.Tests/DomTests.cs new file mode 100644 index 0000000..c4baa29 --- /dev/null +++ b/src/AngleSharp.Js.Tests/DomTests.cs @@ -0,0 +1,296 @@ +namespace AngleSharp.Js.Tests +{ + using NUnit.Framework; + using System.Threading.Tasks; + + [TestFixture] + public class DomTests + { + [Test] + public async Task NodeHasChildNodesIsAFunction() + { + var result = await "document.createElement('div').hasChildNodes".EvalScriptAsync(); + Assert.AreEqual("function hasChildNodes() { [native code] }", result); + } + + [Test] + public async Task NodeHasChildNodesWithoutChildren() + { + var result = await "document.createElement('div').hasChildNodes()".EvalScriptAsync(); + Assert.AreEqual("False", result); + } + + [Test] + public async Task NodeHasChildNodesWithChildren() + { + var result = await "new DOMParser().parseFromString(`
`, 'text/html').body.firstChild.hasChildNodes()".EvalScriptAsync(); + Assert.AreEqual("True", result); + } + + [Test] + public async Task PrototypeChainOfElementIsBuiltCompletely() + { + var result = await "(function () { var p = Object.getPrototypeOf(document.createElement('div')), t = []; while (p) { t.push(p[Symbol.toStringTag]); p = Object.getPrototypeOf(p); } return t.join(); })()".EvalScriptAsync(); + Assert.AreEqual("HTMLDivElement,HTMLElement,Element,Node,EventTarget,", result); + } + + [Test] + public async Task ConstructorPropertyOfPrototypeRefersBackToTheConstructor() + { + var result = await "HTMLDivElement.prototype.constructor === HTMLDivElement".EvalScriptAsync(); + Assert.AreEqual("True", result); + } + + [Test] + public async Task NumericIndexerOfHtmlCollectionYieldsTheElement() + { + var result = await "document.getElementsByTagName('script')[0].nodeName".EvalScriptAsync(); + Assert.AreEqual("SCRIPT", result); + } + + [Test] + public async Task NumericIndexerOfHtmlCollectionOutOfRangeIsUndefined() + { + var result = await "typeof document.getElementsByTagName('script')[5]".EvalScriptAsync(); + Assert.AreEqual("undefined", result); + } + + // Two closed forms of IHtmlCollection in one document - each resolves its own + // indexer, and the explicit IReadOnlyList re-implementation behind them is the + // one accessor that cannot be invoked as declared. + [Test] + public async Task NumericIndexerWorksForHtmlCollectionsOfDifferentItemTypes() + { + var result = await "(function () { var d = new DOMParser().parseFromString(``, 'text/html'); return d.getElementsByTagName('img')[0].id + ',' + d.images[0].id; })()".EvalScriptAsync(); + Assert.AreEqual("x,x", result); + } + + // col and colgroup are separate classes sharing the HTMLTableColElement prototype. + [Test] + public async Task NumericIndexerWorksForElementsSharingAPrototype() + { + var result = await "(function () { var d = new DOMParser().parseFromString(`
`, 'text/html'); var g = d.getElementsByTagName('colgroup')[0], c = d.getElementsByTagName('col')[0]; return g.classList[1] + ',' + c.classList[1]; })()".EvalScriptAsync(); + Assert.AreEqual("b,d", result); + } + + [Test] + public async Task NumericIndexerOfNodeListYieldsTheNode() + { + var result = await "new DOMParser().parseFromString(`
`, 'text/html').body.childNodes[0].nodeName".EvalScriptAsync(); + Assert.AreEqual("DIV", result); + } + + [Test] + public async Task NumericIndexerOfTokenListYieldsTheToken() + { + var result = await "new DOMParser().parseFromString(`
`, 'text/html').body.firstChild.classList[1]".EvalScriptAsync(); + Assert.AreEqual("b", result); + } + + [Test] + public async Task ConsoleIsTheSameObjectOnEveryAccess() + { + var result = await "window.console === window.console".EvalScriptAsync(); + Assert.AreEqual("True", result); + } + + [Test] + public async Task ConsoleKeepsPropertiesAssignedToIt() + { + var result = await "(function () { window.console.marker = 'kept'; return window.console.marker; })()".EvalScriptAsync(); + Assert.AreEqual("kept", result); + } + + [Test] + public async Task InheritedMemberIsNotAnOwnPropertyOfTheNode() + { + var result = await "document.createElement('div').hasOwnProperty('firstChild')".EvalScriptAsync(); + Assert.AreEqual("False", result); + } + + [Test] + public async Task InheritedMemberIsStillVisibleOnTheNode() + { + var result = await "('firstChild' in document.documentElement) + ',' + (typeof document.documentElement.appendChild)".EvalScriptAsync(); + Assert.AreEqual("true,function", result); + } + + [Test] + public async Task InheritedAccessorStillReadsAndWrites() + { + var result = await "(function () { var d = document.createElement('div'); d.id = 'jint'; return d.id; })()".EvalScriptAsync(); + Assert.AreEqual("jint", result); + } + + [Test] + public async Task AssignedPropertyIsReportedConsistently() + { + var result = await "(function () { var d = document.createElement('div'); d.custom = 1; return d.hasOwnProperty('custom') + ',' + Object.getOwnPropertyNames(d).join(); })()".EvalScriptAsync(); + Assert.AreEqual("true,custom", result); + } + + // Reading an index, testing it for existence and enumerating it are three different + // questions the engine asks, and the node answers each of them from a different + // method. They have to agree. + [Test] + public async Task IndexedEntryIsReportedAsAnOwnPropertyAndReads() + { + var result = await "(function () { var c = document.getElementsByTagName('script'); return c.hasOwnProperty(0) + ',' + (0 in c) + ',' + c[0].nodeName; })()".EvalScriptAsync(); + Assert.AreEqual("true,true,SCRIPT", result); + } + + [Test] + public async Task IndexBeyondTheEndIsNoOwnPropertyAndReadsUndefined() + { + var result = await "(function () { var c = document.getElementsByTagName('script'); return c.hasOwnProperty(5) + ',' + (5 in c) + ',' + (typeof c[5]); })()".EvalScriptAsync(); + Assert.AreEqual("false,false,undefined", result); + } + + // A member of an indexed collection must not be mistaken for an index. "length" is + // the collection's own property rather than an inherited one, which is what the + // array-like projection reports for it - a browser has it on the prototype instead. + [Test] + public async Task MemberOfAnIndexedCollectionIsNotMistakenForAnIndex() + { + var result = await "(function () { var c = document.getElementsByTagName('script'); return ('length' in c) + ',' + c.length + ',' + c.propertyIsEnumerable('length'); })()".EvalScriptAsync(); + Assert.AreEqual("true,1,false", result); + } + + // A DOM collection is array-like, not an array - which is exactly what a browser + // reports for one. + [Test] + public async Task CollectionIsNotAnArray() + { + var result = await "Array.isArray(document.getElementsByTagName('script'))".EvalScriptAsync(); + Assert.AreEqual("False", result); + } + + [Test] + public async Task CollectionKeepsItsDomIdentity() + { + var result = await "(function () { var c = document.getElementsByTagName('script'); return Object.prototype.toString.call(c) + ',' + (c instanceof HTMLCollection); })()".EvalScriptAsync(); + Assert.AreEqual("[object HTMLCollection],true", result); + } + + [Test] + public async Task CollectionCanBeIterated() + { + var result = await "(function () { var n = 0; for (var s of document.getElementsByTagName('script')) { n += s.nodeName.length; } return n; })()".EvalScriptAsync(); + Assert.AreEqual("6", result); + } + + [Test] + public async Task CollectionCanBeSpread() + { + var result = await "[...document.getElementsByTagName('script')].length".EvalScriptAsync(); + Assert.AreEqual("1", result); + } + + [Test] + public async Task ArrayGenericsRunOverACollection() + { + var result = await "Array.prototype.map.call(document.getElementsByTagName('script'), function (e) { return e.nodeName; }).join()".EvalScriptAsync(); + Assert.AreEqual("SCRIPT", result); + } + + [Test] + public async Task IndicesOfACollectionAreEnumerated() + { + var result = await "JSON.stringify(Object.keys(document.getElementsByTagName('script')))".EvalScriptAsync(); + Assert.AreEqual("[\"0\"]", result); + } + + // The platform-object shape: the projection owns its indices, so script cannot delete + // one or define over it. + [Test] + public async Task IndexOfACollectionCannotBeDeleted() + { + var result = await "(function () { var c = document.getElementsByTagName('script'); return delete c[0]; })()".EvalScriptAsync(); + Assert.AreEqual("False", result); + } + + [Test] + public async Task ExpandoOnACollectionIsStillPossible() + { + var result = await "(function () { var c = document.getElementsByTagName('script'); c.marker = 'kept'; return c.marker + ',' + c.hasOwnProperty('marker'); })()".EvalScriptAsync(); + Assert.AreEqual("kept,true", result); + } + + // Existence is answered from the collection's length alone, without producing the + // element - so it has to keep agreeing with what reading it would say. + [Test] + public async Task ExistenceOfAnIndexAgreesWithReadingIt() + { + var result = await "(function () { var c = document.getElementsByTagName('script'); var r = []; for (var i = 0; i < 3; i++) { r.push((i in c) + ':' + (c[i] !== undefined)); } return r.join(); })()".EvalScriptAsync(); + Assert.AreEqual("true:true,false:false,false:false", result); + } + + [Test] + public async Task NumericIndexerOfNamedNodeMapStillReadsBothWays() + { + var result = await "(function () { var d = document.createElement('div'); d.setAttribute('title', 't'); var a = d.attributes; return a.length + ',' + a[0].name + ',' + a.title.value; })()".EvalScriptAsync(); + Assert.AreEqual("1,title,t", result); + } + + // An element whose id happens to be numeric must not surface as an index of the + // collection. The array-like projection owns every array-index key - an index past the + // end is authoritatively absent - so the named entry answers only non-index names, + // which is also how WebIDL resolves the collision. Every answer has to say the same + // thing, the descriptor included. + [Test] + public async Task NamedEntryWithAnIndexShapedNameIsNotAnIndex() + { + var result = await "(function () { var f = document.createElement('form'); f.id = '5'; document.documentElement.appendChild(f); var c = document.forms; return (Object.getOwnPropertyDescriptor(c, '5') === undefined) + ',' + ('5' in c) + ',' + (c['5'] === undefined) + ',' + c.hasOwnProperty('5') + ',' + (c[0] === f); })()".EvalScriptAsync(); + Assert.AreEqual("true,false,true,false,true", result); + } + + // The guard above must not overreach: a named entry whose name is not an array index + // keeps resolving, descriptor and all. + [Test] + public async Task NamedEntryWithAnOrdinaryNameStillResolves() + { + var result = await "(function () { var f = document.createElement('form'); f.id = 'login'; document.documentElement.appendChild(f); var c = document.forms; return (c.login === f) + ',' + (Object.getOwnPropertyDescriptor(c, 'login') !== undefined); })()".EvalScriptAsync(); + Assert.AreEqual("true,true", result); + } + + // A symbol cannot be an index, and it must not be turned into one either - the + // well-known symbols are probed on every kind of object by library code. + [Test] + public async Task SymbolKeyOnAnIndexedCollectionIsNotTreatedAsAnIndex() + { + var result = await "(function () { var c = document.getElementsByTagName('script'); return c.hasOwnProperty(Symbol.toStringTag) + ',' + (typeof c[Symbol.toStringTag]); })()".EvalScriptAsync(); + Assert.AreEqual("false,string", result); + } + + // The node's own property set lives in the DOM, so nothing in the engine changes + // when an entry appears there. A name that was absent has to start resolving on the + // node from the very next read, rather than staying on whatever the prototype said. + [Test] + public async Task NamedEntryStartsResolvingOnTheNodeAsSoonAsItExists() + { + var result = await "(function () { var d = document.createElement('div'), a = d.attributes, before = typeof a.title; d.setAttribute('title', 't'); return before + ',' + a.title.value + ',' + a.hasOwnProperty('title'); })()".EvalScriptAsync(); + Assert.AreEqual("undefined,t,true", result); + } + + [Test] + public async Task AccessorDefinedOnANodeByScriptIsInvokedOnRead() + { + var result = await "(function () { var d = document.createElement('div'); Object.defineProperty(d, 'marker', { get: function () { return 'from getter'; } }); return d.marker + ',' + d.hasOwnProperty('marker'); })()".EvalScriptAsync(); + Assert.AreEqual("from getter,true", result); + } + + [Test] + public async Task NonEnumerablePropertyOfANodeIsSeenButNotEnumerated() + { + var result = await "(function () { var d = document.createElement('div'); Object.defineProperty(d, 'hidden2', { value: 1 }); return d.hasOwnProperty('hidden2') + ',' + d.propertyIsEnumerable('hidden2') + ',' + Object.keys(d).length; })()".EvalScriptAsync(); + Assert.AreEqual("true,false,0", result); + } + + [Test] + public async Task PropertyAssignedToANodeIsCopiedAndSerialized() + { + var result = await "(function () { var d = document.createElement('div'); d.custom = 'v'; return JSON.stringify(d) + ',' + JSON.stringify(Object.assign({}, d)); })()".EvalScriptAsync(); + Assert.AreEqual("{\"custom\":\"v\"},{\"custom\":\"v\"}", result); + } + } +} diff --git a/src/AngleSharp.Js.Tests/EcmaTests.cs b/src/AngleSharp.Js.Tests/EcmaTests.cs new file mode 100644 index 0000000..d1c5ce9 --- /dev/null +++ b/src/AngleSharp.Js.Tests/EcmaTests.cs @@ -0,0 +1,481 @@ +namespace AngleSharp.Js.Tests +{ + using AngleSharp.Dom; + using AngleSharp.Io; + using AngleSharp.Js.Tests.Mocks; + using NUnit.Framework; + using System; + using System.Collections.Generic; + using System.Threading.Tasks; + + [TestFixture] + public class EcmaTests + { + private static String SetResult(String eval) => + $"document.querySelector('#result').textContent = {eval};"; + + [Test] + public async Task BootstrapVersionFive() + { + var result = await (new[] { Constants.Bootstrap_5_3_3, SetResult("bootstrap.toString()") }).EvalScriptsAsync() + .ConfigureAwait(false); + Assert.AreNotEqual("", result); + } + + [Test] + public async Task ModuleScriptShouldRun() + { + var config = Configuration.Default + .WithJs() + .WithNavigator() + .With(new MockHttpClientRequester(new Dictionary() + { + { "/example-module.js", "import { $ } from '/jquery_4_0_0_esm.js'; $('#test').remove();" }, + { "/jquery_4_0_0_esm.js", Constants.Jquery4_0_0_ESM } + })) + .WithDefaultLoader(new LoaderOptions() { IsResourceLoadingEnabled = true }); + var context = BrowsingContext.New(config); + var html = "
Test
"; + var document = await context.OpenAsync(r => r.Content(html)); + Assert.IsNull(document.GetElementById("test")); + } + + [Test] + public async Task InlineModuleScriptShouldRun() + { + var config = Configuration.Default + .WithJs() + .WithNavigator() + .With(new MockHttpClientRequester(new Dictionary() + { + { "/jquery_4_0_0_esm.js", Constants.Jquery4_0_0_ESM } + })) + .WithDefaultLoader(new LoaderOptions() { IsResourceLoadingEnabled = true }); + var context = BrowsingContext.New(config); + var html = "
Test
"; + var document = await context.OpenAsync(r => r.Content(html)); + Assert.IsNull(document.GetElementById("test")); + } + + [Test] + public async Task ModuleScriptWithImportMapShouldRun() + { + var config = Configuration.Default + .WithJs() + .WithNavigator() + .With(new MockHttpClientRequester(new Dictionary() + { + { "/jquery_4_0_0_esm.js", Constants.Jquery4_0_0_ESM } + })) + .WithDefaultLoader(new LoaderOptions() { IsResourceLoadingEnabled = true }); + + var context = BrowsingContext.New(config); + var html = "
Test
"; + var document = await context.OpenAsync(r => r.Content(html)); + Assert.IsNull(document.GetElementById("test")); + } + + [Test] + public async Task ModuleScriptWithScopedImportMapShouldRunCorrectScript() + { + var config = Configuration.Default + .WithJs() + .WithNavigator() + .With(new MockHttpClientRequester(new Dictionary() + { + { "/example-module-1.js", "export function test() { document.getElementById('test1').remove(); }" }, + { "/example-module-2.js", "export function test() { document.getElementById('test2').remove(); }" }, + { "/test.js", "import { test } from 'example-module'; test();" }, + { "/test/test.js", "import { test } from 'example-module'; test();" } + })) + .WithDefaultLoader(new LoaderOptions() { IsResourceLoadingEnabled = true }); + + var context = BrowsingContext.New(config); + + var html1 = "
Test
Test
"; + var document1 = await context.OpenAsync(r => r.Content(html1)); + Assert.IsNull(document1.GetElementById("test1")); + Assert.IsNotNull(document1.GetElementById("test2")); + + var html2 = "
Test
Test
"; + var document2 = await context.OpenAsync(r => r.Content(html2)); + Assert.IsNull(document2.GetElementById("test2")); + Assert.IsNotNull(document2.GetElementById("test1")); + } + + [Test] + public async Task ModuleScriptWithQuoteInImportMapShouldRun() + { + var config = Configuration.Default + .WithJs() + .WithNavigator() + .With(new MockHttpClientRequester(new Dictionary() + { + { "/example-module.js", "export function test() { document.getElementById('test').remove(); }" } + })) + .WithDefaultLoader(new LoaderOptions() { IsResourceLoadingEnabled = true }); + + var context = BrowsingContext.New(config); + var html = "
Test
"; + var document = await context.OpenAsync(r => r.Content(html)); + Assert.IsNull(document.GetElementById("test")); + } + + [Test] + public async Task ImportMapContentIsNotEvaluatedAsScript() + { + var config = Configuration.Default.WithJs().WithNavigator(); + var context = BrowsingContext.New(config); + var html = "
Test
"; + var document = await context.OpenAsync(r => r.Content(html)); + Assert.IsNotNull(document.GetElementById("test")); + } + + [Test] + public async Task ModuleScriptWithAbsoluteUrlImportMapShouldRun() + { + var config = Configuration.Default + .WithJs() + .WithNavigator() + .With(new MockHttpClientRequester(new Dictionary() + { + { "/jquery_4_0_0_esm.js", Constants.Jquery4_0_0_ESM } + })) + .WithDefaultLoader(new LoaderOptions() { IsResourceLoadingEnabled = true }); + + var context = BrowsingContext.New(config); + var html = "
Test
"; + var document = await context.OpenAsync(r => r.Content(html)); + Assert.IsNull(document.GetElementById("test")); + } + + [Test] + public async Task ModuleScriptCanUseDynamicImportForFeatureSplit() + { + var config = Configuration.Default + .WithJs() + .WithNavigator() + .With(new MockHttpClientRequester(new Dictionary() + { + { "/app.js", "import('/feature.js').then(m => m.mount());" }, + { "/feature.js", "export function mount() { document.getElementById('test').textContent = 'mounted'; }" } + })) + .WithDefaultLoader(new LoaderOptions() { IsResourceLoadingEnabled = true }); + + var context = BrowsingContext.New(config); + var html = "
pending
"; + var document = await context.OpenAsync(r => r.Content(html)).WhenStable().ConfigureAwait(false); + Assert.AreEqual("mounted", document.GetElementById("test")?.TextContent); + } + + [Test] + public async Task ModuleGraphCanShareSingletonStateAcrossImports() + { + var config = Configuration.Default + .WithJs() + .WithNavigator() + .With(new MockHttpClientRequester(new Dictionary() + { + { "/store.js", "let count = 0; export function inc() { count++; } export function read() { return count; }" }, + { "/worker.js", "import { inc } from '/store.js'; inc();" } + })) + .WithDefaultLoader(new LoaderOptions() { IsResourceLoadingEnabled = true }); + + var context = BrowsingContext.New(config); + var html = "
0
"; + var document = await context.OpenAsync(r => r.Content(html)).WhenStable().ConfigureAwait(false); + Assert.AreEqual("2", document.GetElementById("test")?.TextContent); + } + + [Test] + public async Task ModuleScriptCanUseReExportedBindings() + { + var config = Configuration.Default + .WithJs() + .WithNavigator() + .With(new MockHttpClientRequester(new Dictionary() + { + { "/core.js", "export const version = '1.2.3';" }, + { "/api.js", "export { version } from '/core.js';" } + })) + .WithDefaultLoader(new LoaderOptions() { IsResourceLoadingEnabled = true }); + + var context = BrowsingContext.New(config); + var html = "
"; + var document = await context.OpenAsync(r => r.Content(html)).WhenStable().ConfigureAwait(false); + Assert.AreEqual("1.2.3", document.GetElementById("test")?.TextContent); + } + + [Test] + public async Task ModuleScriptCanUseDefaultAndNamedExportsTogether() + { + var config = Configuration.Default + .WithJs() + .WithNavigator() + .With(new MockHttpClientRequester(new Dictionary() + { + { "/math.js", "export default function sum(a, b) { return a + b; } export const mul = (a, b) => a * b;" } + })) + .WithDefaultLoader(new LoaderOptions() { IsResourceLoadingEnabled = true }); + + var context = BrowsingContext.New(config); + var html = "
"; + var document = await context.OpenAsync(r => r.Content(html)).WhenStable().ConfigureAwait(false); + Assert.AreEqual("5:6", document.GetElementById("test")?.TextContent); + } + + [Test] + public async Task ModuleEvaluationRunsOnlyOnceForSharedDependency() + { + var config = Configuration.Default + .WithJs() + .WithNavigator() + .With(new MockHttpClientRequester(new Dictionary() + { + { "/counter.js", "let runs = 0; runs++; export function getRuns() { return runs; }" }, + { "/a.js", "import { getRuns } from '/counter.js'; export const a = getRuns();" }, + { "/b.js", "import { getRuns } from '/counter.js'; export const b = getRuns();" } + })) + .WithDefaultLoader(new LoaderOptions() { IsResourceLoadingEnabled = true }); + + var context = BrowsingContext.New(config); + var html = "
"; + var document = await context.OpenAsync(r => r.Content(html)).WhenStable().ConfigureAwait(false); + Assert.AreEqual("1:1", document.GetElementById("test")?.TextContent); + } + + [Test] + public async Task ModuleScriptCanRecoverFromDynamicImportFailure() + { + var config = Configuration.Default + .WithJs() + .WithNavigator() + .With(new MockHttpClientRequester(new Dictionary() + { + { "/app.js", "import('/broken.js').catch(() => import('/fallback.js')).then(m => m.start());" }, + { "/broken.js", "throw new Error('broken'); export const x = 1;" }, + { "/fallback.js", "export function start() { document.getElementById('test').textContent = 'fallback'; }" } + })) + .WithDefaultLoader(new LoaderOptions() { IsResourceLoadingEnabled = true }); + + var context = BrowsingContext.New(config); + var html = "
booting
"; + var document = await context.OpenAsync(r => r.Content(html)).WhenStable().ConfigureAwait(false); + Assert.AreEqual("fallback", document.GetElementById("test")?.TextContent); + } + + [Test] + public async Task ModuleScriptCanPerformRetryLikeWorkflow() + { + var config = Configuration.Default + .WithJs() + .WithNavigator() + .With(new MockHttpClientRequester(new Dictionary() + { + { "/task.js", "let attempts = 0; export async function runWithRetry() { attempts++; if (attempts < 2) { throw new Error('transient'); } return 'ok:' + attempts.toString(); }" } + })) + .WithDefaultLoader(new LoaderOptions() { IsResourceLoadingEnabled = true }); + + var context = BrowsingContext.New(config); + var html = "
init
"; + var document = await context.OpenAsync(r => r.Content(html)).WhenStable().ConfigureAwait(false); + Assert.AreEqual("ok:2", document.GetElementById("test")?.TextContent); + } + + [Test] + public async Task ModuleScriptCanComposeParallelAsyncDependencies() + { + var config = Configuration.Default + .WithJs() + .WithNavigator() + .With(new MockHttpClientRequester(new Dictionary() + { + { "/a.js", "export async function readA() { return 'A'; }" }, + { "/b.js", "export async function readB() { return 'B'; }" } + })) + .WithDefaultLoader(new LoaderOptions() { IsResourceLoadingEnabled = true }); + + var context = BrowsingContext.New(config); + var html = "
x
"; + var document = await context.OpenAsync(r => r.Content(html)).WhenStable().ConfigureAwait(false); + Assert.AreEqual("AB", document.GetElementById("test")?.TextContent); + } + + [Test] + public async Task ModuleCycleMaintainsLiveBindings() + { + var config = Configuration.Default + .WithJs() + .WithNavigator() + .With(new MockHttpClientRequester(new Dictionary() + { + { "/a.js", "import { bValue } from '/b.js'; export let aValue = 'a0'; export function hydrateA() { aValue = 'a1:' + bValue; }" }, + { "/b.js", "import { aValue } from '/a.js'; export let bValue = 'b0'; export function hydrateB() { bValue = 'b1:' + aValue; }" } + })) + .WithDefaultLoader(new LoaderOptions() { IsResourceLoadingEnabled = true }); + + var context = BrowsingContext.New(config); + var html = "
"; + var document = await context.OpenAsync(r => r.Content(html)).WhenStable().ConfigureAwait(false); + Assert.AreEqual("a1:b1:a0|b1:a0", document.GetElementById("test")?.TextContent); + } + + [Test] + public async Task DynamicImportUsesModuleCacheAcrossCalls() + { + var config = Configuration.Default + .WithJs() + .WithNavigator() + .With(new MockHttpClientRequester(new Dictionary() + { + { "/counter.js", "let count = 0; count++; export function value() { return count; }" } + })) + .WithDefaultLoader(new LoaderOptions() { IsResourceLoadingEnabled = true }); + + var context = BrowsingContext.New(config); + var html = "
"; + var document = await context.OpenAsync(r => r.Content(html)).WhenStable().ConfigureAwait(false); + Assert.AreEqual("1:1", document.GetElementById("test")?.TextContent); + } + + [Test] + public async Task ImportMapCanProvideVersionedAliasing() + { + var config = Configuration.Default + .WithJs() + .WithNavigator() + .With(new MockHttpClientRequester(new Dictionary() + { + { "/lib-v2.js", "export const version = '2.0.0';" } + })) + .WithDefaultLoader(new LoaderOptions() { IsResourceLoadingEnabled = true }); + + var context = BrowsingContext.New(config); + var html = "
"; + var document = await context.OpenAsync(r => r.Content(html)).WhenStable().ConfigureAwait(false); + Assert.AreEqual("2.0.0", document.GetElementById("test")?.TextContent); + } + + [Test] + public async Task ApplicationBootstrapCanExposeUnhandledErrorsToUi() + { + var config = Configuration.Default + .WithJs() + .WithNavigator() + .With(new MockHttpClientRequester(new Dictionary() + { + { "/boot.js", "export async function start() { throw new Error('fatal'); }" } + })) + .WithDefaultLoader(new LoaderOptions() { IsResourceLoadingEnabled = true }); + + var context = BrowsingContext.New(config); + var html = "
idle
"; + var document = await context.OpenAsync(r => r.Content(html)).WhenStable().ConfigureAwait(false); + Assert.AreEqual("fatal", document.GetElementById("test")?.TextContent); + } + + [Test] + public async Task AppFlowCanIgnoreStaleRequestResults() + { + var config = Configuration.Default + .WithJs() + .WithNavigator() + .With(new MockHttpClientRequester(new Dictionary() + { + { "/api.js", "const resolvers = []; export function request(value) { return new Promise(resolve => resolvers.push(() => resolve(value))); } export function flush(index) { resolvers[index](); }" } + })) + .WithDefaultLoader(new LoaderOptions() { IsResourceLoadingEnabled = true }); + + var context = BrowsingContext.New(config); + var html = "
none
"; + var document = await context.OpenAsync(r => r.Content(html)).WhenStable().ConfigureAwait(false); + Assert.AreEqual("new", document.GetElementById("test")?.TextContent); + } + + [Test] + public async Task AppBootstrapGuardCanEnsureIdempotentStartup() + { + var config = Configuration.Default + .WithJs() + .WithNavigator() + .With(new MockHttpClientRequester(new Dictionary() + { + { "/startup.js", "let started = false; let calls = 0; export function start() { if (started) { return; } started = true; calls++; } export function getCalls() { return calls; }" } + })) + .WithDefaultLoader(new LoaderOptions() { IsResourceLoadingEnabled = true }); + + var context = BrowsingContext.New(config); + var html = "
"; + var document = await context.OpenAsync(r => r.Content(html)).WhenStable().ConfigureAwait(false); + Assert.AreEqual("1", document.GetElementById("test")?.TextContent); + } + + [Test] + public async Task AppFlowCanShareInFlightInitializationAcrossConsumers() + { + var config = Configuration.Default + .WithJs() + .WithNavigator() + .With(new MockHttpClientRequester(new Dictionary() + { + { "/init.js", "let initPromise; let runs = 0; export function ensureReady() { if (!initPromise) { runs++; initPromise = Promise.resolve('ready'); } return initPromise; } export function getRuns() { return runs; }" } + })) + .WithDefaultLoader(new LoaderOptions() { IsResourceLoadingEnabled = true }); + + var context = BrowsingContext.New(config); + var html = "
"; + var document = await context.OpenAsync(r => r.Content(html)).WhenStable().ConfigureAwait(false); + Assert.AreEqual("ready:1", document.GetElementById("test")?.TextContent); + } + + [Test] + public async Task AppFlowCanPropagateCancellationTokenState() + { + var config = Configuration.Default + .WithJs() + .WithNavigator() + .With(new MockHttpClientRequester(new Dictionary() + { + { "/cancel.js", "export function createToken() { return { cancelled: false }; } export function cancel(token) { token.cancelled = true; } export function run(token) { return Promise.resolve().then(() => { if (token.cancelled) { throw new Error('cancelled'); } return 'done'; }); }" } + })) + .WithDefaultLoader(new LoaderOptions() { IsResourceLoadingEnabled = true }); + + var context = BrowsingContext.New(config); + var html = "
"; + var document = await context.OpenAsync(r => r.Content(html)).WhenStable().ConfigureAwait(false); + Assert.AreEqual("cancelled", document.GetElementById("test")?.TextContent); + } + + [Test] + public async Task AppFlowCanDeDuplicateConcurrentRequestsByKey() + { + var config = Configuration.Default + .WithJs() + .WithNavigator() + .With(new MockHttpClientRequester(new Dictionary() + { + { "/users.js", "const inflight = new Map(); let calls = 0; export function fetchUser(id) { if (!inflight.has(id)) { calls++; inflight.set(id, Promise.resolve('user-' + id).finally(() => inflight.delete(id))); } return inflight.get(id); } export function getCalls() { return calls; }" } + })) + .WithDefaultLoader(new LoaderOptions() { IsResourceLoadingEnabled = true }); + + var context = BrowsingContext.New(config); + var html = "
"; + var document = await context.OpenAsync(r => r.Content(html)).WhenStable().ConfigureAwait(false); + Assert.AreEqual("user-42:user-42:1", document.GetElementById("test")?.TextContent); + } + + [Test] + public async Task MicrotasksRunAfterSynchronousWorkInOrder() + { + var config = Configuration.Default + .WithJs() + .WithNavigator() + .WithDefaultLoader(new LoaderOptions() { IsResourceLoadingEnabled = true }); + + var context = BrowsingContext.New(config); + var html = "
"; + var document = await context.OpenAsync(r => r.Content(html)).WhenStable().ConfigureAwait(false); + Assert.AreEqual("sync,m1,m2", document.GetElementById("test")?.TextContent); + } + } +} diff --git a/src/AngleSharp.Js.Tests/EcoTests.cs b/src/AngleSharp.Js.Tests/EcoTests.cs index fd1b7b4..95263c2 100644 --- a/src/AngleSharp.Js.Tests/EcoTests.cs +++ b/src/AngleSharp.Js.Tests/EcoTests.cs @@ -9,7 +9,7 @@ public class EcoTests [Test] public async Task GetCssSheetRuleFromJavaScript() { - var config = Configuration.Default.WithCss().WithJs(); + var config = Configuration.Default.WithCss().WithNavigator().WithJs(); var context = BrowsingContext.New(config); var style = "body { color: red; }"; var script = "var sheet = document.querySelector('style').sheet; var color = sheet.cssRules[0].style.color; document.querySelector('div').textContent = color;"; @@ -20,7 +20,7 @@ public async Task GetCssSheetRuleFromJavaScript() [Test] public async Task GetCssInlineStyleFromJavaScript() { - var config = Configuration.Default.WithCss().WithJs(); + var config = Configuration.Default.WithCss().WithNavigator().WithJs(); var context = BrowsingContext.New(config); var style = "color: blue;"; var script = "var color = document.querySelector('div').style.color; document.querySelector('div').textContent = color;"; diff --git a/src/AngleSharp.Js.Tests/FireEventTests.cs b/src/AngleSharp.Js.Tests/FireEventTests.cs index e468a1f..16d2567 100644 --- a/src/AngleSharp.Js.Tests/FireEventTests.cs +++ b/src/AngleSharp.Js.Tests/FireEventTests.cs @@ -3,7 +3,11 @@ namespace AngleSharp.Js.Tests using AngleSharp.Dom; using AngleSharp.Dom.Events; using AngleSharp.Scripting; + using Jint; using NUnit.Framework; + + using System; + using System.Linq; using System.Threading.Tasks; [TestFixture] @@ -57,11 +61,11 @@ public async Task InvokeLoadEventFromJsAndCustomEventFromJsAndCs() document.AddEventListener("hello", (s, ev) => { - log.Put(log.Get("length").AsNumber().ToString(), "d", false); + log.Set(log.Get("length").AsNumber(), "d", false); }); document.Dispatch(new Event("hello")); - + Assert.AreEqual(4.0, log.Get("length").AsNumber()); Assert.AreEqual("a", log.Get("0").AsString()); Assert.AreEqual("b", log.Get("1").AsString()); @@ -152,6 +156,76 @@ public async Task AddAndInvokeClickHandlerWithStringFunctionWontWork() Assert.IsFalse(clicked); } + [Test] + public async Task ClickHandlerIsKeptPerElement() + { + var service = new JsScriptingService(); + var cfg = Configuration.Default.With(service).WithEventLoop(); + var html = @" + + +
+
+ +"; + var document = await BrowsingContext.New(cfg).OpenAsync(m => m.Content(html)); + var engine = service.GetOrCreateJint(document); + + Assert.IsTrue(engine.GetValue("aIsF").AsBoolean()); + Assert.IsTrue(engine.GetValue("bIsG").AsBoolean()); + Assert.IsFalse(engine.GetValue("shared").AsBoolean()); + + var log = engine.GetValue("log").AsArray(); + Assert.AreEqual(2.0, log.Get("length").AsNumber()); + Assert.AreEqual("f", log.Get("0").AsString()); + Assert.AreEqual("g", log.Get("1").AsString()); + } + + [Test] + public async Task ClearingClickHandlerOfOneElementKeepsTheOther() + { + var service = new JsScriptingService(); + var cfg = Configuration.Default.With(service).WithEventLoop(); + var html = @" + + +
+
+ +"; + var document = await BrowsingContext.New(cfg).OpenAsync(m => m.Content(html)); + var engine = service.GetOrCreateJint(document); + + Assert.IsTrue(engine.GetValue("cleared").AsBoolean()); + + var log = engine.GetValue("log").AsArray(); + Assert.AreEqual(1.0, log.Get("length").AsNumber()); + Assert.AreEqual("g", log.Get("0").AsString()); + } + [Test] public async Task BodyOnloadWorksWhenSetAsAttributeInitially() { @@ -210,10 +284,8 @@ public async Task SetTimeoutWithNormalFunction() } [Test] - public async Task DomContentLoadedEventIsFired_Issue50() + public async Task DomContentLoadedEventIsFiredOnDocument_Issue50() { - //TODO Check this as well on the window level - currently works - //only against document (se AngleSharp#789) var cfg = Configuration.Default.WithJs().WithEventLoop(); var html = @" @@ -233,6 +305,28 @@ public async Task DomContentLoadedEventIsFired_Issue50() Assert.AreEqual("Success!", div?.TextContent); } + [Test] + public async Task DomContentLoadedEventIsFiredOnWindow_Issue50() + { + var cfg = Configuration.Default.WithJs().WithEventLoop(); + var html = @" + + + +"; + var document = await BrowsingContext.New(cfg).OpenAsync(m => m.Content(html)) + .WhenStable(); + + var div = document.QuerySelector("div"); + Assert.AreEqual("Success!", div?.TextContent); + } + [Test] public async Task DocumentLoadEventIsFired_Issue42() { @@ -256,6 +350,60 @@ public async Task DocumentLoadEventIsFired_Issue42() Assert.AreEqual("Success!", div?.TextContent); } + [Test] + public async Task DocumentReadyStateIsComplete_Issue86() + { + var cfg = Configuration.Default.WithJs().WithEventLoop(); + var html = @" + + + +"; + var context = BrowsingContext.New(cfg); + var document = await context.OpenAsync(m => m.Content(html)) + .WhenStable(); + + var divs = document.GetElementsByTagName("div"); + + // expected value will vary depending on AngleSharp package version + // 1.0.2 and greater, expected value will be { "interactive", "complete" + // prior to 1.0.2, expected value will be { "1", "2" } + var expected = new[] { DocumentReadyState.Interactive, DocumentReadyState.Complete } + .Select(e => e.GetOfficialName() ?? Convert.ToInt32(e).ToString()); + CollectionAssert.AreEqual(expected, divs.Select(d => d.TextContent)); + } + + [Test] + public async Task SetTimeoutWithSeveralDifferentFunctions() + { + var service = new JsScriptingService(); + var cfg = Configuration.Default.With(service).WithEventLoop(); + var html = @" + + + +"; + var document = await BrowsingContext.New(cfg).OpenAsync(m => m.Content(html)) + .WhenStable(); + var log = service.GetOrCreateJint(document).GetValue("log").AsArray(); + + Assert.AreEqual(3.0, log.Get("length").AsNumber()); + Assert.AreEqual("a", log.Get("0").AsString()); + Assert.AreEqual("b", log.Get("1").AsString()); + Assert.AreEqual("c", log.Get("2").AsString()); + } + [Test] public async Task SetTimeoutWithStringAsFunction() { diff --git a/src/AngleSharp.Js.Tests/Fixtures/Html5Test/README.md b/src/AngleSharp.Js.Tests/Fixtures/Html5Test/README.md new file mode 100644 index 0000000..9f55fb5 --- /dev/null +++ b/src/AngleSharp.Js.Tests/Fixtures/Html5Test/README.md @@ -0,0 +1,20 @@ +# HTML5test fixture + +This fixture was retrieved from `https://html5test.com/` on 2026-07-27: + +- `https://html5test.com/` +- `https://html5test.com/scripts/base.js` +- `https://html5test.com/scripts/8/engine.js` +- `https://html5test.com/scripts/8/data.js` +- `https://html5test.com/assets/detect.html` +- `https://html5test.com/assets/csp.html` + +HTML5test is Copyright (c) 2010-2016 Niels Leenheer and is distributed under +the MIT license, reproduced in `index.html`. + +The Cloudflare email-decoder script was removed from `index.html`. The remote +WhichBrowser detector was replaced by `whichbrowser-stub.js`, which reports a +clean browser and keeps the fixture deterministic. A wrapper around the +destructuring feature probe converts Jint's unsupported `eval` path into the +feature-test's expected `SyntaxError` (https://github.com/sebastienros/jint/issues/2825). +No test engine code was modified. diff --git a/src/AngleSharp.Js.Tests/Fixtures/Html5Test/assets/csp.html b/src/AngleSharp.Js.Tests/Fixtures/Html5Test/assets/csp.html new file mode 100644 index 0000000..423e959 --- /dev/null +++ b/src/AngleSharp.Js.Tests/Fixtures/Html5Test/assets/csp.html @@ -0,0 +1,19 @@ + + + + + + + \ No newline at end of file diff --git a/src/AngleSharp.Js.Tests/Fixtures/Html5Test/assets/detect.html b/src/AngleSharp.Js.Tests/Fixtures/Html5Test/assets/detect.html new file mode 100644 index 0000000..ed6ea21 --- /dev/null +++ b/src/AngleSharp.Js.Tests/Fixtures/Html5Test/assets/detect.html @@ -0,0 +1 @@ +&&< diff --git a/src/AngleSharp.Js.Tests/Fixtures/Html5Test/index.html b/src/AngleSharp.Js.Tests/Fixtures/Html5Test/index.html new file mode 100644 index 0000000..d6b6072 --- /dev/null +++ b/src/AngleSharp.Js.Tests/Fixtures/Html5Test/index.html @@ -0,0 +1,357 @@ + + + + HTML5test - How well does your browser support HTML5? + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+

HTML5test how well does your browser support HTML5?

+ + +
+ +
+ + +
+
+

HTML5test is dead

+ +
+

+ HTML5test is dead. It's been dead for a while. In fact it hasn't been updated since 2016. +

+

+ And that is fine. This website has served it's purpose and helped popularise HTML5 with a + general audience and developers. It pushed companies to invest in their browsers and it kept them honest. + And from talking to people working for those companies over the years it worked. It helped + convince people higher up to invest more resources, because nobody wants their browser to look + bad. +

+

+ The goal of this website was always to push browsers to adopt HTML5. + To make HTML5 available for users and developers in all browsers. + And if just one feature is now available to developers in all browsers thanks to HTML5test, + this website has served it's purpose. And I know for sure that it has served it's purpose. + HTML5 is now generally supported and there aren't any truly bad browsers anymore. +

+

+ I'll try to keep this page online as a snapshot of the original test, and there is an unofficial + updated version available at html5test.co. +

+

+ It was fun to work on this project while it lasted. I have some awesome memories of talking to + the people at the W3C, Apple, Mozilla, Google and Microsoft. Thanks for all the support over the years! +

+

+ Niels Leenheer +

+
+
+
+ +
+ +
+ + +
+
+
+ + + + diff --git a/src/AngleSharp.Js.Tests/Fixtures/Html5Test/scripts/8/data.js b/src/AngleSharp.Js.Tests/Fixtures/Html5Test/scripts/8/data.js new file mode 100644 index 0000000..d8e2b02 --- /dev/null +++ b/src/AngleSharp.Js.Tests/Fixtures/Html5Test/scripts/8/data.js @@ -0,0 +1,2654 @@ + + +var tests = [ + + { + id: 'semantics', + name: 'Semantics', + column: 'left', + items: [ + { + id: 'parsing', + name: 'Parsing rules', + status: 'stable', + items: [ + { + id: 'doctype', + name: '<!DOCTYPE html> triggers standards mode', + urls: [ + [ 'w3c', 'http://www.w3.org/TR/html5/syntax.html#the-doctype' ], + [ 'whatwg', 'https://html.spec.whatwg.org/multipage/syntax.html#the-doctype' ] + ] + }, { + id: 'tokenizer', + name: 'HTML5 tokenizer', + value: 3, + urls: [ + [ 'w3c', 'http://www.w3.org/TR/html5/syntax.html#parsing' ], + [ 'whatwg', 'https://html.spec.whatwg.org/multipage/syntax.html#parsing' ], + [ 'mdn', '/Web/Guide/HTML/HTML5/HTML5_Parser' ] + ] + }, { + id: 'tree', + name: 'HTML5 tree building', + value: 2, + urls: [ + [ 'w3c', 'http://www.w3.org/TR/html5/syntax.html#parsing' ], + [ 'whatwg', 'https://html.spec.whatwg.org/multipage/syntax.html#parsing' ], + [ 'mdn', '/Web/Guide/HTML/HTML5/HTML5_Parser' ] + ] + }, + + 'HTML5 defines rules for embedding SVG and MathML inside a regular HTML document. The following tests only check if the browser is following the HTML5 parsing rules for inline SVG and MathML, not if the browser can actually understand and render it.', + + { + id: 'svg', + name: 'Parsing inline SVG', + urls: [ + [ 'w3c', 'http://www.w3.org/TR/html5/embedded-content-0.html#svg' ], + [ 'whatwg', 'https://html.spec.whatwg.org/multipage/embedded-content.html#svg-0' ], + [ 'mdn', '/Web/SVG' ] + ] + + }, { + id: 'mathml', + name: 'Parsing inline MathML', + urls: [ + [ 'w3c', 'http://www.w3.org/TR/html5/embedded-content-0.html#mathml' ], + [ 'whatwg', 'https://html.spec.whatwg.org/multipage/embedded-content.html#mathml' ], + [ 'mdn', '/Web/MathML' ] + ] + } + ] + }, { + id: 'elements', + name: 'Elements', + status: 'stable', + items: [ + { + id: 'dataset', + name: 'Embedding custom non-visible data', + value: 2, + urls: [ + [ 'w3c', 'http://www.w3.org/TR/html5/dom.html#embedding-custom-non-visible-data-with-the-data-*-attributes' ], + [ 'whatwg', 'https://html.spec.whatwg.org/multipage/dom.html#embedding-custom-non-visible-data-with-the-data-*-attributes' ], + [ 'mdn', '/Web/API/HTMLElement/dataset' ] + ] + }, + + 'New or modified elements', + + { + id: 'section', + name: 'Section elements', + urls: [ + [ 'mdn', '/Web/Guide/HTML/Sections_and_Outlines_of_an_HTML5_document' ] + ], + items: [ + { + id: 'section', + name: 'section element', + value: 1, + urls: [ + [ 'w3c', 'http://www.w3.org/TR/html5/sections.html#the-section-element' ], + [ 'whatwg', 'https://html.spec.whatwg.org/multipage/semantics.html#the-section-element' ] + ] + }, { + id: 'nav', + name: 'nav element', + value: 1, + urls: [ + [ 'w3c', 'http://www.w3.org/TR/html5/sections.html#the-nav-element' ], + [ 'whatwg', 'https://html.spec.whatwg.org/multipage/semantics.html#the-nav-element' ] + ] + }, { + id: 'article', + name: 'article element', + value: 1, + urls: [ + [ 'w3c', 'http://www.w3.org/TR/html5/sections.html#the-article-element' ], + [ 'whatwg', 'https://html.spec.whatwg.org/multipage/semantics.html#the-article-element' ] + ] + }, { + id: 'aside', + name: 'aside element', + value: 1, + urls: [ + [ 'w3c', 'http://www.w3.org/TR/html5/sections.html#the-aside-element' ], + [ 'whatwg', 'https://html.spec.whatwg.org/multipage/semantics.html#the-aside-element' ] + ] + }, { + id: 'header', + name: 'header element', + value: 1, + urls: [ + [ 'w3c', 'http://www.w3.org/TR/html5/sections.html#the-header-element' ], + [ 'whatwg', 'https://html.spec.whatwg.org/multipage/semantics.html#the-header-element' ] + ] + }, { + id: 'footer', + name: 'footer element', + value: 1, + urls: [ + [ 'w3c', 'http://www.w3.org/TR/html5/sections.html#the-footer-element' ], + [ 'whatwg', 'https://html.spec.whatwg.org/multipage/semantics.html#the-footer-element' ] + ] + } + ] + + }, { + id: 'grouping', + name: 'Grouping content elements', + urls: [ + [ 'mdn', '/Web/Guide/HTML/Sections_and_Outlines_of_an_HTML5_document' ] + ], + items: [ + { + id: 'main', + name: 'main element', + value: 1, + urls: [ + [ 'w3c', 'http://www.w3.org/html/wg/drafts/html/master/single-page.html#the-main-element' ], + [ 'whatwg', 'https://html.spec.whatwg.org/multipage/semantics.html#the-main-element' ] + ] + }, { + id: 'figure', + name: 'figure element', + value: 1, + urls: [ + [ 'w3c', 'http://www.w3.org/TR/html5/grouping-content.html#the-figure-element' ], + [ 'whatwg', 'https://html.spec.whatwg.org/multipage/semantics.html#the-figure-element' ] + ] + }, { + id: 'figcaption', + name: 'figcaption element', + value: 1, + urls: [ + [ 'w3c', 'http://www.w3.org/TR/html5/grouping-content.html#the-figcaption-element' ], + [ 'whatwg', 'https://html.spec.whatwg.org/multipage/semantics.html#the-figcaption-element' ] + ] + }, { + id: 'ol', + name: 'reversed attribute on the ol element', + value: 1, + urls: [ + [ 'w3c', 'http://www.w3.org/TR/html5/grouping-content.html#the-ol-element' ], + [ 'whatwg', 'https://html.spec.whatwg.org/multipage/semantics.html#the-ol-element' ] + ] + } + ] + }, { + id: 'semantic', + name: 'Text-level semantic elements', + items: [ + { + id: 'download', + name: 'download attribute on the a element', + value: 1, + urls: [ + [ 'w3c', 'http://www.w3.org/TR/html5/text-level-semantics.html#the-a-element' ], + [ 'whatwg', 'http://developers.whatwg.org/links.html#attr-hyperlink-download' ] + ] + }, { + id: 'ping', + name: 'ping attribute on the a element', + status: 'proposal', + value: 1, + urls: [ + [ 'whatwg', 'https://html.spec.whatwg.org/multipage/semantics.html#ping' ] + ] + }, { + id: 'mark', + name: 'mark element', + value: 1, + urls: [ + [ 'w3c', 'http://www.w3.org/TR/html5/text-level-semantics.html#the-mark-element' ], + [ 'whatwg', 'https://html.spec.whatwg.org/multipage/semantics.html#the-mark-element' ] + ] + }, { + id: 'ruby', + name: 'ruby, rt and rp elements', + value: 3, + urls: [ + [ 'w3c', 'http://www.w3.org/TR/html5/text-level-semantics.html#the-ruby-element' ], + [ 'whatwg', 'https://html.spec.whatwg.org/multipage/semantics.html#the-ruby-element' ] + ] + }, { + id: 'time', + name: 'time element', + value: 1, + urls: [ + [ 'w3c', 'http://www.w3.org/TR/html5/text-level-semantics.html#the-time-element' ], + [ 'whatwg', 'https://html.spec.whatwg.org/multipage/semantics.html#the-time-element' ] + ] + }, { + id: 'data', + name: 'data element', + value: 1, + urls: [ + [ 'w3c', 'http://www.w3.org/TR/html5/text-level-semantics.html#the-data-element' ], + [ 'whatwg', 'https://html.spec.whatwg.org/multipage/semantics.html#the-data-element' ] + ] + }, { + id: 'wbr', + name: 'wbr element', + value: 1, + urls: [ + [ 'w3c', 'http://www.w3.org/TR/html5/text-level-semantics.html#the-wbr-element' ], + [ 'whatwg', 'https://html.spec.whatwg.org/multipage/semantics.html#the-wbr-element' ] + ] + } + ] + }, { + id: 'interactive', + name: 'Interactive elements', + items: [ + { + id: 'details', + name: 'details element', + value: 1, + urls: [ + [ 'whatwg', 'https://html.spec.whatwg.org/multipage/forms.html#the-details-element' ] + ] + }, { + id: 'summary', + name: 'summary element', + value: 1, + urls: [ + [ 'whatwg', 'https://html.spec.whatwg.org/multipage/forms.html#the-summary-element' ] + ] + }, { + id: 'menutoolbar', + name: 'menu element of type toolbar', + status: 'proposal', + value: { maximum: 1, award: { OLD: 0 } }, + urls: [ + [ 'whatwg', 'https://html.spec.whatwg.org/multipage/forms.html#the-menu-element' ] + ] + }, { + id: 'menucontext', + name: 'menu element of type context', + status: 'proposal', + value: { maximum: 2, award: { OLD: 1 } }, + urls: [ + [ 'whatwg', 'https://html.spec.whatwg.org/multipage/forms.html#the-menu-element' ] + ] + }, { + id: 'dialog', + name: 'dialog element', + status: 'proposal', + value: 1, + urls: [ + [ 'w3c', 'http://www.w3.org/html/wg/drafts/html/master/single-page.html#the-dialog-element' ], + [ 'whatwg', 'https://html.spec.whatwg.org/multipage/forms.html#the-dialog-element' ] + ] + } + ] + }, + + 'Global attributes or methods', + + { + id: 'hidden', + name: 'hidden attribute', + value: 1, + urls: [ + [ 'w3c', 'http://www.w3.org/TR/html5/editing.html#the-hidden-attribute' ], + [ 'whatwg', 'https://html.spec.whatwg.org/multipage/interaction.html#the-hidden-attribute' ] + ] + }, { + id: 'dynamic', + name: 'Dynamic markup insertion', + items: [ + { + id: 'outerHTML', + name: 'outerHTML property', + value: 1, + urls: [ + [ 'w3c', 'https://dvcs.w3.org/hg/innerhtml/raw-file/tip/index.html#widl-Element-outerHTML' ] + ] + }, { + id: 'insertAdjacentHTML', + name: 'insertAdjacentHTML function', + value: 1, + urls: [ + [ 'w3c', 'https://dvcs.w3.org/hg/innerhtml/raw-file/tip/index.html#widl-Element-insertAdjacentHTML-void-DOMString-position-DOMString-text' ] + ] + } + ] + } + ] + }, { + id: 'form', + name: 'Forms', + status: 'stable', + items: [ + 'Field types', + + { + id: 'text', + name: 'input type=text', + items: [ + { + id: 'element', + name: 'Minimal element support' + }, { + id: 'selection', + name: 'Selection Direction', + value: 2 + } + ] + }, { + id: 'search', + name: 'input type=search', + items: [ + { + id: 'element', + name: 'Minimal element support', + value: 1, + url: 'http://www.w3.org/TR/html5/forms.html#text-(type=text)-state-and-search-state-(type=search)' + } + ] + }, { + id: 'tel', + name: 'input type=tel', + items: [ + { + id: 'element', + name: 'Minimal element support', + value: 1, + url: 'http://www.w3.org/TR/html5/forms.html#telephone-state-(type=tel)' + } + ] + }, { + id: 'url', + name: 'input type=url', + items: [ + { + id: 'element', + name: 'Minimal element support', + value: 1, + url: 'http://www.w3.org/TR/html5/forms.html#url-state-(type=url)' + }, { + id: 'validation', + name: 'Field validation', + url: 'http://www.w3.org/TR/html5/forms.html#the-constraint-validation-api' + } + ] + }, { + id: 'email', + name: 'input type=email', + items: [ + { + id: 'element', + name: 'Minimal element support', + value: 1, + url: 'http://www.w3.org/TR/html5/forms.html#e-mail-state-(type=email)' + }, { + id: 'validation', + name: 'Field validation', + url: 'http://www.w3.org/TR/html5/forms.html#the-constraint-validation-api' + } + ] + }, { + id: 'date', + name: 'input type=date', + items: [ + { + id: 'element', + name: 'Minimal element support', + value: 2, + url: 'http://www.w3.org/TR/html5/forms.html#date-state-(type=date)' + }, { + id: 'ui', + value: 2, + name: 'Custom user-interface' + }, { + id: 'sanitization', + name: 'Value sanitization', + url: 'http://www.w3.org/TR/html5/forms.html#value-sanitization-algorithm' + }, { + id: 'min', + name: 'min attribute', + url: 'http://www.w3.org/TR/html5/forms.html#attr-input-min' + }, { + id: 'max', + name: 'max attribute', + url: 'http://www.w3.org/TR/html5/forms.html#attr-input-max' + }, { + id: 'step', + name: 'step attribute', + url: 'http://www.w3.org/TR/html5/forms.html#attr-input-step' + }, { + id: 'stepDown', + name: 'stepDown() method', + url: 'http://www.w3.org/TR/html5/forms.html#dom-input-stepdown' + }, { + id: 'stepUp', + name: 'stepUp() method', + url: 'http://www.w3.org/TR/html5/forms.html#dom-input-stepup' + }, { + id: 'valueAsDate', + name: 'valueAsDate() method', + url: 'http://www.w3.org/TR/html5/forms.html#dom-input-valueasdate' + }, { + id: 'valueAsNumber', + name: 'valueAsNumber() method', + url: 'http://www.w3.org/TR/html5/forms.html#dom-input-valueasnumber' + } + ] + }, { + id: 'month', + name: 'input type=month', + items: [ + { + id: 'element', + name: 'Minimal element support', + value: 2, + url: 'http://www.w3.org/TR/html5/forms.html#month-state-(type=month)' + }, { + id: 'ui', + name: 'Custom user-interface', + value: 2 + }, { + id: 'sanitization', + name: 'Value sanitization', + url: 'http://www.w3.org/TR/html5/forms.html#value-sanitization-algorithm' + }, { + id: 'min', + name: 'min attribute', + url: 'http://www.w3.org/TR/html5/forms.html#attr-input-min' + }, { + id: 'max', + name: 'max attribute', + url: 'http://www.w3.org/TR/html5/forms.html#attr-input-max' + }, { + id: 'step', + name: 'step attribute', + url: 'http://www.w3.org/TR/html5/forms.html#attr-input-step' + }, { + id: 'stepDown', + name: 'stepDown() method', + url: 'http://www.w3.org/TR/html5/forms.html#dom-input-stepdown' + }, { + id: 'stepUp', + name: 'stepUp() method', + url: 'http://www.w3.org/TR/html5/forms.html#dom-input-stepup' + }, { + id: 'valueAsDate', + name: 'valueAsDate() method', + url: 'http://www.w3.org/TR/html5/forms.html#dom-input-valueasdate' + }, { + id: 'valueAsNumber', + name: 'valueAsNumber() method', + url: 'http://www.w3.org/TR/html5/forms.html#dom-input-valueasnumber' + } + ] + }, { + id: 'week', + name: 'input type=week', + items: [ + { + id: 'element', + name: 'Minimal element support', + value: 2, + url: 'http://www.w3.org/TR/html5/forms.html#week-state-(type=week)' + }, { + id: 'ui', + name: 'Custom user-interface', + value: 2 + }, { + id: 'sanitization', + name: 'Value sanitization', + url: 'http://www.w3.org/TR/html5/forms.html#value-sanitization-algorithm' + }, { + id: 'min', + name: 'min attribute', + url: 'http://www.w3.org/TR/html5/forms.html#attr-input-min' + }, { + id: 'max', + name: 'max attribute', + url: 'http://www.w3.org/TR/html5/forms.html#attr-input-max' + }, { + id: 'step', + name: 'step attribute', + url: 'http://www.w3.org/TR/html5/forms.html#attr-input-step' + }, { + id: 'stepDown', + name: 'stepDown() method', + url: 'http://www.w3.org/TR/html5/forms.html#dom-input-stepdown' + }, { + id: 'stepUp', + name: 'stepUp() method', + url: 'http://www.w3.org/TR/html5/forms.html#dom-input-stepup' + }, { + id: 'valueAsDate', + name: 'valueAsDate() method', + url: 'http://www.w3.org/TR/html5/forms.html#dom-input-valueasdate' + }, { + id: 'valueAsNumber', + name: 'valueAsNumber() method', + url: 'http://www.w3.org/TR/html5/forms.html#dom-input-valueasnumber' + } + ] + }, { + id: 'time', + name: 'input type=time', + items: [ + { + id: 'element', + name: 'Minimal element support', + value: 2, + url: 'http://www.w3.org/TR/html5/forms.html#time-state-(type=time)' + }, { + id: 'ui', + name: 'Custom user-interface', + value: 2 + }, { + id: 'sanitization', + name: 'Value sanitization', + url: 'http://www.w3.org/TR/html5/forms.html#value-sanitization-algorithm' + }, { + id: 'min', + name: 'min attribute', + url: 'http://www.w3.org/TR/html5/forms.html#attr-input-min' + }, { + id: 'max', + name: 'max attribute', + url: 'http://www.w3.org/TR/html5/forms.html#attr-input-max' + }, { + id: 'step', + name: 'step attribute', + url: 'http://www.w3.org/TR/html5/forms.html#attr-input-step' + }, { + id: 'stepDown', + name: 'stepDown() method', + url: 'http://www.w3.org/TR/html5/forms.html#dom-input-stepdown' + }, { + id: 'stepUp', + name: 'stepUp() method', + url: 'http://www.w3.org/TR/html5/forms.html#dom-input-stepup' + }, { + id: 'valueAsDate', + name: 'valueAsDate() method', + url: 'http://www.w3.org/TR/html5/forms.html#dom-input-valueasdate' + }, { + id: 'valueAsNumber', + name: 'valueAsNumber() method', + url: 'http://www.w3.org/TR/html5/forms.html#dom-input-valueasnumber' + } + ] + }, { + id: 'datetime-local', + name: 'input type=datetime-local', + items: [ + { + id: 'element', + name: 'Minimal element support', + value: 2, + url: 'http://www.w3.org/TR/html5/forms.html#local-date-and-time-state-(type=datetime-local)' + }, { + id: 'ui', + name: 'Custom user-interface', + value: 2 + }, { + id: 'sanitization', + name: 'Value sanitization', + url: 'http://www.w3.org/TR/html5/forms.html#value-sanitization-algorithm' + }, { + id: 'min', + name: 'min attribute', + url: 'http://www.w3.org/TR/html5/forms.html#attr-input-min' + }, { + id: 'max', + name: 'max attribute', + url: 'http://www.w3.org/TR/html5/forms.html#attr-input-max' + }, { + id: 'step', + name: 'step attribute', + url: 'http://www.w3.org/TR/html5/forms.html#attr-input-step' + }, { + id: 'stepDown', + name: 'stepDown() method', + url: 'http://www.w3.org/TR/html5/forms.html#dom-input-stepdown' + }, { + id: 'stepUp', + name: 'stepUp() method', + url: 'http://www.w3.org/TR/html5/forms.html#dom-input-stepup' + }, { + id: 'valueAsNumber', + name: 'valueAsNumber() method', + url: 'http://www.w3.org/TR/html5/forms.html#dom-input-valueasnumber' + } + ] + }, { + id: 'number', + name: 'input type=number', + items: [ + { + id: 'element', + name: 'Minimal element support', + value: 1, + url: 'http://www.w3.org/TR/html5/forms.html#number-state-(type=number)' + }, { + id: 'ui', + name: 'Custom user-interface', + value: 2 + }, { + id: 'sanitization', + name: 'Value sanitization', + url: 'http://www.w3.org/TR/html5/forms.html#value-sanitization-algorithm' + }, { + id: 'validation', + name: 'Field validation', + url: 'http://www.w3.org/TR/html5/forms.html#the-constraint-validation-api' + }, { + id: 'min', + name: 'min attribute', + url: 'http://www.w3.org/TR/html5/forms.html#attr-input-min' + }, { + id: 'max', + name: 'max attribute', + url: 'http://www.w3.org/TR/html5/forms.html#attr-input-max' + }, { + id: 'step', + name: 'step attribute', + url: 'http://www.w3.org/TR/html5/forms.html#attr-input-step' + }, { + id: 'stepDown', + name: 'stepDown() method', + url: 'http://www.w3.org/TR/html5/forms.html#dom-input-stepdown' + }, { + id: 'stepUp', + name: 'stepUp() method', + url: 'http://www.w3.org/TR/html5/forms.html#dom-input-stepup' + }, { + id: 'valueAsNumber', + name: 'valueAsNumber() method', + url: 'http://www.w3.org/TR/html5/forms.html#dom-input-valueasnumber' + } + ] + }, { + id: 'range', + name: 'input type=range', + items: [ + { + id: 'element', + name: 'Minimal element support', + value: 1, + url: 'http://www.w3.org/TR/html5/forms.html#range-state-(type=range)' + }, { + id: 'ui', + name: 'Custom user-interface', + value: 2 + }, { + id: 'sanitization', + name: 'Value sanitization', + url: 'http://www.w3.org/TR/html5/forms.html#value-sanitization-algorithm' + }, { + id: 'min', + name: 'min attribute', + url: 'http://www.w3.org/TR/html5/forms.html#attr-input-min' + }, { + id: 'max', + name: 'max attribute', + url: 'http://www.w3.org/TR/html5/forms.html#attr-input-max' + }, { + id: 'step', + name: 'step attribute', + url: 'http://www.w3.org/TR/html5/forms.html#attr-input-step' + }, { + id: 'stepDown', + name: 'stepDown() method', + url: 'http://www.w3.org/TR/html5/forms.html#dom-input-stepdown' + }, { + id: 'stepUp', + name: 'stepUp() method', + url: 'http://www.w3.org/TR/html5/forms.html#dom-input-stepup' + }, { + id: 'valueAsNumber', + name: 'valueAsNumber() method', + url: 'http://www.w3.org/TR/html5/forms.html#dom-input-valueasnumber' + } + ] + }, { + id: 'color', + name: 'input type=color', + items: [ + { + id: 'element', + name: 'Minimal element support', + value: 1, + url: 'http://www.w3.org/TR/html5/forms.html#color-state-(type=color)' + }, { + id: 'ui', + name: 'Custom user-interface', + value: 2 + }, { + id: 'sanitization', + name: 'Value sanitization', + url: 'http://www.w3.org/TR/html5/forms.html#value-sanitization-algorithm' + } + ] + }, { + id: 'checkbox', + name: 'input type=checkbox', + items: [ + { + id: 'element', + name: 'Minimal element support', + url: 'http://www.w3.org/TR/html5/forms.html#checkbox-state-(type=checkbox)' + }, { + id: 'indeterminate', + name: 'indeterminate property', + value: 1, + url: 'http://www.w3.org/TR/html5/forms.html#dom-input-indeterminate' + } + ] + }, { + id: 'image', + name: 'input type=image', + items: [ + { + id: 'element', + name: 'Minimal element support', + url: 'http://www.w3.org/TR/html5/forms.html#image-button-state-(type=image)' + }, { + id: 'width', + name: 'width property', + value: 0, + url: 'http://www.w3.org/TR/html5/embedded-content-0.html#attr-dim-width' + }, { + id: 'height', + name: 'height property', + value: 0, + url: 'http://www.w3.org/TR/html5/embedded-content-0.html#attr-dim-height' + } + ] + }, { + id: 'file', + name: 'input type=file', + items: [ + { + id: 'element', + name: 'Minimal element support', + url: 'http://www.w3.org/TR/html5/forms.html#file-upload-state-(type=file)' + }, { + id: 'files', + name: 'files property', + value: 1, + url: 'http://www.w3.org/TR/html5/forms.html#dom-input-files' + }, { + id: 'directory', + status: 'experimental', + name: 'Directory upload support', + value: 1, + url: 'https://wicg.github.io/directory-upload/proposal.html' + } + ] + }, { + id: 'textarea', + name: 'textarea', + items: [ + { + id: 'element', + name: 'Minimal element support', + url: 'http://www.w3.org/TR/html5/forms.html#the-textarea-element' + }, { + id: 'maxlength', + name: 'maxlength attribute', + value: 1, + url: 'http://www.w3.org/TR/html5/forms.html#attr-textarea-maxlength' + }, { + id: 'wrap', + name: 'wrap attribute', + value: 1, + url: 'http://www.w3.org/TR/html5/forms.html#attr-textarea-wrap' + } + ] + }, { + id: 'select', + name: 'select', + items: [ + { + id: 'element', + name: 'Minimal element support', + url: 'http://www.w3.org/TR/html5/forms.html#the-select-element' + }, { + id: 'required', + name: 'required attribute', + value: 1, + url: 'http://www.w3.org/TR/html5/forms.html#attr-select-required' + } + ] + }, { + id: 'fieldset', + name: 'fieldset', + items: [ + { + id: 'element', + name: 'Minimal element support', + url: 'http://www.w3.org/TR/html5/forms.html#the-fieldset-element' + }, { + id: 'elements', + name: 'elements attribute', + value: 1, + url: 'http://www.w3.org/TR/html5/forms.html#dom-fieldset-elements' + }, { + id: 'disabled', + name: 'disabled attribute', + value: 1, + url: 'http://www.w3.org/TR/html5/forms.html#dom-fieldset-disabled' + } + ] + }, { + id: 'datalist', + name: 'datalist', + items: [ + { + id: 'element', + name: 'Minimal element support', + value: 2, + url: 'http://www.w3.org/TR/html5/forms.html#the-datalist-element' + }, { + id: 'list', + name: 'list attribute for fields', + url: 'http://www.w3.org/TR/html5/forms.html#attr-input-list' + } + ] + }, { + id: 'output', + name: 'output', + items: [ + { + id: 'element', + name: 'Minimal element support', + value: 2, + url: 'http://www.w3.org/TR/html5/forms.html#the-output-element' + } + ] + }, { + id: 'progress', + name: 'progress', + items: [ + { + id: 'element', + name: 'Minimal element support', + value: 2, + url: 'http://www.w3.org/TR/html5/forms.html#the-progress-element' + } + ] + }, { + id: 'meter', + name: 'meter', + items: [ + { + id: 'element', + name: 'Minimal element support', + value: 2, + url: 'http://www.w3.org/TR/html5/forms.html#the-meter-element' + } + ] + }, + + 'Fields', + + { + id: 'validation', + name: 'Field validation', + items: [ + { + id: 'pattern', + name: 'pattern attribute', + value: 1, + url: 'http://www.w3.org/TR/html5/forms.html#attr-input-pattern' + }, { + id: 'required', + name: 'required attribute', + value: 1, + url: 'http://www.w3.org/TR/html5/forms.html#attr-input-required' + } + ] + }, { + id: 'association', + name: 'Association of controls and forms', + value: 2, + items: [ + { + id: 'control', + name: 'control property on labels', + url: 'http://www.w3.org/TR/html5/forms.html#dom-label-control' + }, { + id: 'form', + name: 'form property on fields', + url: 'http://www.w3.org/TR/html5/forms.html#attr-fae-form' + }, { + id: 'formAction', + name: 'formAction property on fields', + url: 'http://www.w3.org/TR/html5/forms.html#attr-fs-formaction' + }, { + id: 'formEnctype', + name: 'formEnctype property on fields', + url: 'http://www.w3.org/TR/html5/forms.html#attr-fs-formenctype' + }, { + id: 'formMethod', + name: 'formMethod property on fields', + url: 'http://www.w3.org/TR/html5/forms.html#attr-fs-formmethod' + }, { + id: 'formNoValidate', + name: 'formNoValidate property on fields', + url: 'http://www.w3.org/TR/html5/forms.html#attr-fs-formnovalidate' + }, { + id: 'formTarget', + name: 'formTarget property on fields', + url: 'http://www.w3.org/TR/html5/forms.html#attr-fs-formtarget' + }, { + id: 'labels', + name: 'labels property on fields', + url: 'http://www.w3.org/TR/html5/forms.html#dom-lfe-labels' + } + ] + }, { + id: 'other', + name: 'Other attributes', + value: 2, + items: [ + { + id: 'autofocus', + name: 'autofocus attribute', + url: 'http://www.w3.org/TR/html5/forms.html#attr-fe-autofocus' + }, { + id: 'autocomplete', + name: 'autocomplete attribute', + url: 'http://www.w3.org/TR/html5/forms.html#attr-fe-autocomplete' + }, { + id: 'placeholder', + name: 'placeholder attribute', + url: 'http://www.w3.org/TR/html5/forms.html#attr-input-placeholder' + }, { + id: 'multiple', + name: 'multiple attribute', + url: 'http://www.w3.org/TR/html5/forms.html#attr-input-multiple' + }, { + id: 'dirname', + name: 'dirname attribute', + url: 'https://www.w3.org/TR/html5/forms.html#attr-fe-dirname' + } + ] + }, { + id: 'selectors', + name: 'CSS selectors', + value: 2, + items: [ + { + id: 'valid', + name: ':valid selector', + url: 'http://www.w3.org/TR/html5/links.html#selector-valid' + }, { + id: 'invalid', + name: ':invalid selector', + url: 'http://www.w3.org/TR/html5/links.html#selector-invalid' + }, { + id: 'optional', + name: ':optional selector', + url: 'http://www.w3.org/TR/html5/links.html#selector-optional' + }, { + id: 'required', + name: ':required selector', + url: 'http://www.w3.org/TR/html5/links.html#selector-required' + }, { + id: 'in-range', + name: ':in-range selector', + url: 'http://www.w3.org/TR/html5/links.html#selector-in-range' + }, { + id: 'out-of-range', + name: ':out-of-range selector', + url: 'http://www.w3.org/TR/html5/links.html#selector-out-of-range' + }, { + id: 'read-write', + name: ':read-write selector', + url: 'http://www.w3.org/TR/html5/links.html#selector-read-write' + }, { + id: 'read-only', + name: ':read-only selector', + url: 'http://www.w3.org/TR/html5/links.html#selector-read-only' + } + ] + }, { + id: 'events', + name: 'Events', + value: 2, + items: [ + { + id: 'oninput', + name: 'oninput event', + url: 'http://www.w3.org/TR/html5/forms.html#event-input-input' + }, { + id: 'onchange', + name: 'onchange event', + url: 'http://www.w3.org/TR/html5/forms.html#event-input-change' + }, { + id: 'oninvalid', + name: 'oninvalid event', + url: 'http://www.w3.org/TR/html5/webappapis.html#events' + } + ] + }, + + 'Forms', + + { + id: 'formvalidation', + name: 'Form validation', + items: [ + { + id: 'checkValidity', + name: 'checkValidity method', + value: 3, + url: 'http://www.w3.org/TR/html5/forms.html#dom-form-checkvalidity' + }, { + id: 'noValidate', + name: 'noValidate attribute', + value: 1, + url: 'http://www.w3.org/TR/html5/forms.html#dom-fs-novalidate' + } + ] + } + ] + }, { + id: 'components', + status: 'stable', + name: 'Web Components', + items: [ + { + id: 'custom', + name: 'Custom elements', + value: 4, + urls: [ + [ 'w3c', 'http://w3c.github.io/webcomponents/spec/custom/' ] + ] + }, { + id: 'shadowdom', + name: 'Shadow DOM', + status: 'experimental', + value: { maximum: 4, award: { OLD: 2 } }, + urls: [ + [ 'w3c', 'http://w3c.github.io/webcomponents/spec/shadow/' ] + ] + }, { + id: 'template', + name: 'HTML templates', + value: 2, + urls: [ + [ 'w3c', 'http://www.w3.org/TR/html-templates/' ], + [ 'whatwg', 'https://html.spec.whatwg.org/multipage/scripting.html#the-template-element' ], + [ 'wp', '/tutorials/webcomponents/htmlimports' ] + ] + }, { + id: 'imports', + name: 'HTML imports', + status: 'rejected', + urls: [ + [ 'w3c', 'http://w3c.github.io/webcomponents/spec/imports/' ] + ] + } + ] + } + ] + }, + + + { + id: 'deviceaccess', + name: 'Device Access', + column: 'left', + items: [ + { + id: 'location', + name: 'Location and Orientation', + status: 'stable', + items: [ + { + id: 'geolocation', + name: 'Geolocation', + value: 15, + urls: [ + [ 'w3c', 'http://www.w3.org/TR/geolocation-API/' ], + [ 'wp', '/apis/geolocation' ], + [ 'mdn', '/Web/API/Geolocation/Using_geolocation' ] + ] + }, { + id: 'orientation', + name: 'Device Orientation', + value: 3, + urls: [ + [ 'w3c', 'http://dev.w3.org/geo/api/spec-source-orientation.html' ], + [ 'mdn', '/Web/API/DeviceOrientationEvent' ] + ] + }, { + id: 'motion', + name: 'Device Motion', + value: 2, + urls: [ + [ 'w3c', 'http://dev.w3.org/geo/api/spec-source-orientation.html' ], + [ 'mdn', '/Web/API/DeviceMotionEvent' ] + ] + } + ] + }, { + id: 'output', + name: 'Output', + status: 'proposal', + items: [ + { + id: 'requestFullScreen', + name: 'Full screen support', + value: { maximum: 5, award: { PREFIX: 3 } }, + urls: [ + [ 'w3c', 'http://dvcs.w3.org/hg/fullscreen/raw-file/tip/Overview.html#api' ], + [ 'wp', '/dom/Element/requestFullscreen' ], + [ 'mdn', '/Web/Guide/API/DOM/Using_full_screen_mode' ] + ] + }, { + id: 'notifications', + name: 'Web Notifications', + value: { maximum: 5, award: { PREFIX: 3 } }, + urls: [ + [ 'w3c', 'http://www.w3.org/TR/notifications/' ], + [ 'whatwg', 'https://notifications.spec.whatwg.org' ] + ] + } + ] + }, { + id: 'input', + name: 'Input', + status: 'proposal', + items: [ + { + id: 'getGamepads', + name: 'Gamepad control', + value: { maximum: 2, award: { PREFIX: 1 } }, + urls: [ + [ 'w3c', 'http://www.w3.org/TR/gamepad/' ], + [ 'wp', '/apis/gamepad' ] + ] + }, { + id: 'pointerevents', + name: 'Pointer Events', + value: { maximum: 5, award: { PREFIX: 3 } }, + urls: [ + [ 'w3c', 'http://www.w3.org/TR/pointerevents/' ], + [ 'wp', '/concepts/Pointer_Events' ] + ] + }, { + id: 'pointerLock', + name: 'Pointer Lock support', + value: { maximum: 3, award: { PREFIX: 2 } }, + urls: [ + [ 'w3c', 'http://dvcs.w3.org/hg/pointerlock/raw-file/default/index.html' ], + [ 'wp', '/dom/Element/requestPointerLock' ], + [ 'mdn', '/Web/API/Pointer_Lock_API' ] + ] + } + ] + } + ] + }, + + { + id: 'multimedia', + name: 'Multimedia', + column: 'right', + items: [ + { + id: 'video', + name: 'Video', + status: 'stable', + items: [ + { + id: 'element', + name: 'video element', + value: 16, + urls: [ + [ 'w3c', 'http://www.w3.org/TR/html5/embedded-content-0.html#the-video-element' ], + [ 'wp', '/html/elements/video' ], + [ 'mdn', '/Web/Guide/HTML/Using_HTML5_audio_and_video' ] + ] + }, { + id: 'subtitle', + name: 'Subtitles', + value: 8, + urls: [ + [ 'w3c', 'http://www.w3.org/TR/html5/embedded-content-0.html#the-track-element' ], + [ 'wp', '/html/elements/track' ] + ] + }, { + id: 'audiotracks', + name: 'Audio track selection', + value: 2, + urls: [ + [ 'w3c', 'http://www.w3.org/TR/html5/embedded-content-0.html#dom-media-audiotracks' ] + ] + }, { + id: 'videotracks', + name: 'Video track selection', + value: 2, + urls: [ + [ 'w3c', 'http://www.w3.org/TR/html5/embedded-content-0.html#dom-media-videotracks' ] + ] + }, { + id: 'poster', + name: 'Poster images', + value: 1, + urls: [ + [ 'w3c', 'http://www.w3.org/TR/html5/embedded-content-0.html#attr-video-poster' ], + [ 'wp', '/dom/HTMLVideoElement/poster' ] + ] + }, { + id: 'canplaytype', + name: 'Codec detection', + value: 4, + urls: [ + [ 'w3c', 'http://www.w3.org/TR/html5/embedded-content-0.html#dom-navigator-canplaytype' ], + [ 'wp', '/dom/HTMLMediaElement/canPlayType' ] + ] + }, + + 'Video codecs', + + { + id: 'codecs.mp4.mpeg4', + name: 'MPEG-4 ASP support', + status: 'optional' + }, { + id: 'codecs.mp4.h264', + name: 'H.264 support', + status: 'optional', + urls: [ + [ 'other', 'http://ip.hhi.de/imagecom_G1/assets/pdfs/csvt_overview_0305.pdf' ] + ] + }, { + id: 'codecs.mp4.h265', + name: 'H.265 support', + status: 'optional' + }, { + id: 'codecs.ogg.theora', + name: 'Ogg Theora support', + status: 'optional', + urls: [ + [ 'xiph', 'http://theora.org/doc/Theora.pdf' ] + ] + }, { + id: 'codecs.webm.vp8', + name: 'WebM with VP8 support', + status: 'optional', + urls: [ + [ 'webm', 'http://www.webmproject.org/' ], + [ 'ietf', 'http://www.rfc-editor.org/rfc/rfc6386.txt' ] + ] + }, { + id: 'codecs.webm.vp9', + name: 'WebM with VP9 support', + status: 'optional', + urls: [ + [ 'webm', 'http://www.webmproject.org/' ], + [ 'ietf', 'http://tools.ietf.org/id/draft-grange-vp9-bitstream-00.txt' ] + ] + } + ] + }, { + id: 'audio', + name: 'Audio', + status: 'stable', + items: [ + { + id: 'element', + name: 'audio element', + value: 18, + urls: [ + [ 'w3c', 'http://www.w3.org/TR/html5/embedded-content-0.html#the-audio-element' ], + [ 'wp', '/html/elements/audio' ], + [ 'mdn', '/Web/Guide/HTML/Using_HTML5_audio_and_video' ] + ] + }, { + id: 'loop', + name: 'Loop audio', + value: 1, + url: 'http://www.w3.org/TR/html5/embedded-content-0.html#attr-media-loop' + }, { + id: 'preload', + name: 'Preload in the background', + value: 1, + url: 'http://www.w3.org/TR/html5/embedded-content-0.html#attr-media-preload' + }, + + + 'Advanced', + + { + id: 'webaudio', + name: 'Web Audio API', + value: { maximum: 5, award: { PREFIX: 3 } }, + urls: [ + [ 'w3c', 'http://www.w3.org/TR/webaudio/' ], + [ 'wp', '/apis/webaudio' ] + ] + }, + + { + id: 'speechrecognition', + name: 'Speech Recognition', + status: 'experimental', + value: { maximum: 3, award: { PREFIX: 2 } }, + url: 'https://dvcs.w3.org/hg/speech-api/raw-file/tip/speechapi.html' + }, + + { + id: 'speechsynthesis', + name: 'Speech Synthesis', + status: 'experimental', + value: { maximum: 2, award: { PREFIX: 1 } }, + url: 'https://dvcs.w3.org/hg/speech-api/raw-file/tip/speechapi.html' + }, + + 'Audio codecs', + + { + id: 'codecs.pcm', + name: 'PCM audio support', + status: 'optional' + }, { + id: 'codecs.mp3', + name: 'MP3 support', + status: 'optional' + }, { + id: 'codecs.mp4.aac', + name: 'AAC support', + status: 'optional' + }, { + id: 'codecs.mp4.ac3', + name: 'Dolby Digital support', + status: 'optional' + }, { + id: 'codecs.mp4.ec3', + name: 'Dolby Digital Plus support', + status: 'optional' + }, { + id: 'codecs.ogg.vorbis', + name: 'Ogg Vorbis support', + status: 'optional' + }, { + id: 'codecs.ogg.opus', + name: 'Ogg Opus support', + status: 'optional' + }, { + id: 'codecs.webm.vorbis', + name: 'WebM with Vorbis support', + status: 'optional' + }, { + id: 'codecs.webm.opus', + name: 'WebM with Opus support', + status: 'optional' + } + ] + }, { + id: 'streaming', + name: 'Streaming', + status: 'stable', + items: [ + { + id: 'mediasource', + name: 'Media Source extensions', + value: { maximum: 5, award: { PREFIX: 2 } }, + urls: [ + [ 'w3c', 'http://www.w3.org/TR/media-source/' ], + [ 'wp', '/apis/media_source_extensions' ] + ] + }, { + id: 'drm', + name: 'DRM support', + status: 'controversial', + url: 'http://www.w3.org/TR/encrypted-media/' + }, + + 'Adaptive bit rate', + + { + id: 'type.dash', + name: 'Dynamic Adaptive Streaming / MPEG-DASH' + }, { + id: 'type.hls', + name: 'HTTP Live Streaming / HLS' + }, + + 'Codecs', + + { + id: 'video.codecs', + name: 'Video codecs', + items: [ + { + id: 'mp4.h264', + name: 'MP4 with H.264 support', + status: 'optional', + urls: [ + [ 'other', 'http://ip.hhi.de/imagecom_G1/assets/pdfs/csvt_overview_0305.pdf' ] + ] + }, { + id: 'mp4.h265', + name: 'MP4 with H.265 support', + status: 'optional' + }, { + id: 'ts.h264', + name: 'TS with H.264 support', + status: 'optional', + urls: [ + [ 'other', 'http://ip.hhi.de/imagecom_G1/assets/pdfs/csvt_overview_0305.pdf' ] + ] + }, { + id: 'ts.h265', + name: 'TS with H.265 support', + status: 'optional' + }, { + id: 'webm.vp8', + name: 'WebM with VP8 support', + status: 'optional', + urls: [ + [ 'webm', 'http://www.webmproject.org/' ], + [ 'ietf', 'http://www.rfc-editor.org/rfc/rfc6386.txt' ] + ] + }, { + id: 'webm.vp9', + name: 'WebM with VP9 support', + status: 'optional', + urls: [ + [ 'webm', 'http://www.webmproject.org/' ], + [ 'ietf', 'http://tools.ietf.org/id/draft-grange-vp9-bitstream-00.txt' ] + ] + } + ] + }, { + id: 'audio.codecs', + name: 'Audio codecs', + items: [ + { + id: 'mp4.aac', + name: 'MP4 with AAC support', + status: 'optional' + }, { + id: 'mp4.ac3', + name: 'MP4 with Dolby Digital support', + status: 'optional' + }, { + id: 'mp4.ec3', + name: 'MP4 with Dolby Digital Plus support', + status: 'optional' + }, { + id: 'ts.aac', + name: 'TS with AAC support', + status: 'optional' + }, { + id: 'ts.ac3', + name: 'TS with Dolby Digital support', + status: 'optional' + }, { + id: 'ts.ec3', + name: 'TS with Dolby Digital Plus support', + status: 'optional' + }, { + id: 'webm.vorbis', + name: 'WebM with Vorbis support', + status: 'optional' + }, { + id: 'webm.opus', + name: 'WebM with Opus support', + status: 'optional' + } + ] + } + ] + } + ] + }, + + { + id: 'graphicseffects', + name: '3D, Graphics & Effects', + column: 'right', + items: [ + { + id: 'responsive', + status: 'stable', + name: 'Responsive images', + items: [ + { + id: 'picture', + name: 'picture element', + value: 5, + urls: [ + [ 'ricg', 'http://responsiveimages.org/' ], + [ 'w3c', 'http://www.w3.org/html/wg/drafts/html/master/single-page.html#the-picture-element' ], + [ 'whatwg', 'https://html.spec.whatwg.org/multipage/embedded-content.html#the-picture-element' ] + ] + }, { + id: 'srcset', + name: 'srcset attribute', + value: 5, + urls: [ + [ 'ricg', 'http://responsiveimages.org/' ], + [ 'w3c', 'http://www.w3.org/html/wg/drafts/srcset/w3c-srcset/' ], + [ 'whatwg', 'https://html.spec.whatwg.org/multipage/embedded-content.html#attr-img-srcset' ] + ] + }, { + id: 'sizes', + name: 'sizes attribute', + value: 5, + urls: [ + [ 'ricg', 'http://responsiveimages.org/' ], + [ 'w3c', 'http://www.w3.org/html/wg/drafts/html/master/single-page.html#valid-source-size-list' ], + [ 'whatwg', 'https://html.spec.whatwg.org/multipage/embedded-content.html#dom-img-sizes' ], + ] + } + ] + }, { + id: 'canvas', + name: '2D Graphics', + status: 'stable', + items: [ + { + id: 'context', + name: 'Canvas 2D graphics', + value: 10, + urls: [ + [ 'w3c', 'http://www.w3.org/TR/2dcontext/' ], + [ 'wp', '/apis/canvas' ], + [ 'mdn', '/Web/API/Canvas_API' ] + ] + }, + + 'Drawing primitives', + + { + id: 'text', + name: 'Text support', + value: 2, + urls: [ + [ 'w3c', 'http://www.w3.org/TR/2dcontext/#drawing-text-to-the-canvas' ], + [ 'wp', '/apis/canvas/CanvasRenderingContext2D/fillText' ] + ] + }, { + id: 'path', + name: 'Path support', + value: { maximum: 2, award: { OLD: 1 } }, + urls: [ + [ 'w3c', 'http://www.w3.org/TR/2dcontext/#path-objects' ], + [ 'whatwg', 'https://html.spec.whatwg.org/multipage/scripting.html#path2d-objects' ] + ] + }, { + id: 'ellipse', + name: 'Ellipse support', + value: 2, + urls: [ + [ 'w3c', 'http://www.w3.org/TR/2dcontext/#dom-context-2d-ellipse' ], + [ 'whatwg', 'https://html.spec.whatwg.org/multipage/scripting.html#dom-context-2d-ellipse' ] + ] + }, { + id: 'dashed', + name: 'Dashed line support', + value: 2, + urls: [ + [ 'w3c', 'http://www.w3.org/TR/2dcontext/#dom-context-2d-setlinedash' ], + [ 'whatwg', 'https://html.spec.whatwg.org/multipage/scripting.html#dom-context-2d-setlinedash' ] + ] + }, { + id: 'focusring', + name: 'System focus ring support', + value: 1, + urls: [ + [ 'whatwg', 'https://html.spec.whatwg.org/multipage/scripting.html#dom-context-2d-drawfocusifneeded' ] + ] + }, + + 'Features', + + { + id: 'hittest', + name: 'Hit testing support', + status: 'proposal', + value: 1, + urls: [ + [ 'w3c', 'http://www.w3.org/TR/2dcontext/#dom-context-2d-addhitregion' ], + [ 'whatwg', 'http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#dom-context-2d-addhitregion' ] + ] + }, { + id: 'blending', + name: 'Blending modes', + status: 'proposal', + value: 5, + urls: [ + [ 'w3c', 'http://www.w3.org/TR/compositing-1/#canvascompositingandblending' ] + ] + }, + + 'Image export formats', + + { + id: 'png', + name: 'PNG support', + status: 'optional' + }, { + id: 'jpeg', + name: 'JPEG support', + status: 'optional' + }, { + id: 'jpegxr', + name: 'JPEG-XR support', + status: 'optional' + }, { + id: 'webp', + name: 'WebP support', + status: 'optional' + } + ] + }, { + id: '3d', + status: 'stable', + name: '3D and VR', + items: [ + '3D Graphics', + + { + id: 'webgl', + name: 'WebGL', + value: { maximum: 15, award: { PREFIX: 10 } }, + urls: [ + [ 'khronos', 'https://www.khronos.org/registry/webgl/specs/latest/1.0/' ], + [ 'wp', '/webgl' ], + [ 'mdn', '/Web/API/WebGL_API' ] + ] + + }, { + id: 'webgl2', + name: 'WebGL 2', + status: 'experimental', + value: 5, + urls: [ + [ 'khronos', 'https://www.khronos.org/registry/webgl/specs/latest/2.0/' ], + [ 'wp', '/webgl' ], + [ 'mdn', '/Web/API/WebGL_API' ] + ] + + }, + + 'VR Headset', + + { + id: 'webvr', + name: 'WebVR', + status: 'experimental', + value: 3, + url: 'https://w3c.github.io/webvr/' + + } + ] + }, { + id: 'animation', + status: 'stable', + name: 'Animation', + items: [ + { + id: 'webanimation', + name: 'Web Animations API', + status: 'experimental', + value: 3, + urls: [ + [ 'w3c', 'https://w3c.github.io/web-animations/' ] + ] + }, { + id: 'requestAnimationFrame', + name: 'window.requestAnimationFrame', + value: { maximum: 5, award: { PREFIX: 3 } }, + urls: [ + [ 'w3c', 'http://www.w3.org/TR/animation-timing/#requestAnimationFrame' ], + [ 'wp', '/dom/Window/requestAnimationFrame' ], + [ 'mdn', '/Web/API/window/requestAnimationFrame' ] + ] + } + ] + } + ] + }, + + { + id: 'connectivity', + name: 'Connectivity', + column: 'left', + items: [ + { + id: 'communication', + status: 'stable', + name: 'Communication', + items: [ + { + id: 'eventSource', + name: 'Server-Sent Events', + value: 5, + urls: [ + [ 'w3c', 'http://www.w3.org/TR/eventsource/' ], + [ 'mdn', '/Web/API/Server-sent_events/Using_server-sent_events' ] + ] + }, + + { + id: 'beacon', + name: 'Beacon', + status: 'proposal', + value: 2, + urls: [ + [ 'w3c', 'http://www.w3.org/TR/beacon/' ], + [ 'mdn', '/Web/API/Navigator/sendBeacon' ] + ] + }, + + { + id: 'fetch', + name: 'Fetch', + status: 'proposal', + value: 6, + urls: [ + [ 'whatwg', 'https://fetch.spec.whatwg.org/' ], + [ 'mdn', '/Web/API/Fetch_API' ] + ] + }, + + + 'XMLHttpRequest Level 2', + + { + id: 'xmlhttprequest2.upload', + name: 'Upload files', + value: 5, + url: 'http://www.w3.org/TR/XMLHttpRequest2/#the-upload-attribute' + }, { + id: 'xmlhttprequest2.response', + name: 'Response type support', + urls: [ + [ 'mdn', '/Web/API/XMLHttpRequest' ] + ], + items: [ + { + id: 'text', + name: 'Text response type', + value: 1, + url: 'http://www.w3.org/TR/XMLHttpRequest2/#dom-xmlhttprequest-responsetype' + }, { + id: 'document', + name: 'Document response type', + value: 2, + url: 'http://www.w3.org/TR/XMLHttpRequest2/#dom-xmlhttprequest-responsetype' + }, { + id: 'array', + name: 'ArrayBuffer response type', + value: 2, + url: 'http://www.w3.org/TR/XMLHttpRequest2/#dom-xmlhttprequest-responsetype' + }, { + id: 'blob', + name: 'Blob response type', + value: 2, + url: 'http://www.w3.org/TR/XMLHttpRequest2/#dom-xmlhttprequest-responsetype' + } + ] + }, + + 'WebSocket', + + { + id: 'websocket.basic', + name: 'Basic socket communication', + value: { maximum: 10, award: { PREFIX: 7, OLD: 5 } }, + urls: [ + [ 'w3c', 'http://www.w3.org/TR/websockets/' ], + [ 'mdn', '/Web/API/WebSockets_API' ] + ] + }, { + id: 'websocket.binary', + name: 'ArrayBuffer and Blob support', + value: 5, + urls: [ + [ 'whatwg', 'https://html.spec.whatwg.org/multipage/comms.html#dom-websocket-binarytype' ], + [ 'mdn', '/Web/API/WebSockets_API' ] + ] + } + ] + }, { + id: 'streams', + status: 'experimental', + name: 'Streams', + items: [ + { + id: 'readable', + name: 'Readable streams', + value: 4, + urls: [ + [ 'whatwg', 'https://streams.spec.whatwg.org/' ] + ] + }, { + id: 'writeable', + name: 'Writable streams', + value: 2, + urls: [ + [ 'whatwg', 'https://streams.spec.whatwg.org/' ] + ] + } + ] + }, { + id: 'rtc', + name: 'Peer To Peer', + status: 'stable', + items: [ + 'Connectivity', + + { + id: 'webrtc', + name: 'WebRTC 1.0', + value: { maximum: 15, award: { PREFIX: 10 } }, + urls: [ + [ 'w3c', 'http://www.w3.org/TR/webrtc/' ], + [ 'wp', '/apis/webrtc/RTCPeerConnection' ], + [ 'mdn', '/Web/Guide/API/WebRTC' ] + ] + }, { + id: 'objectrtc', + name: 'ObjectRTC API for WebRTC', + status: 'proposal', + value: { maximum: 15, award: { PREFIX: 10 }, conditional: '!rtc.webrtc' }, + urls: [ + [ 'w3c', 'http://ortc.org/wp-content/uploads/2014/10/ortc.html' ] + ] + }, { + id: 'datachannel', + name: 'Data channel', + value: { maximum: 5, award: { PREFIX: 3 } }, + urls: [ + [ 'w3c', 'http://www.w3.org/TR/webrtc/#peer-to-peer-data-api' ], + [ 'wp', '/apis/webrtc/RTCDataChannel' ], + [ 'mdn', '/Web/Guide/API/WebRTC' ] + ] + }, + + 'Input', + + { + key: 'media.getUserMedia', + name: 'Access the webcam', + value: { maximum: 15, award: { PREFIX: 10, OLD: 10 } }, + urls: [ + [ 'w3c', 'http://dev.w3.org/2011/webrtc/editor/getusermedia.html' ], + [ 'wp', '/dom/Navigator/getUserMedia' ], + [ 'mdn', '/Web/Guide/API/WebRTC' ] + ] + }, { + key: 'media.getDisplayMedia', + name: 'Screen Capture', + status: 'experimental', + value: 5, + urls: [ + [ 'w3c', 'https://w3c.github.io/mediacapture-screen-share/' ] + ] + }, { + key: 'media.enumerateDevices', + name: 'Enumerate devices', + status: 'proposal', + value: 3, + urls: [ + [ 'w3c', 'https://w3c.github.io/mediacapture-main/#mediadevices' ] + ] + }, + + 'Recording', + + { + id: 'recorder', + name: 'Media Stream recorder', + status: 'proposal', + value: 2, + urls: [ + [ 'w3c', 'http://www.w3.org/TR/mediastream-recording/' ] + ] + } + ] + } + ] + }, + + { + id: 'performanceintegration', + name: 'Performance & Integration', + column: 'left', + items: [ + { + id: 'interaction', + status: 'stable', + name: 'User interaction', + items: [ + 'Drag and drop', + + { + id: 'dragdrop.attributes', + name: 'Attributes', + urls: [ + [ 'mdn', '/Web/Guide/HTML/Drag_and_drop' ] + ], + items: [ + { + id: 'draggable', + name: 'draggable attribute', + value: 1, + url: 'http://www.w3.org/TR/html5/editing.html#the-draggable-attribute' + }, { + id: 'dropzone', + name: 'dropzone attribute', + value: 1, + url: 'http://www.w3.org/TR/html5/editing.html#the-dropzone-attribute ' + } + ] + }, { + id: 'dragdrop.events', + name: 'Events', + urls: [ + [ 'mdn', '/Web/Guide/HTML/Drag_and_drop' ] + ], + items: [ + { + id: 'ondrag', + name: 'ondrag event', + url: 'http://www.w3.org/TR/html5/editing.html#dndevents' + }, { + id: 'ondragstart', + name: 'ondragstart event', + url: 'http://www.w3.org/TR/html5/editing.html#dndevents' + }, { + id: 'ondragenter', + name: 'ondragenter event', + url: 'http://www.w3.org/TR/html5/editing.html#dndevents' + }, { + id: 'ondragover', + name: 'ondragover event', + url: 'http://www.w3.org/TR/html5/editing.html#dndevents' + }, { + id: 'ondragleave', + name: 'ondragleave event', + url: 'http://www.w3.org/TR/html5/editing.html#dndevents' + }, { + id: 'ondragend', + name: 'ondragend event', + url: 'http://www.w3.org/TR/html5/editing.html#dndevents' + }, { + id: 'ondrop', + name: 'ondrop event', + url: 'http://www.w3.org/TR/html5/editing.html#dndevents' + } + ] + }, + + 'HTML editing', + + { + id: 'editing.elements', + name: 'Editing elements', + urls: [ + [ 'mdn', '/Web/Guide/HTML/Content_Editable' ] + ], + items: [ + { + id: 'contentEditable', + name: 'contentEditable attribute', + value: 5, + url: 'http://www.w3.org/TR/html5/editing.html#contenteditable' + }, { + id: 'isContentEditable', + name: 'isContentEditable property', + value: 1, + url: 'http://www.w3.org/TR/html5/editing.html#contenteditable' + } + ] + }, { + id: 'editing.documents', + name: 'Editing documents', + urls: [ + [ 'mdn', '/Web/Guide/HTML/Content_Editable' ] + ], + items: [ + { + id: 'designMode', + name: 'designMode attribute', + value: 1, + url: 'http://www.w3.org/TR/html5/editing.html#designMode' + } + ] + }, { + id: 'editing.selectors', + name: 'CSS selectors', + value: { maximum: 2, award: { PREFIX: 1 } }, + urls: [ + [ 'mdn', '/Web/Guide/HTML/Content_Editable' ] + ], + items: [ + { + id: 'read-write', + name: ':read-write selector', + url: 'http://www.w3.org/TR/html5/links.html#selector-read-write' + }, { + id: 'read-only', + name: ':read-only selector', + url: 'http://www.w3.org/TR/html5/links.html#selector-read-only' + } + ] + }, { + id: 'editing.apis', + name: 'APIs', + value: 2, + urls: [ + [ 'mdn', '/Web/Guide/HTML/Content_Editable' ] + ], + items: [ + { + id: 'execCommand', + name: 'execCommand method', + url: 'https://dvcs.w3.org/hg/editing/raw-file/tip/editing.html' + }, { + id: 'queryCommandEnabled', + name: 'queryCommandEnabled method', + url: 'https://dvcs.w3.org/hg/editing/raw-file/tip/editing.html' + }, { + id: 'queryCommandIndeterm', + name: 'queryCommandIndeterm method', + url: 'https://dvcs.w3.org/hg/editing/raw-file/tip/editing.html' + }, { + id: 'queryCommandState', + name: 'queryCommandState method', + url: 'https://dvcs.w3.org/hg/editing/raw-file/tip/editing.html' + }, { + id: 'queryCommandSupported', + name: 'queryCommandSupported method', + url: 'https://dvcs.w3.org/hg/editing/raw-file/tip/editing.html' + }, { + id: 'queryCommandValue', + name: 'queryCommandValue method', + url: 'https://dvcs.w3.org/hg/editing/raw-file/tip/editing.html' + } + ] + }, + + 'Clipboard', + + { + id: 'clipboard', + name: 'Clipboard API and events', + value: 5, + url: 'https://w3c.github.io/clipboard-apis/' + }, + + 'Spellcheck', + + { + id: 'spellcheck', + name: 'spellcheck attribute', + value: 2, + url: 'http://www.w3.org/TR/html5/editing.html#attr-spellcheck' + } + ] + }, { + id: 'performance', + status: 'stable', + name: 'Performance', + items: [ + 'Workers', + + { + id: 'worker', + name: 'Web Workers', + value: 10, + urls: [ + [ 'w3c', 'http://www.w3.org/TR/workers/#dedicated-workers-and-the-worker-interface' ], + [ 'mdn', '/Web/API/Web_Workers_API/Using_web_workers' ] + ] + }, { + id: 'sharedWorker', + name: 'Shared Workers', + value: 1, + urls: [ + [ 'w3c', 'http://www.w3.org/TR/workers/#shared-workers-and-the-sharedworker-interface' ], + [ 'mdn', '/Web/API/Web_Workers_API/Using_web_workers' ] + ] + }, + + 'Other', + + { + id: 'requestIdleCallback', + name: 'window.requestIdleCallback', + status: 'experimental', + value: 1, + url: 'https://w3c.github.io/requestidlecallback/#the-requestidlecallback-method' + } + ] + }, { + id: 'security', + status: 'stable', + name: 'Security', + items: [ + { + id: 'crypto', + name: 'Web Cryptography API', + status: 'proposal', + value: { maximum: 5, award: { PREFIX: 3 } }, + urls: [ + [ 'w3c', 'http://www.w3.org/TR/WebCryptoAPI/' ] + ] + }, { + id: 'csp10', + name: 'Content Security Policy 1', + value: 3, + urls: [ + [ 'w3c', 'http://www.w3.org/TR/CSP1/' ], + [ 'mdn', '/Web/Security/CSP' ] + ] + }, { + id: 'csp11', + name: 'Content Security Policy 2', + status: 'proposal', + value: 2, + urls: [ + [ 'w3c', 'http://www.w3.org/TR/CSP2/' ], + [ 'mdn', '/Web/Security/CSP' ] + ] + }, { + id: 'cors', + name: 'Cross-Origin Resource Sharing', + value: 4, + urls: [ + [ 'mdn', '/Web/HTTP/Access_control_CORS' ] + ] + }, { + id: 'integrity', + name: 'Subresource Integrity', + status: 'proposal', + value: 2, + url: 'http://www.w3.org/TR/SRI/' + }, { + id: 'postMessage', + name: 'Cross-document messaging', + value: 2, + urls: [ + [ 'w3c', 'http://dev.w3.org/html5/postmsg/' ], + [ 'wp', '/apis/web-messaging' ], + [ 'mdn', '/Web/API/Window/postMessage' ] + ] + }, + + 'Authentication', + + { + id: 'authentication', + name: 'Web Authentication / FIDO 2', + status: 'experimental', + value: 3, + url: 'https://w3c.github.io/webauthn/' + }, + + { + id: 'credential', + name: 'Credential Management', + status: 'experimental', + value: 3, + url: 'http://w3c.github.io/webappsec-credential-management/' + }, + + 'Iframes', + + { + id: 'sandbox', + name: 'Sandboxed iframe', + value: 4, + urls: [ + [ 'w3c', 'http://www.w3.org/TR/html5/embedded-content-0.html#attr-iframe-sandbox' ], + [ 'mdn', '/Web/HTML/Element/iframe#attr-sandbox' ] + ] + }, { + id: 'srcdoc', + name: 'iframe with inline contents', + value: 4, + urls: [ + [ 'w3c', 'http://www.w3.org/TR/html5/embedded-content-0.html#attr-iframe-srcdoc' ], + [ 'mdn', '/Web/HTML/Element/iframe#attr-srcdoc' ] + ] + } + ] + }, { + id: 'payments', + status: 'experimental', + name: 'Payments', + items: [ + { + id: 'payments', + name: 'Web Payments', + value: 5, + url: 'https://w3c.github.io/browser-payment-api/specs/paymentrequest.html' + } + ] + } + ] + }, + + { + id: 'offlinestorage', + name: 'Offline & Storage', + column: 'right', + items: [ + { + id: 'offline', + name: 'Web applications', + status: 'stable', + items: [ + 'Offline resources', + + { + id: 'applicationCache', + name: 'Application Cache', + value: 3, + urls: [ + [ 'w3c', 'http://www.w3.org/TR/html5/browsers.html#offline' ], + [ 'wp', '/apis/appcache/ApplicationCache' ], + [ 'mdn', '/Web/HTML/Using_the_application_cache' ] + ] + }, { + id: 'serviceWorkers', + name: 'Service Workers', + status: 'proposal', + value: 10, + urls: [ + [ 'w3c', 'https://www.w3.org/TR/service-workers/' ], + [ 'mdn', '/Web/API/Service_Worker_API' ] + ] + }, { + id: 'pushMessages', + name: 'Push Messages', + status: 'proposal', + value: 2, + urls: [ + [ 'w3c', 'https://w3c.github.io/push-api/' ], + [ 'mdn', '/Web/API/Push_API' ] + ] + }, + + 'Content and Scheme handlers', + + { + id: 'registerProtocolHandler', + name: 'Custom scheme handlers', + value: 1, + urls: [ + [ 'w3c', 'http://www.w3.org/TR/html5/webappapis.html#custom-handlers' ], + [ 'mdn', '/Web-based_protocol_handlers' ] + ] + }, { + id: 'registerContentHandler', + name: 'Custom content handlers', + value: 1, + urls: [ + [ 'w3c', 'http://www.w3.org/TR/html5/webappapis.html#custom-handlers' ], + [ 'mdn', '/Web/API/Navigator/registerContentHandler' ] + ] + } + ] + }, { + id: 'storage', + name: 'Storage', + status: 'stable', + items: [ + 'Key-value storage', + + { + id: 'sessionStorage', + name: 'Session Storage', + value: 5, + urls: [ + [ 'w3c', 'http://www.w3.org/TR/webstorage/#the-sessionstorage-attribute' ], + [ 'wp', '/apis/web-storage' ], + [ 'mdn', '/Web/API/Web_Storage_API' ] + ] + }, { + id: 'localStorage', + name: 'Local Storage', + value: 5, + urls: [ + [ 'w3c', 'http://www.w3.org/TR/webstorage/#the-localstorage-attribute' ], + [ 'wp', '/apis/web-storage' ], + [ 'mdn', '/Web/API/Web_Storage_API' ] + ] + }, + + 'Database storage', + + { + id: 'indexedDB.basic', + name: 'IndexedDB', + value: { maximum: 21, award: { PREFIX: 16 } }, + urls: [ + [ 'w3c', 'http://www.w3.org/TR/IndexedDB/' ], + [ 'wp', '/apis/indexeddb' ], + [ 'mdn', '/Web/API/IndexedDB_API' ] + ] + }, { + id: 'indexedDB.blob', + name: 'Objectstore Blob support', + value: 2, + urls: [ + [ 'w3c', 'http://www.w3.org/TR/IndexedDB/' ], + [ 'wp', '/apis/indexeddb' ], + [ 'mdn', '/Web/API/IndexedDB_API' ] + ] + }, { + id: 'indexedDB.arraybuffer', + name: 'Objectstore ArrayBuffer support', + value: 2, + urls: [ + [ 'w3c', 'http://www.w3.org/TR/IndexedDB/' ], + [ 'wp', '/apis/indexeddb' ], + [ 'mdn', '/Web/API/IndexedDB_API' ] + ] + }, + + 'The Web SQL Database specification is no longer being updated and has been replaced by IndexedDB. Because at least 3 vendors have shipped implementations of this specification we still include it in this test.', + + { + id: 'sqlDatabase', + name: 'Web SQL Database', + status: 'rejected', + value: { maximum: 5, conditional: '!storage.indexedDB.basic' }, + + url: 'http://www.w3.org/TR/webdatabase/' + } + ] + }, { + id: 'files', + name: 'Files', + status: 'stable', + items: [ + 'Reading files', + + { + id: 'fileReader', + name: 'Basic support for reading files', + value: 7, + urls: [ + [ 'w3c', 'http://dev.w3.org/2006/webapi/FileAPI/#filereader-interface' ], + [ 'wp', '/apis/file' ], + [ 'mdn', '/Using_files_from_web_applications' ] + ] + }, { + id: 'fileReader.blob', + name: 'Create a Blob from a file', + value: 2, + urls: [ + [ 'w3c', 'http://dev.w3.org/2006/webapi/FileAPI/#dfn-Blob' ], + ] + }, { + id: 'fileReader.dataURL', + name: 'Create a Data URL from a Blob', + value: 2, + urls: [ + [ 'w3c', 'http://dev.w3.org/2006/webapi/FileAPI/#dfn-readAsDataURL' ], + ] + }, { + id: 'fileReader.arraybuffer', + name: 'Create an ArrayBuffer from a Blob', + value: 2, + urls: [ + [ 'w3c', 'http://dev.w3.org/2006/webapi/FileAPI/#dfn-readAsArrayBuffer' ], + ] + }, { + id: 'fileReader.objectURL', + name: 'Create a Blob URL from a Blob', + value: 2, + urls: [ + [ 'w3c', 'http://dev.w3.org/2006/webapi/FileAPI/#dfn-createObjectURL' ], + ] + }, + + 'Accessing the file system', + + { + id: 'getFileSystem', + name: 'FileSystem API', + status: 'experimental', + urls: [ + [ 'w3c', 'http://w3c.github.io/filesystem-api/' ], + ] + }, + + 'The Directories and System API proposal has failed to gain traction among browser vendors and is only supported in some Webkit based browsers. No additional points are awarded for supporting this API.', + + { + id: 'fileSystem', + name: 'File API: Directories and System', + status: 'rejected', + urls: [ + [ 'w3c', 'http://www.w3.org/TR/file-system-api/' ], + [ 'wp', '/apis/filesystem' ] + ] + } + ] + } + ] + }, + + { + id: 'other', + name: 'Other', + column: 'right', + items: [ + { + id: 'scripting', + name: 'Scripting', + status: 'stable', + items: [ + 'Script execution', + + { + id: 'async', + name: 'Asynchronous script execution', + value: 3, + urls: [ + [ 'w3c', 'http://www.w3.org/TR/html5/scripting-1.html#attr-script-async' ], + [ 'whatwg', 'https://html.spec.whatwg.org/multipage/scripting.html#attr-script-async' ], + [ 'mdn', '/Web/HTML/Element/script' ], + [ 'wp', '/html/elements/script' ] + ] + }, { + id: 'defer', + name: 'Defered script execution', + value: 1, + urls: [ + [ 'w3c', 'http://www.w3.org/TR/html5/scripting-1.html#attr-script-defer' ], + [ 'whatwg', 'https://html.spec.whatwg.org/multipage/scripting.html#attr-script-defer' ], + [ 'mdn', '/Web/HTML/Element/script' ], + [ 'wp', '/html/elements/script' ] + ] + }, { + id: 'executionevents', + name: 'Script execution events', + status: 'rejected', + urls: [ + [ 'w3c', 'http://www.w3.org/TR/html5/scripting-1.html#the-script-element' ], + [ 'whatwg', 'http://www.whatwg.org/specs/web-apps/current-work/multipage/scripting-1.html#the-script-element' ], + [ 'mdn', '/Web/Events/beforescriptexecute' ] + ] + }, { + id: 'onerror', + name: 'Runtime script error reporting', + value: 1, + urls: [ + [ 'w3c', 'http://www.w3.org/TR/html5/webappapis.html#runtime-script-errors' ], + [ 'whatwg', 'https://html.spec.whatwg.org/multipage/webappapis.html#runtime-script-errors' ], + [ 'mdn', '/Web/API/GlobalEventHandlers/onerror' ] + ] + }, + + 'ECMAScript 5', + + { + id: 'es5.json', + name: 'JSON encoding and decoding', + value: 2, + urls: [ + [ 'ecma', 'http://www.ecma-international.org/ecma-262/6.0/#sec-json-object' ], + [ 'mdn', '/JSON' ], + [ 'wp', '/apis/json' ] + ] + }, + + 'ECMAScript 6', + + { + id: 'es6.modules', + name: 'Modules', + value: 3, + urls: [ + [ 'ecma', 'https://tc39.github.io/ecma262/#prod-Module' ], + [ 'whatwg', 'https://html.spec.whatwg.org/multipage/scripting.html#attr-script-type' ], + ] + }, { + id: 'es6.class', + name: 'Classes', + value: 1, + urls: [ + [ 'ecma', 'http://www.ecma-international.org/ecma-262/6.0/#sec-class-definitions' ], + ] + }, { + id: 'es6.arrow', + name: 'Arrow functions', + value: 1, + urls: [ + [ 'ecma', 'http://www.ecma-international.org/ecma-262/6.0/#sec-arrow-function-definitions' ], + ] + }, { + id: 'es6.promises', + name: 'Promises', + value: 3, + urls: [ + [ 'ecma', 'http://www.ecma-international.org/ecma-262/6.0/#sec-promise-objects' ], + [ 'mdn', '/Web/JavaScript/Reference/Global_Objects/Promise' ] + ] + }, { + id: 'es6.template', + name: 'Template strings', + value: 1, + urls: [ + [ 'ecma', 'http://www.ecma-international.org/ecma-262/6.0/#sec-template-literals' ], + ] + }, { + id: 'es6.datatypes', + name: 'Typed arrays', + value: 2, + status: 'stable', + urls: [ + [ 'khronos', 'http://www.khronos.org/registry/typedarray/specs/latest/' ], + [ 'ecma', 'http://www.ecma-international.org/ecma-262/6.0/#sec-structured-data' ] + ] + }, { + id: 'es6.i18n', + name: 'Internationalization', + value: 2, + urls: [ + [ 'ecma', 'http://www.ecma-international.org/ecma-402/1.0/' ], + [ 'mdn', '/Web/JavaScript/Reference/Global_Objects/Intl' ] + ] + }, + + 'ECMAScript 7', + + { + id: 'es7.async', + name: 'Async and Await', + value: 3, + urls: [ + [ 'ecma', 'https://tc39.github.io/ecmascript-asyncawait/' ] + ] + }, + + 'Other API\'s', + + { + id: 'base64', + name: 'Base64 encoding and decoding', + value: 1, + urls: [ + [ 'w3c', 'http://www.w3.org/TR/html5/webappapis.html#atob' ], + [ 'whatwg', 'https://html.spec.whatwg.org/multipage/webappapis.html#atob' ], + [ 'mdn', '/Web/API/WindowBase64/atob' ] + ] + }, { + id: 'mutationObserver', + name: 'Mutation Observer', + value: { maximum: 2, award: { PREFIX: 1 } }, + urls: [ + [ 'w3c', 'http://www.w3.org/TR/dom/#mutation-observers' ], + [ 'mdn', '/Web/API/MutationObserver' ] + ] + }, { + id: 'url', + name: 'URL API', + value: { maximum: 2, award: { PREFIX: 1 } }, + urls: [ + [ 'w3c', 'http://www.w3.org/TR/url/' ] + ] + }, { + id: 'encoding', + name: 'Encoding API', + value: 2, + urls: [ + [ 'whatwg', 'https://encoding.spec.whatwg.org' ], + [ 'mdn', '/Web/API/TextDecoder' ] + ] + } + ] + }, { + id: 'other', + name: 'Other', + status: 'stable', + items: [ + { + id: 'history', + name: 'Session history', + value: 4, + urls: [ + [ 'w3c', 'http://www.w3.org/TR/html5/browsers.html#the-history-interface' ], + [ 'wp', '/dom/History' ], + [ 'mdn', '/Web/Guide/API/DOM/Manipulating_the_browser_history' ] + ] + }, { + id: 'pagevisiblity', + name: 'Page Visibility', + value: 2, + urls: [ + [ 'w3c', 'http://www.w3.org/TR/page-visibility/' ], + [ 'mdn', '/Web/Guide/User_experience/Using_the_Page_Visibility_API' ] + ] + }, { + id: 'getSelection', + name: 'Text selection', + value: 2, + url: 'http://dvcs.w3.org/hg/editing/raw-file/tip/editing.html#selections' + }, { + id: 'scrollIntoView', + name: 'Scroll into view', + value: 1, + url: 'http://dev.w3.org/csswg/cssom-view/#dom-element-scrollintoview' + } + ] + } + ] + } +] diff --git a/src/AngleSharp.Js.Tests/Fixtures/Html5Test/scripts/8/engine.js b/src/AngleSharp.Js.Tests/Fixtures/Html5Test/scripts/8/engine.js new file mode 100644 index 0000000..5041149 --- /dev/null +++ b/src/AngleSharp.Js.Tests/Fixtures/Html5Test/scripts/8/engine.js @@ -0,0 +1,4563 @@ + +Test = +Test8 = (function () { + + var release = 8; + + var NO = 0, + YES = 1, + OLD = 2, + BUGGY = 4, + PREFIX = 8, + BLOCKED = 16, + DISABLED = 32, + UNCONFIRMED = 64, + UNKNOWN = 128, + EXPERIMENTAL = 256; + + var blacklists = []; + + + var testsuite = [ + + /* doctype */ + + function (results) { + results.addItem({ + key: 'parsing.doctype', + passed: document.compatMode == 'CSS1Compat' + }); + }, + + + /* tokenizer */ + + function (results) { + var result = true; + var e = document.createElement('div'); + + try { + e.innerHTML = ""; + result &= e.firstChild && e.firstChild.nodeName == "DIV"; + result &= e.firstChild && (e.firstChild.attributes[0].nodeName == "\"foo" || e.firstChild.attributes[0].name == "\"foo"); + + e.innerHTML = ""; + result &= e.firstChild && e.firstChild.getAttribute("href") == "\nbar"; + + e.innerHTML = ""; + result &= e.firstChild == null; + + e.innerHTML = "\u000D"; + result &= e.firstChild && e.firstChild.nodeValue == "\u000A"; + + e.innerHTML = "⟨⟩"; + result &= e.firstChild.nodeValue == "\u27E8\u27E9"; + + e.innerHTML = "'"; + result &= e.firstChild.nodeValue == "'"; + + e.innerHTML = "ⅈ"; + result &= e.firstChild.nodeValue == "\u2148"; + + e.innerHTML = "𝕂"; + result &= e.firstChild.nodeValue == "\uD835\uDD42"; + + e.innerHTML = "∉"; + result &= e.firstChild.nodeValue == "\u2209"; + + e.innerHTML = ''; + result &= e.firstChild && e.firstChild.nodeType == 8 && e.firstChild.nodeValue == '?import namespace="foo" implementation="#bar"'; + + e.innerHTML = ''; + result &= e.firstChild && e.firstChild.nodeType == 8 && e.firstChild.nodeValue == 'foo--bar'; + + e.innerHTML = ''; + result &= e.firstChild && e.firstChild.nodeType == 8 && e.firstChild.nodeValue == '[CDATA[x]]'; + + e.innerHTML = "-->"; + result &= e.firstChild && e.firstChild.firstChild && e.firstChild.firstChild.nodeValue == ""; + result &= e.firstChild && e.firstChild.firstChild && e.firstChild.firstChild.nodeValue == ""; + result &= e.firstChild && e.firstChild.firstChild && e.firstChild.firstChild.nodeValue == ""; + result &= e.firstChild && e.firstChild.firstChild && e.firstChild.firstChild.nodeValue == " + + 1.0.0 + + + + latest + true + true + $(MSBuildThisFileDirectory)Key.snk + + + + + 1.5.0 - \ No newline at end of file + diff --git a/src/Directory.Packages.props b/src/Directory.Packages.props new file mode 100644 index 0000000..5ae0059 --- /dev/null +++ b/src/Directory.Packages.props @@ -0,0 +1,23 @@ + + + true + + + + + + + + + + + + + + + + + + + diff --git a/src/AngleSharp.Js/Key.snk b/src/Key.snk similarity index 100% rename from src/AngleSharp.Js/Key.snk rename to src/Key.snk diff --git a/tools/anglesharp.cake b/tools/anglesharp.cake deleted file mode 100644 index bfcc3bd..0000000 --- a/tools/anglesharp.cake +++ /dev/null @@ -1,199 +0,0 @@ -#addin "Cake.FileHelpers" -#addin "Octokit" -using Octokit; - -var configuration = Argument("configuration", "Release"); -var isRunningOnUnix = IsRunningOnUnix(); -var isRunningOnWindows = IsRunningOnWindows(); -var isRunningOnGitHubActions = BuildSystem.GitHubActions.IsRunningOnGitHubActions; -var releaseNotes = ParseReleaseNotes("./CHANGELOG.md"); -var version = releaseNotes.Version.ToString(); -var buildDir = Directory($"./src/{projectName}/bin") + Directory(configuration); -var buildResultDir = Directory("./bin") + Directory(version); -var nugetRoot = buildResultDir + Directory("nuget"); - -if (isRunningOnGitHubActions) -{ - var buildNumber = BuildSystem.GitHubActions.Environment.Workflow.RunNumber; - - if (target == "Default") - { - version = $"{version}-ci-{buildNumber}"; - } - else if (target == "PrePublish") - { - version = $"{version}-alpha-{buildNumber}"; - } -} - -if (!isRunningOnWindows) -{ - frameworks.Remove("net46"); - frameworks.Remove("net461"); - frameworks.Remove("net472"); -} - -// Initialization -// ---------------------------------------- - -Setup(_ => -{ - Information($"Building version {version} of {projectName}."); - Information("For the publish target the following environment variables need to be set:"); - Information("- NUGET_API_KEY"); - Information("- GITHUB_API_TOKEN"); -}); - -// Tasks -// ---------------------------------------- - -Task("Clean") - .Does(() => - { - CleanDirectories(new DirectoryPath[] { buildDir, buildResultDir, nugetRoot }); - }); - -Task("Restore-Packages") - .IsDependentOn("Clean") - .Does(() => - { - NuGetRestore($"./src/{solutionName}.sln", new NuGetRestoreSettings - { - ToolPath = "tools/nuget.exe", - }); - }); - -Task("Build") - .IsDependentOn("Restore-Packages") - .Does(() => - { - ReplaceRegexInFiles("./src/Directory.Build.props", "(?<=)(.+?)(?=)", version); - DotNetCoreBuild($"./src/{solutionName}.sln", new DotNetCoreBuildSettings - { - Configuration = configuration, - }); - }); - -Task("Run-Unit-Tests") - .IsDependentOn("Build") - .Does(() => - { - var settings = new DotNetCoreTestSettings - { - Configuration = configuration, - }; - - if (isRunningOnGitHubActions) - { - settings.Loggers.Add("GitHubActions"); - } - - DotNetCoreTest($"./src/{solutionName}.Tests/", settings); - }); - -Task("Copy-Files") - .IsDependentOn("Build") - .Does(() => - { - foreach (var item in frameworks) - { - var targetDir = nugetRoot + Directory("lib") + Directory(item.Key); - CreateDirectory(targetDir); - CopyFiles(new FilePath[] - { - buildDir + Directory(item.Value) + File($"{projectName}.dll"), - buildDir + Directory(item.Value) + File($"{projectName}.xml"), - }, targetDir); - } - - CopyFiles(new FilePath[] { - $"src/{projectName}.nuspec", - "logo.png" - }, nugetRoot); - }); - -Task("Create-Package") - .IsDependentOn("Copy-Files") - .Does(() => - { - var nugetExe = GetFiles("./tools/**/nuget.exe").FirstOrDefault() - ?? throw new InvalidOperationException("Could not find nuget.exe."); - - var nuspec = nugetRoot + File($"{projectName}.nuspec"); - - NuGetPack(nuspec, new NuGetPackSettings - { - Version = version, - OutputDirectory = nugetRoot, - Symbols = false, - Properties = new Dictionary - { - { "Configuration", configuration }, - }, - }); - }); - -Task("Publish-Package") - .IsDependentOn("Create-Package") - .IsDependentOn("Run-Unit-Tests") - .Does(() => - { - var apiKey = EnvironmentVariable("NUGET_API_KEY"); - - if (String.IsNullOrEmpty(apiKey)) - { - throw new InvalidOperationException("Could not resolve the NuGet API key."); - } - - foreach (var nupkg in GetFiles(nugetRoot.Path.FullPath + "/*.nupkg")) - { - NuGetPush(nupkg, new NuGetPushSettings - { - Source = "https://nuget.org/api/v2/package", - ApiKey = apiKey, - }); - } - }); - -Task("Publish-Release") - .IsDependentOn("Publish-Package") - .IsDependentOn("Run-Unit-Tests") - .Does(() => - { - var githubToken = EnvironmentVariable("GITHUB_TOKEN"); - - if (String.IsNullOrEmpty(githubToken)) - { - throw new InvalidOperationException("Could not resolve GitHub token."); - } - - var github = new GitHubClient(new ProductHeaderValue("AngleSharpCakeBuild")) - { - Credentials = new Credentials(githubToken), - }; - - var newRelease = github.Repository.Release; - newRelease.Create("AngleSharp", projectName, new NewRelease("v" + version) - { - Name = version, - Body = String.Join(Environment.NewLine, releaseNotes.Notes), - Prerelease = false, - TargetCommitish = "main", - }).Wait(); - }); - -// Targets -// ---------------------------------------- - -Task("Package") - .IsDependentOn("Run-Unit-Tests") - .IsDependentOn("Create-Package"); - -Task("Default") - .IsDependentOn("Package"); - -Task("Publish") - .IsDependentOn("Publish-Release"); - -Task("PrePublish") - .IsDependentOn("Publish-Package");