diff --git a/.nojekyll b/.nojekyll new file mode 100644 index 000000000..e69de29bb diff --git a/2.x/components/backstack/index.html b/2.x/components/backstack/index.html new file mode 100644 index 000000000..f2b3ca294 --- /dev/null +++ b/2.x/components/backstack/index.html @@ -0,0 +1,2819 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Appyx Components – Back stack - Appyx + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + +

Back stack

+

Implements a simple linear history:

+
    +
  • The last element at the end of the stack is considered "active".
  • +
  • All other elements are considered stashed.
  • +
  • Children associated with stashed elements are off the screen but kept alive (see how the counter values reflect this on the video)
  • +
+

The back stack can never be empty – it always contains at least one element.

+

The back stack also supports different back press strategies (see further down below).

+

Standard visualisations

+

Slider

+

Class: BackStackSlider

+
+ +

Parallax

+

Class: BackStackParallax

+
+ +

3D stack

+

Class: BackStack3D

+
+ +

Fader

+

Class: BackStackFader

+
+ +

Custom

+

You can always create your own visualisations for Appyx components. Find more info in UI representation.

+

ModelState

+
    @Parcelize
+    data class State<InteractionTarget>(
+        /**
+         * Elements that have been created, but not yet moved to an active state
+         */
+        val created: Elements<InteractionTarget> = listOf(),
+
+        /**
+         * The currently active element.
+         * There should be only one such element in the stack.
+         */
+        val active: Element<InteractionTarget>,
+
+        /**
+         * Elements stashed in the back stack (history).
+         */
+        val stashed: Elements<InteractionTarget> = listOf(),
+
+        /**
+         * Elements that will be destroyed after reaching this state.
+         */
+        val destroyed: Elements<InteractionTarget> = listOf(),
+    ) : Parcelable
+
+

Constructing the back stack

+

Note: As the back stack can never be empty, the initial list in the constructor must contain at +least one element.

+
// InteractionTarget – generic type
+sealed class InteractionTarget {
+    data class SomeElement(val someParam: Int) : InteractionTarget()
+}
+
+private val backStack: BackStack<InteractionTarget> = BackStack(
+    model = BackStackModel(
+        initialTargets = listOf(InteractionTarget.SomeElement),
+        savedStateMap = nodeContext.savedStateMap
+    ),
+    visualisation = { BackStackSlider(it) } // or other visualisations 
+)
+
+

Operations

+

Push

+

backStack.push(navTarget)

+

Effect on stack: +

[A, B, C] + Push(D) = [A, B, C, D]
+

+

Transitions the active element ACTIVE -> STASHED. +Adds a new element at the end of the stack with a CREATED -> ACTIVE transition.

+

Replace

+

backStack.replace(navTarget)

+

Effect on stack: +

[A, B, C] + Replace(D) = [A, B, D]
+

+

Transitions the active element ACTIVE -> DESTROYED, which will be removed when the transition finishes. +Adds a new element at the end of the stack with a CREATED -> ACTIVE transition.

+

Pop

+

backStack.pop(navTarget)

+

Effect on stack: +

[A, B, C] + Pop = [A, B]
+

+

Transitions the active element ACTIVE -> DESTROYED, which will be removed when the transition finishes. +Transitions the last stashed element STASHED -> ACTIVE.

+

Single top

+

backStack.singleTop(navTarget)

+

Effect on stack: depends on the contents of the stack:

+
[A, B, C, D] + SingleTop(B)  = [A, B]          // of same type and equals, acts as n * Pop
+[A, B, C, D] + SingleTop(B') = [A, B']         // of same type but not equals, acts as n * Pop + Replace
+[A, B, C, D] + SingleTop(E)  = [A, B, C, D, E] // not found, acts as Push
+
+

Back press strategy

+

You can override the default strategy in the constructor. You're not limited to using the provided classes, feel free to implement your own.

+
class BackStack<NavTarget : Any>(
+    /* ... */
+    backPressHandler: BackPressHandlerStrategy<NavTarget, State> = PopBackPressHandler(),
+    /* ... */
+) 
+
+

PopBackPressHandler

+

The default back press handling strategy. Runs a Pop operation.

+

DontHandleBackPress

+

Serves as a no-op.

+ + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/2.x/components/experimental/index.html b/2.x/components/experimental/index.html new file mode 100644 index 000000000..ed2c58d4b --- /dev/null +++ b/2.x/components/experimental/index.html @@ -0,0 +1,2461 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Appyx Components – Experimental - Appyx + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + +

Experimental

+

Other uses of the library to create complex transitions and gestures.

+

Visualisations

+

Dating Cards

+

Class: CardsVisualisation

+
+ +

Puzzle 15

+

Class: Puzzle15Visualisation

+
+ + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/2.x/components/index.html b/2.x/components/index.html new file mode 100644 index 000000000..b4b522c21 --- /dev/null +++ b/2.x/components/index.html @@ -0,0 +1,2511 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Appyx Components – Overview - Appyx + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + +

Appyx Components

+

Overview

+

With Appyx, you can:

+
    +
  • Create your own components using Appyx interactions, or
  • +
  • You can use the ones published by us.
  • +
+

This section focuses on the latter. Published components come in two major groups: Stable and Experimental.

+

Stable

+

Stable components are ones you can depend on in your project. We maintain them and keep their APIs stable. They might receive non-API breaking updates (e.g. new operations, or additional visualisations).

+

Back stack

+

A standard back stack, with multiple visualisations.

+

Check its own page for more details.

+
+ +

Spotlight

+

A view pager-like component, with multiple visualisations.

+

Check its own page for more details.

+
+ +

Experimental

+

Experimental components are still published as artifacts, but meant only for quick prototyping, experimenting, or playing around. There's no promise they will be maintained, or that their API won't change. They might be removed at any point.

+

Check its own page for more details.

+
+ + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/2.x/components/spotlight/index.html b/2.x/components/spotlight/index.html new file mode 100644 index 000000000..44c7048a4 --- /dev/null +++ b/2.x/components/spotlight/index.html @@ -0,0 +1,2785 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Appyx Components – Spotlight - Appyx + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + +

Spotlight

+

Implements a mechanism analogous to a view pager; has a single active element marked by an activeIndex ("it's in the spotlight", hence the name), but unlike the back stack, it does not remove other elements.

+

It's great for flows or tabbed containers.

+

Standard visualisations

+

Slider

+

Class: SpotlightSlider

+
+ +

Slider + scale

+

Class: SpotlightSliderScale

+
+ +

Slider + rotation

+

Class: SpotlightSliderRotation

+
+ +

3D stack

+

Class: SpotlightStack3D

+
+ +

Fader

+

Class: SpotlightFader

+
+ +

Custom

+

You can always create your own visualisations for Appyx components. Find more info in UI representation.

+

ModelState

+
@Parcelize
+data class State<InteractionTarget>(
+    val positions: @RawValue List<Position<InteractionTarget>>,
+    val activeIndex: Float
+) : Parcelable {
+
+    @Parcelize
+    data class Position<InteractionTarget>(
+        val elements: Map<Element<InteractionTarget>, ElementState> = mapOf()
+    ) : Parcelable
+
+    enum class ElementState {
+        CREATED, STANDARD, DESTROYED
+    }
+
+    fun hasPrevious(): Boolean =
+        activeIndex >= 1
+
+    fun hasNext(): Boolean =
+        activeIndex <= positions.lastIndex - 1
+}
+
+

Note that a Position in the list can contain multiple elements, each with their own state. This allows an element at a specific position to be destroyed (animated out) and another one to be animated in in its place.

+

Constructing an instance

+

Requires defining items and an active index.

+
// InteractionTarget – generic type
+sealed class InteractionTarget {
+    data class SomeElement(val someParam: Int) : InteractionTarget()
+}
+
+private val spotlight = Spotlight(
+    model = SpotlightModel(
+        items = listOf(SomeElement(1), SomeElement(2), SomeElement(3)),
+        savedStateMap = null
+    ),
+    visualisation = { SpotlightSlider(it) }
+)
+
+

Operations

+

Next

+

spotlight.next()

+

Increases the activeIndex.

+

Previous

+

spotlight.previous()

+

Decreases the activeIndex.

+

First

+

spotlight.first()

+

Sets the activeIndex to 0.

+

Last

+

spotlight.last()

+

Sets the activeIndex to the maximum value.

+

Activate

+

spotlight.activate(index)

+

Sets the activeIndex to a specific value.

+

Update elements

+

spotlight.updateElements(items, initialActiveIndex)

+

Replaces elements held by the spotlight instance with a new list. Transitions:

+
    +
  • new elements: CREATEDSTANDARD
  • +
  • destroyed elements: STANDARDDESTROYED.
  • +
+ + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/2.x/experimental/index.html b/2.x/experimental/index.html new file mode 100644 index 000000000..72d41b876 --- /dev/null +++ b/2.x/experimental/index.html @@ -0,0 +1,2332 @@ + + + + + + + + + + + + + + + + + + + + + + + + + Experimental - Appyx + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + +

Experimental

+ +
+

Experimental warning

+

We’re stopping the development of Appyx 2.x until further notice.

+

We’re fully committed to maintaining and developing new features for the stable 1.x version of Appyx.

+

We recommend you use that version instead. The code for 2.x you can find in the 2.x branch of the repository.

+

Documentation root

+
+
+ + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/2.x/faq/index.html b/2.x/faq/index.html new file mode 100644 index 000000000..be0db59d1 --- /dev/null +++ b/2.x/faq/index.html @@ -0,0 +1,2761 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + FAQ - Appyx + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + + +
+
+
+ + + + + + + +
+
+ + + + +

FAQ

+ +

Q: How does Appyx Navigation relate to Jetpack Compose Navigation?

+

We wrote an article on this in the context of Appyx 1.x: Appyx vs Jetpack Compose Navigation.

+

Most of the same arguments apply to Appyx 2.x too.

+

While Appyx represents a different paradigm, it can also co-exist with Jetpack Compose Navigation. This can be helpful if you want to use Appyx for in-screen mechanisms only, or if you plan to migrate gradually.

+

See Appyx + Compose Navigation for more details.

+
+

Q: How does Appyx Navigation compare against other solutions?

+

The core concepts of navigation in Appyx differ from most navigation libraries:

+
    +
  1. You don't have a concept of the "screen" present in the model
  2. +
  3. You can define your own navigation models using Appyx Components
  4. +
  5. On the UI level you can transform what feels like the "screen" itself
  6. +
+

See Model-driven navigation for more details.

+
+

Q: How can I navigate to a specific part of my Appyx tree?

+

In most cases Implicit navigation can be your primary choice, and you don't need to explicitly specify a remote point in the tree. This is helpful to avoid coupling.

+

For those cases when you can't avoid it, Explicit navigation and Deep linking covers you.

+
+

Q: What about dialogs & bottom sheets?

+

You can use Appyx in conjunction with Accompanist or any other Compose mechanism.

+

If you wish, you can model your own Modal with Appyx too. We'll add an example soon.

+
+

Using Appyx in an app

+

Q: Is it an all or nothing approach?

+

No, you can adopt Appyx gradually:

+ +
+

Q: What architectural patterns can I use?

+

Appyx is agnostic of architectural patterns. You can use any architectural pattern in the Nodes you'd like. You can even use a different one in each.

+
+

Q: Can I use it with ViewModel?

+

Please see Appyx + ViewModel.

+
+

Q: Can I use it with Hilt?

+

Please see Appyx + DI frameworks.

+
+ +

Q: Are Nodes kept alive?

+

In short: you can decide whether a Node:

+
    +
  • is on-screen
  • +
  • is off-screen but kept alive
  • +
  • is off-screen and becomes destroyed
  • +
+

Check the Lifecycle for more details.

+
+

On the project itself

+

Q: Is it production ready?

+

Appyx matured to its stable version in the 1.x branch.

+

The 2.x branch is currently in alpha.

+

We use Appyx at Bumble in production, and as such, we're committed to maintaining and improving it.

+
+

Other

+

Have a question? Come over to #appyx on Kotlinlang Slack!

+ + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/2.x/index.html b/2.x/index.html new file mode 100644 index 000000000..0e2bcb639 --- /dev/null +++ b/2.x/index.html @@ -0,0 +1,2627 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Appyx 2.x (experimental) – Overview - Appyx + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + + +
+
+
+ + + + + + + +
+
+ + + + +

Appyx

+

badge-android +badge-jvm +badge-macos +badge-js +badge-wasm +badge-ios

+

Model-driven navigation + UI components with gesture control for Compose Multiplatform.

+

https://github.com/bumble-tech/appyx

+

Find us on Kotlinlang Slack: #appyx

+

Setup

+

See Downloads and Navigation quick start guide.

+

Overview

+

Appyx is a collection of libraries:

+

Overview

+

Appyx Navigation

+

Type-safe navigation for Compose Multiplatform directly from code.

+
    +
  • Tree-based, composable
  • +
  • Leverages the transitions and gesture-based capabilities of Appyx Interactions to build beautiful, custom navigation.
  • +
  • Use any component for navigation, whether pre-built (Appyx Components), or custom-built by you (Appyx Interactions).
  • +
+

» More details

+
+ +

Appyx Interactions

+

Component kit for Compose Multiplatform.

+
    +
  • Create custom UI components quickly, which can then be used on their own, or inside your navigation tree.
  • +
  • Animation without writing animation code.
  • +
  • Gesture control without the usual gesture detection code.
  • +
+

» More details

+
+ +

Appyx Components

+

Component gallery.

+

Back stack, Spotlight (pager), and other UI components built using Appyx Interactions.

+

» More details

+
+ +

Where to start?

+

Depending on what's your use-case with Appyx:

+

Appyx as a navigation solution

+

Check out Appyx Navigation and some of the Appyx Components you can use in your navigation tree.

+

Creating your own components

+

Stacks, custom pagers, custom UI components – whether for navigation, or standalone: check out what Appyx Interactions can do for you.

+

2.x migration guide

+

If you used Appyx 1.x before, you can find a summary of differences here.

+

1.x documentation

+

This page is about Appyx 2.x (alpha).

+

You can find 1.x related documentation here.

+ + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/2.x/interactions/appyxcomponent/index.html b/2.x/interactions/appyxcomponent/index.html new file mode 100644 index 000000000..2e68907a9 --- /dev/null +++ b/2.x/interactions/appyxcomponent/index.html @@ -0,0 +1,2453 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Appyx Interactions – Creating your own components - Appyx + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + +

Appyx Interactions – Creating your own components

+

Overview

+ +

A component packaged together with Appyx is called an AppyxComponent. It consists of:

+
    +
  • An abstract model representation
  • +
  • One or more UI representations
  • +
  • Gesture interpretation (usually – but not necessarily – tied to the UI representation)
  • +
+
+

This section of the documentation deals with the internals of an AppyxComponent. If you're solely interested in using already created components, you can simply refer to Using components instead.

+

The big picture:

+
flowchart TB
+  subgraph AC[AppyxComponent]
+    direction TB
+    T[TransitionModel] --> |"#lt;ModelState#gt;"| V[Visualisation];
+  end
+  O[Operation] --> AC --> |Modifier| C(["@Composable"]);
+  C --> |Gesture| O
+  B([Business logic]) --> O
+

Where:

+
    +
  • TransitionModel – Defines the abstract description of <ModelState>, accepts Operations
  • +
  • Visualisation – Translates <ModelState> to UI middle representation, eventually Modifier
  • +
  • AppyxComponent – Packages the above as a high level component; its output will result in @Composable elements with animated Modifiers
  • +
  • Operation – Defines a (ModelState) -> ModelState change
  • +
+

You can read more about each in the next sections of the documentation.

+

Where to start

+

It's recommended to extend BaseAppyxComponent for your implementation. You could also take a look at existing samples in the repository and use them as starting points.

+ + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/2.x/interactions/gestures/index.html b/2.x/interactions/gestures/index.html new file mode 100644 index 000000000..bcbfddbeb --- /dev/null +++ b/2.x/interactions/gestures/index.html @@ -0,0 +1,2782 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Appyx Interactions – Gestures - Appyx + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + + +
+
+
+ + + + + + + +
+
+ + + + +

Appyx Interactions – Gestures

+ +

Gesture-based transitions are usually done by direct UI mutation. In Appyx, they're done by gradually mutating the abstract model instead.

+
+

Gestures in Appyx translate to Operations to be executed gradually over the model state. This allows you to greatly simplify gesture-handling client code, as well as allowing gestures to result in the same exact visual outcome as any other transition between two states.

+

The big picture:

+
    +
  1. You receive information on the gesture's direction and the current model state
  2. +
  3. Based on this, you will declare what operation the gesture corresponds to (if any)
  4. +
  5. You will also specify which visual endpoint should the gesture complete this operation towards
  6. +
+

Detecting the gesture's direction

+

Appyx comes with the following gesture-related helpers that report the direction of a drag:

+
/**
+ * The angle of the drag such that:
+ *
+ * - 12 o'clock = 0 degrees
+ * - 3 o'clock = 90 degrees
+ */
+fun dragAngleDegrees(delta: Offset): Float
+
+/**
+ * The horizontal aspect of the drag (LEFT or RIGHT), regardless of the dominant direction
+ */
+fun dragHorizontalDirection(delta: Offset): Drag.HorizontalDirection
+
+/**
+ * The vertical aspect of the drag (UP or DOWN), regardless of the dominant direction
+ */
+fun dragVerticalDirection(delta: Offset): Drag.VerticalDirection
+
+/**
+ * The dominant direction of the drag of 4 possible directions
+ */
+fun dragDirection4(delta: Offset): Drag.Direction4
+
+/**
+ * The dominant direction of the drag of 8 possible directions
+ */
+fun dragDirection8(delta: Offset): Drag.Direction8
+
+/**
+ * The drag direction interpreted on the clock
+ */
+fun dragClockDirection(delta: Offset): Drag.ClockDirection
+
+
+enum class HorizontalDirection {
+    LEFT, RIGHT
+}
+
+enum class VerticalDirection {
+    UP, DOWN
+}
+
+enum class Direction4 {
+    UP, DOWN, LEFT, RIGHT
+}
+
+enum class Direction8 {
+    UP, UPRIGHT, RIGHT, DOWNRIGHT, DOWN, DOWNLEFT, LEFT, UPLEFT
+}
+
+enum class ClockDirection(val digit: Int) {
+    Clock1(1),
+    Clock2(2),
+    Clock3(3),
+    Clock4(4),
+    Clock5(5),
+    Clock6(6),
+    Clock7(7),
+    Clock8(8),
+    Clock9(9),
+    Clock10(10),
+    Clock11(11),
+    Clock12(12)
+}
+
+

Gesture

+

A gesture is defined as:

+
open class Gesture<InteractionTarget, ModelState>(
+    val operation: Operation<ModelState>,
+    val completeAt: Offset
+)
+
+

It completes an Operation at a visual endpoint represented by an Offset. You can read about the latter further below on this page.

+

Gesture factory

+

A GestureFactory is expected to return an instance of a Gesture given:

+
    +
  • the current model state
  • +
  • the drag delta of the gesture
  • +
  • the density
  • +
+
class Gestures<InteractionTarget> : GestureFactory<InteractionTarget, SomeModel.State<InteractionTarget>> {
+
+    override fun createGesture(
+        state: SomeModel.State<InteractionTarget>,
+        delta: Offset,
+        density: Density
+    ): Gesture<InteractionTarget, SomeModel.State<InteractionTarget>> {
+        TODO()
+    }
+}
+
+

GestureFactory implementations are usually nested in a specific UI representation. This makes sense since driving a model with gestures usually results in a natural UX if the gestures are in sync with what happens in the UI. However, it's not a requirement – you could use different gestures than the default one for the UI representation.

+

GestureFactory contains a Boolean field called isContinuous that indicates if during a drag gesture this operation completes but there's still offset to process a new gesture will be created that handles the remaining amount. +This defaults to true however it can be overridden and changed as needed.

+

Choosing an operation

+

Let's see how the internal demo, TestDrive implements its gestures:

+
+ +

Based on this, what we'd want is:

+
    +
  • If the element is in state A, a rightward gesture should move it to state B
  • +
  • If the element is in state B, a downward gesture should move it to state C
  • +
  • If the element is in state C, a leftward gesture should move it to state D
  • +
  • If the element is in state D, an upward gesture should move it to state A
  • +
+

TestDrive already comes with an operation MoveTo(private val elementState: TestDriveModel.State.ElementState) that we can make use of.

+

The GestureFactory implementation should look like this then:

+

class Gestures<InteractionTarget>(
+    transitionBounds: TransitionBounds,
+) : GestureFactory<InteractionTarget, TestDriveModel.State<InteractionTarget>> {
+    // We calculate these based on the offset differences between the actual TargetUiState values
+    private val widthDp = offsetB.x - offsetA.x
+    private val heightDp = offsetD.y - offsetA.y
+
+    override fun createGesture(
+        state: TestDriveModel.State<InteractionTarget>,
+        delta: Offset,
+        density: Density
+    ): Gesture<InteractionTarget, TestDriveModel.State<InteractionTarget>> {
+        val width = with(density) { widthDp.toPx() }
+        val height = with(density) { heightDp.toPx() }
+
+        val direction = dragDirection8(delta)
+        return when (state.elementState) {
+            A -> when (direction) {
+                RIGHT -> Gesture(MoveTo(B), Offset(width, 0f))
+                else -> Gesture.Noop()
+            }
+            B -> when (direction) {
+                DOWN -> Gesture(MoveTo(C), Offset(0f, height))
+                else -> Gesture.Noop()
+            }
+            C -> when (direction) {
+                LEFT -> Gesture(MoveTo(D), Offset(-width, 0f))
+                else -> Gesture.Noop()
+            }
+            D -> when (direction) {
+                UP -> Gesture(MoveTo(A), Offset(0f, -height))
+                else -> Gesture.Noop()
+            }
+        }
+    }
+}
+
+A more advanced version allows every state to move to each of the other 3 states. Try this sample - here you can also move the element backwards to the previous state, and also diagonally across:

+
+ +

To allow this, we now handle more directions:

+
class Gestures<InteractionTarget>(
+    transitionBounds: TransitionBounds,
+) : GestureFactory<InteractionTarget, TestDriveModel.State<InteractionTarget>> {
+    private val widthDp = offsetB.x - offsetA.x
+    private val heightDp = offsetD.y - offsetA.y
+
+    override fun createGesture(
+        state: TestDriveModel.State<InteractionTarget>,
+        delta: Offset,
+        density: Density
+    ): Gesture<InteractionTarget, TestDriveModel.State<InteractionTarget>> {
+        val width = with(density) { widthDp.toPx() }
+        val height = with(density) { heightDp.toPx() }
+
+        val direction = dragDirection8(delta)
+        return when (state.elementState) {
+            A -> when (direction) {
+                RIGHT -> Gesture(MoveTo(B), Offset(width, 0f))
+                DOWNRIGHT -> Gesture(MoveTo(C), Offset(width, height))
+                DOWN -> Gesture(MoveTo(D), Offset(0f, height))
+                else -> Gesture.Noop()
+            }
+            B -> when (direction) {
+                DOWN -> Gesture(MoveTo(C), Offset(0f, height))
+                DOWNLEFT -> Gesture(MoveTo(D), Offset(-width, height))
+                LEFT -> Gesture(MoveTo(A), Offset(-width, 0f))
+                else -> Gesture.Noop()
+            }
+            C -> when (direction) {
+                LEFT -> Gesture(MoveTo(D), Offset(-width, 0f))
+                UPLEFT -> Gesture(MoveTo(A), Offset(-width, -height))
+                UP -> Gesture(MoveTo(B), Offset(0f, -height))
+                else -> Gesture.Noop()
+            }
+            D -> when (direction) {
+                UP -> Gesture(MoveTo(A), Offset(0f, -height))
+                UPRIGHT -> Gesture(MoveTo(B), Offset(width, -height))
+                RIGHT -> Gesture(MoveTo(C), Offset(width, 0f))
+                else -> Gesture.Noop()
+            }
+        }
+    }
+}
+
+

Note how whenever a direction should not result in doing anything, you can always return Gesture.Noop()

+

Visual endpoint

+

In all of the above cases we needed to pass an Offset as a second argument to the Gesture.

+

Note how these offsets have different values in each case: they represent the vector from the current visual to the expected target visual state. This is the vector along which Appyx will interpret the gesture from a 0% to a 100% completion.

+

For example, the vector between A and C is Offset(width, height) as the gesture should be completed along the downward right diagonal from A.

+

Note that while you're not strictly required to match this offset with how an element moves on the screen, it's recommended to do so – otherwise the UX will be confusing in most cases.

+

Drag prediction

+

The target UI state can be rendered immediately upon starting a drag. Note how the target state here matches not only the position, but the scale and the rotation too.

+
+ +

Settling incomplete gestures

+

When releasing the drag before reaching the target, the operation is settled. Depending on how far the gesture got, it might be:

+
    +
  • rounded up towards completion, or
  • +
  • rounded down towards reverting it.
  • +
+

The default threshold is 0.5f (50%), and can be changed between 0f (0%) and 1f (100%). For example, a value of 0.2f would mean the gesture would be considered completed even after a relatively short movement.

+

This can be configured in GestureSettleConfig, along with animation specs of completion and reversal:

+
GestureSettleConfig(
+    completionThreshold = 0.2f, // the default is 0.5f
+    completeGestureSpec = spring(),
+    revertGestureSpec = spring(),
+)
+
+

Here's an example that uses a completionThreshold value of 0.15f (15%). Notice that now you can release the drag much closer to the starting point and it will still complete the animation:

+
+ +

Configuring gestures in the AppyxComponent

+

You can connect your gesture detection to your AppyxComponent in client code such as:

+
val appyxComponent = 
+    SomeAppyxComponent(
+        // Required
+        model = SomeTransitionModel(/*...*/),
+        visualisation = { SomeVisualisation(/*...*/) } ,
+
+        // Optional
+        animationSpec = spring(stiffness = Spring.StiffnessLow),
+        gestureFactory = { SomeVisualisation.Gestures(/*...*/) },
+        gestureSettleConfig = GestureSettleConfig(
+            completionThreshold = 0.5f,
+            completeGestureSpec = spring(),
+            revertGestureSpec = spring(),
+        ),
+    )
+
+

Note that as stated above, gestures are usually come hand in hand with a specific visual representation, but you're not strictly limited to using the same ones. For example, you could use a combination of SpotlightFader + SpotlightSlider.Gestures to have cross-fading visuals controlled by swiping gestures.

+ + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/2.x/interactions/index.html b/2.x/interactions/index.html new file mode 100644 index 000000000..abdfb31a6 --- /dev/null +++ b/2.x/interactions/index.html @@ -0,0 +1,2606 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Appyx Interactions – Overview - Appyx + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + + +
+
+
+ + + +
+ +
+ + + +
+
+ + + + +

Appyx Interactions – Overview

+

Component kit with gesture control for Compose Multiplatform.

+

State-driven motion

+

Try this interactive sample! You can either:

+
    +
  • press the Next button, or
  • +
  • 👆 drag the element to move it to its next state.
  • +
+
+ +

How does this work?

+ +

Appyx has a state-driven approach to UI and motion:

+
    +
  • The Model & its UI representation are separated.
  • +
  • The Model is an abstract representation that knows nothing of the UI.
  • +
  • The UI is a function of the Model, and is not mutated directly.
  • +
  • The Model is the single source of truth. To change the UI, we change the Model.
  • +
  • All transitions and even gestures are implemented as operations acting on the Model directly.
  • +
+
+

Let's see some of the backing code for the above example. Our model here can be in 4 possible states:

+
/* Code is shortened for demonstration purposes */
+class TestDriveModel {
+
+    @Parcelize
+    data class State(
+        val elementState: ElementState
+    ) : Parcelable {
+
+        enum class ElementState {
+            A, B, C, D;
+        }
+    }
+}
+
+

This model is then mapped to the corresponding UI states:

+
@MutableUiStateSpecs
+class TargetUiState(
+    val position: Position.Target,
+    val backgroundColor: BackgroundColor.Target,
+)
+
+private val topLeftCorner = TargetUiState(
+    position = Position.Target(Alignment.TopStart),
+    backgroundColor = BackgroundColor.Target(Color(0xFFFFC629))
+)
+
+private val topRightCorner = TargetUiState(
+    position = Position.Target(Alignment.TopEnd),
+    backgroundColor = BackgroundColor.Target(Color(0xFF353535))
+)
+
+private val bottomRightCorner = TargetUiState(
+    position = Position.Target(Alignment.BottomEnd),
+    backgroundColor = BackgroundColor.Target(Color(0xFFFE9763))
+)
+
+private val bottomLeftCorner = TargetUiState(
+    position = Position.Target(Alignment.BottomStart),
+    backgroundColor = BackgroundColor.Target(Color(0xFF855353))
+)
+
+fun ElementState.toTargetUiState(): TargetUiState =
+    when (this) {
+        A -> topLeftCorner
+        B -> topRightCorner
+        C -> bottomRightCorner
+        D -> bottomLeftCorner
+    }
+
+

Playing with the UI representation

+

Let's spice up the UI representation a bit! Let's see what happens if we also associate rotation-related values with the target UI states:

+
@MutableUiStateSpecs
+class TargetUiState(
+    val position: Position.Target,
+    val rotationZ: RotationZ.Target, // <-- +Rotation
+    val backgroundColor: BackgroundColor.Target,
+)
+
+private val topLeftCorner = TargetUiState(
+    position = Position.Target(Alignment.TopStart),
+    rotationZ = RotationZ.Target(0f), // <-- +Rotation
+    backgroundColor = BackgroundColor.Target(Color(0xFFFFC629))
+)
+
+private val topRightCorner = TargetUiState(
+    position = Position.Target(Alignment.TopEnd),
+    rotationZ = RotationZ.Target(180f), // <-- +Rotation
+    backgroundColor = BackgroundColor.Target(Color(0xFF353535))
+)
+
+private val bottomRightCorner = TargetUiState(
+    position = Position.Target(Alignment.BottomEnd),
+    rotationZ = RotationZ.Target(270f), // <-- +Rotation
+    backgroundColor = BackgroundColor.Target(Color(0xFFFE9763))
+)
+
+private val bottomLeftCorner = TargetUiState(
+    position = Position.Target(Alignment.BottomStart),
+    rotationZ = RotationZ.Target(540f), // <-- +Rotation
+    backgroundColor = BackgroundColor.Target(Color(0xFF855353))
+)
+
+

Adding this new UI property will result in the below sample with no additional effort. Notice how you can still drag the element to its next state, and now the rotation is also animated automatically:

+
+ +

Gestures

+ +

Regardless of whether a specific state change is triggered by a button press (business logic in general) or gestures (UI interaction in general), in Appyx both cases result in the same exact visual outcome.

+
+

If you haven't tried it yet, check how both of the above examples can be manipulated by dragging as well!

+

The second example demonstrates the power of Appyx's approach to gesture handling: the gesture changes the model, rather than the UI. When the state is translated to UI properties any and all additional UI parameters we added (rotation in this case) are automatically transitioned along with the gesture – no change is required in the gesture handling in client code.

+

You can find more information in Gestures.

+

Operation modes

+ +

Appyx supports two main operation modes: Keyframe and Immediate. The main difference is in how they handle interrupts: what should happen when new operations are triggered while a transition is already happening.

+
+

You can achieve a very different effect by spamming the buttons a few times:

+
+ +

In KEYFRAME mode the current transition isn’t interrupted, and it will be guaranteed to finish before any additional transitions. New states are enqueued for execution afterwards. The overall execution progress speeds up proportionally to the size of enqueued operations.

+

In IMMEDIATE mode the current transition is interrupted and will not be finished. New operations overwrite the current target state, resulting in the UI dynamically changing course always towards the latest one.

+

What to use Appyx interactions for

+

Generally speaking:

+
    +
  1. You can create just about any component that you can describe with abstract states
  2. +
  3. Which then you can dress up with various UI representations
  4. +
  5. And manipulate with gestures
  6. +
+

Check out some our packaged examples in Appyx components!

+ + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/2.x/interactions/ksp/index.html b/2.x/interactions/ksp/index.html new file mode 100644 index 000000000..5345ffc87 --- /dev/null +++ b/2.x/interactions/ksp/index.html @@ -0,0 +1,2489 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Appyx Interactions – KSP setup - Appyx + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + +

Appyx Interactions – KSP setup

+

Defining TargetUiStates is easy, as demonstrated in UI representation

+
@MutableUiStateSpecs
+class TargetUiState(
+    val position: Position.Target,
+    val rotation: Rotation.Target,
+    val backgroundColor: BackgroundColor.Target,
+)
+
+

However, for every TargetUiState, there needs to exist a corresponding MutableUiState class containing the animation code.

+

By adding the @MutableUiStateSpecs annotation, if you follow the below setup guide, you can have the Appyx KSP mutable ui state processor generate this class for you automatically.

+

Setup

+

Works with Kotlin 1.8.10. For our migration to Kotlin 1.9 please check:

+
    +
  • #547 - Upgrade Compose Multiplatform / Kotlin
  • +
+

Main build.gradle

+
plugins {
+    id("com.google.devtools.ksp") version libs.versions.ksp.get() apply false
+    // Alternatively: 
+    // id("com.google.devtools.ksp") version '1.8.0-1.0.8' apply false
+}
+
+

App build.gradle

+
plugins {
+    id("com.google.devtools.ksp") 
+}
+
+composeOptions {
+    kotlinCompilerExtensionVersion = '1.4.3'
+}
+
+dependencies {
+    ksp("com.bumble.appyx:appyx-processor:{latest version}")
+}
+
+ + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/2.x/interactions/operations/index.html b/2.x/interactions/operations/index.html new file mode 100644 index 000000000..c70ecc29d --- /dev/null +++ b/2.x/interactions/operations/index.html @@ -0,0 +1,2612 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Appyx Interactions – Operations - Appyx + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + +

Appyx Interactions – Operations

+

Overview

+ +

A TransitionModel behaves like a state machine. If you want a new ModelState to be created, you must pass it an instance of an Operation.

+
+

Operation

+

Operation defines a (ModelState) -> ModelState change.

+
interface Operation<ModelState> {
+
+    val mode: Mode
+
+    fun isApplicable(state: ModelState): Boolean
+
+    operator fun invoke(state: ModelState): StateTransition<ModelState>
+}
+
+

Operation.Mode

+

A mode maybe specified by client code to define how the TransitionModel should handle the state change with regards to interrupts.

+
interface Operation<ModelState> {
+
+    enum class Mode {
+        IMMEDIATE, KEYFRAME
+    }
+}
+
+

You can test their effects on the below sample. Try spamming the buttons:

+
+ +

In code, both buttons invoke the same operation, only with different modes:

+
{ testDrive.next(mode = KEYFRAME) }
+
+// vs
+
+{ testDrive.next(mode = IMMEDIATE, animationSpec = spring(
+    stiffness = Spring.StiffnessVeryLow,
+    dampingRatio = Spring.DampingRatioMediumBouncy
+)) }
+
+

In KEYFRAME mode the current transition isn’t interrupted, and it will be guaranteed to finish before any additional transitions. New states are enqueued for execution afterwards. The overall execution progress speeds up proportionally to the size of enqueued operations.

+

In IMMEDIATE mode the current transition is interrupted and will not be finished. New operations overwrite the current target state, resulting in the UI dynamically changing course always towards the latest one.

+

Applicability check

+
fun isApplicable(state: ModelState): Boolean
+
+

This method is used for checks without actually executing an Operation.

+

Invoking the Operation

+

This method should create a StateTransition<ModelState>:

+
operator fun invoke(state: ModelState): StateTransition<ModelState>
+
+

Which is defined as:

+
data class StateTransition<ModelState>(
+    val fromState: ModelState,
+    val targetState: ModelState
+)
+
+

It is imperative that:

+
    +
  1. fromState is derived from the baseline state (the one passed to invoke)
  2. +
  3. fromState adds any new elements to the state that should be animated in and are not contained in the baseline state
  4. +
  5. targetState is derived from fromState and never directly from the baseline state
  6. +
+

To make it easier, it is recommended to extend BaseOperation which helps to enforce the above:

+
abstract class BaseOperation<ModelState> : Operation<ModelState> {
+
+    final override fun invoke(baseLineState: ModelState): StateTransition<ModelState> {
+        val fromState = createFromState(baseLineState)
+        val targetState = createTargetState(fromState)
+
+        return StateTransition(
+            fromState = fromState,
+            targetState = targetState
+        )
+    }
+
+    /**
+     * @return ModelState If the operation adds any new elements to the scene,
+     *                    it MUST add them to the state here.
+     *                    Otherwise, just return baseLineState unchanged.
+     */
+    abstract fun createFromState(baseLineState: ModelState): ModelState
+
+    /**
+     * @return ModelState Any elements present in this state
+     *                    MUST also be present in the fromState already.
+     */
+    abstract fun createTargetState(fromState: ModelState): ModelState
+}
+
+

Examples

+

You can find examples for implementing operations in the repo. Some possible points of interest:

+
    +
  • TestDrive / MoveTo
  • +
  • BackStack / Push, Pop, Replace
  • +
  • Spotlight / Next, Prev, First, Last
  • +
+

Notice how each of them also comes with their convenient extension methods that allow these operations to be invoked directly on their corresponding AppyxComponent, so that client code can use a simple API:

+
// Without extension methods
+testDrive.accept(MoveTo(A))
+backStack.accept(Pop())
+spotlight.accept(Last())
+
+// With extension methods
+testDrive.moveTo(A)
+backStack.pop()
+spotlight.last() 
+
+ + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/2.x/interactions/transitionmodel/index.html b/2.x/interactions/transitionmodel/index.html new file mode 100644 index 000000000..5f024faca --- /dev/null +++ b/2.x/interactions/transitionmodel/index.html @@ -0,0 +1,2549 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Appyx Interactions – TransitionModel - Appyx + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + +

Appyx Interactions – TransitionModel

+

Overview

+ +

A TransitionModel defines the abstract model of the AppyxComponent.

+
+

<ModelState>

+

TransitionModel holds an instance of <ModelState>, a type defined by the implementation class.

+

You can find various examples in the repository. Some possible points of interest:

+
    +
  • TestDriveModel.State
  • +
  • BackStackModel.State
  • +
  • SpotlightModel.State
  • +
  • CardsModel.State
  • +
+ +

Model state should have no knowledge of UI-related properties – all of that belongs to the UI layer. You should strive to describe your model in a semantic way that assumes nothing of its visual representation.

+
+

For example, TestDriveModel.State describes an element that can be in 4 possible configurations (the example in this documentation's main page).

+

Notice how the states, even though simplistic, aren't named TOPLEFT, TOPRIGHT, BOTTOMRIGHT, BOTTOMLEFT, as the positioning is only done in the UI. This allows to represent them in various different ways, without any assumptions baked in the model.

+
    @Parcelize
+    data class State<InteractionTarget>(
+        val element: Element<InteractionTarget>,
+        val elementState: ElementState
+    ) : Parcelable {
+        enum class ElementState {
+            A, B, C, D;
+
+            fun next(): ElementState =
+                when (this) {
+                    A -> B
+                    B -> C
+                    C -> D
+                    D -> A
+                }
+        }
+    }
+
+

Another example from BackStackModel.State, with purely semantic naming:

+
    @Parcelize
+    data class State<InteractionTarget>(
+        /**
+         * Elements that have been created, but not yet moved to an active state
+         */
+        val created: Elements<InteractionTarget> = listOf(),
+
+        /**
+         * The currently active element.
+         * There should be only one such element in the stack.
+         */
+        val active: Element<InteractionTarget>,
+
+        /**
+         * Elements stashed in the back stack (history).
+         */
+        val stashed: Elements<InteractionTarget> = listOf(),
+
+        /**
+         * Elements that will be destroyed after reaching this state.
+         */
+        val destroyed: Elements<InteractionTarget> = listOf(),
+    ) : Parcelable
+
+

<InteractionTarget>

+

This generic type is simply meant for you to identify different child elements in your client code. For example, if you have a tabbed component, and each tab would be identified by a different instance of an enum or a sealed class, then this enum or sealed class would be your <InteractionTarget>.

+

Then, depending on your <ModelState> and operation implementations, you can affect these targets differently.

+

Changing the state

+

A TransitionModel behaves like a state machine. If you want a new ModelState to be created, you must pass it an instance of an Operation. You can read more about them in Operations.

+

Output

+

TransitionModel has an output flow:

+
val output: StateFlow<Output<ModelState>>
+
+

In most cases don't need to deal with this output directly. The AppyxComponent takes care of that, and channels this flow to the Visualisation to translate it to a UI representation.

+ + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/2.x/interactions/ui-representation/index.html b/2.x/interactions/ui-representation/index.html new file mode 100644 index 000000000..28a3ab2be --- /dev/null +++ b/2.x/interactions/ui-representation/index.html @@ -0,0 +1,2639 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Appyx Interactions – UI representation - Appyx + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + + +
+
+
+ + + +
+ +
+ + + +
+
+ + + + +

Appyx Interactions – UI representation

+

Overview

+ +

The UI representation translates the abstract model of the TransitionModel to visual properties.

+
+

The big picture is that you will define a set of animatable properties, such as:

+
@MutableUiStateSpecs
+class TargetUiState(
+    val position: Position.Target,
+    val rotationZ: RotationZ.Target,
+    val backgroundColor: BackgroundColor.Target,
+)
+
+

Then you will create instances of it to represent some key states an element can be in on the UI:

+
private val topLeftCorner = TargetUiState(
+    position = Position.Target(Alignment.TopStart),
+    rotationZ = RotationZ.Target(0f), // <-- +Rotation
+    backgroundColor = BackgroundColor.Target(Color(0xFFFFC629))
+)
+
+private val topRightCorner = TargetUiState(
+    position = Position.Target(Alignment.TopEnd),
+    rotationZ = RotationZ.Target(180f), // <-- +Rotation
+    backgroundColor = BackgroundColor.Target(Color(0xFF353535))
+)
+
+private val bottomRightCorner = TargetUiState(
+    position = Position.Target(Alignment.BottomEnd),
+    rotationZ = RotationZ.Target(270f), // <-- +Rotation
+    backgroundColor = BackgroundColor.Target(Color(0xFFFE9763))
+)
+
+private val bottomLeftCorner = TargetUiState(
+    position = Position.Target(Alignment.BottomStart),
+    rotationZ = RotationZ.Target(540f), // <-- +Rotation
+    backgroundColor = BackgroundColor.Target(Color(0xFF855353))
+)
+
+

Finally, you will map all elements in your model state to instances of TargetUiState. In our example there's only one element:

+
override fun TestDriveModel.State<InteractionTarget>.toUiTargets(): List<MatchedTargetUiState<InteractionTarget, TargetUiState>> =
+    listOf(
+        MatchedTargetUiState(element, elementState.toTargetUiState())
+    )
+
+fun ElementState.toTargetUiState(): TargetUiState =
+    when (this) {
+        A -> uiStateA
+        B -> uiStateB
+        C -> uiStateC
+        D -> uiStateD
+    }
+
+

Doing so, Appyx will animate elements between these TargetUiStates as the abstract model changes:

+
+ +

A more detailed view

+

Classes related to the UI representation:

+
    +
  • MotionProperty – defines an animatable UI property
  • +
  • MotionProperty.Target – defines a target value for its own MotionProperty
  • +
  • TargetUiState – a collection of different MotionProperty.Target values representing an element's desired representation
  • +
  • MutableUiState – mappable from TargetUiState, this class holds the transient state of animated values as they change between different targets
  • +
+

MotionProperty

+

Appyx comes with a set of classes derived from MotionProperty. A non-exhaustive list of them:

+
    +
  • Alpha
  • +
  • BackgroundColor
  • +
  • Position
  • +
  • RotationX
  • +
  • RotationY
  • +
  • RotationZ
  • +
  • Scale
  • +
  • ZIndex
  • +
  • etc.
  • +
+

They represent UI properties that Appyx can animate. Their animated values will be automatically mapped to Compose Modifier instances.

+

If you need a property that's not provided by Appyx, it should be very easy to create one (also consider submitting a PR).

+

Target values

+

A MotionProperty is mutable towards target values. Target specifies such a value. They're defined by each MotionProperty, and are supposed to be immutable.

+

For example Alpha.Target expects a Float:

+
class Alpha /*...*/ {
+
+    class Target(
+        val value: Float,
+        /*...*/
+    ) : MotionProperty.Target
+}
+
+

While Position.Target deals with Offset:

+
class Position /*...*/ {
+
+    class Target(
+        val value: DpOffset,
+        /*...*/
+    ) : MotionProperty.Target
+}
+
+

TargetUiState

+

Create your TargetUiState as the collection of MotionProperty.Target properties that you want Appyx to animate. For example, if you only want cross-fade animation, you would only need Alpha in your UI. Then you would define your TargetUiState such as:

+
@MutableUiStateSpecs
+class TargetUiState(
+    val alpha: Alpha.Target,
+)
+
+

Or, as in the TestDrive example, position, rotation, and color:

+
@MutableUiStateSpecs
+class TargetUiState(
+    val position: Position.Target,
+    val rotation: Rotation.Target,
+    val backgroundColor: BackgroundColor.Target,
+)
+
+

MutableUiState

+

You might have noticed that the above classes are annotated with @MutableUiStateSpecs. Appyx comes with a KSP processor that will generate the corresponding MutableUiState class for you, so that you don't have to.

+

Please refer to the KSP setup guide.

+

Observing MotionProperty in children UI

+

Sometimes children UI can depend on the transition animation values. Appyx provides an API to observe MotionProperty inside children UI.

+

In this example, children composable retrieve and display DpOffset value from Position motion property as well as Float value from RotationY using this API:

+
// returns dpOffset value if transition has Position MotionProperty and null otherwise 
+val dpOffset : DpOffset? = motionPropertyRenderValue<Position.Value, Position>()?.offset
+
+// returns rotationY value if transition has RotationY MotionProperty and null otherwise 
+val rotationY = motionPropertyRenderValue<Float, RotationY>()
+
+
+ + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/2.x/interactions/usage/index.html b/2.x/interactions/usage/index.html new file mode 100644 index 000000000..01eb6abfc --- /dev/null +++ b/2.x/interactions/usage/index.html @@ -0,0 +1,2661 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Appyx Interactions – Using components - Appyx + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + + +
+
+
+ + + + + + + +
+
+ + + + +

Appyx Interactions – Using components

+

Overview

+ +

A component packaged together with Appyx is called an AppyxComponent.

+
+

Using AppyxComponents

+

If you're solely interested in using already created components, you don't need to learn about the internals of them. This page introduces the minimum information to get them into your Compose-based projects.

+

The big picture

+
flowchart TB
+  O[Operation] --> I[AppyxComponent] --> |Modifier| C(["@Composable"]);
+  C --> |Gesture| O
+  B([Business logic]) --> O
+

Where:

+
    +
  • AppyxComponent – The packaged component; its output will result in @Composable elements with animated Modifiers
  • +
  • Operation – Allows to change the state of the component. It can be triggered programmatically or by gestures. Both options are specific to the component implementation.
  • +
+

Instantiating & configuration

+

This section shows a generic approach that should be applicable to most components. For specific parameters and optional configuration, please refer to the actual component's own API.

+
@Composable
+fun SomeComposable() {
+    val appyxComponent = remember {
+        SomeAppyxComponent(
+            // List of elements, initial state, etc. go in the model:
+            model = SomeTransitionModel(/*...*/),
+
+            // The visual representation (slider, fader, etc.) 
+            visualisation = { SomeVisualisation(/*...*/) } ,
+
+            // Optional – configure animations
+            animationSpec = spring(stiffness = Spring.StiffnessLow),
+
+            // What kind of gestures to control this model with 
+            gestureFactory = { SomeVisualisation.Gestures(/*...*/) },
+
+            // Optional – configure behaviour of incomplete gestures
+            gestureSettleConfig = GestureSettleConfig(
+                completionThreshold = 0.2f,
+                completeGestureSpec = spring(),
+                revertGestureSpec = spring(),
+            ),
+        )
+    }
+}
+
+

Rendering the AppyxComponent

+

In the scope of Appyx Interactions

+

You can render your component with the AppyxInteractionsContainer composable.

+

Make sure to:

+
    +
  • Apply elementUiModel.modifier if you override the optional element rendering.
  • +
  • Provide screenWidthPx and screenWidthPx
  • +
+
@Composable
+fun SomeComposable() {
+    AppyxInteractionsContainer(
+        appyxComponent = yourComponent,
+        screenWidthPx = TODO(),
+        screenHeightPx = TODO(),
+        clipToBounds = false,
+        modifier = Modifier,
+        element = { elementUiModel ->
+            YourElementComposable(
+                elementUiModel = elementUiModel,
+                modifier = elementUiModel.modifier
+            )
+        }
+    )
+}
+
+

In the scope of Appyx Navigation

+

Appyx Navigation extends on the functionality of AppyxInteractionsContainer and adds AppyxNavigationContainer as a wrapper around it.

+

For client code usage they're almost identical. However, you should always use the latter when using Appyx Navigation as it makes sure the related child Nodes are lifecycled properly.

+

Also note:

+
    +
  • This composable is only accessible inside of a Node.
  • +
  • You should use it inside the View composable.
  • +
  • You don't need to specify screen dimensions.
  • +
+
class YourNode(
+    /*...*/
+) : Node<T> {
+
+    @Composable
+    override fun Content(modifier: Modifier) {
+        AppyxNavigationContainer(
+            appyxComponent = yourComponent,
+            modifier = modifier
+        )   
+    }
+}
+
+

When to use which?

+

You should use AppyxInteractionsContainer if you're adding standalone Appyx components to your project without using navigation.

+

You should always use AppyxNavigationContainer if you're using Appyx Navigation.

+

Interacting with the AppyxComponent

+

How you use your model will depend on the specific component. However, typically, you will have a high level API to trigger changes, such as:

+
{ backStack.pop() }
+{ spotlight.next() }
+etc.
+
+

Models should also offer gestures to control them; please refer to their specific API documentation.

+ + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/2.x/migrationguide/index.html b/2.x/migrationguide/index.html new file mode 100644 index 000000000..2d8ef3ebe --- /dev/null +++ b/2.x/migrationguide/index.html @@ -0,0 +1,2841 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + Appyx – 2.x Migration guide - Appyx + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + +

Appyx – Migration guide

+

Project organisation

+

1.x

+

Packaged as a single library, implementing Model-driven navigation with transitions together.

+

2.x

+

The library is packaged as multiple artifacts.

+

:appyx-navigation

+
    +
  • Mostly analogous to Appyx 1.0’s functionality but without the transitions
  • +
  • Cares about the Node structure & Android-related functionality
  • +
  • Uses :appyx-interactions as a dependency to implement navigation using gestures and transitions
  • +
  • Android library
  • +
  • Compose Multiplatform.
  • +
+

Check also Multiplatform documentation and the :demos:appyx-navigation module for code examples.

+

:appyx-interactions

+
    +
  • Gesture-driven, state-based motion kit & transition engine
  • +
  • Does not contain Node-related functionality → moved to :appyx-navigation
  • +
  • Does not contain Android-specific functionality → moved to :appyx-navigation
  • +
  • Compose Multiplatform
  • +
+

:appyx-components

+
    +
  • Pre-packaged components to use with :appyx-navigation
  • +
  • Compose Multiplatform
  • +
+

Rough equivalents

+
    +
  • 1.x → 2.x
  • +
  • NavModelAppyxComponent
  • +
  • TransitionHandlerVisualisation
  • +
  • BuildContextNodeContext
  • +
+

Migration guide

+

Gradle

+

Maven Central

+

Core

+

Note that BackStack and Spotlight are now standalone artifacts. Check your usage, you might only need backstack:

+
-implementation("com.bumble.appyx:core:1.x.x")
++implementation("com.bumble.appyx:appyx-navigation:2.0.0-alpha01")
++implementation("com.bumble.appyx:backstack-android:2.0.0-alpha01")
++implementation("com.bumble.appyx:spotlight-android:2.0.0-alpha01")
+
+

Testing

+

Artifacts have a utils- prefix:

+
-implementation("com.bumble.appyx:testing-ui")
+-implementation("com.bumble.appyx:testing-unit-common")
+-implementation("com.bumble.appyx:testing-junit4")
+-implementation("com.bumble.appyx:testing-junit5")
+
++implementation("com.bumble.appyx:utils-testing-ui")
++implementation("com.bumble.appyx:utils-testing-unit-common")
++implementation("com.bumble.appyx:utils-testing-junit4")
++implementation("com.bumble.appyx:utils-testing-junit5")
+
+

MainActivity

+

If you migrate all of your codebase from 1.x to 2.x in a single go, you should skip this step. However, if you intend to keep them in parallel for a gradual migration, you must do the following:

+
    +
  • Update your 1.x version to 1.4.1-migrate-2.x
  • +
  • Rename appyxIntegrationPoint usage in 1.x code to appyxV1IntegrationPoint:
  • +
+
    import com.bumble.appyx.core.integration.NodeHost
+    import com.bumble.appyx.core.integrationpoint.NodeActivity
+
+    class MainActivity : NodeActivity() {
+         super.onCreate(savedInstanceState)
+         setContent {
+             HelloAppyxTheme {
+-                NodeHost(integrationPoint = appyxIntegrationPoint) {
++                NodeHost(integrationPoint = appyxV1IntegrationPoint) {
+                     RootNode(it)
+                 }
+             }
+     }
+
+

Usage site (RootNode)

+
-import com.bumble.appyx.core.composable.Children
+-import com.bumble.appyx.core.modality.BuildContext
+-import com.bumble.appyx.core.node.Node
+-import com.bumble.appyx.core.node.ParentNode
+-import com.bumble.appyx.navmodel.backstack.BackStack
+-import com.bumble.appyx.navmodel.backstack.operation.push
+-import com.bumble.appyx.navmodel.backstack.transitionhandler.rememberBackstackFader
+
++import com.bumble.appyx.components.backstack.BackStack
++import com.bumble.appyx.components.backstack.BackStackModel
++import com.bumble.appyx.components.backstack.operation.push
++import com.bumble.appyx.components.backstack.ui.fader.BackStackFader
++import com.bumble.appyx.navigation.composable.AppyxNavigationContainer
++import com.bumble.appyx.navigation.modality.NodeContext
++import com.bumble.appyx.navigation.node.Node
++import com.bumble.appyx.navigation.node.ParentNode
+
+class RootNode(
+-    buildContext: BuildContext,
++    nodeContext: NodeContext,
+    private val backStack: BackStack<NavTarget> = BackStack(
+-        initialElement = Child1,
+-        savedStateMap = buildContext.savedStateMap
++        model = BackStackModel(
++            initialTarget = Child1,
++            savedStateMap = nodeContext.savedStateMap
++        ),
++        visualisation = { BackStackFader(it) },
+     ),
+ ) : ParentNode<RootNode.NavTarget>(
+-    navModel = backStack,
++    appyxComponent = backStack,
+-     buildContext = buildContext
++     nodeContext = nodeContext
+ ) {
+
+    sealed class NavTarget {
+        object Child1 : NavTarget()
+        object Child2 : NavTarget()
+    }
+
+-    override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node =
+-        when (navTarget) {
+-            is Child1 -> Child1Node(buildContext) { backStack.push(Child2) }
+-            is Child2 -> Child2Node(buildContext)
++    override fun buildChildNode(reference: NavTarget, nodeContext: NodeContext): Node =
++        when (reference) {
++            is Child1 -> Child1Node(nodeContext) { backStack.push(Child2) }
++            is Child2 -> Child2Node(nodeContext)
+        }
+
+    @Composable
+    override fun Content(modifier: Modifier) {
+-        Children(
+-            navModel = backStack,
+-            transitionHandler = rememberBackstackFader(transitionSpec = { spring() }),
++        AppyxNavigationContainer(
++            appyxComponent = backStack,
+            modifier = Modifier.fillMaxSize()
+        )
+    }
+}
+
+

Usage site (@Preview)

+
-import com.bumble.appyx.core.integration.NodeHost
+-import com.bumble.appyx.core.integrationpoint.IntegrationPointStub
+
++import com.bumble.appyx.navigation.integration.NodeHost
++import com.bumble.appyx.navigation.integration.IntegrationPointStub
+
+@Preview
+@Composable
+fun RootNodePreview() {
+    Box(Modifier.fillMaxSize()) {
+        NodeHost(integrationPoint = IntegrationPointStub()) {
+            RootNode(
+                nodeContext = NodeContext.root(null)
+            )
+        }
+    }
+}
+
+

If you have a broken import that's not listed above, please open an issue/PR, or let us know on the #appyx channel on Kotlinlang Slack. Thanks!

+ + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/2.x/navigation/concepts/composable-navigation/index.html b/2.x/navigation/concepts/composable-navigation/index.html new file mode 100644 index 000000000..033f085dc --- /dev/null +++ b/2.x/navigation/concepts/composable-navigation/index.html @@ -0,0 +1,2552 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Appyx Navigation – Composable navigation - Appyx + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + + +
+
+
+ + + + + + + +
+
+ + + + +

Composable navigation

+

AppyxComponents in Appyx are composable.

+

As a single AppyxComponent won't be enough for the whole of your whole app, you can use many in a composable way. That is, any managed element of a AppyxComponent can also host its own AppyxComponent.

+

Nodes – structural elements for composing navigation

+

Nodes are the main structural element in Appyx. They can form a tree.

+

You can think of a Node as a standalone unit of your app with:

+
    +
  • Its own AppyxComponent
  • +
  • Its own Lifecycle
  • +
  • State restoration
  • +
  • A @Composable view
  • +
  • Business logic that's kept alive even when the view isn't added to the composition
  • +
  • The ability to host generic Plugins to extract extra concerns without enforcing any particular architectural pattern
  • +
+

This allows you to make your app's business logic also composable by leveraging Nodes as lifecycled components.

+

Parent nodes, child nodes

+

Nodes can have other Nodes as children. This means you can represent your whole application as a tree of Appyx nodes.

+

+

You can go as granular or as high-level as it fits you. This allows to keep the complexity low in individual Nodes by extracting responsibilities to children, as well as composing other components to build more complex functionality.

+

ChildAware API

+

A Node can react to dynamically added child Nodes in the tree: ChildAware API

+ +

+

Once you've structured your navigation in a composable way, you can add an AppyxComponent to a Node of this tree and make it dynamic:

+
    +
  • Some parts in this tree are active while others ore not
  • +
  • The active parts define what state the application is in, and what the user sees on the screen
  • +
  • We can change what's active by using an AppyxComponent on each level of the tree
  • +
  • Changes will feel like navigation to the user
  • +
+

See Implicit navigation and Explicit navigation for building complex navigation behaviours with this approach.

+

How AppyxComponents affect Nodes

+

AppyxComponent operations will typically result in:

+
    +
  • Adding or removing child Nodes of a Node
  • +
  • Move them on and off the screen
  • +
  • Change their states
  • +
+

As an illustration:

+

+

Here:

+
    +
  • Back stack illustrates adding and removing child Nodes
  • +
  • Tiles illustrates changing the state of children and removing them from the Node
  • +
+

These are just two examples, you're of course not limited to using them.

+

Summary

+

A summary of Appyx's approach to structuring applications:

+
    +
  • Compose your app out of Nodes with their own lifecycles and state
  • +
  • Navigation is local, composed of individual pieces of AppyxComponents
  • +
  • Navigation is stateful
  • +
  • Navigation is unit-testable
  • +
  • Nested navigation and multi-module navigation works as a default
  • +
  • You're free to implement your own navigable components by utilising AppyxComponents
  • +
  • Avoid global navigation concerns, like shared modules needing to know about the application, or the application needing to know about all its possible modules
  • +
+ + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/2.x/navigation/concepts/explicit-navigation/index.html b/2.x/navigation/concepts/explicit-navigation/index.html new file mode 100644 index 000000000..96eb623e9 --- /dev/null +++ b/2.x/navigation/concepts/explicit-navigation/index.html @@ -0,0 +1,2718 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Appyx Navigation – Explicit navigation - Appyx + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + + +
+
+
+ + + + + + + +
+
+ + + + +

Explicit navigation

+

When Implicit navigation doesn't fit your use case, you can try an explicit approach.

+
+

Relevant methods

+
    +
  • Node.attachChild()
  • +
  • Node.waitForChildAttached()
  • +
+
+

Using these methods we can chain together a path which leads from the root of the tree to a specific Node.

+

Use case

+

We want to navigate from Chat

+

+

to onboarding's first screen O1:

+

+

This time we'll want to do this explicitly by calling a function.

+

The plan

+
    +
  1. Create a public method on Root that attaches Onboarding
  2. +
  3. Create a public method on Onboarding that attaches the first onboarding screen
  4. +
  5. Create a Navigator, that starting from an instance of Root, can chain these public methods together into a single action: navigateToO1()
  6. +
  7. Capture an instance of Root to use with Navigator
  8. +
  9. Call navigateToO1() on our Navigator instance
  10. +
+

Step 1 – RootOnboarding

+

First, we need to define how to programmatically attach Onboarding to the Root:

+
class RootNode(
+    nodeContext: NodeContext,
+    backStack: BackStack<NavTarget>
+) : Node<NavTarget>(
+    nodeContext = nodeContext,
+    appyxComponent = backStack,
+) {
+
+    suspend fun attachOnboarding(): OnboardingNode {
+        return attachChild {
+            backStack.replace(NavTarget.Onboarding)
+        }
+    }
+}
+
+

Let's break down what happens here:

+
    +
  1. Since attachChild has a generic <T> return type, it will conform to the defined OnboardingNode type
  2. +
  3. However, attachChild doesn't know how to create navigation to OnboardingNode – that's something only we can do with the provided lambda
  4. +
  5. We replace NavTarget.Onboarding into the back stack
  6. +
  7. Doing this should result in OnboardingNode being created and added to RootNode as a child
  8. +
  9. attachChild expects an instance of OnboardingNode to appear as a child of Root as a consequence of executing our lambda
  10. +
  11. Once it appears, attachChild returns it
  12. +
+
+

Important

+

It's our responsibility to make sure that the provided lambda actually results in the expected child being added. If we accidentally do something else instead, for example:

+
suspend fun attachOnboarding(): OnboardingNode {
+    return attachChild {
+        backStack.replace(NavTarget.Main) // Wrong NavTarget
+    }
+}
+
+

Then an exception will be thrown after a timeout.

+
+

Step 2 – OnboardingO1

+

Unlike Root, Onboarding uses Spotlight instead of BackStack as an AppyxComponent, so navigation to the first screen is slightly different:

+
class OnboardingNode(
+    nodeContext: NodeContext,
+    spotlight: Spotlight<NavTarget>
+) : Node<NavTarget>(
+    nodeContext = nodeContext,
+    appyxComponent = spotlight,
+) {
+
+    suspend fun attachO1(): O1Node {
+        return attachChild {
+            spotlight.activate(index = 0)
+        }
+    }
+}
+
+

Step 3 – Our Navigator

+
interface Navigator {
+     fun navigateToO1()
+}
+
+

In this case we'll implement it directly with our activity:

+
class ExplicitNavigationExampleActivity : NodeActivity(), Navigator {
+
+    lateinit var rootNode: RootNode // See the next step
+
+     override fun navigateToO1() {
+         lifecycleScope.launch {
+             rootNode
+                 .attachOnboarding()
+                 .attachO1()
+         }
+     }
+}
+
+

Step 4 – An instance of RootNode

+

As the last piece of the puzzle, we'll also need to capture the instance of RootNode to make it all work. We can do that by a NodeReadyObserver plugin when setting up our tree:

+
class ExplicitNavigationExampleActivity : NodeActivity(), Navigator {
+
+    lateinit var rootNode: RootNode
+
+    override fun navigateToO1() { /*...*/ }
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        setContent {
+            NodeHost(integrationPoint = appyxIntegrationPoint) {
+                RootNode(
+                    nodeContext = it,
+                    navigator = this@ExplicitNavigationExampleActivity,
+                    plugins = listOf(object : NodeReadyObserver<RootNode> {
+                        override fun init(node: RootNode) {
+                            rootNode = node
+                        }
+                    })
+                )
+            }
+        }
+    }
+}
+
+

Step 5 – Using the Navigator

+

See how in the previous snippet RootNode receives a navigator dependency.

+

It can pass it further down the tree as a dependency to other nodes. Those nodes can call the methods of the Navigator, which will change the global navigation state directly.

+
+

Bonus: Wait for a child to be attached

+

There might be cases when we want to wait for a certain action to be performed by the user, rather than us, to result in a child being attached.

+

In these cases we can use Node.waitForChildAttached() instead.

+

Use case – Wait for login

+

A typical case building an explicit navigation chain that relies on Logged in being attached. Most probably Logged in has a dependency on some kind of a User object. Here we want to wait for the user to authenticate themselves, rather than creating a dummy user object ourselves.

+
class RootNode(
+    nodeContext: NodeContext,
+) : Node<NavTarget>(
+    nodeContext = nodeContext
+) {
+
+    suspend fun waitForLoggedIn(): LoggedInNode = 
+        waitForChildAttached<LoggedInNode>()
+}
+
+

This method will wait for LoggedInNode to appear in the child list of RootNode and return with it. If it's already there, it returns immediately.

+

A navigation chain using it could look like:

+
class ExplicitNavigationExampleActivity : NodeActivity(), Navigator {
+
+     override fun navigateToProfile() {
+         lifecycleScope.launch {
+             rootNode
+                 .waitForLoggedIn()
+                 .attachMain()
+                 .attachProfile()
+         }
+     }
+}
+
+

You can find related code examples in ExplicitNavigationExampleActivity in our samples.

+ + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/2.x/navigation/concepts/implicit-navigation/index.html b/2.x/navigation/concepts/implicit-navigation/index.html new file mode 100644 index 000000000..9786fc581 --- /dev/null +++ b/2.x/navigation/concepts/implicit-navigation/index.html @@ -0,0 +1,2581 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Appyx Navigation – Implicit navigation - Appyx + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + +

Implicit navigation

+

+

How can we go from one part of the tree to another? In almost all cases navigation can be implicit instead of explicit. We don't need to specify the target – navigation will happen as a consequence of individual pieces of the puzzle.

+
+

Relevant methods

+
    +
  • Node.onChildFinished(child: Node) can be overridden by client code to handle a child finishing
  • +
  • Node.finish() invokes the above method on its parent
  • +
+
+

Use-case 1

+

+

Requirement

+

After onboarding finishes, the user should land in the message list screen.

+

Solution

+
    +
  1. O3 calls its finish() method
  2. +
  3. Onboarding notices O3 finished; if it had more children, it could switch to another; now it calls finish() too
  4. +
  5. Logged in notices Onboarding finished, and switches its navigation to Main
  6. +
  7. Main is initialised, and loads its default navigation target (based on product requirements) to be Messages
  8. +
  9. Messages is initialised, and loads its default navigation target to be List
  10. +
+
+

Bonus

+

Every Node in the above sequence only needed to care about its own local concern.

+
+

Use-case 2

+

+

Requirement

+

Pressing the logout button on the profile screen should land us back to the login screen.

+

Solution

+
    +
  1. Root either implements a logout callback, or subscribes to the changes of a user repository; in both cases, either the callback or the repository is passed down the tree as a dependency
  2. +
  3. Profile invokes the callback or a logout method on the repository
  4. +
  5. Root notices the state change, and switches its navigation to the Logged out scope
  6. +
  7. Logged out loads its initial navigation target, Login
  8. +
+
+

Bonus

+

Note how the entire Logged in scope is destroyed without any extra effort. The next time a login happens, all state is created anew.

+
+

Summary

+

Implicit navigation allows you to implement navigation without introducing unnecessary coupling in the tree, and successfully covers the majority of navigation scenarios.

+

In case it's not enough to meet your needs, see the next chapter, Explicit navigation

+ + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/2.x/navigation/concepts/model-driven-navigation/index.html b/2.x/navigation/concepts/model-driven-navigation/index.html new file mode 100644 index 000000000..25836ded4 --- /dev/null +++ b/2.x/navigation/concepts/model-driven-navigation/index.html @@ -0,0 +1,2481 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Appyx Navigation – Model-driven navigation - Appyx + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + +

Model-driven navigation

+

Your own navigation model

+

Generally speaking, most navigation solutions have fixed navigation mechanisms (e.g. a back stack).

+

Appyx gives you the freedom to define your own navigation model using any Appyx component. For example, you can implement any of the examples you see here with the same approach, then use them in your navigation tree:

+

+

No screen, only a viewport

+

Generally speaking, most navigation solutions model a "Screen" and focus on how to get from one screen to another.

+

Appyx does not have the concept of the screen baked in – there's only a viewport, and whatever fills the available space will feel like the screen to the user.

+

This freedom allows you to implement:

+
    +
  • Navigation that feels like going from "screen to screen"
  • +
  • Navigation "inside the screen"
  • +
  • Navigation that bridges between the two
  • +
+

For example, you can transform the screen itself as part of navigation:

+

+

AppyxComponents

+

An AppyxComponent implements any of the above mechanisms.

+

See AppyxComponents for more details.

+

Composable navigation

+

AppyxComponents in Appyx are composable.

+

See Composable navigation for more details.

+ + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/2.x/navigation/features/childaware/index.html b/2.x/navigation/features/childaware/index.html new file mode 100644 index 000000000..3b950dc18 --- /dev/null +++ b/2.x/navigation/features/childaware/index.html @@ -0,0 +1,2504 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Appyx Navigation – ChildAware API - Appyx + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + +

ChildAware API

+

The ChildAware interface allows you to scope communication with (or between) dynamically available child nodes easily.

+

Baseline

+

In the next examples:

+
    +
  1. Let's assume that SomeNode can host multiple child nodes: Child1, Child2, etc.
  2. +
  3. SomeInteractor belongs to SomeNode and is passed as a Plugin + to it
  4. +
  5. SomeInteractor extends the Interactor helper class from the framework:
      +
    • It implements NodeLifecycleAware, which makes sure it will receive the onCreate callback + from the framework
    • +
    • It implements ChildAware, which unlocks the DSL we'll see in the following + snippets
    • +
    +
  6. +
+

Single child scenario

+
import com.bumble.appyx.navigation.lifecycle.Lifecycle
+import com.bumble.appyx.core.children.whenChildAttached
+import com.bumble.appyx.core.children.whenChildrenAttached
+import com.bumble.appyx.core.clienthelper.interactor.Interactor
+
+
+class SomeInteractor : Interactor<SomeNode>() {
+
+    override fun onCreate(lifecycle: Lifecycle) {
+        lifecycle.subscribe(onCreate = {
+
+            // This lambda is executed every time a node of type Child1Node is attached:
+            whenChildAttached { commonLifecycle: Lifecycle, child1: Child1Node ->
+                // TODO:
+                //  - establish communication with child1 
+                //  - use commonLifecycle for scoping 
+                //  - it will be capped by the lifecycles of child1 and the parent
+            }
+        })
+    }
+}
+
+

Note: Lifecycle is multiplatform.

+

Multiple children

+
import androidx.lifecycle.Lifecycle
+import com.bumble.appyx.core.children.whenChildAttached
+import com.bumble.appyx.core.children.whenChildrenAttached
+import com.bumble.appyx.core.clienthelper.interactor.Interactor
+
+
+class SomeInteractor : Interactor<SomeNode>() {
+
+    override fun onCreate(lifecycle: Lifecycle) {
+        lifecycle.subscribe(onCreate = {
+
+            // This lambda is executed every time these two nodes are attached at the same time:
+            whenChildrenAttached { commonLifecycle: Lifecycle, child1: Child1Node, child2: Child2Node ->
+                // TODO
+                //  - establish communication between child1 & child2 
+                //  - use commonLifecycle for scoping
+                //  - it will be capped by the lifecycles of child1, child2 and the parent
+            }
+        })
+    }
+}
+
+ + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/2.x/navigation/features/deep-linking/index.html b/2.x/navigation/features/deep-linking/index.html new file mode 100644 index 000000000..1c2ce4294 --- /dev/null +++ b/2.x/navigation/features/deep-linking/index.html @@ -0,0 +1,2387 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Appyx Navigation – Deep linking - Appyx + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + +

Deep linking

+

Building on top of explicit navigation, implementing deep links is straightforward:

+
class ExplicitNavigationExampleActivity : NodeActivity(), Navigator {
+
+    lateinit var rootNode: RootNode
+
+    fun handleDeepLink(intent: Intent) {
+        if (intent.action == Intent.ACTION_VIEW) {
+            when {
+                (it.data?.host == "onboarding") -> navigateToOnBoarding()
+                else -> Unit
+            }
+        }
+    }
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        setContent {
+            NodeHost(integrationPoint = appyxIntegrationPoint) {
+                RootNode(
+                    nodeContext = it,
+                    plugins = listOf(object : NodeReadyObserver<RootNode> {
+                        override fun init(node: RootNode) {
+                            rootNode = node
+                            handleDeepLink(intent = intent)
+                        }
+                    })
+                )
+            }
+        }
+    }
+
+    private fun navigateToOnBoarding() {
+        // implement explicit navigation
+    }
+}
+
+

Check ExplicitNavigationExampleActivity in the samples to inspect the full code.

+ + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/2.x/navigation/features/lifecycle/index.html b/2.x/navigation/features/lifecycle/index.html new file mode 100644 index 000000000..78797c2d9 --- /dev/null +++ b/2.x/navigation/features/lifecycle/index.html @@ -0,0 +1,2482 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Appyx Navigation – Lifecycle - Appyx + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + +

Lifecycle

+

Nodes have their own lifecycles.

+

The relevant class Lifecycle is multiplatform.

+

Capping

+

No node can be in a higher lifecycle state than any of its parents or the platform-relevant App component (e.g. Android Activity) it lives in.

+

On-screen & off-screen

+

Appyx controls which children of an AppyxComponent should be added to the composition and which should not, based on their state and properties. This is done automatically.

+

Lifecycle changes

+

Child elements automatically receive appropriate lifecycle callbacks. The lifecycle state can be affected by:

+
    +
  • The AppyxComponent of the parent (adding or removing child Nodes and changing their on-screen status)
  • +
  • The parent's lifecycle state capping its children (transitive in the tree)
  • +
  • On Android, Activity lifecycle will be capping the whole tree
  • +
+

Back stack node lifecycle

+

An example demonstrating the above:

+

+

Note that individual AppyxComponents might have their slight differences (e.g. whether their operations remove a Node only from the view, or completely destroy it).

+

In the case of the back stack:

+
    +
  • The Push operation adds a new element and stashes the currently active one – the stashed one will be removed from the view & STOPPED
  • +
  • The Pop operation removes an element, the child Node will be DESTROYED
  • +
+ + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/2.x/navigation/features/material3/index.html b/2.x/navigation/features/material3/index.html new file mode 100644 index 000000000..d26b20e9e --- /dev/null +++ b/2.x/navigation/features/material3/index.html @@ -0,0 +1,2585 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Appyx Navigation – Material 3 support - Appyx + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + + +
+
+
+ + + + + + + +
+
+ + + + +

Material 3 support

+

Optional support for Material 3 that renders Appyx navigation using a Navigation bar or a Navigation rail.

+

How to use

+

1. Setup

+

To add the proper gradle artifacts, please refer to: Downloads

+

2. Define your navigation config

+
import com.bumble.appyx.utils.material3.AppyxNavItem
+import com.bumble.appyx.utils.multiplatform.Parcelable
+import com.bumble.appyx.utils.multiplatform.Parcelize
+
+@Parcelize
+enum class MainNavItem : Parcelable {
+    DESTINATION1, DESTINATION2, DESTINATION3;
+
+    companion object {
+        val resolver: (MainNavItem) -> AppyxNavItem = { navBarItem ->
+            when (navBarItem) {
+                DESTINATION1 -> AppyxNavItem(
+                    text = "Title 1",
+                    unselectedIcon = TODO(),
+                    selectedIcon = TODO(),
+                    iconModifier = Modifier,
+                    node = { YourAppyxNode1(it) }
+                )
+
+                DESTINATION2 -> AppyxNavItem(
+                    text = "Title 2",
+                    unselectedIcon = TODO(),
+                    selectedIcon = TODO(),
+                    iconModifier = Modifier,
+                    node = { YourAppyxNode2(it) }
+                )
+
+                DESTINATION3 -> AppyxNavItem(
+                    text = "Title 3",
+                    unselectedIcon = TODO(),
+                    selectedIcon = TODO(),
+                    iconModifier = Modifier,
+                    node = { YourAppyxNode3(it) }
+                )
+            }
+        }
+    }
+}
+
+

3. Create an instance of AppyxMaterial3NavNode and pass your config to it

+
AppyxMaterial3NavNode<MainNavItem>(
+    nodeContext = nodeContext,
+    navTargets = MainNavItem.values().toList(),
+    navTargetResolver = MainNavItem.resolver
+)
+
+

4. Done!

+

Use this node as you would use any other Appyx node. AppyxMaterial3NavNode renders either a Navigation bar or a Navigation rail depending on the screen size:

+

Appyx + Navigation bar Appyx + Navigation rail

+

You can check the live multiplatform demo in the :demos:appyx-navigation module.

+

Customisation options

+

You can set a different animation spec applied to the default crossfade:

+
AppyxMaterial3NavNode<MainNavItem>(
+    animationSpec = spring(Spring.StiffnessMedium)
+)
+
+

Or you can set a different visualisation altogether:

+
AppyxMaterial3NavNode<MainNavItem>(
+    /*...*/
+    visualisation = { SpotlightSlider(it) }
+)
+
+

For a list of possible visualisations you can refer to Spotlight documentation.

+

You can also extend the class and override when to display a Navigation bar or a rail. These are the defaults:

+
@Composable
+open fun shouldUseNavigationBar(): Boolean =
+    LocalScreenSize.current.windowSizeClass == COMPACT
+
+@Composable
+open fun shouldUseNavigationRail(): Boolean =
+    !shouldUseNavigationBar()
+
+ + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/2.x/navigation/features/plugins/index.html b/2.x/navigation/features/plugins/index.html new file mode 100644 index 000000000..fa9633a20 --- /dev/null +++ b/2.x/navigation/features/plugins/index.html @@ -0,0 +1,2584 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Appyx Navigation – Plugins - Appyx + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + +

Plugins

+

Keeping extra concerns out of Node

+

Nodes are meant to be simple structural elements, and should be kept lean.

+

To keep the framework agnostic of any specific approach / pattern you want to use, there aren't any fixed parts. Rather, the Node offers an extension point using Plugins in its constructor:

+
abstract class Node(
+    nodeContext: NodeContext,
+    val view: NodeView = EmptyNodeView,
+    plugins: List<Plugin> = emptyList() // <--
+)
+
+

So what is a Plugin?

+

A Plugin is an empty interface extended by many actual ones:

+
interface Plugin
+
+

Plugins

+ +
interface NodeLifecycleAware : Plugin {
+    fun onCreate(lifecycle: Lifecycle) {}
+}
+
+fun interface Destroyable : Plugin {
+    fun destroy()
+}
+
+

Component level plugins

+

Sometimes you need to grab a reference to the component as a whole, either as an interface, or its implementation, the Node.

+

This will come especially handy when working with workflows.

+
interface NodeAware : Plugin {
+    val node: Node<*>
+
+    fun init(node: Node<*>) {}
+}
+
+

There are helper classes found in the library, so you don't have to implement the above interfaces, you can just use delegation:

+
class SomeClass(
+    private val nodeAware: NodeAware = NodeAwareImpl()
+) : NodeAware by nodeAware {
+
+    fun foo() {
+        // [node] is an automatically available property coming from the NodeAware interface
+        // the reference is automatically set for you by the framework + the NodeAwareImpl class
+        // so you can use it right away:
+        node.doSomething()
+    }
+}
+
+

⚠️ Note: the reference to node is set by Node automatically, and isn't available immediately after constructing your object, but only after the construction of the Node itself.

+ +

In case if you need to control navigation behaviour, you can use these plugins:

+
interface UpNavigationHandler : Plugin {
+    fun handleUpNavigation(): Boolean = false
+}
+
+interface BackPressHandler : Plugin {
+    val onBackPressedCallback: OnBackPressedCallback? get() = null
+}
+
+

UpNavigationHandler controls Node.navigateUp behaviour and allows to intercept its invocation.

+

BackPressHandler controls device back press behaviour via androidx.activity.OnBackPressedCallback. +You can read more about it here.

+

⚠️ Note: OnBackPressedCallback are invoked in the following order: +1. From children to parents. Render order of children matters! The last rendered child will be the first to handle back press. +2. Direct order of plugins within a node. Plugins are invoked in order they appears in Node(plugins = ...) before the AppyxComponent.

+

Using Plugins

+

All plugins are designed to have empty {} default implementations (or other sensible defaults when a return value is defined), so it's convenient to implement them only if you need.

+

Don't forget to pass your Plugins to your Node:

+
internal class MyNode(
+    // ...
+    plugins: List<Plugins> = emptyList()
+    // ...
+) : Node<Nothing>(
+    // ...
+    plugins = plugins
+    // ...
+)
+
+

⚠️ Note: plugins is a List, as the order matters here. All Plugin instances are invoked in the order they appear in the list.

+ + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/2.x/navigation/features/scoped-di/index.html b/2.x/navigation/features/scoped-di/index.html new file mode 100644 index 000000000..9d979ba4c --- /dev/null +++ b/2.x/navigation/features/scoped-di/index.html @@ -0,0 +1,2450 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Appyx Navigation – Scoped DI - Appyx + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + +

Scoped DI

+

Once you represent your navigation in a Composable way, you will get powerful DI scoping as a pleasant side-effect:

+

+

Appyx gives every single Node its own DI scope for free, with no extra effort required to clean up these scopes other than navigating away from them.

+

How does this work?

+
    +
  1. Imagine you create an object in the Node related to Onboarding, and make it available to all of its children.
  2. +
  3. While navigation is advancing between the individual screens of Onboarding, O1O2O3, this object will be the same instance.
  4. +
  5. As soon as the navigation switches to Main, the entire subtree of Onboarding is destroyed and all held objects are released.
  6. +
  7. Should the navigation ever go back to Onboarding, said object would be created from scratch.
  8. +
+

This of course applies to every other Node in the tree.

+

Scoping in practice

+

Imagine in a larger tree:

+

+
    +
  1. A logout action is represented as switching the navigation back to the Logged out node
  2. +
  3. This will destroy the entire Logged in scope automatically
  4. +
  5. All objects held in the scope of an authenticated state are released without any special effort.
  6. +
+

You could also use this for DI scoping flows (e.g. a cart object during a multi-screen product checkout).

+

With a regular approach these cases could be more difficult to represent:

+
    +
  • Screen-bound scopes wouldn't allow multi-screen lifetime of the objects.
  • +
  • Application-scoped singletons would require extra attention of cleaning up once the flow ends.
  • +
+

With Appyx you get best of both worlds for free.

+ + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/2.x/navigation/features/surviving-configuration-changes/index.html b/2.x/navigation/features/surviving-configuration-changes/index.html new file mode 100644 index 000000000..a731c50c7 --- /dev/null +++ b/2.x/navigation/features/surviving-configuration-changes/index.html @@ -0,0 +1,2478 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Appyx Navigation – Surviving configuration changes - Appyx + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + +

Surviving configuration changes

+

To retain objects during configuration change you can use the RetainedInstanceStore class.

+

How does it work?

+ +

The RetainedInstanceStore stores objects within a singleton.

+

Every Node has access to the RetainedInstanceStore and manages these cases automatically:

+
    +
  • The Activity is recreated: the retained instance is returned instead of a new instance.
  • +
  • The Activity is destroyed: the retained instance is removed and disposed.
  • +
+
+

Example

+

Appyx provides extension methods on the NodeContext class (an instance which is passed to every Node upon creation) to access the RetainedInstanceStore. You can use these to:

+
    +
  • Create your retained objects (factory)
  • +
  • Define cleanup mechanisms (disposer) to be run when the retained object will be removed on Activity destroy.
  • +
+

You can opt to use the Builder pattern to provide dependencies to your Node and separate this logic:

+ +

Note: to use the rx2/rx3 getRetainedDisposable extension methods you see below, you need to add the relevant gradle dependencies. Please refer to Downloads.

+
+
import com.bumble.appyx.navigation.builder.Builder
+import com.bumble.appyx.navigation.modality.NodeContext
+import com.bumble.appyx.navigation.node.Node
+import com.bumble.appyx.navigation.store.getRetainedInstance
+import com.bumble.appyx.utils.interop.rx2.store.getRetainedDisposable
+
+class YourNodeBuilder : Builder<YourPayload>() {
+
+    override fun build(nodeContext: NodeContext, payload: YourPayload): Node {
+        // Case 1:
+        // If your type implements an rx2/rx3 Disposable,
+        // you don't need to pass a disposer:
+        val retainedFoo = nodeContext.getRetainedDisposable {
+            Foo(payload)
+        }
+
+        // Case 2:
+        // If your type doesn't implement an rx2/rx3 Disposable,
+        // you can define a custom cleanup mechanism:
+        val retainedFoo = nodeContext.getRetainedInstance(
+            factory = { Foo(payload) },
+            disposer = { it.cleanup() } // it: Foo
+        )
+
+        val view = YourNodeViewImpl()
+
+        return YourNode(
+            nodeContext = nodeContext,
+            foo = retainedFoo, 
+            view = view,
+        )
+    }
+}
+
+ + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/2.x/navigation/index.html b/2.x/navigation/index.html new file mode 100644 index 000000000..e31ca1edc --- /dev/null +++ b/2.x/navigation/index.html @@ -0,0 +1,2579 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Appyx Navigation – Overview - Appyx + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + + +
+
+
+ + + +
+ +
+ + + +
+
+ + + + +

Appyx Navigation

+

Type-safe navigation for Compose directly from code

+
    +
  • Tree-based, composable
  • +
  • Leverages the transitions and gesture-based capabilities of Appyx Interactions to build beautiful, custom navigation.
  • +
  • Use any component for navigation, whether pre-built (Appyx Components), or custom-built by you (Appyx Interactions).
  • +
+

+

Sample app

+
+ +

The above is an interactive example for Appyx Navigation. You can play with it right here in the browser (try it!). You can of course launch it on mobile and desktop too.

+

For more info and points of interest, make sure to check out:

+

» Sample app

+

Quick start

+

Quick start guide

+

Concepts

+ +

Features

+ +

Integrations

+ +

Multiplatform

+ +

Components

+
+ +

See Appyx Components for Back stack, Spotlight (pager) and other components you can use in navigation.

+

See Appyx Interactions on how to build your own components with state-based transitions and easy-to-create gesture control.

+ + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/2.x/navigation/integrations/compose-navigation/index.html b/2.x/navigation/integrations/compose-navigation/index.html new file mode 100644 index 000000000..89abe4c97 --- /dev/null +++ b/2.x/navigation/integrations/compose-navigation/index.html @@ -0,0 +1,2463 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Appyx + Jetpack Compose Navigation - Appyx + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + +

Appyx + Jetpack Compose Navigation

+

You can easily make Appyx co-exist with Jetpack Compose Navigation. This might be useful if:

+
    +
  • You want to to use some AppyxComponents for navigation in your non-Appyx app.
  • +
  • You want to migrate to Appyx gradually.
  • +
+

No special downloads are required.

+

Sample

+
/**
+ * This Composable demonstrates how to add Appyx into Jetpack Compose Navigation.
+ */
+@Composable
+fun ComposeNavigationRoot(modifier: Modifier = Modifier) {
+    val googleNavController = rememberNavController()
+    NavHost(
+        navController = googleNavController,
+        startDestination = "google-route"
+    ) {
+        composable("google-route") {
+            GoogleRoute { googleNavController.navigate("appyx-route") }
+        }
+        composable("appyx-route") {
+            AppyxRoute { googleNavController.navigate("google-route") }
+        }
+    }
+}
+
+@Composable
+internal fun GoogleRoute(
+    modifier: Modifier = Modifier,
+    onNavigateToAppyxRoute: () -> Unit
+) {
+    Column(
+        modifier = modifier.fillMaxWidth(),
+        horizontalAlignment = Alignment.CenterHorizontally
+    ) {
+        Text("Compose Navigation screen")
+        Button(onClick = { onNavigateToAppyxRoute() }) {
+            Text("Navigate to Appyx")
+        }
+    }
+}
+
+
+@Composable
+internal fun AppyxRoute(
+    onNavigateToGoogleRoute: () -> Unit
+) {
+    NodeHost(integrationPoint = LocalIntegrationPoint.current) {
+        RootNode(
+            nodeContext = it,
+            // use this in the Node / some child Node,
+            // in business logic or in the view on some button:
+            onNavigateToGoogleRoute = onNavigateToGoogleRoute 
+        )
+    }
+}
+
+ + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/2.x/navigation/integrations/di-frameworks/index.html b/2.x/navigation/integrations/di-frameworks/index.html new file mode 100644 index 000000000..3319bd0a0 --- /dev/null +++ b/2.x/navigation/integrations/di-frameworks/index.html @@ -0,0 +1,2504 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Appyx + DI frameworks - Appyx + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + +

Appyx + DI frameworks

+

Appyx + Hilt

+

We've experimented with adding Hilt support for Appyx Nodes, however the solution is not yet finalised.

+

You can track and vote on the issue here:

+
    +
  • #552 - Integration: Appyx + Hilt
  • +
+

Appyx + Kodein

+

If you'd like us to prioritise it, you can vote on the issue here:

+
    +
  • #555 - Integration: Appyx + Kodein
  • +
+

Appyx + Koin

+

If you'd like us to prioritise it, you can vote on the issue here:

+
    +
  • #554 - Integration: Appyx + Koin
  • +
+

Appyx + Dagger

+

If you're not too bothered to create the extra classes for Dagger yourself, you can make it work pretty nicely with a tree-based approach like Appyx. We have detailed this in this project's predecessor, badoo/RIBs – however we moved away from it and didn't implement a sample in the scope Appyx.

+

Appyx + Manual DI

+

It's worth mentioning that while manual DI in an unstructured project sounds like a bad idea, with a tree-scoped project structure it can be viable.

+

Benefits:

+
    +
  • Scoped DI for free.
  • +
  • The tree provides a good enough structure to make it understandable.
  • +
  • Less boilerplate than with Dagger.
  • +
+ + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/2.x/navigation/integrations/ribs/index.html b/2.x/navigation/integrations/ribs/index.html new file mode 100644 index 000000000..14191c9af --- /dev/null +++ b/2.x/navigation/integrations/ribs/index.html @@ -0,0 +1,2415 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Appyx + RIBs - Appyx + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + +

Appyx + RIBs

+

Interop classes for a gradual migration from badoo/RIBs to Appyx. Please refer to Downloads for gradle artifacts.

+

Provided classes

+
    +
  • InteropActivity
  • +
  • InteropBackPressHandler
  • +
  • InteropBuilder
  • +
  • InteropNode
  • +
  • InteropView
  • +
+ + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/2.x/navigation/integrations/rx/index.html b/2.x/navigation/integrations/rx/index.html new file mode 100644 index 000000000..0454ab220 --- /dev/null +++ b/2.x/navigation/integrations/rx/index.html @@ -0,0 +1,2455 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Appyx + RxJava - Appyx + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + +

Appyx + RxJava

+

Rx2 and Rx3 implementations are provided for the functionality below. Please refer to Downloads for gradle artifacts.

+

Connectable

+

You can use this together with the ChildAware API to set up communication between Nodes in a decoupled way:

+
interface Connectable<Input, Output> : NodeLifecycleAware {
+    val input: Relay<Input>
+    val output: Relay<Output>
+}
+
+

RetainedInstanceStore

+

Provides a singleton store to survive configuration changes. The rx2/rx3 helpers add automatic disposal on objects implementing Disposable.

+

You can find more details here: Surviving configuration change

+
import com.bumble.appyx.utils.interop.rx2.store.getRetainedDisposable
+
+class YourNodeBuilder : Builder<YourPayload>() {
+
+    override fun build(nodeContext: NodeContext, payload: YourPayload): Node {
+
+        // If your type implements an rx2/rx3 Disposable,
+        // you don't need to pass a disposer:
+        val retainedFoo = nodeContext.getRetainedDisposable {
+            Foo(payload)
+        }
+
+        return YourNode(
+            nodeContext = nodeContext,
+            foo = retainedFoo, 
+            view = view,
+        )
+    }
+}
+
+ + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/2.x/navigation/integrations/viewmodel/index.html b/2.x/navigation/integrations/viewmodel/index.html new file mode 100644 index 000000000..66485348f --- /dev/null +++ b/2.x/navigation/integrations/viewmodel/index.html @@ -0,0 +1,2413 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Appyx + ViewModel - Appyx + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + +

Appyx + ViewModel

+

We've experimented with adding ViewModel support for Appyx Nodes, however the solution is not finalised.

+

You can track and vote on the issue here:

+
    +
  • #553 - Integration: Appyx + ViewModel
  • +
+

Alternative: RetainedInstanceStore

+

Even without a direct ViewModel support, you can pretty much achieve the same by using the RetainedInstanceStore Appyx provides.

+ + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/2.x/navigation/multiplatform/index.html b/2.x/navigation/multiplatform/index.html new file mode 100644 index 000000000..c3bcb50b9 --- /dev/null +++ b/2.x/navigation/multiplatform/index.html @@ -0,0 +1,3308 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Appyx Navigation – Multiplatform - Appyx + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + + +
+
+
+ + + + + + + +
+
+ + + + +

Multiplatform

+

Supported platforms

+

badge-android +badge-jvm +badge-macos +badge-js +badge-wasm +badge-ios

+

Lifecycle

+

Multiplatform interface:

+
package com.bumble.appyx.navigation.lifecycle
+
+interface Lifecycle {
+
+    val currentState: State
+
+    val coroutineScope: CoroutineScope
+
+    fun addObserver(observer: PlatformLifecycleObserver)
+
+    fun removeObserver(observer: PlatformLifecycleObserver)
+
+    fun asFlow(): Flow<State>
+
+    enum class State {
+        INITIALIZED,
+        CREATED,
+        STARTED,
+        RESUMED,
+        DESTROYED,
+    }
+
+    enum class Event {
+        ON_CREATE,
+        ON_START,
+        ON_RESUME,
+        ON_PAUSE,
+        ON_STOP,
+        ON_DESTROY,
+        ON_ANY,
+    }
+}
+
+
    +
  • On Android, it is implemented by AndroidLifecycle, which is backed by androidx.lifecycle.
  • +
  • On other platforms, it is implemented by the corresponding PlatformLifecycleRegistry
  • +
+

Parcelable, Parcelize, RawValue

+

Multiplatform typealiases that are backed by their proper platform-specific ones.

+

E.g. on Android:

+
package com.bumble.appyx.utils.multiplatform
+
+actual typealias Parcelize = kotlinx.parcelize.Parcelize
+
+actual typealias Parcelable = android.os.Parcelable
+
+actual typealias RawValue = kotlinx.parcelize.RawValue
+
+

Node hosts

+

The root node of an Appyx navigation tree needs to be connected to the platform. This ensures that system events (lifecycle, back press, etc.) reach your components in the tree.

+

You only need to do this for the root of the tree.

+

Android

+
// Please note we are extending NodeActivity
+class MainActivity : NodeActivity() {
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        setContent {
+            YourAppTheme {
+                NodeHost(
+                    lifecycle = AndroidLifecycle(LocalLifecycleOwner.current.lifecycle),
+                    integrationPoint = appyxIntegrationPoint
+                ) {
+                    RootNode(nodeContext = it)
+                }
+            }
+        }
+    }
+}
+
+

Desktop

+
fun main() = application {
+    val events: Channel<Events> = Channel()
+    val windowState = rememberWindowState(size = DpSize(480.dp, 640.dp))
+    val eventScope = remember { CoroutineScope(SupervisorJob() + Dispatchers.Main) }
+
+    Window(
+        state = windowState,
+        onCloseRequest = ::exitApplication,
+        onKeyEvent = {
+            // See back handling section in the docs below!    
+            onKeyEvent(it, events, eventScope) 
+        },
+    ) {
+        YourAppTheme {
+            Surface(color = MaterialTheme.colorScheme.background) {
+                DesktopNodeHost(
+                    windowState = windowState,
+                    onBackPressedEvents = events.receiveAsFlow().mapNotNull {
+                        if (it is Events.OnBackPressed) Unit else null
+                    }
+                ) { 
+                    RootNode(nodeContext = it)
+                }
+            }
+        }
+    }
+}
+
+

Web

+
// Add this only when targeting Kotlin/Wasm as this method isn't exposed anywhere
+external fun onWasmReady(onReady: () -> Unit)
+
+fun main() {
+    val events: Channel<Unit> = Channel()
+    onWasmReady {
+       CanvasBasedWindow("Your app") {
+            val requester = remember { FocusRequester() }
+            var hasFocus by remember { mutableStateOf(false) }
+            var screenSize by remember { mutableStateOf(ScreenSize(0.dp, 0.dp)) }
+            val eventScope = remember { CoroutineScope(SupervisorJob() + Dispatchers.Main) }
+
+            YourAppTheme {
+                Surface(
+                    color = MaterialTheme.colorScheme.background,
+                    modifier = Modifier
+                        .fillMaxSize()
+                        .onSizeChanged { screenSize = ScreenSize(it.width.dp, it.height.dp) }
+                        .onKeyEvent {
+                            // See back handling section in the docs below!    
+                            onKeyEvent(it, events, eventScope) 
+                        }
+                        .focusRequester(requester)
+                        .focusable()
+                        .onFocusChanged { hasFocus = it.hasFocus }
+                ) {
+                    WebNodeHost(
+                        screenSize = screenSize,
+                        onBackPressedEvents = events.receiveAsFlow(),
+                    ) { 
+                        RootNode(nodeContext = it)
+                    }
+                }
+
+                if (!hasFocus) {
+                    LaunchedEffect(Unit) {
+                        requester.requestFocus()
+                    }
+                }
+            }
+        }
+    }
+}
+
+

iOS

+
val backEvents: Channel<Unit> = Channel()
+
+fun MainViewController() = ComposeUIViewController {
+    YourAppTheme {
+        IosNodeHost(
+            modifier = Modifier,
+            // See back handling section in the docs below!
+            onBackPressedEvents = backEvents.receiveAsFlow()
+        ) {
+            RootNode(
+               nodeContext = it
+            )
+        }
+    }
+}
+
+@Composable
+private fun BackButton(coroutineScope: CoroutineScope) {
+    IconButton(
+        onClick = {
+            coroutineScope.launch {
+                backEvents.send(Unit)
+            }
+        },
+        modifier = Modifier.zIndex(99f)
+    ) {
+        Icon(
+            imageVector = Icons.Default.ArrowBack,
+            tint = Color.White,
+            contentDescription = "Go Back"
+        )
+    }
+}
+
+

Back handling

+

Android

+

On Android back events are handled automatically.

+

Desktop & Web

+

In the above desktop and web examples there is a reference to an onKeyEvent method.

+

You can configure any KeyEvent to trigger a back event via the events Channel. In this example the OnBackPressed event is launched when the backspace key is pressed down:

+
private fun onKeyEvent(
+    keyEvent: KeyEvent,
+    events: Channel<Events>,
+    coroutineScope: CoroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.Main),
+): Boolean =
+    when {
+        // You can also register e.g. Key.Escape instead of BackSpace: 
+        keyEvent.type == KeyEventType.KeyDown && keyEvent.key == Key.Backspace -> {
+            coroutineScope.launch { events.send(Events.OnBackPressed) }
+            true
+        }
+
+        else -> false
+    }
+
+

iOS

+

On iOS, you can design a user interface element to enable back navigation, similar to how it's done on other platforms. +In the example mentioned earlier, we create a Composable component BackButton that includes an ArrowBack icon. +When this button is clicked, it triggers the back event through the backEvents Channel.

+

Setting up the environment for execution

+

Setting up the environment for execution

+ +

Warning

+

In order to launch the iOS target, you need a Mac with macOS to write and run iOS-specific code on simulated or real devices.

+

This is an Apple requirement.

+

The instructions here are tweaked and inspired from the Compose-Multiplatform-iOS template.

+
+

To work with this project, you need the following:

+ +

Check your environment

+

Before you start, use the KDoctor tool to ensure that your development environment is configured correctly:

+
    +
  1. +

    Install KDoctor with Homebrew:

    +
    brew install kdoctor
    +
    +
  2. +
  3. +

    Run KDoctor in your terminal:

    +
    kdoctor
    +
    +
  4. +
+

If everything is set up correctly, you'll see valid output:

+
Environment diagnose (to see all details, use -v option):
+[✓] Operation System
+[✓] Java
+[✓] Android Studio
+[✓] Xcode
+[✓] Cocoapods
+
+Conclusion:
+  ✓ Your system is ready for Kotlin Multiplatform Mobile development!
+
+

Otherwise, KDoctor will highlight which parts of your setup still need to be configured and will suggest a way to fix them.

+

The project structure

+

Open the project in Android Studio and switch the view from Android to Project to see all the files and targets belonging to the project. +The :demos module contains the sample target appyx-navigation.

+

This module follows the latest compose multiplatform project structure, with a mainApp module targeting all available platforms (Android, Desktop, iOS and Web). +There is also a companion module named iosApp with the needed glue code for iOS. There are the targets and purposes:

+

commonMain

+

This Kotlin module contains the logic common for Android, Desktop, iOS and web applications, that is, the code you share between platforms.

+

androidMain

+

This is a Kotlin module that builds into an Android application.

+

desktopMain

+

This module builds into a Desktop application.

+

iosMain

+

This is an Xcode project that builds into an iOS application.

+

wasmJsMain

+

This module builds into a Web app.

+

Run your application

+

On Android

+

To run your application on an Android emulator:

+
    +
  1. Ensure you have an Android virtual device available. Otherwise, create one.
  2. +
  3. In the list of run configurations, select demos.appyx-navigation.android.
  4. +
  5. Choose your virtual/physical device and click Run.
  6. +
+

On iOS

+

Running on a simulator

+

To run your application on an iOS simulator in Android Studio, modify the iOS run configuration:

+ +
    +
  1. In the list of run configurations, select Edit Configurations:
  2. +
  3. Navigate to iOS Application | iosApp.
  4. +
  5. Select the desired .xcworkspace file under XCode project file which can be found in /demos/appyx-navigation/iosApp/iosApp.xcworkspace.
  6. +
  7. Ensure Xcode project scheme is set to iosApp.
  8. +
  9. In the Execution target list, select your target device. Click OK.
  10. +
  11. The iosApp run configuration is now available. Click Run next to your virtual device.
  12. +
+
+

Running on a real device

+

To run the Compose Multiplatform application on a real iOS device. You'll need the following:

+
    +
  • The TEAM_ID associated with your Apple ID
  • +
  • The iOS device registered in Xcode
  • +
+
Finding your Team ID
+

In the terminal, run kdoctor --team-ids to find your Team ID. +KDoctor will list all Team IDs currently configured on your system.

+

To run the application, set the TEAM_ID:

+ +
    +
  1. In the project, navigate to the iosApp/Configuration/Config.xcconfig file.
  2. +
  3. Set your TEAM_ID.
  4. +
  5. Re-open the project in Android Studio. It should show the registered iOS device in the iosApp run configuration.
  6. +
+
+

On Desktop

+

To run the application as a JVM target on desktop:

+ +
    +
  1. In the list of run configurations, select Edit Configurations.
  2. +
  3. Click Add new configuration and select Gradle.
  4. +
  5. Set run configuration under Run.
  6. +
  7. Select the desired target under Gradle project to be executed (for example: appyx:demos:appyx-navigation:desktop).
  8. +
  9. The desktop configuration for the desired target is now available. Click Run to execute.
  10. +
+
+

On Web

+

To run the application on web:

+ +
    +
  1. In the list of run configurations, select Edit Configurations.
  2. +
  3. Click Add new configuration and select Gradle.
  4. +
  5. Decide which target you prefer and choose the appropriate task under run:
      +
    • jsBrowserDevelopmentRun for targeting Kotlin/JS.
    • +
    • wasmJsBrowserDevelopmentRun for targeting Kotlin/Wasm.
    • +
    +
  6. +
  7. Select the desired target under Gradle project to be executed (for example: appyx:demos:appyx-navigation:web).
  8. +
  9. The web configuration for the desired target is now available. Click Run to execute.
  10. +
+
+ + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/2.x/navigation/quick-start/index.html b/2.x/navigation/quick-start/index.html new file mode 100644 index 000000000..5a77689bc --- /dev/null +++ b/2.x/navigation/quick-start/index.html @@ -0,0 +1,2754 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Appyx Navigation – Quick start guide - Appyx + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + + +
+
+
+ + + + + + + +
+
+ + + + +

Quick start guide

+

You can check out Composable navigation, which explains the concepts you'll encounter in this guide.

+

The scope of this guide

+

The steps below will cover:

+
    +
  1. Integrating Appyx into your project
  2. +
  3. Creating a very simple Node hierarchy
  4. +
  5. We'll use a simple back stack for navigation
  6. +
  7. We'll see how to change transition animations easily
  8. +
+

This should be enough to get you started as a rudimentary application structure.

+

1. Add Appyx to your project

+

Please refer to the Downloads to find the relevant artifacts +(also depending on whether you're doing this in a multiplatform project or not).

+

For the scope of this quick start guide, you will need to add dependencies for:

+ +

2. Create a root Node

+
class RootNode(
+    nodeContext: NodeContext
+) : LeafNode(
+    nodeContext = nodeContext
+) {
+    @Composable
+    override fun Content(modifier: Modifier) {
+        Text("Hello world!")
+    }
+}
+
+

3. Connect to your platform

+

Plug your root node into your platform: Multiplatform | Node hosts.

+

Use the multiplatform imports Appyx provides in any of the code snippets from now on, such as:

+
import com.bumble.appyx.utils.multiplatform.Parcelable
+import com.bumble.appyx.utils.multiplatform.Parcelize
+import com.bumble.appyx.utils.multiplatform.RawValue
+
+

4. Define children

+

A single leaf node isn't all that interesting. Let's add some children to the root!

+

First, let's define the possible set of children using a sealed class. We'll refer them via these navigation targets:

+
import com.bumble.appyx.utils.multiplatform.Parcelable
+import com.bumble.appyx.utils.multiplatform.Parcelize
+
+/**
+ * You can create this class inside the body of RootNode
+ * 
+ * Note: You must apply the 'kotlin-parcelize' plugin to use @Parcelize
+ * https://developer.android.com/kotlin/parcelize
+ */
+sealed class NavTarget : Parcelable {
+    @Parcelize
+    object Child1 : NavTarget()
+
+    @Parcelize
+    object Child2 : NavTarget()
+
+    @Parcelize
+    object Child3 : NavTarget()
+}
+
+

Next, let's modify RootNode so it extends Node instead of LeafNode:

+
class RootNode(
+    nodeContext: NodeContext
+) : Node<NavTarget>(
+    appyxComponent = TODO("We will come back to this in Step 5"),
+    nodeContext = nodeContext
+) {
+
+

Node expects us to implement the abstract method buildChildNode. This is how we relate navigation targets to actual children. Let's use these helper methods to define some placeholders for the time being – we'll soon make them more appealing:

+
override fun buildChildNode(reference: NavTarget, nodeContext: NodeContext): Node =
+    when (reference) {
+        NavTarget.Child1 -> node(nodeContext) { Text(text = "Placeholder for child 1") }
+        NavTarget.Child2 -> node(nodeContext) { Text(text = "Placeholder for child 2") } 
+        NavTarget.Child3 -> node(nodeContext) { Text(text = "Placeholder for child 3") }
+    }
+
+

Great! With this mapping created, we can now just refer to children using the sealed class elements, and Appyx will be able to relate them to other nodes.

+

5. Add a back stack

+

The project wouldn't compile just yet. Node expects us to pass an instance of an AppyxComponent – the main control structure in any case when we want to add children. No need to worry now – for simplicity, let's just go with a simple BackStack implementation here:

+
class RootNode(
+    nodeContext: NodeContext,
+    private val backStack: BackStack<NavTarget> = BackStack(
+        model = BackStackModel(
+            initialTarget = NavTarget.Child1,
+            savedStateMap = nodeContext.savedStateMap,        
+        ),
+        visualisation = { BackStackFader(it) }
+    )
+) : Node<NavTarget>(
+    nodeContext = nodeContext,
+    appyxComponent = backStack // pass it here
+) {
+
+

With this simple addition we've immediately gained a lot of power! Now we can use the back stack's API to add, replace, pop children with operations like:

+
backStack.push(NavTarget.Child2)    // will add a new navigation target to the end of the stack and make it active 
+backStack.replace(NavTarget.Child3) // will replace the currently active child
+backStack.pop()                     // will remove the currently active child and restore the one before it
+
+

Since we passed the back stack to the Node, all such changes will be immediately reflected. We only need to add it to the composition:

+
@Composable
+override fun Content(modifier: Modifier) {
+    Column(
+        modifier = modifier
+    ) {
+        // Let's include the elements of our component into the composition
+        AppyxNavigationContainer(
+            appyxComponent = backStack,
+            modifier = Modifier.weight(0.9f)
+        )
+
+        // Let's also add some controls so we can test it
+        Row(
+            horizontalArrangement = Arrangement.Center,
+            modifier = Modifier
+                .fillMaxWidth()
+                .weight(0.1f)
+        ) {
+            TextButton(onClick = { backStack.push(NavTarget.Child1) }) {
+                Text(text = "Push child 1")
+            }
+            TextButton(onClick = { backStack.push(NavTarget.Child2) }) {
+                Text(text = "Push child 2")
+            }
+            TextButton(onClick = { backStack.push(NavTarget.Child3) }) {
+                Text(text = "Push child 3")
+            }
+            TextButton(onClick = { backStack.pop() }) {
+                Text(text = "Pop")
+            }
+        }
+    }
+}
+
+

6. Visualisations, transitions

+

Adding a different visual representation of our model and its state changes is as simple as changing this:

+
visualisation = { BackStackFader(it) }
+
+

to this:

+
visualisation = { BackStackSlider(it) }
+
+

or this:

+
visualisation = { BackStackParallax(it) }
+
+

or this:

+
visualisation = { BackStack3D(it) }
+
+

Be sure to check the Back stack documentation where you can also find live previews of the above visualisations.

+

Need something more custom?

+
    +
  1. You can create your own visualisations.
  2. +
  3. Instead of a back stack, you can also find other Components in the library, or you can create your own.
  4. +
+

7. Proper child nodes

+

As a last step, let's replace at least one of the child placeholders with another proper node.

+

Let's create a dedicated class:

+
class SomeChildNode(
+    nodeContext: NodeContext
+) : LeafNode(
+    nodeContext = nodeContext
+) {
+    @Composable
+    override fun Content(modifier: Modifier) {
+        Text("This is SomeChildNode")
+    }
+}
+
+

Now we can update the buildChildNode method in RootNode so that the target Child3 refers to this node. It should work out of the box:

+
override fun buildChildNode(reference: NavTarget, nodeContext: NodeContext): Node =
+    when (reference) {
+        NavTarget.Child1 -> node(nodeContext) { Text(text = "Placeholder for child 1") }
+        NavTarget.Child2 -> node(nodeContext) { Text(text = "Placeholder for child 2") } 
+        NavTarget.Child3 -> SomeChildNode(nodeContext)
+    }
+
+

What's next?

+

Congrats, you've just built your first Appyx tree!

+

You can repeat the same pattern and make any embedded children also a Node with their own children, navigation models, and transitions. As complexity grows, generally you would:

+
    +
  1. Have a LeafNode
  2. +
  3. At some point make it a Node and add children to it
  4. +
  5. At some point extract the increasing complexity from a placeholder to another LeafNode
  6. +
  7. Repeat the same on children, go to 1.
  8. +
+

Further reading

+
    +
  • Check out Model-driven navigation how to take your navigation to the next level
  • +
  • You can (and probably should) also extract local business logic, the view, any any other components into separate classes and Plugins.
  • +
+ + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/2.x/navigation/sample-app/index.html b/2.x/navigation/sample-app/index.html new file mode 100644 index 000000000..5dbd5bbc0 --- /dev/null +++ b/2.x/navigation/sample-app/index.html @@ -0,0 +1,2661 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Appyx Navigation – Sample app - Appyx + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + + +
+
+
+ + + + + + + +
+
+ + + + +

Appyx Navigation – Sample app

+
+ +

You can try out the app right here in the browser. The above example is interactive!

+

Multiplatform

+

Overview

+

Check out the :demos:appyx-navigation module in the project (see on GitHub) to launch platform-specific variants.

+

Points of interest

+

You can experiment with the following in the sample app:

+

Gestures & transitions

+
    +
  • Swipe left-right in the cake pager
  • +
  • Tap on any cake to enter or exit hero mode
  • +
  • Swipe left-right while in hero mode
  • +
  • Swipe up-down between hero and list mode for a gradual transition
  • +
+

Remote triggered transitions

+

In the app:

+
    +
  • Go to Home, tap the Go to a random cake button
  • +
+

Trigger via deep link (basic):

+
    +
  • Android: adb shell am start -a "android.intent.action.VIEW" -d "appyx://randomcake"
  • +
  • iOS: xcrun simctl openurl booted 'appyx://randomcake'
  • +
+

Trigger via deep link (advanced, waits for user to finish logging in):

+
    +
  1. Go to Profile in the bottom menu, tap Log out
  2. +
  3. Close the app
  4. +
  5. Trigger the deep link
      +
    • Android: adb shell am start -a "android.intent.action.VIEW" -d "appyx://randomcake-wait"
    • +
    • iOS: xcrun simctl openurl booted 'appyx://randomcake-wait'
    • +
    +
  6. +
  7. Tap Log in and see the deep link action resume
  8. +
+

Scoped dependencies

+

+

The cart object lives inside the MainNode. It's the same instance passed to all child nodes in the tree, but is destroyed when logging out and logging back in. Try:

+
    +
  • Add items to cart, log out from Profile, log back in
  • +
  • Add items to cart, go to checkout, manipulate cart, finish checkout flow
  • +
+

Other

+
    +
  • Resize window (desktop or standalone launched web) to see the Material 3 Navigation Bar automatically switches to a Navigation Rail depending on screen size.
  • +
+

Relevant pages from the documentation

+

Concepts

+

Topics on how the sample app is put together, and how navigation works in it:

+ +

Features

+

Library features used in the app:

+ +

What's behind the custom component

+

The cake slider / hero transition is a custom component. Check these out on how it's put together:

+ + + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/2.x/releases/2.0.0-alpha10/index.html b/2.x/releases/2.0.0-alpha10/index.html new file mode 100644 index 000000000..5838727aa --- /dev/null +++ b/2.x/releases/2.0.0-alpha10/index.html @@ -0,0 +1,2562 @@ + + + + + + + + + + + + + + + + + + + + + + + + + 2.0.0-alpha10 – Migration guide - Appyx + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + + +
+
+
+ + + + + + + +
+
+ + + + +

2.0.0-alpha10 – Migration guide

+

Artifact id changes

+
// Material3 helpers
+-implementation("com.bumble.appyx:material3-android:$version")
++implementation("com.bumble.appyx:utils-material3-android:$version")
+
+// KSP (only used for custom Visualisations)
+-ksp("com.bumble.appyx:mutable-ui-processor:{latest version}")
++ksp("com.bumble.appyx:appyx-processor:{latest version}")
+
+

Package changes

+
// Moved all classes:
+-com.bumble.appyx.navigation.integrationpoint.*
++com.bumble.appyx.navigation.integration.*
+
+// Moved class:
+-com.bumble.appyx.navigation.navigation.UpNavigationHandler
++com.bumble.appyx.navigation.integration.UpNavigationHandler
+
+// Removed duplicate typealias and moved to utils:
+-import com.bumble.appyx.interactions.core.state.SavedStateMap
+-import com.bumble.appyx.navigation.state.SavedStateMap
++import com.bumble.appyx.utils.multiplatform.SavedStateMap
+
+

Rename Node & ParentNode

+
class YourNode(
+    /*...*/
+-) : Node(
++) : LeafNode(
+    /*...*/
+) {
+}
+
+
class YourNode(
+    /*...*/
+-) : ParentNode<T>(
++) : Node<T>(
+    /*...*/
+) {
+}
+
+

Rename View composable

+
class YourNode(
+    /*...*/
+) : Node<T> {
+
+    @Composable
+-    override fun View(modifier: Modifier) {
++    override fun Content(modifier: Modifier) {
+    }
+}
+
+

Rename ambiguous AppyxComponent composables

+

When used in the scope of Appyx Navigation:

+
-import com.bumble.appyx.navigation.composable.AppyxComponent
++import com.bumble.appyx.navigation.composable.AppyxNavigationContainer
+
+class YourNode(
+    /*...*/
+) : Node<T> {
+
+    @Composable
+    override fun Content(modifier: Modifier) {
+-        AppyxComponent(
++        AppyxNavigationContainer(
+            appyxComponent = yourComponent,
+            modifier = modifier
+        )   
+    }
+}
+
+

When used in the scope of Appyx Interactions:

+
-import com.bumble.appyx.interactions.core.AppyxComponent
++import com.bumble.appyx.interactions.core.AppyxInteractionsContainer
+
+@Composable
+fun SomeComposable() {
+-    AppyxComponent(
++    AppyxInteractionsContainer(
+        appyxComponent = yourComponent,
+        screenWidthPx = TODO(),
+        screenHeightPx = TODO(),
+        clipToBounds = false,
+        modifier = Modifier,
+        element = { elementUiModel ->
+            YourElementComposable(
+                elementUiModel = elementUiModel,
+                modifier = elementUiModel.modifier
+            )
+        }
+    )
+}
+
+

Rename BuildContext & buildContext

+
-import com.bumble.appyx.navigation.modality.BuildContext
++import com.bumble.appyx.navigation.modality.NodeContext
+
+class YourNode(
+-    buildContext: BuildContext,
++    nodeContext: NodeContext,
+    /*...*/
+) : Node<NavTarget> {
+-    buildContext = buildContext
++    nodeContext = nodeContext
+    /*...*/
+) {
+}
+
+

Rename resolve

+
class YourNode(
+    /*...*/
+) : Node<NavTarget> {
+    /*...*/
+) {
+    sealed class NavTarget : Parcelable {
+        /*...*/
+    }
+
+-    override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node =
++    override fun buildChildNode(navTarget: NavTarget, nodeContext: NodeContext): Node =
+        when (navTarget) {
+            TODO()
+        }
+
+

 

+

Changes unlikely to affect you directly

+

Changes to NodeView

+

You only need to care about these changes if you separated out your view to a class implementing the NodeView interface.

+

If you implemented your @Composable override fun Content(modifier: Modifier) directly in your Node, you don't need to do anything.

+
    +
  1. +

    We've dropped generics from NodeView interface

    +
    -interface NodeView<NavTarget : Any>
    ++interface NodeView
    +
    +

    If you had an implementation of this interface, drop the generic from your class too.

    +
  2. +
  3. +

    We've unified NodeView and ParentNodeView

    +

    If you used ParentNodeView, just replace the usage with NodeView.

    +
  4. +
  5. +

    We've unified EmptyNodeView and EmptyParentNodeView

    +

    If you used EmptyParentNodeView, just replace the usage with EmptyNodeView.

    +
  6. +
+

Swap the order of TargetUiState, MutableUiState

+

You only need to do this if you implemented your own Visualisation. If you only relied on the ones supplied by the library, you don't need to do anything.

+
class SomeVisualisation(
+    /*...*/
+-) : BaseVisualisation<T, State<T>, MutableUiState, TargetUiState>(
++) : BaseVisualisation<T, State<T>, TargetUiState, MutableUiState>(
+
+

KSP generated method name change

+

You only need to do this if you implemented your own Visualisation. If you only relied on the ones supplied by the library, you don't need to do anything.

+
override fun mutableUiStateFor(
+    uiContext: UiContext,
+    targetUiState: TargetUiState
+): MutableUiState =
+-    targetUiState.toMutableState(uiContext)
++    targetUiState.toMutableUiState(uiContext)
+
+ + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/2.x/releases/2.0.0/index.html b/2.x/releases/2.0.0/index.html new file mode 100644 index 000000000..b93a8cb54 --- /dev/null +++ b/2.x/releases/2.0.0/index.html @@ -0,0 +1,2407 @@ + + + + + + + + + + + + + + + + + + + + + + + + + 2.0.0-alpha10 -> 2.0.0 – Migration guide - Appyx + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + +

2.0.0-alpha10 -> 2.0.0 – Migration guide

+

Package changes

+
// Moved all classes:
+-com.bumble.appyx.interactions.core.gesture.*
+-com.bumble.appyx.interactions.ui.gesture.*
++com.bumble.appyx.interactions.gesture.*
+
+// Moved specific classes:
+-com.bumble.appyx.interactions.AppyxInteractionContainer
++com.bumble.appyx.interactions.composable.AppyxInteractionContainer
+
+-com.bumble.appyx.interactions.GestureReferencePoint
++com.bumble.appyx.interactions.gesture.GestureReferencePoint
+
+-com.bumble.appyx.interactions.Element
+-com.bumble.appyx.interactions.Elements
++com.bumble.appyx.interactions.model.Element
++com.bumble.appyx.interactions.model.Elements
+
+
+// Moved all remaining packages in core to interactions root:
+-com.bumble.appyx.interactions.core.*
++com.bumble.appyx.interactions.*
+
+

Fixed canHandeBackPress typo

+
interface AppyxComponent /*...*/ {
+    /*...*/
+-    fun canHandeBackPress(): StateFlow<Boolean>
++    fun canHandleBackPress(): StateFlow<Boolean>
+
+

Moved Builder, SimpleBuilder, and Interactor

+

If you were depending on these classes being part of appyx-navigation, you’ll now need to get them from "com.bumble.appyx:utils-ribs-helpers:$version".

+
+    implementation("com.bumble.appyx:utils-ribs-helpers:$version")
+
+-    import com.bumble.appyx.navigation.builder.Builder
++    import com.bumble.appyx.utils.ribshelpers.builder.Builder
+
+-    import com.bumble.appyx.navigation.builder.SimpleBuilder
++    import com.bumble.appyx.utils.ribshelpers.builder.SimpleBuilder
+
+-    import com.bumble.appyx.navigation.clienthelper.interactor.Interactor
++    import com.bumble.appyx.utils.ribshelpers.interactor.Interactor
+
+ + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/2.x/releases/changelog/index.html b/2.x/releases/changelog/index.html new file mode 100644 index 000000000..ecb617e21 --- /dev/null +++ b/2.x/releases/changelog/index.html @@ -0,0 +1,2341 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Changelog - Appyx + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + +

Changelog

+ +

../../CHANGELOG.md

+ + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/2.x/releases/downloads/index.html b/2.x/releases/downloads/index.html new file mode 100644 index 000000000..0dd964a23 --- /dev/null +++ b/2.x/releases/downloads/index.html @@ -0,0 +1,2842 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Downloads - Appyx + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + + +
+
+
+ + + + + + + +
+
+ + + + +

Downloads

+

Latest version

+

Maven Central

+

Repository

+
repositories {
+    mavenCentral()
+}
+
+

Appyx Navigation

+

Adding the gradle dependency in a non-multiplatform project:

+
dependencies {
+    // Platform-specific (pick the right one for your platform)
+    implementation("com.bumble.appyx:appyx-navigation-android:$version")
+    implementation("com.bumble.appyx:appyx-navigation-desktop:$version")
+    implementation("com.bumble.appyx:appyx-navigation-js:$version")
+
+    // For iOS, you need separate dependencies for Simulator, x86 & Arm
+    implementation("com.bumble.appyx:appyx-navigation-iossimulatorarm64:$version")
+    implementation("com.bumble.appyx:appyx-navigation-iosx64:$version")
+    implementation("com.bumble.appyx:appyx-navigation-iosarm64:$version")
+}
+
+

Adding the gradle dependency in a multiplatform project:

+
kotlin {
+    sourceSets {
+        val commonMain by getting {
+            dependencies {
+                implementation("com.bumble.appyx:appyx-navigation:$version")
+            }
+        }
+    }
+}
+
+

Appyx Interactions

+

Adding the gradle dependency in a non-multiplatform project:

+
dependencies {
+    // Platform-specific (pick the right one for your platform)
+    implementation("com.bumble.appyx:appyx-interactions-android:$version")
+    implementation("com.bumble.appyx:appyx-interactions-desktop:$version")
+    implementation("com.bumble.appyx:appyx-interactions-js:$version")
+
+    // For iOS, you need separate dependencies for Simulator, x86 & Arm
+    implementation("com.bumble.appyx:appyx-interactions-iossimulatorarm64:$version")
+    implementation("com.bumble.appyx:appyx-interactions-iosx64:$version")
+    implementation("com.bumble.appyx:appyx-interactions-iosarm64:$version")
+}
+
+

Adding the gradle dependency in a multiplatform project:

+
kotlin {
+    sourceSets {
+        val commonMain by getting {
+            dependencies {
+                implementation("com.bumble.appyx:appyx-interactions:$version")
+            }
+        }
+    }
+}
+
+

Appyx Components

+

Back stack

+

Adding the gradle dependency in a non-multiplatform project:

+
dependencies {
+    // Platform-specific (pick the right one for your platform)
+    implementation("com.bumble.appyx:backstack-android:$version")
+    implementation("com.bumble.appyx:backstack-desktop:$version")
+    implementation("com.bumble.appyx:backstack-js:$version")
+
+    // For iOS, you need separate dependencies for Simulator, x86 & Arm
+    implementation("com.bumble.appyx:backstack-iossimulatorarm64:$version")
+    implementation("com.bumble.appyx:backstack-iosx64:$version")
+    implementation("com.bumble.appyx:backstack-iosarm64:$version")
+}
+
+

Adding the gradle dependency in a multiplatform project:

+
kotlin {
+    sourceSets {
+        val commonMain by getting {
+            dependencies {
+                // Use api, not implementation!
+                api("com.bumble.appyx:backstack:$version")
+            }
+        }
+
+    }
+}
+
+

Spotlight

+

Adding the gradle dependency in a non-multiplatform project:

+
dependencies {
+    // Platform-specific (pick the right one for your platform)
+    implementation("com.bumble.appyx:spotlight-android:$version")
+    implementation("com.bumble.appyx:spotlight-desktop:$version")
+    implementation("com.bumble.appyx:spotlight-js:$version")
+
+    // For iOS, you need separate dependencies for Simulator, x86 & Arm
+    implementation("com.bumble.appyx:spotlight-iossimulatorarm64:$version")
+    implementation("com.bumble.appyx:spotlight-iosx64:$version")
+    implementation("com.bumble.appyx:spotlight-iosarm64:$version")
+}
+
+

Adding the gradle dependency in a multiplatform project:

+
kotlin {
+    sourceSets {
+        val commonMain by getting {
+            dependencies {
+                // Use api, not implementation!
+                api("com.bumble.appyx:spotlight:$version")
+            }
+        }
+
+    }
+}
+
+

Utils and interop with other libraries

+

Material 3 support

+

Support for Navigation bar, Navigation rail to use easily together with Appyx Navigation.

+

See more in Material 3 support.

+

Adding the gradle dependency in a non-multiplatform project:

+
dependencies {
+    // Platform-specific (pick the right one for your platform)
+    implementation("com.bumble.appyx:utils-material3-android:$version")
+    implementation("com.bumble.appyx:utils-material3-desktop:$version")
+    implementation("com.bumble.appyx:utils-material3-js:$version")
+    implementation("com.bumble.appyx:utils-material3-iosarm64:$version")
+    implementation("com.bumble.appyx:utils-material3-iossimulatorarm64:$version")
+    implementation("com.bumble.appyx:utils-material3-iosx64:$version")
+}
+
+

Adding the gradle dependency in a multiplatform project:

+
kotlin {
+    sourceSets {
+        val commonMain by getting {
+            dependencies {
+                // Use api, not implementation!
+                api("com.bumble.appyx:utils-material3:$version")
+            }
+        }
+    }
+}
+
+

RxJava 2

+
dependencies {
+    // Optional support for RxJava 2/3
+    implementation("com.bumble.appyx:utils-interop-rx2:$version")
+}
+
+

RxJava 3

+
dependencies {
+    implementation("com.bumble.appyx:utils-interop-rx3:$version")
+}
+
+

badoo/RIBs

+
repositories {
+    // Don't forget to add this, since badoo/RIBs is hosted on jitpack:
+    maven(url = "https://jitpack.io")
+}
+
+dependencies {
+    implementation("com.bumble.appyx:utils-interop-ribs:$version")
+}
+
+

RIBs like helpers

+

Adds client code helper classes like Builder, SimpleBuilder, and Interactor

+
dependencies {
+    implementation("com.bumble.appyx:utils-ribs-helpers:$version")
+}
+
+

Testing

+
// Test rules and utility classes for testing on Android
+debugImplementation("com.bumble.appyx:utils-testing-ui-activity:$version")
+androidTestImplementation("com.bumble.appyx:utils-testing-ui:$version")
+
+// Utility classes for unit testing
+testImplementation("com.bumble.appyx:utils-testing-unit-common:$version")
+
+// Test rules and utility classes for unit testing using JUnit4
+testImplementation("com.bumble.appyx:utils-testing-junit4:$version")
+
+// Test extensions and utility classes for unit testing using JUnit5
+testImplementation("com.bumble.appyx:utils-testing-junit5:$version")
+
+ + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/404.html b/404.html new file mode 100644 index 000000000..42664b45a --- /dev/null +++ b/404.html @@ -0,0 +1,2277 @@ + + + + + + + + + + + + + + + + + + + + + + + Appyx + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ +

404 - Not found

+ +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/apps/childaware/index.html b/apps/childaware/index.html new file mode 100644 index 000000000..3fbff75f9 --- /dev/null +++ b/apps/childaware/index.html @@ -0,0 +1,2500 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ChildAware API - Appyx + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + +

ChildAware API

+

The framework includes the ChildAware interface which comes with a powerful API.

+

It allows you to scope communication with (or between) dynamically available child nodes easily.

+

Baseline

+

In the next examples:

+
    +
  1. Let's assume that SomeNode can host multiple child nodes: Child1, Child2, etc.
  2. +
  3. SomeInteractor belongs to SomeNode and is passed as a Plugin + to it
  4. +
  5. SomeInteractor extends the Interactor helper class from the framework:
      +
    • It implements NodeLifecycleAware, which makes sure it will receive the onCreate callback + from the framework
    • +
    • It implements ChildAware, which unlocks the DSL we'll see in the following + snippets
    • +
    +
  6. +
+

Single child scenario

+
import androidx.lifecycle.Lifecycle
+import com.bumble.appyx.core.children.whenChildAttached
+import com.bumble.appyx.core.children.whenChildrenAttached
+import com.bumble.appyx.core.clienthelper.interactor.Interactor
+
+
+class SomeInteractor : Interactor<SomeNode>() {
+
+    override fun onCreate(lifecycle: Lifecycle) {
+        lifecycle.subscribe(onCreate = {
+
+            // This lambda is executed every time a node of type Child1Node is attached:
+            whenChildAttached { commonLifecycle: Lifecycle, child1: Child1Node ->
+                // TODO:
+                //  - establish communication with child1 
+                //  - use commonLifecycle for scoping 
+                //  - it will be capped by the lifecycles of child1 and the parent
+            }
+        })
+    }
+}
+
+

Multiple children

+
import androidx.lifecycle.Lifecycle
+import com.bumble.appyx.core.children.whenChildAttached
+import com.bumble.appyx.core.children.whenChildrenAttached
+import com.bumble.appyx.core.clienthelper.interactor.Interactor
+
+
+class SomeInteractor : Interactor<SomeNode>() {
+
+    override fun onCreate(lifecycle: Lifecycle) {
+        lifecycle.subscribe(onCreate = {
+
+            // This lambda is executed every time these two nodes are attached at the same time:
+            whenChildrenAttached { commonLifecycle: Lifecycle, child1: Child1Node, child2: Child2Node ->
+                // TODO
+                //  - establish communication between child1 & child2 
+                //  - use commonLifecycle for scoping
+                //  - it will be capped by the lifecycles of child1, child2 and the parent
+            }
+        })
+    }
+}
+
+ + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/apps/configuration/index.html b/apps/configuration/index.html new file mode 100644 index 000000000..eb07c3595 --- /dev/null +++ b/apps/configuration/index.html @@ -0,0 +1,2460 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Configuration changes - Appyx + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + +

Configuration change

+

To retain objects during configuration change you can use the RetainedInstanceStore class.

+

How does it work?

+

The RetainedInstanceStore stores the objects within a singleton. The node manages whether the content should be removed by checking whether the Activity is being recreated due to a configuration change or not.

+

These are the following scenarios: +- If the Activity is recreated: the retained instance is returned instead of a new instance. +- If the Activity is destroyed: the retained instance is removed and disposed.

+

Example

+

Here is an example of how you can use the RetainedInstanceStore:

+
import com.bumble.appyx.core.builder.Builder
+import com.bumble.appyx.core.modality.BuildContext
+import com.bumble.appyx.core.node.Node
+import com.bumble.appyx.core.store.getRetainedInstance
+import com.bumble.appyx.interop.rx2.store.getRetainedDisposable
+
+class RetainedInstancesBuilder : Builder<String>() {
+
+    override fun build(buildContext: BuildContext, payload: String): Node {
+        val retainedNonDisposable = buildContext.getRetainedInstance(
+            factory = { NonDisposableClass(payload) },
+            disposer = { feature.cleanUp() }
+        ) 
+        val retainedFeature = buildContext.getRetainedDisposable {
+            RetainedInstancesFeature(payload)
+        }
+
+        val view = RetainedInstancesViewImpl()
+        val interactor = RetainedInstancesInteractor(
+            feature = retainedFeature,
+            nonDisposable = retainedNonDisposable,
+            view = view
+        )
+
+        return RetainedInstancesNode(
+            buildContext = buildContext,
+            view = view,
+            plugins = listOf(interactor)
+        )
+    }
+}
+
+ + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/apps/lifecycle/index.html b/apps/lifecycle/index.html new file mode 100644 index 000000000..17ef3957b --- /dev/null +++ b/apps/lifecycle/index.html @@ -0,0 +1,2487 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Lifecycle - Appyx + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + +

Lifecycle

+

Nodes have their own lifecycles, directly using the related classes of androidx.lifecycle.

+

Capping

+

No node can be in a higher lifecycle state than any of its parents or the Android Activity it lives in.

+

On-screen & off-screen

+

NavModel controls which children should be rendered on the screen and which should not with NavModel.screenState. +The behaviour is customisable in BaseNavModel via OnScreenStateResolver.

+

When a NavElement of the node is marked as on-screen, its lifecycle follows the parent node's lifecycle. +The rendering status does not affect it – the node might not be added to Compose view and still be in a RESUMED state.

+

When a NavElement of the node is marked as off-screen, the following might happen:

+
    +
  • Its lifecycle is capped with CREATED (or STOPPED) in case of ChildEntry.KeepMode.KEEP.
  • +
  • The node is destroyed and its state is saved in case of ChildEntry.KeepMode.SUSPEND.
  • +
+

ChildEntry.KeepMode settings can be configured for each ParentNode individually or globally via Appyx.defaultChildKeepMode.

+

When a node is removed completely from NavModel, it will be in DESTROYED state.

+

Lifecycle changes

+

The lifecycle state can be affected by:

+
    +
  • The NavModel of the parent (adding or removing child Nodes and changing their on-screen status)
  • +
  • The parent's lifecycle state capping its children (transitive in the tree)
  • +
  • Android lifecycle (Activity) capping the whole tree
  • +
+

Back stack node lifecycle

+

An example demonstrating the above:

+

+

Note that NavModels might have their slight differences (e.g. whether their operations remove a Node only from the view, or completely destroy it).

+

In the case of the back stack:

+
    +
  • The Push operation adds a new element and stashes the currently active one – the stashed one will be removed from the view & STOPPED
  • +
  • The Pop operation removes an element, the child Node will be DESTROYED
  • +
+ + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/apps/plugins/index.html b/apps/plugins/index.html new file mode 100644 index 000000000..0913926af --- /dev/null +++ b/apps/plugins/index.html @@ -0,0 +1,2580 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Plugins - Appyx + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + +

Plugins

+

Keeping extra concerns out of Node

+

Nodes are meant to be simple structural elements, and should be kept lean.

+

To keep the framework agnostic of any specific approach / pattern you want to use, there aren't any fixed parts. Rather, the Node offers an extension point using Plugins in its constructor:

+
abstract class Node(
+    buildContext: BuildContext,
+    val view: NodeView = EmptyNodeView,
+    plugins: List<Plugin> = emptyList() // <--
+)
+
+

So what is a Plugin?

+

A Plugin is an empty interface extended by many actual ones:

+
interface Plugin
+
+

Plugins

+ +
interface NodeLifecycleAware : Plugin {
+    fun onCreate(lifecycle: Lifecycle) {}
+}
+
+fun interface Destroyable : Plugin {
+    fun destroy()
+}
+
+

Component level plugins

+

Sometimes you need to grab a reference to the component as a whole, either as an interface, or its implementation, the Node.

+

This will come especially handy when working with workflows.

+
interface NodeAware : Plugin {
+    val node: Node<*>
+
+    fun init(node: Node<*>) {}
+}
+
+

There are helper classes found in the library, so you don't have to implement the above interfaces, you can just use delegation:

+
class SomeClass(
+    private val nodeAware: NodeAware = NodeAwareImpl()
+) : NodeAware by nodeAware {
+
+    fun foo() {
+        // [node] is an automatically available property coming from the NodeAware interface
+        // the reference is automatically set for you by the framework + the NodeAwareImpl class
+        // so you can use it right away:
+        node.doSomething()
+    }
+}
+
+

⚠️ Note: the reference to node is set by Node automatically, and isn't available immediately after constructing your object, but only after the construction of the Node itself.

+ +

In case if you need to control navigation behaviour, you can use these plugins:

+
interface UpNavigationHandler : Plugin {
+    fun handleUpNavigation(): Boolean = false
+}
+
+interface BackPressHandler : Plugin {
+    val onBackPressedCallback: OnBackPressedCallback? get() = null
+}
+
+

UpNavigationHandler controls Node.navigateUp behaviour and allows to intercept its invocation.

+

BackPressHandler controls device back press behaviour via androidx.activity.OnBackPressedCallback. +You can read more about it here.

+

⚠️ Note: OnBackPressedCallback are invoked in the following order: +1. From children to parents. Render order of children matters! The last rendered child will be the first to handle back press. +2. Direct order of plugins within a node. Plugins are invoked in order they appears in Node(plugins = ...) before the NavModel.

+

Using Plugins

+

All plugins are designed to have empty {} default implementations (or other sensible defaults when a return value is defined), so it's convenient to implement them only if you need.

+

Don't forget to pass your Plugins to your Node:

+
internal class MyNode(
+    // ...
+    plugins: List<Plugins> = emptyList()
+    // ...
+) : Node<Nothing>(
+    // ...
+    plugins = plugins
+    // ...
+)
+
+

⚠️ Note: plugins is a List, as the order matters here. All Plugin instances are invoked in the order they appear in the list.

+ + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/apps/structure/index.html b/apps/structure/index.html new file mode 100644 index 000000000..82330bfb5 --- /dev/null +++ b/apps/structure/index.html @@ -0,0 +1,2548 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Structuring your app navigation - Appyx + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + +

Structuring your app navigation

+

As seen in Composable navigation, you can make NavModels composable.

+

To achieve this, Appyx offers the Node class as the structural element.

+

Node illustration

+

In many of the examples you'll see this panel as an illustration of a very simple Node – it has some local state (id, colour, and a counter).

+

+

If you launch the sample app in the :app module, you can also change its state (colour) by tapping it. Its counter is stepped automatically. This is to illustrate that it has its own state, persisted and restored.

+

Node overview

+

You can think of a Node as a standalone component with:

+
    +
  • Its own simplified lifecycle
  • +
  • State restoration
  • +
  • A @Composable view
  • +
  • Business logic that's kept alive even when the view isn't added to the composition
  • +
  • The ability to host generic Plugins to extract extra concerns without enforcing any particular architectural pattern
  • +
+

Parent nodes, child nodes

+

ParentNodes can have other Nodes as children. This means you can represent your whole application as a tree of Appyx nodes.

+

+

You can go as granular or as high-level as it fits you. This allows to keep the complexity low in individual Nodes by extracting responsibilities to children, as well as composing other components to build more complex functionality.

+

Composable navigation

+

+

Nodes offer the structure – NavModels add dynamism to it.

+

Read more in Composable navigation

+

Lifecycle

+

Nodes have their own lifecycles, directly using the related classes of androidx.lifecycle.

+

Read more in Lifecycle

+

ChildAware API

+

React to dynamically added child nodes in the tree: ChildAware API

+

Summary

+

A summary of Appyx's approach to structuring applications:

+
    +
  • Compose your app out of Nodes with their own lifecycles and state
  • +
  • Navigation is local, composed of individual pieces of NavModels
  • +
  • Navigation is stateful
  • +
  • Navigation is unit-testable
  • +
  • You're free to implement your own navigable components by utilising NavModels
  • +
  • Avoid global navigation concerns, like shared modules needing to know about the application, or the application needing to know about all its possible modules
  • +
+ + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/assets/favicon.svg b/assets/favicon.svg new file mode 100644 index 000000000..a8351167e --- /dev/null +++ b/assets/favicon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/images/favicon.png b/assets/images/favicon.png new file mode 100644 index 000000000..1cf13b9f9 Binary files /dev/null and b/assets/images/favicon.png differ diff --git a/assets/images/social/2.x/components/backstack.png b/assets/images/social/2.x/components/backstack.png new file mode 100644 index 000000000..116c3df34 Binary files /dev/null and b/assets/images/social/2.x/components/backstack.png differ diff --git a/assets/images/social/2.x/components/experimental.png b/assets/images/social/2.x/components/experimental.png new file mode 100644 index 000000000..79c69aecd Binary files /dev/null and b/assets/images/social/2.x/components/experimental.png differ diff --git a/assets/images/social/2.x/components/index.png b/assets/images/social/2.x/components/index.png new file mode 100644 index 000000000..efc84fb03 Binary files /dev/null and b/assets/images/social/2.x/components/index.png differ diff --git a/assets/images/social/2.x/components/spotlight.png b/assets/images/social/2.x/components/spotlight.png new file mode 100644 index 000000000..26a9ab95e Binary files /dev/null and b/assets/images/social/2.x/components/spotlight.png differ diff --git a/assets/images/social/2.x/experimental.png b/assets/images/social/2.x/experimental.png new file mode 100644 index 000000000..aefeee001 Binary files /dev/null and b/assets/images/social/2.x/experimental.png differ diff --git a/assets/images/social/2.x/faq.png b/assets/images/social/2.x/faq.png new file mode 100644 index 000000000..a5b891d58 Binary files /dev/null and b/assets/images/social/2.x/faq.png differ diff --git a/assets/images/social/2.x/index.png b/assets/images/social/2.x/index.png new file mode 100644 index 000000000..b6ee9a687 Binary files /dev/null and b/assets/images/social/2.x/index.png differ diff --git a/assets/images/social/2.x/interactions/appyxcomponent.png b/assets/images/social/2.x/interactions/appyxcomponent.png new file mode 100644 index 000000000..88a1c62bf Binary files /dev/null and b/assets/images/social/2.x/interactions/appyxcomponent.png differ diff --git a/assets/images/social/2.x/interactions/gestures.png b/assets/images/social/2.x/interactions/gestures.png new file mode 100644 index 000000000..ba60bec90 Binary files /dev/null and b/assets/images/social/2.x/interactions/gestures.png differ diff --git a/assets/images/social/2.x/interactions/index.png b/assets/images/social/2.x/interactions/index.png new file mode 100644 index 000000000..885423610 Binary files /dev/null and b/assets/images/social/2.x/interactions/index.png differ diff --git a/assets/images/social/2.x/interactions/ksp.png b/assets/images/social/2.x/interactions/ksp.png new file mode 100644 index 000000000..70e47f74f Binary files /dev/null and b/assets/images/social/2.x/interactions/ksp.png differ diff --git a/assets/images/social/2.x/interactions/operations.png b/assets/images/social/2.x/interactions/operations.png new file mode 100644 index 000000000..6abb00e95 Binary files /dev/null and b/assets/images/social/2.x/interactions/operations.png differ diff --git a/assets/images/social/2.x/interactions/transitionmodel.png b/assets/images/social/2.x/interactions/transitionmodel.png new file mode 100644 index 000000000..0db2ae9fe Binary files /dev/null and b/assets/images/social/2.x/interactions/transitionmodel.png differ diff --git a/assets/images/social/2.x/interactions/ui-representation.png b/assets/images/social/2.x/interactions/ui-representation.png new file mode 100644 index 000000000..5f7f6012d Binary files /dev/null and b/assets/images/social/2.x/interactions/ui-representation.png differ diff --git a/assets/images/social/2.x/interactions/usage.png b/assets/images/social/2.x/interactions/usage.png new file mode 100644 index 000000000..0405cc79c Binary files /dev/null and b/assets/images/social/2.x/interactions/usage.png differ diff --git a/assets/images/social/2.x/migrationguide.png b/assets/images/social/2.x/migrationguide.png new file mode 100644 index 000000000..592bdd5d0 Binary files /dev/null and b/assets/images/social/2.x/migrationguide.png differ diff --git a/assets/images/social/2.x/navigation/concepts/composable-navigation.png b/assets/images/social/2.x/navigation/concepts/composable-navigation.png new file mode 100644 index 000000000..0a5bdc743 Binary files /dev/null and b/assets/images/social/2.x/navigation/concepts/composable-navigation.png differ diff --git a/assets/images/social/2.x/navigation/concepts/explicit-navigation.png b/assets/images/social/2.x/navigation/concepts/explicit-navigation.png new file mode 100644 index 000000000..7153a24f3 Binary files /dev/null and b/assets/images/social/2.x/navigation/concepts/explicit-navigation.png differ diff --git a/assets/images/social/2.x/navigation/concepts/implicit-navigation.png b/assets/images/social/2.x/navigation/concepts/implicit-navigation.png new file mode 100644 index 000000000..2f6dfba32 Binary files /dev/null and b/assets/images/social/2.x/navigation/concepts/implicit-navigation.png differ diff --git a/assets/images/social/2.x/navigation/concepts/model-driven-navigation.png b/assets/images/social/2.x/navigation/concepts/model-driven-navigation.png new file mode 100644 index 000000000..ae1c2a4e2 Binary files /dev/null and b/assets/images/social/2.x/navigation/concepts/model-driven-navigation.png differ diff --git a/assets/images/social/2.x/navigation/features/childaware.png b/assets/images/social/2.x/navigation/features/childaware.png new file mode 100644 index 000000000..d54dfa402 Binary files /dev/null and b/assets/images/social/2.x/navigation/features/childaware.png differ diff --git a/assets/images/social/2.x/navigation/features/deep-linking.png b/assets/images/social/2.x/navigation/features/deep-linking.png new file mode 100644 index 000000000..2313f9100 Binary files /dev/null and b/assets/images/social/2.x/navigation/features/deep-linking.png differ diff --git a/assets/images/social/2.x/navigation/features/lifecycle.png b/assets/images/social/2.x/navigation/features/lifecycle.png new file mode 100644 index 000000000..61414af2a Binary files /dev/null and b/assets/images/social/2.x/navigation/features/lifecycle.png differ diff --git a/assets/images/social/2.x/navigation/features/material3.png b/assets/images/social/2.x/navigation/features/material3.png new file mode 100644 index 000000000..30d0cb2b0 Binary files /dev/null and b/assets/images/social/2.x/navigation/features/material3.png differ diff --git a/assets/images/social/2.x/navigation/features/plugins.png b/assets/images/social/2.x/navigation/features/plugins.png new file mode 100644 index 000000000..1cd3bc604 Binary files /dev/null and b/assets/images/social/2.x/navigation/features/plugins.png differ diff --git a/assets/images/social/2.x/navigation/features/scoped-di.png b/assets/images/social/2.x/navigation/features/scoped-di.png new file mode 100644 index 000000000..6ef0b2d4a Binary files /dev/null and b/assets/images/social/2.x/navigation/features/scoped-di.png differ diff --git a/assets/images/social/2.x/navigation/features/surviving-configuration-changes.png b/assets/images/social/2.x/navigation/features/surviving-configuration-changes.png new file mode 100644 index 000000000..50525e607 Binary files /dev/null and b/assets/images/social/2.x/navigation/features/surviving-configuration-changes.png differ diff --git a/assets/images/social/2.x/navigation/index.png b/assets/images/social/2.x/navigation/index.png new file mode 100644 index 000000000..652eb6908 Binary files /dev/null and b/assets/images/social/2.x/navigation/index.png differ diff --git a/assets/images/social/2.x/navigation/integrations/compose-navigation.png b/assets/images/social/2.x/navigation/integrations/compose-navigation.png new file mode 100644 index 000000000..2a32cd014 Binary files /dev/null and b/assets/images/social/2.x/navigation/integrations/compose-navigation.png differ diff --git a/assets/images/social/2.x/navigation/integrations/di-frameworks.png b/assets/images/social/2.x/navigation/integrations/di-frameworks.png new file mode 100644 index 000000000..c6ab470fa Binary files /dev/null and b/assets/images/social/2.x/navigation/integrations/di-frameworks.png differ diff --git a/assets/images/social/2.x/navigation/integrations/ribs.png b/assets/images/social/2.x/navigation/integrations/ribs.png new file mode 100644 index 000000000..bef629b46 Binary files /dev/null and b/assets/images/social/2.x/navigation/integrations/ribs.png differ diff --git a/assets/images/social/2.x/navigation/integrations/rx.png b/assets/images/social/2.x/navigation/integrations/rx.png new file mode 100644 index 000000000..f8e827619 Binary files /dev/null and b/assets/images/social/2.x/navigation/integrations/rx.png differ diff --git a/assets/images/social/2.x/navigation/integrations/viewmodel.png b/assets/images/social/2.x/navigation/integrations/viewmodel.png new file mode 100644 index 000000000..f0aa5b345 Binary files /dev/null and b/assets/images/social/2.x/navigation/integrations/viewmodel.png differ diff --git a/assets/images/social/2.x/navigation/multiplatform.png b/assets/images/social/2.x/navigation/multiplatform.png new file mode 100644 index 000000000..f7bf282d5 Binary files /dev/null and b/assets/images/social/2.x/navigation/multiplatform.png differ diff --git a/assets/images/social/2.x/navigation/quick-start.png b/assets/images/social/2.x/navigation/quick-start.png new file mode 100644 index 000000000..03e79b69b Binary files /dev/null and b/assets/images/social/2.x/navigation/quick-start.png differ diff --git a/assets/images/social/2.x/navigation/sample-app.png b/assets/images/social/2.x/navigation/sample-app.png new file mode 100644 index 000000000..2735d9243 Binary files /dev/null and b/assets/images/social/2.x/navigation/sample-app.png differ diff --git a/assets/images/social/2.x/releases/2.0.0-alpha10.png b/assets/images/social/2.x/releases/2.0.0-alpha10.png new file mode 100644 index 000000000..cefd2f14b Binary files /dev/null and b/assets/images/social/2.x/releases/2.0.0-alpha10.png differ diff --git a/assets/images/social/2.x/releases/2.0.0.png b/assets/images/social/2.x/releases/2.0.0.png new file mode 100644 index 000000000..f0c4069de Binary files /dev/null and b/assets/images/social/2.x/releases/2.0.0.png differ diff --git a/assets/images/social/2.x/releases/changelog.png b/assets/images/social/2.x/releases/changelog.png new file mode 100644 index 000000000..3d947e686 Binary files /dev/null and b/assets/images/social/2.x/releases/changelog.png differ diff --git a/assets/images/social/2.x/releases/downloads.png b/assets/images/social/2.x/releases/downloads.png new file mode 100644 index 000000000..b152262f1 Binary files /dev/null and b/assets/images/social/2.x/releases/downloads.png differ diff --git a/assets/images/social/apps/childaware.png b/assets/images/social/apps/childaware.png new file mode 100644 index 000000000..ff143a307 Binary files /dev/null and b/assets/images/social/apps/childaware.png differ diff --git a/assets/images/social/apps/configuration.png b/assets/images/social/apps/configuration.png new file mode 100644 index 000000000..d96e35970 Binary files /dev/null and b/assets/images/social/apps/configuration.png differ diff --git a/assets/images/social/apps/lifecycle.png b/assets/images/social/apps/lifecycle.png new file mode 100644 index 000000000..0b03f9483 Binary files /dev/null and b/assets/images/social/apps/lifecycle.png differ diff --git a/assets/images/social/apps/plugins.png b/assets/images/social/apps/plugins.png new file mode 100644 index 000000000..4dee8b9d8 Binary files /dev/null and b/assets/images/social/apps/plugins.png differ diff --git a/assets/images/social/apps/structure.png b/assets/images/social/apps/structure.png new file mode 100644 index 000000000..9adf964d7 Binary files /dev/null and b/assets/images/social/apps/structure.png differ diff --git a/assets/images/social/faq.png b/assets/images/social/faq.png new file mode 100644 index 000000000..a5b891d58 Binary files /dev/null and b/assets/images/social/faq.png differ diff --git a/assets/images/social/how-to-use-appyx/codelabs.png b/assets/images/social/how-to-use-appyx/codelabs.png new file mode 100644 index 000000000..88792c080 Binary files /dev/null and b/assets/images/social/how-to-use-appyx/codelabs.png differ diff --git a/assets/images/social/how-to-use-appyx/coding-challenges.png b/assets/images/social/how-to-use-appyx/coding-challenges.png new file mode 100644 index 000000000..f4360fb4e Binary files /dev/null and b/assets/images/social/how-to-use-appyx/coding-challenges.png differ diff --git a/assets/images/social/how-to-use-appyx/quick-start.png b/assets/images/social/how-to-use-appyx/quick-start.png new file mode 100644 index 000000000..0b969530b Binary files /dev/null and b/assets/images/social/how-to-use-appyx/quick-start.png differ diff --git a/assets/images/social/how-to-use-appyx/sample-apps.png b/assets/images/social/how-to-use-appyx/sample-apps.png new file mode 100644 index 000000000..eb51eeb6c Binary files /dev/null and b/assets/images/social/how-to-use-appyx/sample-apps.png differ diff --git a/assets/images/social/index.png b/assets/images/social/index.png new file mode 100644 index 000000000..222e4f76f Binary files /dev/null and b/assets/images/social/index.png differ diff --git a/assets/images/social/navigation/composable-navigation.png b/assets/images/social/navigation/composable-navigation.png new file mode 100644 index 000000000..bf02bd3b6 Binary files /dev/null and b/assets/images/social/navigation/composable-navigation.png differ diff --git a/assets/images/social/navigation/deep-linking.png b/assets/images/social/navigation/deep-linking.png new file mode 100644 index 000000000..940e13fca Binary files /dev/null and b/assets/images/social/navigation/deep-linking.png differ diff --git a/assets/images/social/navigation/explicit-navigation.png b/assets/images/social/navigation/explicit-navigation.png new file mode 100644 index 000000000..7b0829e8f Binary files /dev/null and b/assets/images/social/navigation/explicit-navigation.png differ diff --git a/assets/images/social/navigation/implicit-navigation.png b/assets/images/social/navigation/implicit-navigation.png new file mode 100644 index 000000000..0a5d5491d Binary files /dev/null and b/assets/images/social/navigation/implicit-navigation.png differ diff --git a/assets/images/social/navigation/model-driven-navigation.png b/assets/images/social/navigation/model-driven-navigation.png new file mode 100644 index 000000000..dc75b2010 Binary files /dev/null and b/assets/images/social/navigation/model-driven-navigation.png differ diff --git a/assets/images/social/navmodel/backstack.png b/assets/images/social/navmodel/backstack.png new file mode 100644 index 000000000..71939b971 Binary files /dev/null and b/assets/images/social/navmodel/backstack.png differ diff --git a/assets/images/social/navmodel/cards.png b/assets/images/social/navmodel/cards.png new file mode 100644 index 000000000..75905f483 Binary files /dev/null and b/assets/images/social/navmodel/cards.png differ diff --git a/assets/images/social/navmodel/custom.png b/assets/images/social/navmodel/custom.png new file mode 100644 index 000000000..2d34da0e0 Binary files /dev/null and b/assets/images/social/navmodel/custom.png differ diff --git a/assets/images/social/navmodel/index.png b/assets/images/social/navmodel/index.png new file mode 100644 index 000000000..5ddc36089 Binary files /dev/null and b/assets/images/social/navmodel/index.png differ diff --git a/assets/images/social/navmodel/promoter.png b/assets/images/social/navmodel/promoter.png new file mode 100644 index 000000000..f5faed922 Binary files /dev/null and b/assets/images/social/navmodel/promoter.png differ diff --git a/assets/images/social/navmodel/spotlight.png b/assets/images/social/navmodel/spotlight.png new file mode 100644 index 000000000..76b2cf676 Binary files /dev/null and b/assets/images/social/navmodel/spotlight.png differ diff --git a/assets/images/social/navmodel/tiles.png b/assets/images/social/navmodel/tiles.png new file mode 100644 index 000000000..a736430cd Binary files /dev/null and b/assets/images/social/navmodel/tiles.png differ diff --git a/assets/images/social/releases/changelog.png b/assets/images/social/releases/changelog.png new file mode 100644 index 000000000..3d947e686 Binary files /dev/null and b/assets/images/social/releases/changelog.png differ diff --git a/assets/images/social/releases/downloads.png b/assets/images/social/releases/downloads.png new file mode 100644 index 000000000..b152262f1 Binary files /dev/null and b/assets/images/social/releases/downloads.png differ diff --git a/assets/images/social/ui/children-view.png b/assets/images/social/ui/children-view.png new file mode 100644 index 000000000..dad3f2e80 Binary files /dev/null and b/assets/images/social/ui/children-view.png differ diff --git a/assets/images/social/ui/transitions.png b/assets/images/social/ui/transitions.png new file mode 100644 index 000000000..280d421dd Binary files /dev/null and b/assets/images/social/ui/transitions.png differ diff --git a/assets/javascripts/bundle.fe8b6f2b.min.js b/assets/javascripts/bundle.fe8b6f2b.min.js new file mode 100644 index 000000000..cf778d428 --- /dev/null +++ b/assets/javascripts/bundle.fe8b6f2b.min.js @@ -0,0 +1,29 @@ +"use strict";(()=>{var Fi=Object.create;var gr=Object.defineProperty;var ji=Object.getOwnPropertyDescriptor;var Wi=Object.getOwnPropertyNames,Dt=Object.getOwnPropertySymbols,Ui=Object.getPrototypeOf,xr=Object.prototype.hasOwnProperty,no=Object.prototype.propertyIsEnumerable;var oo=(e,t,r)=>t in e?gr(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r,R=(e,t)=>{for(var r in t||(t={}))xr.call(t,r)&&oo(e,r,t[r]);if(Dt)for(var r of Dt(t))no.call(t,r)&&oo(e,r,t[r]);return e};var io=(e,t)=>{var r={};for(var o in e)xr.call(e,o)&&t.indexOf(o)<0&&(r[o]=e[o]);if(e!=null&&Dt)for(var o of Dt(e))t.indexOf(o)<0&&no.call(e,o)&&(r[o]=e[o]);return r};var yr=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports);var Di=(e,t,r,o)=>{if(t&&typeof t=="object"||typeof t=="function")for(let n of Wi(t))!xr.call(e,n)&&n!==r&&gr(e,n,{get:()=>t[n],enumerable:!(o=ji(t,n))||o.enumerable});return e};var Vt=(e,t,r)=>(r=e!=null?Fi(Ui(e)):{},Di(t||!e||!e.__esModule?gr(r,"default",{value:e,enumerable:!0}):r,e));var ao=(e,t,r)=>new Promise((o,n)=>{var i=p=>{try{s(r.next(p))}catch(c){n(c)}},a=p=>{try{s(r.throw(p))}catch(c){n(c)}},s=p=>p.done?o(p.value):Promise.resolve(p.value).then(i,a);s((r=r.apply(e,t)).next())});var co=yr((Er,so)=>{(function(e,t){typeof Er=="object"&&typeof so!="undefined"?t():typeof define=="function"&&define.amd?define(t):t()})(Er,function(){"use strict";function e(r){var o=!0,n=!1,i=null,a={text:!0,search:!0,url:!0,tel:!0,email:!0,password:!0,number:!0,date:!0,month:!0,week:!0,time:!0,datetime:!0,"datetime-local":!0};function s(H){return!!(H&&H!==document&&H.nodeName!=="HTML"&&H.nodeName!=="BODY"&&"classList"in H&&"contains"in H.classList)}function p(H){var mt=H.type,ze=H.tagName;return!!(ze==="INPUT"&&a[mt]&&!H.readOnly||ze==="TEXTAREA"&&!H.readOnly||H.isContentEditable)}function c(H){H.classList.contains("focus-visible")||(H.classList.add("focus-visible"),H.setAttribute("data-focus-visible-added",""))}function l(H){H.hasAttribute("data-focus-visible-added")&&(H.classList.remove("focus-visible"),H.removeAttribute("data-focus-visible-added"))}function f(H){H.metaKey||H.altKey||H.ctrlKey||(s(r.activeElement)&&c(r.activeElement),o=!0)}function u(H){o=!1}function h(H){s(H.target)&&(o||p(H.target))&&c(H.target)}function w(H){s(H.target)&&(H.target.classList.contains("focus-visible")||H.target.hasAttribute("data-focus-visible-added"))&&(n=!0,window.clearTimeout(i),i=window.setTimeout(function(){n=!1},100),l(H.target))}function A(H){document.visibilityState==="hidden"&&(n&&(o=!0),te())}function te(){document.addEventListener("mousemove",J),document.addEventListener("mousedown",J),document.addEventListener("mouseup",J),document.addEventListener("pointermove",J),document.addEventListener("pointerdown",J),document.addEventListener("pointerup",J),document.addEventListener("touchmove",J),document.addEventListener("touchstart",J),document.addEventListener("touchend",J)}function ie(){document.removeEventListener("mousemove",J),document.removeEventListener("mousedown",J),document.removeEventListener("mouseup",J),document.removeEventListener("pointermove",J),document.removeEventListener("pointerdown",J),document.removeEventListener("pointerup",J),document.removeEventListener("touchmove",J),document.removeEventListener("touchstart",J),document.removeEventListener("touchend",J)}function J(H){H.target.nodeName&&H.target.nodeName.toLowerCase()==="html"||(o=!1,ie())}document.addEventListener("keydown",f,!0),document.addEventListener("mousedown",u,!0),document.addEventListener("pointerdown",u,!0),document.addEventListener("touchstart",u,!0),document.addEventListener("visibilitychange",A,!0),te(),r.addEventListener("focus",h,!0),r.addEventListener("blur",w,!0),r.nodeType===Node.DOCUMENT_FRAGMENT_NODE&&r.host?r.host.setAttribute("data-js-focus-visible",""):r.nodeType===Node.DOCUMENT_NODE&&(document.documentElement.classList.add("js-focus-visible"),document.documentElement.setAttribute("data-js-focus-visible",""))}if(typeof window!="undefined"&&typeof document!="undefined"){window.applyFocusVisiblePolyfill=e;var t;try{t=new CustomEvent("focus-visible-polyfill-ready")}catch(r){t=document.createEvent("CustomEvent"),t.initCustomEvent("focus-visible-polyfill-ready",!1,!1,{})}window.dispatchEvent(t)}typeof document!="undefined"&&e(document)})});var Yr=yr((Rt,Kr)=>{/*! + * clipboard.js v2.0.11 + * https://clipboardjs.com/ + * + * Licensed MIT © Zeno Rocha + */(function(t,r){typeof Rt=="object"&&typeof Kr=="object"?Kr.exports=r():typeof define=="function"&&define.amd?define([],r):typeof Rt=="object"?Rt.ClipboardJS=r():t.ClipboardJS=r()})(Rt,function(){return function(){var e={686:function(o,n,i){"use strict";i.d(n,{default:function(){return Ii}});var a=i(279),s=i.n(a),p=i(370),c=i.n(p),l=i(817),f=i.n(l);function u(V){try{return document.execCommand(V)}catch(_){return!1}}var h=function(_){var M=f()(_);return u("cut"),M},w=h;function A(V){var _=document.documentElement.getAttribute("dir")==="rtl",M=document.createElement("textarea");M.style.fontSize="12pt",M.style.border="0",M.style.padding="0",M.style.margin="0",M.style.position="absolute",M.style[_?"right":"left"]="-9999px";var j=window.pageYOffset||document.documentElement.scrollTop;return M.style.top="".concat(j,"px"),M.setAttribute("readonly",""),M.value=V,M}var te=function(_,M){var j=A(_);M.container.appendChild(j);var D=f()(j);return u("copy"),j.remove(),D},ie=function(_){var M=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{container:document.body},j="";return typeof _=="string"?j=te(_,M):_ instanceof HTMLInputElement&&!["text","search","url","tel","password"].includes(_==null?void 0:_.type)?j=te(_.value,M):(j=f()(_),u("copy")),j},J=ie;function H(V){"@babel/helpers - typeof";return typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?H=function(M){return typeof M}:H=function(M){return M&&typeof Symbol=="function"&&M.constructor===Symbol&&M!==Symbol.prototype?"symbol":typeof M},H(V)}var mt=function(){var _=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{},M=_.action,j=M===void 0?"copy":M,D=_.container,Y=_.target,ke=_.text;if(j!=="copy"&&j!=="cut")throw new Error('Invalid "action" value, use either "copy" or "cut"');if(Y!==void 0)if(Y&&H(Y)==="object"&&Y.nodeType===1){if(j==="copy"&&Y.hasAttribute("disabled"))throw new Error('Invalid "target" attribute. Please use "readonly" instead of "disabled" attribute');if(j==="cut"&&(Y.hasAttribute("readonly")||Y.hasAttribute("disabled")))throw new Error(`Invalid "target" attribute. You can't cut text from elements with "readonly" or "disabled" attributes`)}else throw new Error('Invalid "target" value, use a valid Element');if(ke)return J(ke,{container:D});if(Y)return j==="cut"?w(Y):J(Y,{container:D})},ze=mt;function Ie(V){"@babel/helpers - typeof";return typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?Ie=function(M){return typeof M}:Ie=function(M){return M&&typeof Symbol=="function"&&M.constructor===Symbol&&M!==Symbol.prototype?"symbol":typeof M},Ie(V)}function _i(V,_){if(!(V instanceof _))throw new TypeError("Cannot call a class as a function")}function ro(V,_){for(var M=0;M<_.length;M++){var j=_[M];j.enumerable=j.enumerable||!1,j.configurable=!0,"value"in j&&(j.writable=!0),Object.defineProperty(V,j.key,j)}}function Ai(V,_,M){return _&&ro(V.prototype,_),M&&ro(V,M),V}function Ci(V,_){if(typeof _!="function"&&_!==null)throw new TypeError("Super expression must either be null or a function");V.prototype=Object.create(_&&_.prototype,{constructor:{value:V,writable:!0,configurable:!0}}),_&&br(V,_)}function br(V,_){return br=Object.setPrototypeOf||function(j,D){return j.__proto__=D,j},br(V,_)}function Hi(V){var _=Pi();return function(){var j=Wt(V),D;if(_){var Y=Wt(this).constructor;D=Reflect.construct(j,arguments,Y)}else D=j.apply(this,arguments);return ki(this,D)}}function ki(V,_){return _&&(Ie(_)==="object"||typeof _=="function")?_:$i(V)}function $i(V){if(V===void 0)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return V}function Pi(){if(typeof Reflect=="undefined"||!Reflect.construct||Reflect.construct.sham)return!1;if(typeof Proxy=="function")return!0;try{return Date.prototype.toString.call(Reflect.construct(Date,[],function(){})),!0}catch(V){return!1}}function Wt(V){return Wt=Object.setPrototypeOf?Object.getPrototypeOf:function(M){return M.__proto__||Object.getPrototypeOf(M)},Wt(V)}function vr(V,_){var M="data-clipboard-".concat(V);if(_.hasAttribute(M))return _.getAttribute(M)}var Ri=function(V){Ci(M,V);var _=Hi(M);function M(j,D){var Y;return _i(this,M),Y=_.call(this),Y.resolveOptions(D),Y.listenClick(j),Y}return Ai(M,[{key:"resolveOptions",value:function(){var D=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{};this.action=typeof D.action=="function"?D.action:this.defaultAction,this.target=typeof D.target=="function"?D.target:this.defaultTarget,this.text=typeof D.text=="function"?D.text:this.defaultText,this.container=Ie(D.container)==="object"?D.container:document.body}},{key:"listenClick",value:function(D){var Y=this;this.listener=c()(D,"click",function(ke){return Y.onClick(ke)})}},{key:"onClick",value:function(D){var Y=D.delegateTarget||D.currentTarget,ke=this.action(Y)||"copy",Ut=ze({action:ke,container:this.container,target:this.target(Y),text:this.text(Y)});this.emit(Ut?"success":"error",{action:ke,text:Ut,trigger:Y,clearSelection:function(){Y&&Y.focus(),window.getSelection().removeAllRanges()}})}},{key:"defaultAction",value:function(D){return vr("action",D)}},{key:"defaultTarget",value:function(D){var Y=vr("target",D);if(Y)return document.querySelector(Y)}},{key:"defaultText",value:function(D){return vr("text",D)}},{key:"destroy",value:function(){this.listener.destroy()}}],[{key:"copy",value:function(D){var Y=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{container:document.body};return J(D,Y)}},{key:"cut",value:function(D){return w(D)}},{key:"isSupported",value:function(){var D=arguments.length>0&&arguments[0]!==void 0?arguments[0]:["copy","cut"],Y=typeof D=="string"?[D]:D,ke=!!document.queryCommandSupported;return Y.forEach(function(Ut){ke=ke&&!!document.queryCommandSupported(Ut)}),ke}}]),M}(s()),Ii=Ri},828:function(o){var n=9;if(typeof Element!="undefined"&&!Element.prototype.matches){var i=Element.prototype;i.matches=i.matchesSelector||i.mozMatchesSelector||i.msMatchesSelector||i.oMatchesSelector||i.webkitMatchesSelector}function a(s,p){for(;s&&s.nodeType!==n;){if(typeof s.matches=="function"&&s.matches(p))return s;s=s.parentNode}}o.exports=a},438:function(o,n,i){var a=i(828);function s(l,f,u,h,w){var A=c.apply(this,arguments);return l.addEventListener(u,A,w),{destroy:function(){l.removeEventListener(u,A,w)}}}function p(l,f,u,h,w){return typeof l.addEventListener=="function"?s.apply(null,arguments):typeof u=="function"?s.bind(null,document).apply(null,arguments):(typeof l=="string"&&(l=document.querySelectorAll(l)),Array.prototype.map.call(l,function(A){return s(A,f,u,h,w)}))}function c(l,f,u,h){return function(w){w.delegateTarget=a(w.target,f),w.delegateTarget&&h.call(l,w)}}o.exports=p},879:function(o,n){n.node=function(i){return i!==void 0&&i instanceof HTMLElement&&i.nodeType===1},n.nodeList=function(i){var a=Object.prototype.toString.call(i);return i!==void 0&&(a==="[object NodeList]"||a==="[object HTMLCollection]")&&"length"in i&&(i.length===0||n.node(i[0]))},n.string=function(i){return typeof i=="string"||i instanceof String},n.fn=function(i){var a=Object.prototype.toString.call(i);return a==="[object Function]"}},370:function(o,n,i){var a=i(879),s=i(438);function p(u,h,w){if(!u&&!h&&!w)throw new Error("Missing required arguments");if(!a.string(h))throw new TypeError("Second argument must be a String");if(!a.fn(w))throw new TypeError("Third argument must be a Function");if(a.node(u))return c(u,h,w);if(a.nodeList(u))return l(u,h,w);if(a.string(u))return f(u,h,w);throw new TypeError("First argument must be a String, HTMLElement, HTMLCollection, or NodeList")}function c(u,h,w){return u.addEventListener(h,w),{destroy:function(){u.removeEventListener(h,w)}}}function l(u,h,w){return Array.prototype.forEach.call(u,function(A){A.addEventListener(h,w)}),{destroy:function(){Array.prototype.forEach.call(u,function(A){A.removeEventListener(h,w)})}}}function f(u,h,w){return s(document.body,u,h,w)}o.exports=p},817:function(o){function n(i){var a;if(i.nodeName==="SELECT")i.focus(),a=i.value;else if(i.nodeName==="INPUT"||i.nodeName==="TEXTAREA"){var s=i.hasAttribute("readonly");s||i.setAttribute("readonly",""),i.select(),i.setSelectionRange(0,i.value.length),s||i.removeAttribute("readonly"),a=i.value}else{i.hasAttribute("contenteditable")&&i.focus();var p=window.getSelection(),c=document.createRange();c.selectNodeContents(i),p.removeAllRanges(),p.addRange(c),a=p.toString()}return a}o.exports=n},279:function(o){function n(){}n.prototype={on:function(i,a,s){var p=this.e||(this.e={});return(p[i]||(p[i]=[])).push({fn:a,ctx:s}),this},once:function(i,a,s){var p=this;function c(){p.off(i,c),a.apply(s,arguments)}return c._=a,this.on(i,c,s)},emit:function(i){var a=[].slice.call(arguments,1),s=((this.e||(this.e={}))[i]||[]).slice(),p=0,c=s.length;for(p;p{"use strict";/*! + * escape-html + * Copyright(c) 2012-2013 TJ Holowaychuk + * Copyright(c) 2015 Andreas Lubbe + * Copyright(c) 2015 Tiancheng "Timothy" Gu + * MIT Licensed + */var ts=/["'&<>]/;ei.exports=rs;function rs(e){var t=""+e,r=ts.exec(t);if(!r)return t;var o,n="",i=0,a=0;for(i=r.index;i0&&i[i.length-1])&&(c[0]===6||c[0]===2)){r=0;continue}if(c[0]===3&&(!i||c[1]>i[0]&&c[1]=e.length&&(e=void 0),{value:e&&e[o++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")}function N(e,t){var r=typeof Symbol=="function"&&e[Symbol.iterator];if(!r)return e;var o=r.call(e),n,i=[],a;try{for(;(t===void 0||t-- >0)&&!(n=o.next()).done;)i.push(n.value)}catch(s){a={error:s}}finally{try{n&&!n.done&&(r=o.return)&&r.call(o)}finally{if(a)throw a.error}}return i}function q(e,t,r){if(r||arguments.length===2)for(var o=0,n=t.length,i;o1||s(u,h)})})}function s(u,h){try{p(o[u](h))}catch(w){f(i[0][3],w)}}function p(u){u.value instanceof nt?Promise.resolve(u.value.v).then(c,l):f(i[0][2],u)}function c(u){s("next",u)}function l(u){s("throw",u)}function f(u,h){u(h),i.shift(),i.length&&s(i[0][0],i[0][1])}}function mo(e){if(!Symbol.asyncIterator)throw new TypeError("Symbol.asyncIterator is not defined.");var t=e[Symbol.asyncIterator],r;return t?t.call(e):(e=typeof de=="function"?de(e):e[Symbol.iterator](),r={},o("next"),o("throw"),o("return"),r[Symbol.asyncIterator]=function(){return this},r);function o(i){r[i]=e[i]&&function(a){return new Promise(function(s,p){a=e[i](a),n(s,p,a.done,a.value)})}}function n(i,a,s,p){Promise.resolve(p).then(function(c){i({value:c,done:s})},a)}}function k(e){return typeof e=="function"}function ft(e){var t=function(o){Error.call(o),o.stack=new Error().stack},r=e(t);return r.prototype=Object.create(Error.prototype),r.prototype.constructor=r,r}var zt=ft(function(e){return function(r){e(this),this.message=r?r.length+` errors occurred during unsubscription: +`+r.map(function(o,n){return n+1+") "+o.toString()}).join(` + `):"",this.name="UnsubscriptionError",this.errors=r}});function qe(e,t){if(e){var r=e.indexOf(t);0<=r&&e.splice(r,1)}}var Fe=function(){function e(t){this.initialTeardown=t,this.closed=!1,this._parentage=null,this._finalizers=null}return e.prototype.unsubscribe=function(){var t,r,o,n,i;if(!this.closed){this.closed=!0;var a=this._parentage;if(a)if(this._parentage=null,Array.isArray(a))try{for(var s=de(a),p=s.next();!p.done;p=s.next()){var c=p.value;c.remove(this)}}catch(A){t={error:A}}finally{try{p&&!p.done&&(r=s.return)&&r.call(s)}finally{if(t)throw t.error}}else a.remove(this);var l=this.initialTeardown;if(k(l))try{l()}catch(A){i=A instanceof zt?A.errors:[A]}var f=this._finalizers;if(f){this._finalizers=null;try{for(var u=de(f),h=u.next();!h.done;h=u.next()){var w=h.value;try{fo(w)}catch(A){i=i!=null?i:[],A instanceof zt?i=q(q([],N(i)),N(A.errors)):i.push(A)}}}catch(A){o={error:A}}finally{try{h&&!h.done&&(n=u.return)&&n.call(u)}finally{if(o)throw o.error}}}if(i)throw new zt(i)}},e.prototype.add=function(t){var r;if(t&&t!==this)if(this.closed)fo(t);else{if(t instanceof e){if(t.closed||t._hasParent(this))return;t._addParent(this)}(this._finalizers=(r=this._finalizers)!==null&&r!==void 0?r:[]).push(t)}},e.prototype._hasParent=function(t){var r=this._parentage;return r===t||Array.isArray(r)&&r.includes(t)},e.prototype._addParent=function(t){var r=this._parentage;this._parentage=Array.isArray(r)?(r.push(t),r):r?[r,t]:t},e.prototype._removeParent=function(t){var r=this._parentage;r===t?this._parentage=null:Array.isArray(r)&&qe(r,t)},e.prototype.remove=function(t){var r=this._finalizers;r&&qe(r,t),t instanceof e&&t._removeParent(this)},e.EMPTY=function(){var t=new e;return t.closed=!0,t}(),e}();var Tr=Fe.EMPTY;function qt(e){return e instanceof Fe||e&&"closed"in e&&k(e.remove)&&k(e.add)&&k(e.unsubscribe)}function fo(e){k(e)?e():e.unsubscribe()}var $e={onUnhandledError:null,onStoppedNotification:null,Promise:void 0,useDeprecatedSynchronousErrorHandling:!1,useDeprecatedNextContext:!1};var ut={setTimeout:function(e,t){for(var r=[],o=2;o0},enumerable:!1,configurable:!0}),t.prototype._trySubscribe=function(r){return this._throwIfClosed(),e.prototype._trySubscribe.call(this,r)},t.prototype._subscribe=function(r){return this._throwIfClosed(),this._checkFinalizedStatuses(r),this._innerSubscribe(r)},t.prototype._innerSubscribe=function(r){var o=this,n=this,i=n.hasError,a=n.isStopped,s=n.observers;return i||a?Tr:(this.currentObservers=null,s.push(r),new Fe(function(){o.currentObservers=null,qe(s,r)}))},t.prototype._checkFinalizedStatuses=function(r){var o=this,n=o.hasError,i=o.thrownError,a=o.isStopped;n?r.error(i):a&&r.complete()},t.prototype.asObservable=function(){var r=new F;return r.source=this,r},t.create=function(r,o){return new Eo(r,o)},t}(F);var Eo=function(e){re(t,e);function t(r,o){var n=e.call(this)||this;return n.destination=r,n.source=o,n}return t.prototype.next=function(r){var o,n;(n=(o=this.destination)===null||o===void 0?void 0:o.next)===null||n===void 0||n.call(o,r)},t.prototype.error=function(r){var o,n;(n=(o=this.destination)===null||o===void 0?void 0:o.error)===null||n===void 0||n.call(o,r)},t.prototype.complete=function(){var r,o;(o=(r=this.destination)===null||r===void 0?void 0:r.complete)===null||o===void 0||o.call(r)},t.prototype._subscribe=function(r){var o,n;return(n=(o=this.source)===null||o===void 0?void 0:o.subscribe(r))!==null&&n!==void 0?n:Tr},t}(g);var _r=function(e){re(t,e);function t(r){var o=e.call(this)||this;return o._value=r,o}return Object.defineProperty(t.prototype,"value",{get:function(){return this.getValue()},enumerable:!1,configurable:!0}),t.prototype._subscribe=function(r){var o=e.prototype._subscribe.call(this,r);return!o.closed&&r.next(this._value),o},t.prototype.getValue=function(){var r=this,o=r.hasError,n=r.thrownError,i=r._value;if(o)throw n;return this._throwIfClosed(),i},t.prototype.next=function(r){e.prototype.next.call(this,this._value=r)},t}(g);var Lt={now:function(){return(Lt.delegate||Date).now()},delegate:void 0};var _t=function(e){re(t,e);function t(r,o,n){r===void 0&&(r=1/0),o===void 0&&(o=1/0),n===void 0&&(n=Lt);var i=e.call(this)||this;return i._bufferSize=r,i._windowTime=o,i._timestampProvider=n,i._buffer=[],i._infiniteTimeWindow=!0,i._infiniteTimeWindow=o===1/0,i._bufferSize=Math.max(1,r),i._windowTime=Math.max(1,o),i}return t.prototype.next=function(r){var o=this,n=o.isStopped,i=o._buffer,a=o._infiniteTimeWindow,s=o._timestampProvider,p=o._windowTime;n||(i.push(r),!a&&i.push(s.now()+p)),this._trimBuffer(),e.prototype.next.call(this,r)},t.prototype._subscribe=function(r){this._throwIfClosed(),this._trimBuffer();for(var o=this._innerSubscribe(r),n=this,i=n._infiniteTimeWindow,a=n._buffer,s=a.slice(),p=0;p0?e.prototype.schedule.call(this,r,o):(this.delay=o,this.state=r,this.scheduler.flush(this),this)},t.prototype.execute=function(r,o){return o>0||this.closed?e.prototype.execute.call(this,r,o):this._execute(r,o)},t.prototype.requestAsyncId=function(r,o,n){return n===void 0&&(n=0),n!=null&&n>0||n==null&&this.delay>0?e.prototype.requestAsyncId.call(this,r,o,n):(r.flush(this),0)},t}(vt);var So=function(e){re(t,e);function t(){return e!==null&&e.apply(this,arguments)||this}return t}(gt);var Hr=new So(To);var Oo=function(e){re(t,e);function t(r,o){var n=e.call(this,r,o)||this;return n.scheduler=r,n.work=o,n}return t.prototype.requestAsyncId=function(r,o,n){return n===void 0&&(n=0),n!==null&&n>0?e.prototype.requestAsyncId.call(this,r,o,n):(r.actions.push(this),r._scheduled||(r._scheduled=bt.requestAnimationFrame(function(){return r.flush(void 0)})))},t.prototype.recycleAsyncId=function(r,o,n){var i;if(n===void 0&&(n=0),n!=null?n>0:this.delay>0)return e.prototype.recycleAsyncId.call(this,r,o,n);var a=r.actions;o!=null&&((i=a[a.length-1])===null||i===void 0?void 0:i.id)!==o&&(bt.cancelAnimationFrame(o),r._scheduled=void 0)},t}(vt);var Mo=function(e){re(t,e);function t(){return e!==null&&e.apply(this,arguments)||this}return t.prototype.flush=function(r){this._active=!0;var o=this._scheduled;this._scheduled=void 0;var n=this.actions,i;r=r||n.shift();do if(i=r.execute(r.state,r.delay))break;while((r=n[0])&&r.id===o&&n.shift());if(this._active=!1,i){for(;(r=n[0])&&r.id===o&&n.shift();)r.unsubscribe();throw i}},t}(gt);var me=new Mo(Oo);var O=new F(function(e){return e.complete()});function Yt(e){return e&&k(e.schedule)}function kr(e){return e[e.length-1]}function Xe(e){return k(kr(e))?e.pop():void 0}function He(e){return Yt(kr(e))?e.pop():void 0}function Bt(e,t){return typeof kr(e)=="number"?e.pop():t}var xt=function(e){return e&&typeof e.length=="number"&&typeof e!="function"};function Gt(e){return k(e==null?void 0:e.then)}function Jt(e){return k(e[ht])}function Xt(e){return Symbol.asyncIterator&&k(e==null?void 0:e[Symbol.asyncIterator])}function Zt(e){return new TypeError("You provided "+(e!==null&&typeof e=="object"?"an invalid object":"'"+e+"'")+" where a stream was expected. You can provide an Observable, Promise, ReadableStream, Array, AsyncIterable, or Iterable.")}function Gi(){return typeof Symbol!="function"||!Symbol.iterator?"@@iterator":Symbol.iterator}var er=Gi();function tr(e){return k(e==null?void 0:e[er])}function rr(e){return lo(this,arguments,function(){var r,o,n,i;return Nt(this,function(a){switch(a.label){case 0:r=e.getReader(),a.label=1;case 1:a.trys.push([1,,9,10]),a.label=2;case 2:return[4,nt(r.read())];case 3:return o=a.sent(),n=o.value,i=o.done,i?[4,nt(void 0)]:[3,5];case 4:return[2,a.sent()];case 5:return[4,nt(n)];case 6:return[4,a.sent()];case 7:return a.sent(),[3,2];case 8:return[3,10];case 9:return r.releaseLock(),[7];case 10:return[2]}})})}function or(e){return k(e==null?void 0:e.getReader)}function W(e){if(e instanceof F)return e;if(e!=null){if(Jt(e))return Ji(e);if(xt(e))return Xi(e);if(Gt(e))return Zi(e);if(Xt(e))return Lo(e);if(tr(e))return ea(e);if(or(e))return ta(e)}throw Zt(e)}function Ji(e){return new F(function(t){var r=e[ht]();if(k(r.subscribe))return r.subscribe(t);throw new TypeError("Provided object does not correctly implement Symbol.observable")})}function Xi(e){return new F(function(t){for(var r=0;r=2;return function(o){return o.pipe(e?b(function(n,i){return e(n,i,o)}):le,Te(1),r?Be(t):zo(function(){return new ir}))}}function Fr(e){return e<=0?function(){return O}:y(function(t,r){var o=[];t.subscribe(T(r,function(n){o.push(n),e=2,!0))}function pe(e){e===void 0&&(e={});var t=e.connector,r=t===void 0?function(){return new g}:t,o=e.resetOnError,n=o===void 0?!0:o,i=e.resetOnComplete,a=i===void 0?!0:i,s=e.resetOnRefCountZero,p=s===void 0?!0:s;return function(c){var l,f,u,h=0,w=!1,A=!1,te=function(){f==null||f.unsubscribe(),f=void 0},ie=function(){te(),l=u=void 0,w=A=!1},J=function(){var H=l;ie(),H==null||H.unsubscribe()};return y(function(H,mt){h++,!A&&!w&&te();var ze=u=u!=null?u:r();mt.add(function(){h--,h===0&&!A&&!w&&(f=Wr(J,p))}),ze.subscribe(mt),!l&&h>0&&(l=new at({next:function(Ie){return ze.next(Ie)},error:function(Ie){A=!0,te(),f=Wr(ie,n,Ie),ze.error(Ie)},complete:function(){w=!0,te(),f=Wr(ie,a),ze.complete()}}),W(H).subscribe(l))})(c)}}function Wr(e,t){for(var r=[],o=2;oe.next(document)),e}function $(e,t=document){return Array.from(t.querySelectorAll(e))}function P(e,t=document){let r=fe(e,t);if(typeof r=="undefined")throw new ReferenceError(`Missing element: expected "${e}" to be present`);return r}function fe(e,t=document){return t.querySelector(e)||void 0}function Re(){var e,t,r,o;return(o=(r=(t=(e=document.activeElement)==null?void 0:e.shadowRoot)==null?void 0:t.activeElement)!=null?r:document.activeElement)!=null?o:void 0}var xa=S(d(document.body,"focusin"),d(document.body,"focusout")).pipe(_e(1),Q(void 0),m(()=>Re()||document.body),G(1));function et(e){return xa.pipe(m(t=>e.contains(t)),K())}function kt(e,t){return C(()=>S(d(e,"mouseenter").pipe(m(()=>!0)),d(e,"mouseleave").pipe(m(()=>!1))).pipe(t?Ht(r=>Me(+!r*t)):le,Q(e.matches(":hover"))))}function Bo(e,t){if(typeof t=="string"||typeof t=="number")e.innerHTML+=t.toString();else if(t instanceof Node)e.appendChild(t);else if(Array.isArray(t))for(let r of t)Bo(e,r)}function x(e,t,...r){let o=document.createElement(e);if(t)for(let n of Object.keys(t))typeof t[n]!="undefined"&&(typeof t[n]!="boolean"?o.setAttribute(n,t[n]):o.setAttribute(n,""));for(let n of r)Bo(o,n);return o}function sr(e){if(e>999){let t=+((e-950)%1e3>99);return`${((e+1e-6)/1e3).toFixed(t)}k`}else return e.toString()}function wt(e){let t=x("script",{src:e});return C(()=>(document.head.appendChild(t),S(d(t,"load"),d(t,"error").pipe(v(()=>$r(()=>new ReferenceError(`Invalid script: ${e}`))))).pipe(m(()=>{}),L(()=>document.head.removeChild(t)),Te(1))))}var Go=new g,ya=C(()=>typeof ResizeObserver=="undefined"?wt("https://unpkg.com/resize-observer-polyfill"):I(void 0)).pipe(m(()=>new ResizeObserver(e=>e.forEach(t=>Go.next(t)))),v(e=>S(Ke,I(e)).pipe(L(()=>e.disconnect()))),G(1));function ce(e){return{width:e.offsetWidth,height:e.offsetHeight}}function ge(e){let t=e;for(;t.clientWidth===0&&t.parentElement;)t=t.parentElement;return ya.pipe(E(r=>r.observe(t)),v(r=>Go.pipe(b(o=>o.target===t),L(()=>r.unobserve(t)))),m(()=>ce(e)),Q(ce(e)))}function Tt(e){return{width:e.scrollWidth,height:e.scrollHeight}}function cr(e){let t=e.parentElement;for(;t&&(e.scrollWidth<=t.scrollWidth&&e.scrollHeight<=t.scrollHeight);)t=(e=t).parentElement;return t?e:void 0}function Jo(e){let t=[],r=e.parentElement;for(;r;)(e.clientWidth>r.clientWidth||e.clientHeight>r.clientHeight)&&t.push(r),r=(e=r).parentElement;return t.length===0&&t.push(document.documentElement),t}function Ue(e){return{x:e.offsetLeft,y:e.offsetTop}}function Xo(e){let t=e.getBoundingClientRect();return{x:t.x+window.scrollX,y:t.y+window.scrollY}}function Zo(e){return S(d(window,"load"),d(window,"resize")).pipe(Le(0,me),m(()=>Ue(e)),Q(Ue(e)))}function pr(e){return{x:e.scrollLeft,y:e.scrollTop}}function De(e){return S(d(e,"scroll"),d(window,"scroll"),d(window,"resize")).pipe(Le(0,me),m(()=>pr(e)),Q(pr(e)))}var en=new g,Ea=C(()=>I(new IntersectionObserver(e=>{for(let t of e)en.next(t)},{threshold:0}))).pipe(v(e=>S(Ke,I(e)).pipe(L(()=>e.disconnect()))),G(1));function tt(e){return Ea.pipe(E(t=>t.observe(e)),v(t=>en.pipe(b(({target:r})=>r===e),L(()=>t.unobserve(e)),m(({isIntersecting:r})=>r))))}function tn(e,t=16){return De(e).pipe(m(({y:r})=>{let o=ce(e),n=Tt(e);return r>=n.height-o.height-t}),K())}var lr={drawer:P("[data-md-toggle=drawer]"),search:P("[data-md-toggle=search]")};function rn(e){return lr[e].checked}function Je(e,t){lr[e].checked!==t&&lr[e].click()}function Ve(e){let t=lr[e];return d(t,"change").pipe(m(()=>t.checked),Q(t.checked))}function wa(e,t){switch(e.constructor){case HTMLInputElement:return e.type==="radio"?/^Arrow/.test(t):!0;case HTMLSelectElement:case HTMLTextAreaElement:return!0;default:return e.isContentEditable}}function Ta(){return S(d(window,"compositionstart").pipe(m(()=>!0)),d(window,"compositionend").pipe(m(()=>!1))).pipe(Q(!1))}function on(){let e=d(window,"keydown").pipe(b(t=>!(t.metaKey||t.ctrlKey)),m(t=>({mode:rn("search")?"search":"global",type:t.key,claim(){t.preventDefault(),t.stopPropagation()}})),b(({mode:t,type:r})=>{if(t==="global"){let o=Re();if(typeof o!="undefined")return!wa(o,r)}return!0}),pe());return Ta().pipe(v(t=>t?O:e))}function xe(){return new URL(location.href)}function pt(e,t=!1){if(B("navigation.instant")&&!t){let r=x("a",{href:e.href});document.body.appendChild(r),r.click(),r.remove()}else location.href=e.href}function nn(){return new g}function an(){return location.hash.slice(1)}function sn(e){let t=x("a",{href:e});t.addEventListener("click",r=>r.stopPropagation()),t.click()}function Sa(e){return S(d(window,"hashchange"),e).pipe(m(an),Q(an()),b(t=>t.length>0),G(1))}function cn(e){return Sa(e).pipe(m(t=>fe(`[id="${t}"]`)),b(t=>typeof t!="undefined"))}function $t(e){let t=matchMedia(e);return ar(r=>t.addListener(()=>r(t.matches))).pipe(Q(t.matches))}function pn(){let e=matchMedia("print");return S(d(window,"beforeprint").pipe(m(()=>!0)),d(window,"afterprint").pipe(m(()=>!1))).pipe(Q(e.matches))}function Nr(e,t){return e.pipe(v(r=>r?t():O))}function zr(e,t){return new F(r=>{let o=new XMLHttpRequest;return o.open("GET",`${e}`),o.responseType="blob",o.addEventListener("load",()=>{o.status>=200&&o.status<300?(r.next(o.response),r.complete()):r.error(new Error(o.statusText))}),o.addEventListener("error",()=>{r.error(new Error("Network error"))}),o.addEventListener("abort",()=>{r.complete()}),typeof(t==null?void 0:t.progress$)!="undefined"&&(o.addEventListener("progress",n=>{var i;if(n.lengthComputable)t.progress$.next(n.loaded/n.total*100);else{let a=(i=o.getResponseHeader("Content-Length"))!=null?i:0;t.progress$.next(n.loaded/+a*100)}}),t.progress$.next(5)),o.send(),()=>o.abort()})}function Ne(e,t){return zr(e,t).pipe(v(r=>r.text()),m(r=>JSON.parse(r)),G(1))}function ln(e,t){let r=new DOMParser;return zr(e,t).pipe(v(o=>o.text()),m(o=>r.parseFromString(o,"text/html")),G(1))}function mn(e,t){let r=new DOMParser;return zr(e,t).pipe(v(o=>o.text()),m(o=>r.parseFromString(o,"text/xml")),G(1))}function fn(){return{x:Math.max(0,scrollX),y:Math.max(0,scrollY)}}function un(){return S(d(window,"scroll",{passive:!0}),d(window,"resize",{passive:!0})).pipe(m(fn),Q(fn()))}function dn(){return{width:innerWidth,height:innerHeight}}function hn(){return d(window,"resize",{passive:!0}).pipe(m(dn),Q(dn()))}function bn(){return z([un(),hn()]).pipe(m(([e,t])=>({offset:e,size:t})),G(1))}function mr(e,{viewport$:t,header$:r}){let o=t.pipe(Z("size")),n=z([o,r]).pipe(m(()=>Ue(e)));return z([r,t,n]).pipe(m(([{height:i},{offset:a,size:s},{x:p,y:c}])=>({offset:{x:a.x-p,y:a.y-c+i},size:s})))}function Oa(e){return d(e,"message",t=>t.data)}function Ma(e){let t=new g;return t.subscribe(r=>e.postMessage(r)),t}function vn(e,t=new Worker(e)){let r=Oa(t),o=Ma(t),n=new g;n.subscribe(o);let i=o.pipe(X(),ne(!0));return n.pipe(X(),Pe(r.pipe(U(i))),pe())}var La=P("#__config"),St=JSON.parse(La.textContent);St.base=`${new URL(St.base,xe())}`;function ye(){return St}function B(e){return St.features.includes(e)}function Ee(e,t){return typeof t!="undefined"?St.translations[e].replace("#",t.toString()):St.translations[e]}function Se(e,t=document){return P(`[data-md-component=${e}]`,t)}function ae(e,t=document){return $(`[data-md-component=${e}]`,t)}function _a(e){let t=P(".md-typeset > :first-child",e);return d(t,"click",{once:!0}).pipe(m(()=>P(".md-typeset",e)),m(r=>({hash:__md_hash(r.innerHTML)})))}function gn(e){if(!B("announce.dismiss")||!e.childElementCount)return O;if(!e.hidden){let t=P(".md-typeset",e);__md_hash(t.innerHTML)===__md_get("__announce")&&(e.hidden=!0)}return C(()=>{let t=new g;return t.subscribe(({hash:r})=>{e.hidden=!0,__md_set("__announce",r)}),_a(e).pipe(E(r=>t.next(r)),L(()=>t.complete()),m(r=>R({ref:e},r)))})}function Aa(e,{target$:t}){return t.pipe(m(r=>({hidden:r!==e})))}function xn(e,t){let r=new g;return r.subscribe(({hidden:o})=>{e.hidden=o}),Aa(e,t).pipe(E(o=>r.next(o)),L(()=>r.complete()),m(o=>R({ref:e},o)))}function Pt(e,t){return t==="inline"?x("div",{class:"md-tooltip md-tooltip--inline",id:e,role:"tooltip"},x("div",{class:"md-tooltip__inner md-typeset"})):x("div",{class:"md-tooltip",id:e,role:"tooltip"},x("div",{class:"md-tooltip__inner md-typeset"}))}function yn(...e){return x("div",{class:"md-tooltip2",role:"tooltip"},x("div",{class:"md-tooltip2__inner md-typeset"},e))}function En(e,t){if(t=t?`${t}_annotation_${e}`:void 0,t){let r=t?`#${t}`:void 0;return x("aside",{class:"md-annotation",tabIndex:0},Pt(t),x("a",{href:r,class:"md-annotation__index",tabIndex:-1},x("span",{"data-md-annotation-id":e})))}else return x("aside",{class:"md-annotation",tabIndex:0},Pt(t),x("span",{class:"md-annotation__index",tabIndex:-1},x("span",{"data-md-annotation-id":e})))}function wn(e){return x("button",{class:"md-clipboard md-icon",title:Ee("clipboard.copy"),"data-clipboard-target":`#${e} > code`})}function qr(e,t){let r=t&2,o=t&1,n=Object.keys(e.terms).filter(p=>!e.terms[p]).reduce((p,c)=>[...p,x("del",null,c)," "],[]).slice(0,-1),i=ye(),a=new URL(e.location,i.base);B("search.highlight")&&a.searchParams.set("h",Object.entries(e.terms).filter(([,p])=>p).reduce((p,[c])=>`${p} ${c}`.trim(),""));let{tags:s}=ye();return x("a",{href:`${a}`,class:"md-search-result__link",tabIndex:-1},x("article",{class:"md-search-result__article md-typeset","data-md-score":e.score.toFixed(2)},r>0&&x("div",{class:"md-search-result__icon md-icon"}),r>0&&x("h1",null,e.title),r<=0&&x("h2",null,e.title),o>0&&e.text.length>0&&e.text,e.tags&&e.tags.map(p=>{let c=s?p in s?`md-tag-icon md-tag--${s[p]}`:"md-tag-icon":"";return x("span",{class:`md-tag ${c}`},p)}),o>0&&n.length>0&&x("p",{class:"md-search-result__terms"},Ee("search.result.term.missing"),": ",...n)))}function Tn(e){let t=e[0].score,r=[...e],o=ye(),n=r.findIndex(l=>!`${new URL(l.location,o.base)}`.includes("#")),[i]=r.splice(n,1),a=r.findIndex(l=>l.scoreqr(l,1)),...p.length?[x("details",{class:"md-search-result__more"},x("summary",{tabIndex:-1},x("div",null,p.length>0&&p.length===1?Ee("search.result.more.one"):Ee("search.result.more.other",p.length))),...p.map(l=>qr(l,1)))]:[]];return x("li",{class:"md-search-result__item"},c)}function Sn(e){return x("ul",{class:"md-source__facts"},Object.entries(e).map(([t,r])=>x("li",{class:`md-source__fact md-source__fact--${t}`},typeof r=="number"?sr(r):r)))}function Qr(e){let t=`tabbed-control tabbed-control--${e}`;return x("div",{class:t,hidden:!0},x("button",{class:"tabbed-button",tabIndex:-1,"aria-hidden":"true"}))}function On(e){return x("div",{class:"md-typeset__scrollwrap"},x("div",{class:"md-typeset__table"},e))}function Ca(e){var o;let t=ye(),r=new URL(`../${e.version}/`,t.base);return x("li",{class:"md-version__item"},x("a",{href:`${r}`,class:"md-version__link"},e.title,((o=t.version)==null?void 0:o.alias)&&e.aliases.length>0&&x("span",{class:"md-version__alias"},e.aliases[0])))}function Mn(e,t){var o;let r=ye();return e=e.filter(n=>{var i;return!((i=n.properties)!=null&&i.hidden)}),x("div",{class:"md-version"},x("button",{class:"md-version__current","aria-label":Ee("select.version")},t.title,((o=r.version)==null?void 0:o.alias)&&t.aliases.length>0&&x("span",{class:"md-version__alias"},t.aliases[0])),x("ul",{class:"md-version__list"},e.map(Ca)))}var Ha=0;function ka(e){let t=z([et(e),kt(e)]).pipe(m(([o,n])=>o||n),K()),r=C(()=>Jo(e)).pipe(oe(De),ct(1),m(()=>Xo(e)));return t.pipe(Ae(o=>o),v(()=>z([t,r])),m(([o,n])=>({active:o,offset:n})),pe())}function $a(e,t){let{content$:r,viewport$:o}=t,n=`__tooltip2_${Ha++}`;return C(()=>{let i=new g,a=new _r(!1);i.pipe(X(),ne(!1)).subscribe(a);let s=a.pipe(Ht(c=>Me(+!c*250,Hr)),K(),v(c=>c?r:O),E(c=>c.id=n),pe());z([i.pipe(m(({active:c})=>c)),s.pipe(v(c=>kt(c,250)),Q(!1))]).pipe(m(c=>c.some(l=>l))).subscribe(a);let p=a.pipe(b(c=>c),ee(s,o),m(([c,l,{size:f}])=>{let u=e.getBoundingClientRect(),h=u.width/2;if(l.role==="tooltip")return{x:h,y:8+u.height};if(u.y>=f.height/2){let{height:w}=ce(l);return{x:h,y:-16-w}}else return{x:h,y:16+u.height}}));return z([s,i,p]).subscribe(([c,{offset:l},f])=>{c.style.setProperty("--md-tooltip-host-x",`${l.x}px`),c.style.setProperty("--md-tooltip-host-y",`${l.y}px`),c.style.setProperty("--md-tooltip-x",`${f.x}px`),c.style.setProperty("--md-tooltip-y",`${f.y}px`),c.classList.toggle("md-tooltip2--top",f.y<0),c.classList.toggle("md-tooltip2--bottom",f.y>=0)}),a.pipe(b(c=>c),ee(s,(c,l)=>l),b(c=>c.role==="tooltip")).subscribe(c=>{let l=ce(P(":scope > *",c));c.style.setProperty("--md-tooltip-width",`${l.width}px`),c.style.setProperty("--md-tooltip-tail","0px")}),a.pipe(K(),be(me),ee(s)).subscribe(([c,l])=>{l.classList.toggle("md-tooltip2--active",c)}),z([a.pipe(b(c=>c)),s]).subscribe(([c,l])=>{l.role==="dialog"?(e.setAttribute("aria-controls",n),e.setAttribute("aria-haspopup","dialog")):e.setAttribute("aria-describedby",n)}),a.pipe(b(c=>!c)).subscribe(()=>{e.removeAttribute("aria-controls"),e.removeAttribute("aria-describedby"),e.removeAttribute("aria-haspopup")}),ka(e).pipe(E(c=>i.next(c)),L(()=>i.complete()),m(c=>R({ref:e},c)))})}function lt(e,{viewport$:t},r=document.body){return $a(e,{content$:new F(o=>{let n=e.title,i=yn(n);return o.next(i),e.removeAttribute("title"),r.append(i),()=>{i.remove(),e.setAttribute("title",n)}}),viewport$:t})}function Pa(e,t){let r=C(()=>z([Zo(e),De(t)])).pipe(m(([{x:o,y:n},i])=>{let{width:a,height:s}=ce(e);return{x:o-i.x+a/2,y:n-i.y+s/2}}));return et(e).pipe(v(o=>r.pipe(m(n=>({active:o,offset:n})),Te(+!o||1/0))))}function Ln(e,t,{target$:r}){let[o,n]=Array.from(e.children);return C(()=>{let i=new g,a=i.pipe(X(),ne(!0));return i.subscribe({next({offset:s}){e.style.setProperty("--md-tooltip-x",`${s.x}px`),e.style.setProperty("--md-tooltip-y",`${s.y}px`)},complete(){e.style.removeProperty("--md-tooltip-x"),e.style.removeProperty("--md-tooltip-y")}}),tt(e).pipe(U(a)).subscribe(s=>{e.toggleAttribute("data-md-visible",s)}),S(i.pipe(b(({active:s})=>s)),i.pipe(_e(250),b(({active:s})=>!s))).subscribe({next({active:s}){s?e.prepend(o):o.remove()},complete(){e.prepend(o)}}),i.pipe(Le(16,me)).subscribe(({active:s})=>{o.classList.toggle("md-tooltip--active",s)}),i.pipe(ct(125,me),b(()=>!!e.offsetParent),m(()=>e.offsetParent.getBoundingClientRect()),m(({x:s})=>s)).subscribe({next(s){s?e.style.setProperty("--md-tooltip-0",`${-s}px`):e.style.removeProperty("--md-tooltip-0")},complete(){e.style.removeProperty("--md-tooltip-0")}}),d(n,"click").pipe(U(a),b(s=>!(s.metaKey||s.ctrlKey))).subscribe(s=>{s.stopPropagation(),s.preventDefault()}),d(n,"mousedown").pipe(U(a),ee(i)).subscribe(([s,{active:p}])=>{var c;if(s.button!==0||s.metaKey||s.ctrlKey)s.preventDefault();else if(p){s.preventDefault();let l=e.parentElement.closest(".md-annotation");l instanceof HTMLElement?l.focus():(c=Re())==null||c.blur()}}),r.pipe(U(a),b(s=>s===o),Ge(125)).subscribe(()=>e.focus()),Pa(e,t).pipe(E(s=>i.next(s)),L(()=>i.complete()),m(s=>R({ref:e},s)))})}function Ra(e){return e.tagName==="CODE"?$(".c, .c1, .cm",e):[e]}function Ia(e){let t=[];for(let r of Ra(e)){let o=[],n=document.createNodeIterator(r,NodeFilter.SHOW_TEXT);for(let i=n.nextNode();i;i=n.nextNode())o.push(i);for(let i of o){let a;for(;a=/(\(\d+\))(!)?/.exec(i.textContent);){let[,s,p]=a;if(typeof p=="undefined"){let c=i.splitText(a.index);i=c.splitText(s.length),t.push(c)}else{i.textContent=s,t.push(i);break}}}}return t}function _n(e,t){t.append(...Array.from(e.childNodes))}function fr(e,t,{target$:r,print$:o}){let n=t.closest("[id]"),i=n==null?void 0:n.id,a=new Map;for(let s of Ia(t)){let[,p]=s.textContent.match(/\((\d+)\)/);fe(`:scope > li:nth-child(${p})`,e)&&(a.set(p,En(p,i)),s.replaceWith(a.get(p)))}return a.size===0?O:C(()=>{let s=new g,p=s.pipe(X(),ne(!0)),c=[];for(let[l,f]of a)c.push([P(".md-typeset",f),P(`:scope > li:nth-child(${l})`,e)]);return o.pipe(U(p)).subscribe(l=>{e.hidden=!l,e.classList.toggle("md-annotation-list",l);for(let[f,u]of c)l?_n(f,u):_n(u,f)}),S(...[...a].map(([,l])=>Ln(l,t,{target$:r}))).pipe(L(()=>s.complete()),pe())})}function An(e){if(e.nextElementSibling){let t=e.nextElementSibling;if(t.tagName==="OL")return t;if(t.tagName==="P"&&!t.children.length)return An(t)}}function Cn(e,t){return C(()=>{let r=An(e);return typeof r!="undefined"?fr(r,e,t):O})}var Hn=Vt(Yr());var Fa=0;function kn(e){if(e.nextElementSibling){let t=e.nextElementSibling;if(t.tagName==="OL")return t;if(t.tagName==="P"&&!t.children.length)return kn(t)}}function ja(e){return ge(e).pipe(m(({width:t})=>({scrollable:Tt(e).width>t})),Z("scrollable"))}function $n(e,t){let{matches:r}=matchMedia("(hover)"),o=C(()=>{let n=new g,i=n.pipe(Fr(1));n.subscribe(({scrollable:c})=>{c&&r?e.setAttribute("tabindex","0"):e.removeAttribute("tabindex")});let a=[];if(Hn.default.isSupported()&&(e.closest(".copy")||B("content.code.copy")&&!e.closest(".no-copy"))){let c=e.closest("pre");c.id=`__code_${Fa++}`;let l=wn(c.id);c.insertBefore(l,e),B("content.tooltips")&&a.push(lt(l,{viewport$}))}let s=e.closest(".highlight");if(s instanceof HTMLElement){let c=kn(s);if(typeof c!="undefined"&&(s.classList.contains("annotate")||B("content.code.annotate"))){let l=fr(c,e,t);a.push(ge(s).pipe(U(i),m(({width:f,height:u})=>f&&u),K(),v(f=>f?l:O)))}}return $(":scope > span[id]",e).length&&e.classList.add("md-code__content"),ja(e).pipe(E(c=>n.next(c)),L(()=>n.complete()),m(c=>R({ref:e},c)),Pe(...a))});return B("content.lazy")?tt(e).pipe(b(n=>n),Te(1),v(()=>o)):o}function Wa(e,{target$:t,print$:r}){let o=!0;return S(t.pipe(m(n=>n.closest("details:not([open])")),b(n=>e===n),m(()=>({action:"open",reveal:!0}))),r.pipe(b(n=>n||!o),E(()=>o=e.open),m(n=>({action:n?"open":"close"}))))}function Pn(e,t){return C(()=>{let r=new g;return r.subscribe(({action:o,reveal:n})=>{e.toggleAttribute("open",o==="open"),n&&e.scrollIntoView()}),Wa(e,t).pipe(E(o=>r.next(o)),L(()=>r.complete()),m(o=>R({ref:e},o)))})}var Rn=".node circle,.node ellipse,.node path,.node polygon,.node rect{fill:var(--md-mermaid-node-bg-color);stroke:var(--md-mermaid-node-fg-color)}marker{fill:var(--md-mermaid-edge-color)!important}.edgeLabel .label rect{fill:#0000}.label{color:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}.label foreignObject{line-height:normal;overflow:visible}.label div .edgeLabel{color:var(--md-mermaid-label-fg-color)}.edgeLabel,.edgeLabel rect,.label div .edgeLabel{background-color:var(--md-mermaid-label-bg-color)}.edgeLabel,.edgeLabel rect{fill:var(--md-mermaid-label-bg-color);color:var(--md-mermaid-edge-color)}.edgePath .path,.flowchart-link{stroke:var(--md-mermaid-edge-color);stroke-width:.05rem}.edgePath .arrowheadPath{fill:var(--md-mermaid-edge-color);stroke:none}.cluster rect{fill:var(--md-default-fg-color--lightest);stroke:var(--md-default-fg-color--lighter)}.cluster span{color:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}g #flowchart-circleEnd,g #flowchart-circleStart,g #flowchart-crossEnd,g #flowchart-crossStart,g #flowchart-pointEnd,g #flowchart-pointStart{stroke:none}g.classGroup line,g.classGroup rect{fill:var(--md-mermaid-node-bg-color);stroke:var(--md-mermaid-node-fg-color)}g.classGroup text{fill:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}.classLabel .box{fill:var(--md-mermaid-label-bg-color);background-color:var(--md-mermaid-label-bg-color);opacity:1}.classLabel .label{fill:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}.node .divider{stroke:var(--md-mermaid-node-fg-color)}.relation{stroke:var(--md-mermaid-edge-color)}.cardinality{fill:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}.cardinality text{fill:inherit!important}defs #classDiagram-compositionEnd,defs #classDiagram-compositionStart,defs #classDiagram-dependencyEnd,defs #classDiagram-dependencyStart,defs #classDiagram-extensionEnd,defs #classDiagram-extensionStart{fill:var(--md-mermaid-edge-color)!important;stroke:var(--md-mermaid-edge-color)!important}defs #classDiagram-aggregationEnd,defs #classDiagram-aggregationStart{fill:var(--md-mermaid-label-bg-color)!important;stroke:var(--md-mermaid-edge-color)!important}g.stateGroup rect{fill:var(--md-mermaid-node-bg-color);stroke:var(--md-mermaid-node-fg-color)}g.stateGroup .state-title{fill:var(--md-mermaid-label-fg-color)!important;font-family:var(--md-mermaid-font-family)}g.stateGroup .composit{fill:var(--md-mermaid-label-bg-color)}.nodeLabel,.nodeLabel p{color:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}a .nodeLabel{text-decoration:underline}.node circle.state-end,.node circle.state-start,.start-state{fill:var(--md-mermaid-edge-color);stroke:none}.end-state-inner,.end-state-outer{fill:var(--md-mermaid-edge-color)}.end-state-inner,.node circle.state-end{stroke:var(--md-mermaid-label-bg-color)}.transition{stroke:var(--md-mermaid-edge-color)}[id^=state-fork] rect,[id^=state-join] rect{fill:var(--md-mermaid-edge-color)!important;stroke:none!important}.statediagram-cluster.statediagram-cluster .inner{fill:var(--md-default-bg-color)}.statediagram-cluster rect{fill:var(--md-mermaid-node-bg-color);stroke:var(--md-mermaid-node-fg-color)}.statediagram-state rect.divider{fill:var(--md-default-fg-color--lightest);stroke:var(--md-default-fg-color--lighter)}defs #statediagram-barbEnd{stroke:var(--md-mermaid-edge-color)}.attributeBoxEven,.attributeBoxOdd{fill:var(--md-mermaid-node-bg-color);stroke:var(--md-mermaid-node-fg-color)}.entityBox{fill:var(--md-mermaid-label-bg-color);stroke:var(--md-mermaid-node-fg-color)}.entityLabel{fill:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}.relationshipLabelBox{fill:var(--md-mermaid-label-bg-color);fill-opacity:1;background-color:var(--md-mermaid-label-bg-color);opacity:1}.relationshipLabel{fill:var(--md-mermaid-label-fg-color)}.relationshipLine{stroke:var(--md-mermaid-edge-color)}defs #ONE_OR_MORE_END *,defs #ONE_OR_MORE_START *,defs #ONLY_ONE_END *,defs #ONLY_ONE_START *,defs #ZERO_OR_MORE_END *,defs #ZERO_OR_MORE_START *,defs #ZERO_OR_ONE_END *,defs #ZERO_OR_ONE_START *{stroke:var(--md-mermaid-edge-color)!important}defs #ZERO_OR_MORE_END circle,defs #ZERO_OR_MORE_START circle{fill:var(--md-mermaid-label-bg-color)}.actor{fill:var(--md-mermaid-sequence-actor-bg-color);stroke:var(--md-mermaid-sequence-actor-border-color)}text.actor>tspan{fill:var(--md-mermaid-sequence-actor-fg-color);font-family:var(--md-mermaid-font-family)}line{stroke:var(--md-mermaid-sequence-actor-line-color)}.actor-man circle,.actor-man line{fill:var(--md-mermaid-sequence-actorman-bg-color);stroke:var(--md-mermaid-sequence-actorman-line-color)}.messageLine0,.messageLine1{stroke:var(--md-mermaid-sequence-message-line-color)}.note{fill:var(--md-mermaid-sequence-note-bg-color);stroke:var(--md-mermaid-sequence-note-border-color)}.loopText,.loopText>tspan,.messageText,.noteText>tspan{stroke:none;font-family:var(--md-mermaid-font-family)!important}.messageText{fill:var(--md-mermaid-sequence-message-fg-color)}.loopText,.loopText>tspan{fill:var(--md-mermaid-sequence-loop-fg-color)}.noteText>tspan{fill:var(--md-mermaid-sequence-note-fg-color)}#arrowhead path{fill:var(--md-mermaid-sequence-message-line-color);stroke:none}.loopLine{fill:var(--md-mermaid-sequence-loop-bg-color);stroke:var(--md-mermaid-sequence-loop-border-color)}.labelBox{fill:var(--md-mermaid-sequence-label-bg-color);stroke:none}.labelText,.labelText>span{fill:var(--md-mermaid-sequence-label-fg-color);font-family:var(--md-mermaid-font-family)}.sequenceNumber{fill:var(--md-mermaid-sequence-number-fg-color)}rect.rect{fill:var(--md-mermaid-sequence-box-bg-color);stroke:none}rect.rect+text.text{fill:var(--md-mermaid-sequence-box-fg-color)}defs #sequencenumber{fill:var(--md-mermaid-sequence-number-bg-color)!important}";var Br,Da=0;function Va(){return typeof mermaid=="undefined"||mermaid instanceof Element?wt("https://unpkg.com/mermaid@10/dist/mermaid.min.js"):I(void 0)}function In(e){return e.classList.remove("mermaid"),Br||(Br=Va().pipe(E(()=>mermaid.initialize({startOnLoad:!1,themeCSS:Rn,sequence:{actorFontSize:"16px",messageFontSize:"16px",noteFontSize:"16px"}})),m(()=>{}),G(1))),Br.subscribe(()=>ao(this,null,function*(){e.classList.add("mermaid");let t=`__mermaid_${Da++}`,r=x("div",{class:"mermaid"}),o=e.textContent,{svg:n,fn:i}=yield mermaid.render(t,o),a=r.attachShadow({mode:"closed"});a.innerHTML=n,e.replaceWith(r),i==null||i(a)})),Br.pipe(m(()=>({ref:e})))}var Fn=x("table");function jn(e){return e.replaceWith(Fn),Fn.replaceWith(On(e)),I({ref:e})}function Na(e){let t=e.find(r=>r.checked)||e[0];return S(...e.map(r=>d(r,"change").pipe(m(()=>P(`label[for="${r.id}"]`))))).pipe(Q(P(`label[for="${t.id}"]`)),m(r=>({active:r})))}function Wn(e,{viewport$:t,target$:r}){let o=P(".tabbed-labels",e),n=$(":scope > input",e),i=Qr("prev");e.append(i);let a=Qr("next");return e.append(a),C(()=>{let s=new g,p=s.pipe(X(),ne(!0));z([s,ge(e),tt(e)]).pipe(U(p),Le(1,me)).subscribe({next([{active:c},l]){let f=Ue(c),{width:u}=ce(c);e.style.setProperty("--md-indicator-x",`${f.x}px`),e.style.setProperty("--md-indicator-width",`${u}px`);let h=pr(o);(f.xh.x+l.width)&&o.scrollTo({left:Math.max(0,f.x-16),behavior:"smooth"})},complete(){e.style.removeProperty("--md-indicator-x"),e.style.removeProperty("--md-indicator-width")}}),z([De(o),ge(o)]).pipe(U(p)).subscribe(([c,l])=>{let f=Tt(o);i.hidden=c.x<16,a.hidden=c.x>f.width-l.width-16}),S(d(i,"click").pipe(m(()=>-1)),d(a,"click").pipe(m(()=>1))).pipe(U(p)).subscribe(c=>{let{width:l}=ce(o);o.scrollBy({left:l*c,behavior:"smooth"})}),r.pipe(U(p),b(c=>n.includes(c))).subscribe(c=>c.click()),o.classList.add("tabbed-labels--linked");for(let c of n){let l=P(`label[for="${c.id}"]`);l.replaceChildren(x("a",{href:`#${l.htmlFor}`,tabIndex:-1},...Array.from(l.childNodes))),d(l.firstElementChild,"click").pipe(U(p),b(f=>!(f.metaKey||f.ctrlKey)),E(f=>{f.preventDefault(),f.stopPropagation()})).subscribe(()=>{history.replaceState({},"",`#${l.htmlFor}`),l.click()})}return B("content.tabs.link")&&s.pipe(Ce(1),ee(t)).subscribe(([{active:c},{offset:l}])=>{let f=c.innerText.trim();if(c.hasAttribute("data-md-switching"))c.removeAttribute("data-md-switching");else{let u=e.offsetTop-l.y;for(let w of $("[data-tabs]"))for(let A of $(":scope > input",w)){let te=P(`label[for="${A.id}"]`);if(te!==c&&te.innerText.trim()===f){te.setAttribute("data-md-switching",""),A.click();break}}window.scrollTo({top:e.offsetTop-u});let h=__md_get("__tabs")||[];__md_set("__tabs",[...new Set([f,...h])])}}),s.pipe(U(p)).subscribe(()=>{for(let c of $("audio, video",e))c.pause()}),Na(n).pipe(E(c=>s.next(c)),L(()=>s.complete()),m(c=>R({ref:e},c)))}).pipe(Qe(se))}function Un(e,{viewport$:t,target$:r,print$:o}){return S(...$(".annotate:not(.highlight)",e).map(n=>Cn(n,{target$:r,print$:o})),...$("pre:not(.mermaid) > code",e).map(n=>$n(n,{target$:r,print$:o})),...$("pre.mermaid",e).map(n=>In(n)),...$("table:not([class])",e).map(n=>jn(n)),...$("details",e).map(n=>Pn(n,{target$:r,print$:o})),...$("[data-tabs]",e).map(n=>Wn(n,{viewport$:t,target$:r})),...$("[title]",e).filter(()=>B("content.tooltips")).map(n=>lt(n,{viewport$:t})))}function za(e,{alert$:t}){return t.pipe(v(r=>S(I(!0),I(!1).pipe(Ge(2e3))).pipe(m(o=>({message:r,active:o})))))}function Dn(e,t){let r=P(".md-typeset",e);return C(()=>{let o=new g;return o.subscribe(({message:n,active:i})=>{e.classList.toggle("md-dialog--active",i),r.textContent=n}),za(e,t).pipe(E(n=>o.next(n)),L(()=>o.complete()),m(n=>R({ref:e},n)))})}var qa=0;function Qa(e,t){document.body.append(e);let{width:r}=ce(e);e.style.setProperty("--md-tooltip-width",`${r}px`),e.remove();let o=cr(t),n=typeof o!="undefined"?De(o):I({x:0,y:0}),i=S(et(t),kt(t)).pipe(K());return z([i,n]).pipe(m(([a,s])=>{let{x:p,y:c}=Ue(t),l=ce(t),f=t.closest("table");return f&&t.parentElement&&(p+=f.offsetLeft+t.parentElement.offsetLeft,c+=f.offsetTop+t.parentElement.offsetTop),{active:a,offset:{x:p-s.x+l.width/2-r/2,y:c-s.y+l.height+8}}}))}function Vn(e){let t=e.title;if(!t.length)return O;let r=`__tooltip_${qa++}`,o=Pt(r,"inline"),n=P(".md-typeset",o);return n.innerHTML=t,C(()=>{let i=new g;return i.subscribe({next({offset:a}){o.style.setProperty("--md-tooltip-x",`${a.x}px`),o.style.setProperty("--md-tooltip-y",`${a.y}px`)},complete(){o.style.removeProperty("--md-tooltip-x"),o.style.removeProperty("--md-tooltip-y")}}),S(i.pipe(b(({active:a})=>a)),i.pipe(_e(250),b(({active:a})=>!a))).subscribe({next({active:a}){a?(e.insertAdjacentElement("afterend",o),e.setAttribute("aria-describedby",r),e.removeAttribute("title")):(o.remove(),e.removeAttribute("aria-describedby"),e.setAttribute("title",t))},complete(){o.remove(),e.removeAttribute("aria-describedby"),e.setAttribute("title",t)}}),i.pipe(Le(16,me)).subscribe(({active:a})=>{o.classList.toggle("md-tooltip--active",a)}),i.pipe(ct(125,me),b(()=>!!e.offsetParent),m(()=>e.offsetParent.getBoundingClientRect()),m(({x:a})=>a)).subscribe({next(a){a?o.style.setProperty("--md-tooltip-0",`${-a}px`):o.style.removeProperty("--md-tooltip-0")},complete(){o.style.removeProperty("--md-tooltip-0")}}),Qa(o,e).pipe(E(a=>i.next(a)),L(()=>i.complete()),m(a=>R({ref:e},a)))}).pipe(Qe(se))}function Ka({viewport$:e}){if(!B("header.autohide"))return I(!1);let t=e.pipe(m(({offset:{y:n}})=>n),Ye(2,1),m(([n,i])=>[nMath.abs(i-n.y)>100),m(([,[n]])=>n),K()),o=Ve("search");return z([e,o]).pipe(m(([{offset:n},i])=>n.y>400&&!i),K(),v(n=>n?r:I(!1)),Q(!1))}function Nn(e,t){return C(()=>z([ge(e),Ka(t)])).pipe(m(([{height:r},o])=>({height:r,hidden:o})),K((r,o)=>r.height===o.height&&r.hidden===o.hidden),G(1))}function zn(e,{header$:t,main$:r}){return C(()=>{let o=new g,n=o.pipe(X(),ne(!0));o.pipe(Z("active"),We(t)).subscribe(([{active:a},{hidden:s}])=>{e.classList.toggle("md-header--shadow",a&&!s),e.hidden=s});let i=ue($("[title]",e)).pipe(b(()=>B("content.tooltips")),oe(a=>Vn(a)));return r.subscribe(o),t.pipe(U(n),m(a=>R({ref:e},a)),Pe(i.pipe(U(n))))})}function Ya(e,{viewport$:t,header$:r}){return mr(e,{viewport$:t,header$:r}).pipe(m(({offset:{y:o}})=>{let{height:n}=ce(e);return{active:o>=n}}),Z("active"))}function qn(e,t){return C(()=>{let r=new g;r.subscribe({next({active:n}){e.classList.toggle("md-header__title--active",n)},complete(){e.classList.remove("md-header__title--active")}});let o=fe(".md-content h1");return typeof o=="undefined"?O:Ya(o,t).pipe(E(n=>r.next(n)),L(()=>r.complete()),m(n=>R({ref:e},n)))})}function Qn(e,{viewport$:t,header$:r}){let o=r.pipe(m(({height:i})=>i),K()),n=o.pipe(v(()=>ge(e).pipe(m(({height:i})=>({top:e.offsetTop,bottom:e.offsetTop+i})),Z("bottom"))));return z([o,n,t]).pipe(m(([i,{top:a,bottom:s},{offset:{y:p},size:{height:c}}])=>(c=Math.max(0,c-Math.max(0,a-p,i)-Math.max(0,c+p-s)),{offset:a-i,height:c,active:a-i<=p})),K((i,a)=>i.offset===a.offset&&i.height===a.height&&i.active===a.active))}function Ba(e){let t=__md_get("__palette")||{index:e.findIndex(o=>matchMedia(o.getAttribute("data-md-color-media")).matches)},r=Math.max(0,Math.min(t.index,e.length-1));return I(...e).pipe(oe(o=>d(o,"change").pipe(m(()=>o))),Q(e[r]),m(o=>({index:e.indexOf(o),color:{media:o.getAttribute("data-md-color-media"),scheme:o.getAttribute("data-md-color-scheme"),primary:o.getAttribute("data-md-color-primary"),accent:o.getAttribute("data-md-color-accent")}})),G(1))}function Kn(e){let t=$("input",e),r=x("meta",{name:"theme-color"});document.head.appendChild(r);let o=x("meta",{name:"color-scheme"});document.head.appendChild(o);let n=$t("(prefers-color-scheme: light)");return C(()=>{let i=new g;return i.subscribe(a=>{if(document.body.setAttribute("data-md-color-switching",""),a.color.media==="(prefers-color-scheme)"){let s=matchMedia("(prefers-color-scheme: light)"),p=document.querySelector(s.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");a.color.scheme=p.getAttribute("data-md-color-scheme"),a.color.primary=p.getAttribute("data-md-color-primary"),a.color.accent=p.getAttribute("data-md-color-accent")}for(let[s,p]of Object.entries(a.color))document.body.setAttribute(`data-md-color-${s}`,p);for(let s=0;sa.key==="Enter"),ee(i,(a,s)=>s)).subscribe(({index:a})=>{a=(a+1)%t.length,t[a].click(),t[a].focus()}),i.pipe(m(()=>{let a=Se("header"),s=window.getComputedStyle(a);return o.content=s.colorScheme,s.backgroundColor.match(/\d+/g).map(p=>(+p).toString(16).padStart(2,"0")).join("")})).subscribe(a=>r.content=`#${a}`),i.pipe(be(se)).subscribe(()=>{document.body.removeAttribute("data-md-color-switching")}),Ba(t).pipe(U(n.pipe(Ce(1))),st(),E(a=>i.next(a)),L(()=>i.complete()),m(a=>R({ref:e},a)))})}function Yn(e,{progress$:t}){return C(()=>{let r=new g;return r.subscribe(({value:o})=>{e.style.setProperty("--md-progress-value",`${o}`)}),t.pipe(E(o=>r.next({value:o})),L(()=>r.complete()),m(o=>({ref:e,value:o})))})}var Gr=Vt(Yr());function Ga(e){e.setAttribute("data-md-copying","");let t=e.closest("[data-copy]"),r=t?t.getAttribute("data-copy"):e.innerText;return e.removeAttribute("data-md-copying"),r.trimEnd()}function Bn({alert$:e}){Gr.default.isSupported()&&new F(t=>{new Gr.default("[data-clipboard-target], [data-clipboard-text]",{text:r=>r.getAttribute("data-clipboard-text")||Ga(P(r.getAttribute("data-clipboard-target")))}).on("success",r=>t.next(r))}).pipe(E(t=>{t.trigger.focus()}),m(()=>Ee("clipboard.copied"))).subscribe(e)}function Gn(e,t){return e.protocol=t.protocol,e.hostname=t.hostname,e}function Ja(e,t){let r=new Map;for(let o of $("url",e)){let n=P("loc",o),i=[Gn(new URL(n.textContent),t)];r.set(`${i[0]}`,i);for(let a of $("[rel=alternate]",o)){let s=a.getAttribute("href");s!=null&&i.push(Gn(new URL(s),t))}}return r}function ur(e){return mn(new URL("sitemap.xml",e)).pipe(m(t=>Ja(t,new URL(e))),ve(()=>I(new Map)))}function Xa(e,t){if(!(e.target instanceof Element))return O;let r=e.target.closest("a");if(r===null)return O;if(r.target||e.metaKey||e.ctrlKey)return O;let o=new URL(r.href);return o.search=o.hash="",t.has(`${o}`)?(e.preventDefault(),I(new URL(r.href))):O}function Jn(e){let t=new Map;for(let r of $(":scope > *",e.head))t.set(r.outerHTML,r);return t}function Xn(e){for(let t of $("[href], [src]",e))for(let r of["href","src"]){let o=t.getAttribute(r);if(o&&!/^(?:[a-z]+:)?\/\//i.test(o)){t[r]=t[r];break}}return I(e)}function Za(e){for(let o of["[data-md-component=announce]","[data-md-component=container]","[data-md-component=header-topic]","[data-md-component=outdated]","[data-md-component=logo]","[data-md-component=skip]",...B("navigation.tabs.sticky")?["[data-md-component=tabs]"]:[]]){let n=fe(o),i=fe(o,e);typeof n!="undefined"&&typeof i!="undefined"&&n.replaceWith(i)}let t=Jn(document);for(let[o,n]of Jn(e))t.has(o)?t.delete(o):document.head.appendChild(n);for(let o of t.values()){let n=o.getAttribute("name");n!=="theme-color"&&n!=="color-scheme"&&o.remove()}let r=Se("container");return je($("script",r)).pipe(v(o=>{let n=e.createElement("script");if(o.src){for(let i of o.getAttributeNames())n.setAttribute(i,o.getAttribute(i));return o.replaceWith(n),new F(i=>{n.onload=()=>i.complete()})}else return n.textContent=o.textContent,o.replaceWith(n),O}),X(),ne(document))}function Zn({location$:e,viewport$:t,progress$:r}){let o=ye();if(location.protocol==="file:")return O;let n=ur(o.base);I(document).subscribe(Xn);let i=d(document.body,"click").pipe(We(n),v(([p,c])=>Xa(p,c)),pe()),a=d(window,"popstate").pipe(m(xe),pe());i.pipe(ee(t)).subscribe(([p,{offset:c}])=>{history.replaceState(c,""),history.pushState(null,"",p)}),S(i,a).subscribe(e);let s=e.pipe(Z("pathname"),v(p=>ln(p,{progress$:r}).pipe(ve(()=>(pt(p,!0),O)))),v(Xn),v(Za),pe());return S(s.pipe(ee(e,(p,c)=>c)),s.pipe(v(()=>e),Z("pathname"),v(()=>e),Z("hash")),e.pipe(K((p,c)=>p.pathname===c.pathname&&p.hash===c.hash),v(()=>i),E(()=>history.back()))).subscribe(p=>{var c,l;history.state!==null||!p.hash?window.scrollTo(0,(l=(c=history.state)==null?void 0:c.y)!=null?l:0):(history.scrollRestoration="auto",sn(p.hash),history.scrollRestoration="manual")}),e.subscribe(()=>{history.scrollRestoration="manual"}),d(window,"beforeunload").subscribe(()=>{history.scrollRestoration="auto"}),t.pipe(Z("offset"),_e(100)).subscribe(({offset:p})=>{history.replaceState(p,"")}),s}var ri=Vt(ti());function oi(e){let t=e.separator.split("|").map(n=>n.replace(/(\(\?[!=<][^)]+\))/g,"").length===0?"\uFFFD":n).join("|"),r=new RegExp(t,"img"),o=(n,i,a)=>`${i}${a}`;return n=>{n=n.replace(/[\s*+\-:~^]+/g," ").trim();let i=new RegExp(`(^|${e.separator}|)(${n.replace(/[|\\{}()[\]^$+*?.-]/g,"\\$&").replace(r,"|")})`,"img");return a=>(0,ri.default)(a).replace(i,o).replace(/<\/mark>(\s+)]*>/img,"$1")}}function It(e){return e.type===1}function dr(e){return e.type===3}function ni(e,t){let r=vn(e);return S(I(location.protocol!=="file:"),Ve("search")).pipe(Ae(o=>o),v(()=>t)).subscribe(({config:o,docs:n})=>r.next({type:0,data:{config:o,docs:n,options:{suggest:B("search.suggest")}}})),r}function ii({document$:e}){let t=ye(),r=Ne(new URL("../versions.json",t.base)).pipe(ve(()=>O)),o=r.pipe(m(n=>{let[,i]=t.base.match(/([^/]+)\/?$/);return n.find(({version:a,aliases:s})=>a===i||s.includes(i))||n[0]}));r.pipe(m(n=>new Map(n.map(i=>[`${new URL(`../${i.version}/`,t.base)}`,i]))),v(n=>d(document.body,"click").pipe(b(i=>!i.metaKey&&!i.ctrlKey),ee(o),v(([i,a])=>{if(i.target instanceof Element){let s=i.target.closest("a");if(s&&!s.target&&n.has(s.href)){let p=s.href;return!i.target.closest(".md-version")&&n.get(p)===a?O:(i.preventDefault(),I(p))}}return O}),v(i=>ur(new URL(i)).pipe(m(a=>{let p=xe().href.replace(t.base,i);return a.has(p.split("#")[0])?new URL(p):new URL(i)})))))).subscribe(n=>pt(n,!0)),z([r,o]).subscribe(([n,i])=>{P(".md-header__topic").appendChild(Mn(n,i))}),e.pipe(v(()=>o)).subscribe(n=>{var a;let i=__md_get("__outdated",sessionStorage);if(i===null){i=!0;let s=((a=t.version)==null?void 0:a.default)||"latest";Array.isArray(s)||(s=[s]);e:for(let p of s)for(let c of n.aliases.concat(n.version))if(new RegExp(p,"i").test(c)){i=!1;break e}__md_set("__outdated",i,sessionStorage)}if(i)for(let s of ae("outdated"))s.hidden=!1})}function ns(e,{worker$:t}){let{searchParams:r}=xe();r.has("q")&&(Je("search",!0),e.value=r.get("q"),e.focus(),Ve("search").pipe(Ae(i=>!i)).subscribe(()=>{let i=xe();i.searchParams.delete("q"),history.replaceState({},"",`${i}`)}));let o=et(e),n=S(t.pipe(Ae(It)),d(e,"keyup"),o).pipe(m(()=>e.value),K());return z([n,o]).pipe(m(([i,a])=>({value:i,focus:a})),G(1))}function ai(e,{worker$:t}){let r=new g,o=r.pipe(X(),ne(!0));z([t.pipe(Ae(It)),r],(i,a)=>a).pipe(Z("value")).subscribe(({value:i})=>t.next({type:2,data:i})),r.pipe(Z("focus")).subscribe(({focus:i})=>{i&&Je("search",i)}),d(e.form,"reset").pipe(U(o)).subscribe(()=>e.focus());let n=P("header [for=__search]");return d(n,"click").subscribe(()=>e.focus()),ns(e,{worker$:t}).pipe(E(i=>r.next(i)),L(()=>r.complete()),m(i=>R({ref:e},i)),G(1))}function si(e,{worker$:t,query$:r}){let o=new g,n=tn(e.parentElement).pipe(b(Boolean)),i=e.parentElement,a=P(":scope > :first-child",e),s=P(":scope > :last-child",e);Ve("search").subscribe(l=>s.setAttribute("role",l?"list":"presentation")),o.pipe(ee(r),Ur(t.pipe(Ae(It)))).subscribe(([{items:l},{value:f}])=>{switch(l.length){case 0:a.textContent=f.length?Ee("search.result.none"):Ee("search.result.placeholder");break;case 1:a.textContent=Ee("search.result.one");break;default:let u=sr(l.length);a.textContent=Ee("search.result.other",u)}});let p=o.pipe(E(()=>s.innerHTML=""),v(({items:l})=>S(I(...l.slice(0,10)),I(...l.slice(10)).pipe(Ye(4),Vr(n),v(([f])=>f)))),m(Tn),pe());return p.subscribe(l=>s.appendChild(l)),p.pipe(oe(l=>{let f=fe("details",l);return typeof f=="undefined"?O:d(f,"toggle").pipe(U(o),m(()=>f))})).subscribe(l=>{l.open===!1&&l.offsetTop<=i.scrollTop&&i.scrollTo({top:l.offsetTop})}),t.pipe(b(dr),m(({data:l})=>l)).pipe(E(l=>o.next(l)),L(()=>o.complete()),m(l=>R({ref:e},l)))}function is(e,{query$:t}){return t.pipe(m(({value:r})=>{let o=xe();return o.hash="",r=r.replace(/\s+/g,"+").replace(/&/g,"%26").replace(/=/g,"%3D"),o.search=`q=${r}`,{url:o}}))}function ci(e,t){let r=new g,o=r.pipe(X(),ne(!0));return r.subscribe(({url:n})=>{e.setAttribute("data-clipboard-text",e.href),e.href=`${n}`}),d(e,"click").pipe(U(o)).subscribe(n=>n.preventDefault()),is(e,t).pipe(E(n=>r.next(n)),L(()=>r.complete()),m(n=>R({ref:e},n)))}function pi(e,{worker$:t,keyboard$:r}){let o=new g,n=Se("search-query"),i=S(d(n,"keydown"),d(n,"focus")).pipe(be(se),m(()=>n.value),K());return o.pipe(We(i),m(([{suggest:s},p])=>{let c=p.split(/([\s-]+)/);if(s!=null&&s.length&&c[c.length-1]){let l=s[s.length-1];l.startsWith(c[c.length-1])&&(c[c.length-1]=l)}else c.length=0;return c})).subscribe(s=>e.innerHTML=s.join("").replace(/\s/g," ")),r.pipe(b(({mode:s})=>s==="search")).subscribe(s=>{switch(s.type){case"ArrowRight":e.innerText.length&&n.selectionStart===n.value.length&&(n.value=e.innerText);break}}),t.pipe(b(dr),m(({data:s})=>s)).pipe(E(s=>o.next(s)),L(()=>o.complete()),m(()=>({ref:e})))}function li(e,{index$:t,keyboard$:r}){let o=ye();try{let n=ni(o.search,t),i=Se("search-query",e),a=Se("search-result",e);d(e,"click").pipe(b(({target:p})=>p instanceof Element&&!!p.closest("a"))).subscribe(()=>Je("search",!1)),r.pipe(b(({mode:p})=>p==="search")).subscribe(p=>{let c=Re();switch(p.type){case"Enter":if(c===i){let l=new Map;for(let f of $(":first-child [href]",a)){let u=f.firstElementChild;l.set(f,parseFloat(u.getAttribute("data-md-score")))}if(l.size){let[[f]]=[...l].sort(([,u],[,h])=>h-u);f.click()}p.claim()}break;case"Escape":case"Tab":Je("search",!1),i.blur();break;case"ArrowUp":case"ArrowDown":if(typeof c=="undefined")i.focus();else{let l=[i,...$(":not(details) > [href], summary, details[open] [href]",a)],f=Math.max(0,(Math.max(0,l.indexOf(c))+l.length+(p.type==="ArrowUp"?-1:1))%l.length);l[f].focus()}p.claim();break;default:i!==Re()&&i.focus()}}),r.pipe(b(({mode:p})=>p==="global")).subscribe(p=>{switch(p.type){case"f":case"s":case"/":i.focus(),i.select(),p.claim();break}});let s=ai(i,{worker$:n});return S(s,si(a,{worker$:n,query$:s})).pipe(Pe(...ae("search-share",e).map(p=>ci(p,{query$:s})),...ae("search-suggest",e).map(p=>pi(p,{worker$:n,keyboard$:r}))))}catch(n){return e.hidden=!0,Ke}}function mi(e,{index$:t,location$:r}){return z([t,r.pipe(Q(xe()),b(o=>!!o.searchParams.get("h")))]).pipe(m(([o,n])=>oi(o.config)(n.searchParams.get("h"))),m(o=>{var a;let n=new Map,i=document.createNodeIterator(e,NodeFilter.SHOW_TEXT);for(let s=i.nextNode();s;s=i.nextNode())if((a=s.parentElement)!=null&&a.offsetHeight){let p=s.textContent,c=o(p);c.length>p.length&&n.set(s,c)}for(let[s,p]of n){let{childNodes:c}=x("span",null,p);s.replaceWith(...Array.from(c))}return{ref:e,nodes:n}}))}function as(e,{viewport$:t,main$:r}){let o=e.closest(".md-grid"),n=o.offsetTop-o.parentElement.offsetTop;return z([r,t]).pipe(m(([{offset:i,height:a},{offset:{y:s}}])=>(a=a+Math.min(n,Math.max(0,s-i))-n,{height:a,locked:s>=i+n})),K((i,a)=>i.height===a.height&&i.locked===a.locked))}function Jr(e,o){var n=o,{header$:t}=n,r=io(n,["header$"]);let i=P(".md-sidebar__scrollwrap",e),{y:a}=Ue(i);return C(()=>{let s=new g,p=s.pipe(X(),ne(!0)),c=s.pipe(Le(0,me));return c.pipe(ee(t)).subscribe({next([{height:l},{height:f}]){i.style.height=`${l-2*a}px`,e.style.top=`${f}px`},complete(){i.style.height="",e.style.top=""}}),c.pipe(Ae()).subscribe(()=>{for(let l of $(".md-nav__link--active[href]",e)){if(!l.clientHeight)continue;let f=l.closest(".md-sidebar__scrollwrap");if(typeof f!="undefined"){let u=l.offsetTop-f.offsetTop,{height:h}=ce(f);f.scrollTo({top:u-h/2})}}}),ue($("label[tabindex]",e)).pipe(oe(l=>d(l,"click").pipe(be(se),m(()=>l),U(p)))).subscribe(l=>{let f=P(`[id="${l.htmlFor}"]`);P(`[aria-labelledby="${l.id}"]`).setAttribute("aria-expanded",`${f.checked}`)}),as(e,r).pipe(E(l=>s.next(l)),L(()=>s.complete()),m(l=>R({ref:e},l)))})}function fi(e,t){if(typeof t!="undefined"){let r=`https://api.github.com/repos/${e}/${t}`;return Ct(Ne(`${r}/releases/latest`).pipe(ve(()=>O),m(o=>({version:o.tag_name})),Be({})),Ne(r).pipe(ve(()=>O),m(o=>({stars:o.stargazers_count,forks:o.forks_count})),Be({}))).pipe(m(([o,n])=>R(R({},o),n)))}else{let r=`https://api.github.com/users/${e}`;return Ne(r).pipe(m(o=>({repositories:o.public_repos})),Be({}))}}function ui(e,t){let r=`https://${e}/api/v4/projects/${encodeURIComponent(t)}`;return Ne(r).pipe(ve(()=>O),m(({star_count:o,forks_count:n})=>({stars:o,forks:n})),Be({}))}function di(e){let t=e.match(/^.+github\.com\/([^/]+)\/?([^/]+)?/i);if(t){let[,r,o]=t;return fi(r,o)}if(t=e.match(/^.+?([^/]*gitlab[^/]+)\/(.+?)\/?$/i),t){let[,r,o]=t;return ui(r,o)}return O}var ss;function cs(e){return ss||(ss=C(()=>{let t=__md_get("__source",sessionStorage);if(t)return I(t);if(ae("consent").length){let o=__md_get("__consent");if(!(o&&o.github))return O}return di(e.href).pipe(E(o=>__md_set("__source",o,sessionStorage)))}).pipe(ve(()=>O),b(t=>Object.keys(t).length>0),m(t=>({facts:t})),G(1)))}function hi(e){let t=P(":scope > :last-child",e);return C(()=>{let r=new g;return r.subscribe(({facts:o})=>{t.appendChild(Sn(o)),t.classList.add("md-source__repository--active")}),cs(e).pipe(E(o=>r.next(o)),L(()=>r.complete()),m(o=>R({ref:e},o)))})}function ps(e,{viewport$:t,header$:r}){return ge(document.body).pipe(v(()=>mr(e,{header$:r,viewport$:t})),m(({offset:{y:o}})=>({hidden:o>=10})),Z("hidden"))}function bi(e,t){return C(()=>{let r=new g;return r.subscribe({next({hidden:o}){e.hidden=o},complete(){e.hidden=!1}}),(B("navigation.tabs.sticky")?I({hidden:!1}):ps(e,t)).pipe(E(o=>r.next(o)),L(()=>r.complete()),m(o=>R({ref:e},o)))})}function ls(e,{viewport$:t,header$:r}){let o=new Map,n=$(".md-nav__link",e);for(let s of n){let p=decodeURIComponent(s.hash.substring(1)),c=fe(`[id="${p}"]`);typeof c!="undefined"&&o.set(s,c)}let i=r.pipe(Z("height"),m(({height:s})=>{let p=Se("main"),c=P(":scope > :first-child",p);return s+.8*(c.offsetTop-p.offsetTop)}),pe());return ge(document.body).pipe(Z("height"),v(s=>C(()=>{let p=[];return I([...o].reduce((c,[l,f])=>{for(;p.length&&o.get(p[p.length-1]).tagName>=f.tagName;)p.pop();let u=f.offsetTop;for(;!u&&f.parentElement;)f=f.parentElement,u=f.offsetTop;let h=f.offsetParent;for(;h;h=h.offsetParent)u+=h.offsetTop;return c.set([...p=[...p,l]].reverse(),u)},new Map))}).pipe(m(p=>new Map([...p].sort(([,c],[,l])=>c-l))),We(i),v(([p,c])=>t.pipe(jr(([l,f],{offset:{y:u},size:h})=>{let w=u+h.height>=Math.floor(s.height);for(;f.length;){let[,A]=f[0];if(A-c=u&&!w)f=[l.pop(),...f];else break}return[l,f]},[[],[...p]]),K((l,f)=>l[0]===f[0]&&l[1]===f[1])))))).pipe(m(([s,p])=>({prev:s.map(([c])=>c),next:p.map(([c])=>c)})),Q({prev:[],next:[]}),Ye(2,1),m(([s,p])=>s.prev.length{let i=new g,a=i.pipe(X(),ne(!0));if(i.subscribe(({prev:s,next:p})=>{for(let[c]of p)c.classList.remove("md-nav__link--passed"),c.classList.remove("md-nav__link--active");for(let[c,[l]]of s.entries())l.classList.add("md-nav__link--passed"),l.classList.toggle("md-nav__link--active",c===s.length-1)}),B("toc.follow")){let s=S(t.pipe(_e(1),m(()=>{})),t.pipe(_e(250),m(()=>"smooth")));i.pipe(b(({prev:p})=>p.length>0),We(o.pipe(be(se))),ee(s)).subscribe(([[{prev:p}],c])=>{let[l]=p[p.length-1];if(l.offsetHeight){let f=cr(l);if(typeof f!="undefined"){let u=l.offsetTop-f.offsetTop,{height:h}=ce(f);f.scrollTo({top:u-h/2,behavior:c})}}})}return B("navigation.tracking")&&t.pipe(U(a),Z("offset"),_e(250),Ce(1),U(n.pipe(Ce(1))),st({delay:250}),ee(i)).subscribe(([,{prev:s}])=>{let p=xe(),c=s[s.length-1];if(c&&c.length){let[l]=c,{hash:f}=new URL(l.href);p.hash!==f&&(p.hash=f,history.replaceState({},"",`${p}`))}else p.hash="",history.replaceState({},"",`${p}`)}),ls(e,{viewport$:t,header$:r}).pipe(E(s=>i.next(s)),L(()=>i.complete()),m(s=>R({ref:e},s)))})}function ms(e,{viewport$:t,main$:r,target$:o}){let n=t.pipe(m(({offset:{y:a}})=>a),Ye(2,1),m(([a,s])=>a>s&&s>0),K()),i=r.pipe(m(({active:a})=>a));return z([i,n]).pipe(m(([a,s])=>!(a&&s)),K(),U(o.pipe(Ce(1))),ne(!0),st({delay:250}),m(a=>({hidden:a})))}function gi(e,{viewport$:t,header$:r,main$:o,target$:n}){let i=new g,a=i.pipe(X(),ne(!0));return i.subscribe({next({hidden:s}){e.hidden=s,s?(e.setAttribute("tabindex","-1"),e.blur()):e.removeAttribute("tabindex")},complete(){e.style.top="",e.hidden=!0,e.removeAttribute("tabindex")}}),r.pipe(U(a),Z("height")).subscribe(({height:s})=>{e.style.top=`${s+16}px`}),d(e,"click").subscribe(s=>{s.preventDefault(),window.scrollTo({top:0})}),ms(e,{viewport$:t,main$:o,target$:n}).pipe(E(s=>i.next(s)),L(()=>i.complete()),m(s=>R({ref:e},s)))}function xi({document$:e,viewport$:t}){e.pipe(v(()=>$(".md-ellipsis")),oe(r=>tt(r).pipe(U(e.pipe(Ce(1))),b(o=>o),m(()=>r),Te(1))),b(r=>r.offsetWidth{let o=r.innerText,n=r.closest("a")||r;return n.title=o,B("content.tooltips")?lt(n,{viewport$:t}).pipe(U(e.pipe(Ce(1))),L(()=>n.removeAttribute("title"))):O})).subscribe(),B("content.tooltips")&&e.pipe(v(()=>$(".md-status")),oe(r=>lt(r,{viewport$:t}))).subscribe()}function yi({document$:e,tablet$:t}){e.pipe(v(()=>$(".md-toggle--indeterminate")),E(r=>{r.indeterminate=!0,r.checked=!1}),oe(r=>d(r,"change").pipe(Dr(()=>r.classList.contains("md-toggle--indeterminate")),m(()=>r))),ee(t)).subscribe(([r,o])=>{r.classList.remove("md-toggle--indeterminate"),o&&(r.checked=!1)})}function fs(){return/(iPad|iPhone|iPod)/.test(navigator.userAgent)}function Ei({document$:e}){e.pipe(v(()=>$("[data-md-scrollfix]")),E(t=>t.removeAttribute("data-md-scrollfix")),b(fs),oe(t=>d(t,"touchstart").pipe(m(()=>t)))).subscribe(t=>{let r=t.scrollTop;r===0?t.scrollTop=1:r+t.offsetHeight===t.scrollHeight&&(t.scrollTop=r-1)})}function wi({viewport$:e,tablet$:t}){z([Ve("search"),t]).pipe(m(([r,o])=>r&&!o),v(r=>I(r).pipe(Ge(r?400:100))),ee(e)).subscribe(([r,{offset:{y:o}}])=>{if(r)document.body.setAttribute("data-md-scrolllock",""),document.body.style.top=`-${o}px`;else{let n=-1*parseInt(document.body.style.top,10);document.body.removeAttribute("data-md-scrolllock"),document.body.style.top="",n&&window.scrollTo(0,n)}})}Object.entries||(Object.entries=function(e){let t=[];for(let r of Object.keys(e))t.push([r,e[r]]);return t});Object.values||(Object.values=function(e){let t=[];for(let r of Object.keys(e))t.push(e[r]);return t});typeof Element!="undefined"&&(Element.prototype.scrollTo||(Element.prototype.scrollTo=function(e,t){typeof e=="object"?(this.scrollLeft=e.left,this.scrollTop=e.top):(this.scrollLeft=e,this.scrollTop=t)}),Element.prototype.replaceWith||(Element.prototype.replaceWith=function(...e){let t=this.parentNode;if(t){e.length===0&&t.removeChild(this);for(let r=e.length-1;r>=0;r--){let o=e[r];typeof o=="string"?o=document.createTextNode(o):o.parentNode&&o.parentNode.removeChild(o),r?t.insertBefore(this.previousSibling,o):t.replaceChild(o,this)}}}));function us(){return location.protocol==="file:"?wt(`${new URL("search/search_index.js",Xr.base)}`).pipe(m(()=>__index),G(1)):Ne(new URL("search/search_index.json",Xr.base))}document.documentElement.classList.remove("no-js");document.documentElement.classList.add("js");var ot=Yo(),jt=nn(),Ot=cn(jt),Zr=on(),Oe=bn(),hr=$t("(min-width: 960px)"),Si=$t("(min-width: 1220px)"),Oi=pn(),Xr=ye(),Mi=document.forms.namedItem("search")?us():Ke,eo=new g;Bn({alert$:eo});var to=new g;B("navigation.instant")&&Zn({location$:jt,viewport$:Oe,progress$:to}).subscribe(ot);var Ti;((Ti=Xr.version)==null?void 0:Ti.provider)==="mike"&&ii({document$:ot});S(jt,Ot).pipe(Ge(125)).subscribe(()=>{Je("drawer",!1),Je("search",!1)});Zr.pipe(b(({mode:e})=>e==="global")).subscribe(e=>{switch(e.type){case"p":case",":let t=fe("link[rel=prev]");typeof t!="undefined"&&pt(t);break;case"n":case".":let r=fe("link[rel=next]");typeof r!="undefined"&&pt(r);break;case"Enter":let o=Re();o instanceof HTMLLabelElement&&o.click()}});xi({viewport$:Oe,document$:ot});yi({document$:ot,tablet$:hr});Ei({document$:ot});wi({viewport$:Oe,tablet$:hr});var rt=Nn(Se("header"),{viewport$:Oe}),Ft=ot.pipe(m(()=>Se("main")),v(e=>Qn(e,{viewport$:Oe,header$:rt})),G(1)),ds=S(...ae("consent").map(e=>xn(e,{target$:Ot})),...ae("dialog").map(e=>Dn(e,{alert$:eo})),...ae("header").map(e=>zn(e,{viewport$:Oe,header$:rt,main$:Ft})),...ae("palette").map(e=>Kn(e)),...ae("progress").map(e=>Yn(e,{progress$:to})),...ae("search").map(e=>li(e,{index$:Mi,keyboard$:Zr})),...ae("source").map(e=>hi(e))),hs=C(()=>S(...ae("announce").map(e=>gn(e)),...ae("content").map(e=>Un(e,{viewport$:Oe,target$:Ot,print$:Oi})),...ae("content").map(e=>B("search.highlight")?mi(e,{index$:Mi,location$:jt}):O),...ae("header-title").map(e=>qn(e,{viewport$:Oe,header$:rt})),...ae("sidebar").map(e=>e.getAttribute("data-md-type")==="navigation"?Nr(Si,()=>Jr(e,{viewport$:Oe,header$:rt,main$:Ft})):Nr(hr,()=>Jr(e,{viewport$:Oe,header$:rt,main$:Ft}))),...ae("tabs").map(e=>bi(e,{viewport$:Oe,header$:rt})),...ae("toc").map(e=>vi(e,{viewport$:Oe,header$:rt,main$:Ft,target$:Ot})),...ae("top").map(e=>gi(e,{viewport$:Oe,header$:rt,main$:Ft,target$:Ot})))),Li=ot.pipe(v(()=>hs),Pe(ds),G(1));Li.subscribe();window.document$=ot;window.location$=jt;window.target$=Ot;window.keyboard$=Zr;window.viewport$=Oe;window.tablet$=hr;window.screen$=Si;window.print$=Oi;window.alert$=eo;window.progress$=to;window.component$=Li;})(); +//# sourceMappingURL=bundle.fe8b6f2b.min.js.map + diff --git a/assets/javascripts/bundle.fe8b6f2b.min.js.map b/assets/javascripts/bundle.fe8b6f2b.min.js.map new file mode 100644 index 000000000..82635852a --- /dev/null +++ b/assets/javascripts/bundle.fe8b6f2b.min.js.map @@ -0,0 +1,7 @@ +{ + "version": 3, + "sources": ["node_modules/focus-visible/dist/focus-visible.js", "node_modules/clipboard/dist/clipboard.js", "node_modules/escape-html/index.js", "src/templates/assets/javascripts/bundle.ts", "node_modules/rxjs/node_modules/tslib/tslib.es6.js", "node_modules/rxjs/src/internal/util/isFunction.ts", "node_modules/rxjs/src/internal/util/createErrorClass.ts", "node_modules/rxjs/src/internal/util/UnsubscriptionError.ts", "node_modules/rxjs/src/internal/util/arrRemove.ts", "node_modules/rxjs/src/internal/Subscription.ts", "node_modules/rxjs/src/internal/config.ts", "node_modules/rxjs/src/internal/scheduler/timeoutProvider.ts", "node_modules/rxjs/src/internal/util/reportUnhandledError.ts", "node_modules/rxjs/src/internal/util/noop.ts", "node_modules/rxjs/src/internal/NotificationFactories.ts", "node_modules/rxjs/src/internal/util/errorContext.ts", "node_modules/rxjs/src/internal/Subscriber.ts", "node_modules/rxjs/src/internal/symbol/observable.ts", "node_modules/rxjs/src/internal/util/identity.ts", "node_modules/rxjs/src/internal/util/pipe.ts", "node_modules/rxjs/src/internal/Observable.ts", "node_modules/rxjs/src/internal/util/lift.ts", "node_modules/rxjs/src/internal/operators/OperatorSubscriber.ts", "node_modules/rxjs/src/internal/scheduler/animationFrameProvider.ts", "node_modules/rxjs/src/internal/util/ObjectUnsubscribedError.ts", "node_modules/rxjs/src/internal/Subject.ts", "node_modules/rxjs/src/internal/BehaviorSubject.ts", "node_modules/rxjs/src/internal/scheduler/dateTimestampProvider.ts", "node_modules/rxjs/src/internal/ReplaySubject.ts", "node_modules/rxjs/src/internal/scheduler/Action.ts", "node_modules/rxjs/src/internal/scheduler/intervalProvider.ts", "node_modules/rxjs/src/internal/scheduler/AsyncAction.ts", "node_modules/rxjs/src/internal/Scheduler.ts", "node_modules/rxjs/src/internal/scheduler/AsyncScheduler.ts", "node_modules/rxjs/src/internal/scheduler/async.ts", "node_modules/rxjs/src/internal/scheduler/QueueAction.ts", "node_modules/rxjs/src/internal/scheduler/QueueScheduler.ts", "node_modules/rxjs/src/internal/scheduler/queue.ts", "node_modules/rxjs/src/internal/scheduler/AnimationFrameAction.ts", "node_modules/rxjs/src/internal/scheduler/AnimationFrameScheduler.ts", "node_modules/rxjs/src/internal/scheduler/animationFrame.ts", "node_modules/rxjs/src/internal/observable/empty.ts", "node_modules/rxjs/src/internal/util/isScheduler.ts", "node_modules/rxjs/src/internal/util/args.ts", "node_modules/rxjs/src/internal/util/isArrayLike.ts", "node_modules/rxjs/src/internal/util/isPromise.ts", "node_modules/rxjs/src/internal/util/isInteropObservable.ts", "node_modules/rxjs/src/internal/util/isAsyncIterable.ts", "node_modules/rxjs/src/internal/util/throwUnobservableError.ts", "node_modules/rxjs/src/internal/symbol/iterator.ts", "node_modules/rxjs/src/internal/util/isIterable.ts", "node_modules/rxjs/src/internal/util/isReadableStreamLike.ts", "node_modules/rxjs/src/internal/observable/innerFrom.ts", "node_modules/rxjs/src/internal/util/executeSchedule.ts", "node_modules/rxjs/src/internal/operators/observeOn.ts", "node_modules/rxjs/src/internal/operators/subscribeOn.ts", "node_modules/rxjs/src/internal/scheduled/scheduleObservable.ts", "node_modules/rxjs/src/internal/scheduled/schedulePromise.ts", "node_modules/rxjs/src/internal/scheduled/scheduleArray.ts", "node_modules/rxjs/src/internal/scheduled/scheduleIterable.ts", "node_modules/rxjs/src/internal/scheduled/scheduleAsyncIterable.ts", "node_modules/rxjs/src/internal/scheduled/scheduleReadableStreamLike.ts", "node_modules/rxjs/src/internal/scheduled/scheduled.ts", "node_modules/rxjs/src/internal/observable/from.ts", "node_modules/rxjs/src/internal/observable/of.ts", "node_modules/rxjs/src/internal/observable/throwError.ts", "node_modules/rxjs/src/internal/util/EmptyError.ts", "node_modules/rxjs/src/internal/util/isDate.ts", "node_modules/rxjs/src/internal/operators/map.ts", "node_modules/rxjs/src/internal/util/mapOneOrManyArgs.ts", "node_modules/rxjs/src/internal/util/argsArgArrayOrObject.ts", "node_modules/rxjs/src/internal/util/createObject.ts", "node_modules/rxjs/src/internal/observable/combineLatest.ts", "node_modules/rxjs/src/internal/operators/mergeInternals.ts", "node_modules/rxjs/src/internal/operators/mergeMap.ts", "node_modules/rxjs/src/internal/operators/mergeAll.ts", "node_modules/rxjs/src/internal/operators/concatAll.ts", "node_modules/rxjs/src/internal/observable/concat.ts", "node_modules/rxjs/src/internal/observable/defer.ts", "node_modules/rxjs/src/internal/observable/fromEvent.ts", "node_modules/rxjs/src/internal/observable/fromEventPattern.ts", "node_modules/rxjs/src/internal/observable/timer.ts", "node_modules/rxjs/src/internal/observable/merge.ts", "node_modules/rxjs/src/internal/observable/never.ts", "node_modules/rxjs/src/internal/util/argsOrArgArray.ts", "node_modules/rxjs/src/internal/operators/filter.ts", "node_modules/rxjs/src/internal/observable/zip.ts", "node_modules/rxjs/src/internal/operators/audit.ts", "node_modules/rxjs/src/internal/operators/auditTime.ts", "node_modules/rxjs/src/internal/operators/bufferCount.ts", "node_modules/rxjs/src/internal/operators/catchError.ts", "node_modules/rxjs/src/internal/operators/scanInternals.ts", "node_modules/rxjs/src/internal/operators/combineLatest.ts", "node_modules/rxjs/src/internal/operators/combineLatestWith.ts", "node_modules/rxjs/src/internal/operators/debounce.ts", "node_modules/rxjs/src/internal/operators/debounceTime.ts", "node_modules/rxjs/src/internal/operators/defaultIfEmpty.ts", "node_modules/rxjs/src/internal/operators/take.ts", "node_modules/rxjs/src/internal/operators/ignoreElements.ts", "node_modules/rxjs/src/internal/operators/mapTo.ts", "node_modules/rxjs/src/internal/operators/delayWhen.ts", "node_modules/rxjs/src/internal/operators/delay.ts", "node_modules/rxjs/src/internal/operators/distinctUntilChanged.ts", "node_modules/rxjs/src/internal/operators/distinctUntilKeyChanged.ts", "node_modules/rxjs/src/internal/operators/throwIfEmpty.ts", "node_modules/rxjs/src/internal/operators/endWith.ts", "node_modules/rxjs/src/internal/operators/finalize.ts", "node_modules/rxjs/src/internal/operators/first.ts", "node_modules/rxjs/src/internal/operators/takeLast.ts", "node_modules/rxjs/src/internal/operators/merge.ts", "node_modules/rxjs/src/internal/operators/mergeWith.ts", "node_modules/rxjs/src/internal/operators/repeat.ts", "node_modules/rxjs/src/internal/operators/scan.ts", "node_modules/rxjs/src/internal/operators/share.ts", "node_modules/rxjs/src/internal/operators/shareReplay.ts", "node_modules/rxjs/src/internal/operators/skip.ts", "node_modules/rxjs/src/internal/operators/skipUntil.ts", "node_modules/rxjs/src/internal/operators/startWith.ts", "node_modules/rxjs/src/internal/operators/switchMap.ts", "node_modules/rxjs/src/internal/operators/takeUntil.ts", "node_modules/rxjs/src/internal/operators/takeWhile.ts", "node_modules/rxjs/src/internal/operators/tap.ts", "node_modules/rxjs/src/internal/operators/throttle.ts", "node_modules/rxjs/src/internal/operators/throttleTime.ts", "node_modules/rxjs/src/internal/operators/withLatestFrom.ts", "node_modules/rxjs/src/internal/operators/zip.ts", "node_modules/rxjs/src/internal/operators/zipWith.ts", "src/templates/assets/javascripts/browser/document/index.ts", "src/templates/assets/javascripts/browser/element/_/index.ts", "src/templates/assets/javascripts/browser/element/focus/index.ts", "src/templates/assets/javascripts/browser/element/hover/index.ts", "src/templates/assets/javascripts/utilities/h/index.ts", "src/templates/assets/javascripts/utilities/round/index.ts", "src/templates/assets/javascripts/browser/script/index.ts", "src/templates/assets/javascripts/browser/element/size/_/index.ts", "src/templates/assets/javascripts/browser/element/size/content/index.ts", "src/templates/assets/javascripts/browser/element/offset/_/index.ts", "src/templates/assets/javascripts/browser/element/offset/content/index.ts", "src/templates/assets/javascripts/browser/element/visibility/index.ts", "src/templates/assets/javascripts/browser/toggle/index.ts", "src/templates/assets/javascripts/browser/keyboard/index.ts", "src/templates/assets/javascripts/browser/location/_/index.ts", "src/templates/assets/javascripts/browser/location/hash/index.ts", "src/templates/assets/javascripts/browser/media/index.ts", "src/templates/assets/javascripts/browser/request/index.ts", "src/templates/assets/javascripts/browser/viewport/offset/index.ts", "src/templates/assets/javascripts/browser/viewport/size/index.ts", "src/templates/assets/javascripts/browser/viewport/_/index.ts", "src/templates/assets/javascripts/browser/viewport/at/index.ts", "src/templates/assets/javascripts/browser/worker/index.ts", "src/templates/assets/javascripts/_/index.ts", "src/templates/assets/javascripts/components/_/index.ts", "src/templates/assets/javascripts/components/announce/index.ts", "src/templates/assets/javascripts/components/consent/index.ts", "src/templates/assets/javascripts/templates/tooltip/index.tsx", "src/templates/assets/javascripts/templates/annotation/index.tsx", "src/templates/assets/javascripts/templates/clipboard/index.tsx", "src/templates/assets/javascripts/templates/search/index.tsx", "src/templates/assets/javascripts/templates/source/index.tsx", "src/templates/assets/javascripts/templates/tabbed/index.tsx", "src/templates/assets/javascripts/templates/table/index.tsx", "src/templates/assets/javascripts/templates/version/index.tsx", "src/templates/assets/javascripts/components/tooltip2/index.ts", "src/templates/assets/javascripts/components/content/annotation/_/index.ts", "src/templates/assets/javascripts/components/content/annotation/list/index.ts", "src/templates/assets/javascripts/components/content/annotation/block/index.ts", "src/templates/assets/javascripts/components/content/code/_/index.ts", "src/templates/assets/javascripts/components/content/details/index.ts", "src/templates/assets/javascripts/components/content/mermaid/index.css", "src/templates/assets/javascripts/components/content/mermaid/index.ts", "src/templates/assets/javascripts/components/content/table/index.ts", "src/templates/assets/javascripts/components/content/tabs/index.ts", "src/templates/assets/javascripts/components/content/_/index.ts", "src/templates/assets/javascripts/components/dialog/index.ts", "src/templates/assets/javascripts/components/tooltip/index.ts", "src/templates/assets/javascripts/components/header/_/index.ts", "src/templates/assets/javascripts/components/header/title/index.ts", "src/templates/assets/javascripts/components/main/index.ts", "src/templates/assets/javascripts/components/palette/index.ts", "src/templates/assets/javascripts/components/progress/index.ts", "src/templates/assets/javascripts/integrations/clipboard/index.ts", "src/templates/assets/javascripts/integrations/sitemap/index.ts", "src/templates/assets/javascripts/integrations/instant/index.ts", "src/templates/assets/javascripts/integrations/search/highlighter/index.ts", "src/templates/assets/javascripts/integrations/search/worker/message/index.ts", "src/templates/assets/javascripts/integrations/search/worker/_/index.ts", "src/templates/assets/javascripts/integrations/version/index.ts", "src/templates/assets/javascripts/components/search/query/index.ts", "src/templates/assets/javascripts/components/search/result/index.ts", "src/templates/assets/javascripts/components/search/share/index.ts", "src/templates/assets/javascripts/components/search/suggest/index.ts", "src/templates/assets/javascripts/components/search/_/index.ts", "src/templates/assets/javascripts/components/search/highlight/index.ts", "src/templates/assets/javascripts/components/sidebar/index.ts", "src/templates/assets/javascripts/components/source/facts/github/index.ts", "src/templates/assets/javascripts/components/source/facts/gitlab/index.ts", "src/templates/assets/javascripts/components/source/facts/_/index.ts", "src/templates/assets/javascripts/components/source/_/index.ts", "src/templates/assets/javascripts/components/tabs/index.ts", "src/templates/assets/javascripts/components/toc/index.ts", "src/templates/assets/javascripts/components/top/index.ts", "src/templates/assets/javascripts/patches/ellipsis/index.ts", "src/templates/assets/javascripts/patches/indeterminate/index.ts", "src/templates/assets/javascripts/patches/scrollfix/index.ts", "src/templates/assets/javascripts/patches/scrolllock/index.ts", "src/templates/assets/javascripts/polyfills/index.ts"], + "sourcesContent": ["(function (global, factory) {\n typeof exports === 'object' && typeof module !== 'undefined' ? factory() :\n typeof define === 'function' && define.amd ? define(factory) :\n (factory());\n}(this, (function () { 'use strict';\n\n /**\n * Applies the :focus-visible polyfill at the given scope.\n * A scope in this case is either the top-level Document or a Shadow Root.\n *\n * @param {(Document|ShadowRoot)} scope\n * @see https://github.com/WICG/focus-visible\n */\n function applyFocusVisiblePolyfill(scope) {\n var hadKeyboardEvent = true;\n var hadFocusVisibleRecently = false;\n var hadFocusVisibleRecentlyTimeout = null;\n\n var inputTypesAllowlist = {\n text: true,\n search: true,\n url: true,\n tel: true,\n email: true,\n password: true,\n number: true,\n date: true,\n month: true,\n week: true,\n time: true,\n datetime: true,\n 'datetime-local': true\n };\n\n /**\n * Helper function for legacy browsers and iframes which sometimes focus\n * elements like document, body, and non-interactive SVG.\n * @param {Element} el\n */\n function isValidFocusTarget(el) {\n if (\n el &&\n el !== document &&\n el.nodeName !== 'HTML' &&\n el.nodeName !== 'BODY' &&\n 'classList' in el &&\n 'contains' in el.classList\n ) {\n return true;\n }\n return false;\n }\n\n /**\n * Computes whether the given element should automatically trigger the\n * `focus-visible` class being added, i.e. whether it should always match\n * `:focus-visible` when focused.\n * @param {Element} el\n * @return {boolean}\n */\n function focusTriggersKeyboardModality(el) {\n var type = el.type;\n var tagName = el.tagName;\n\n if (tagName === 'INPUT' && inputTypesAllowlist[type] && !el.readOnly) {\n return true;\n }\n\n if (tagName === 'TEXTAREA' && !el.readOnly) {\n return true;\n }\n\n if (el.isContentEditable) {\n return true;\n }\n\n return false;\n }\n\n /**\n * Add the `focus-visible` class to the given element if it was not added by\n * the author.\n * @param {Element} el\n */\n function addFocusVisibleClass(el) {\n if (el.classList.contains('focus-visible')) {\n return;\n }\n el.classList.add('focus-visible');\n el.setAttribute('data-focus-visible-added', '');\n }\n\n /**\n * Remove the `focus-visible` class from the given element if it was not\n * originally added by the author.\n * @param {Element} el\n */\n function removeFocusVisibleClass(el) {\n if (!el.hasAttribute('data-focus-visible-added')) {\n return;\n }\n el.classList.remove('focus-visible');\n el.removeAttribute('data-focus-visible-added');\n }\n\n /**\n * If the most recent user interaction was via the keyboard;\n * and the key press did not include a meta, alt/option, or control key;\n * then the modality is keyboard. Otherwise, the modality is not keyboard.\n * Apply `focus-visible` to any current active element and keep track\n * of our keyboard modality state with `hadKeyboardEvent`.\n * @param {KeyboardEvent} e\n */\n function onKeyDown(e) {\n if (e.metaKey || e.altKey || e.ctrlKey) {\n return;\n }\n\n if (isValidFocusTarget(scope.activeElement)) {\n addFocusVisibleClass(scope.activeElement);\n }\n\n hadKeyboardEvent = true;\n }\n\n /**\n * If at any point a user clicks with a pointing device, ensure that we change\n * the modality away from keyboard.\n * This avoids the situation where a user presses a key on an already focused\n * element, and then clicks on a different element, focusing it with a\n * pointing device, while we still think we're in keyboard modality.\n * @param {Event} e\n */\n function onPointerDown(e) {\n hadKeyboardEvent = false;\n }\n\n /**\n * On `focus`, add the `focus-visible` class to the target if:\n * - the target received focus as a result of keyboard navigation, or\n * - the event target is an element that will likely require interaction\n * via the keyboard (e.g. a text box)\n * @param {Event} e\n */\n function onFocus(e) {\n // Prevent IE from focusing the document or HTML element.\n if (!isValidFocusTarget(e.target)) {\n return;\n }\n\n if (hadKeyboardEvent || focusTriggersKeyboardModality(e.target)) {\n addFocusVisibleClass(e.target);\n }\n }\n\n /**\n * On `blur`, remove the `focus-visible` class from the target.\n * @param {Event} e\n */\n function onBlur(e) {\n if (!isValidFocusTarget(e.target)) {\n return;\n }\n\n if (\n e.target.classList.contains('focus-visible') ||\n e.target.hasAttribute('data-focus-visible-added')\n ) {\n // To detect a tab/window switch, we look for a blur event followed\n // rapidly by a visibility change.\n // If we don't see a visibility change within 100ms, it's probably a\n // regular focus change.\n hadFocusVisibleRecently = true;\n window.clearTimeout(hadFocusVisibleRecentlyTimeout);\n hadFocusVisibleRecentlyTimeout = window.setTimeout(function() {\n hadFocusVisibleRecently = false;\n }, 100);\n removeFocusVisibleClass(e.target);\n }\n }\n\n /**\n * If the user changes tabs, keep track of whether or not the previously\n * focused element had .focus-visible.\n * @param {Event} e\n */\n function onVisibilityChange(e) {\n if (document.visibilityState === 'hidden') {\n // If the tab becomes active again, the browser will handle calling focus\n // on the element (Safari actually calls it twice).\n // If this tab change caused a blur on an element with focus-visible,\n // re-apply the class when the user switches back to the tab.\n if (hadFocusVisibleRecently) {\n hadKeyboardEvent = true;\n }\n addInitialPointerMoveListeners();\n }\n }\n\n /**\n * Add a group of listeners to detect usage of any pointing devices.\n * These listeners will be added when the polyfill first loads, and anytime\n * the window is blurred, so that they are active when the window regains\n * focus.\n */\n function addInitialPointerMoveListeners() {\n document.addEventListener('mousemove', onInitialPointerMove);\n document.addEventListener('mousedown', onInitialPointerMove);\n document.addEventListener('mouseup', onInitialPointerMove);\n document.addEventListener('pointermove', onInitialPointerMove);\n document.addEventListener('pointerdown', onInitialPointerMove);\n document.addEventListener('pointerup', onInitialPointerMove);\n document.addEventListener('touchmove', onInitialPointerMove);\n document.addEventListener('touchstart', onInitialPointerMove);\n document.addEventListener('touchend', onInitialPointerMove);\n }\n\n function removeInitialPointerMoveListeners() {\n document.removeEventListener('mousemove', onInitialPointerMove);\n document.removeEventListener('mousedown', onInitialPointerMove);\n document.removeEventListener('mouseup', onInitialPointerMove);\n document.removeEventListener('pointermove', onInitialPointerMove);\n document.removeEventListener('pointerdown', onInitialPointerMove);\n document.removeEventListener('pointerup', onInitialPointerMove);\n document.removeEventListener('touchmove', onInitialPointerMove);\n document.removeEventListener('touchstart', onInitialPointerMove);\n document.removeEventListener('touchend', onInitialPointerMove);\n }\n\n /**\n * When the polfyill first loads, assume the user is in keyboard modality.\n * If any event is received from a pointing device (e.g. mouse, pointer,\n * touch), turn off keyboard modality.\n * This accounts for situations where focus enters the page from the URL bar.\n * @param {Event} e\n */\n function onInitialPointerMove(e) {\n // Work around a Safari quirk that fires a mousemove on whenever the\n // window blurs, even if you're tabbing out of the page. \u00AF\\_(\u30C4)_/\u00AF\n if (e.target.nodeName && e.target.nodeName.toLowerCase() === 'html') {\n return;\n }\n\n hadKeyboardEvent = false;\n removeInitialPointerMoveListeners();\n }\n\n // For some kinds of state, we are interested in changes at the global scope\n // only. For example, global pointer input, global key presses and global\n // visibility change should affect the state at every scope:\n document.addEventListener('keydown', onKeyDown, true);\n document.addEventListener('mousedown', onPointerDown, true);\n document.addEventListener('pointerdown', onPointerDown, true);\n document.addEventListener('touchstart', onPointerDown, true);\n document.addEventListener('visibilitychange', onVisibilityChange, true);\n\n addInitialPointerMoveListeners();\n\n // For focus and blur, we specifically care about state changes in the local\n // scope. This is because focus / blur events that originate from within a\n // shadow root are not re-dispatched from the host element if it was already\n // the active element in its own scope:\n scope.addEventListener('focus', onFocus, true);\n scope.addEventListener('blur', onBlur, true);\n\n // We detect that a node is a ShadowRoot by ensuring that it is a\n // DocumentFragment and also has a host property. This check covers native\n // implementation and polyfill implementation transparently. If we only cared\n // about the native implementation, we could just check if the scope was\n // an instance of a ShadowRoot.\n if (scope.nodeType === Node.DOCUMENT_FRAGMENT_NODE && scope.host) {\n // Since a ShadowRoot is a special kind of DocumentFragment, it does not\n // have a root element to add a class to. So, we add this attribute to the\n // host element instead:\n scope.host.setAttribute('data-js-focus-visible', '');\n } else if (scope.nodeType === Node.DOCUMENT_NODE) {\n document.documentElement.classList.add('js-focus-visible');\n document.documentElement.setAttribute('data-js-focus-visible', '');\n }\n }\n\n // It is important to wrap all references to global window and document in\n // these checks to support server-side rendering use cases\n // @see https://github.com/WICG/focus-visible/issues/199\n if (typeof window !== 'undefined' && typeof document !== 'undefined') {\n // Make the polyfill helper globally available. This can be used as a signal\n // to interested libraries that wish to coordinate with the polyfill for e.g.,\n // applying the polyfill to a shadow root:\n window.applyFocusVisiblePolyfill = applyFocusVisiblePolyfill;\n\n // Notify interested libraries of the polyfill's presence, in case the\n // polyfill was loaded lazily:\n var event;\n\n try {\n event = new CustomEvent('focus-visible-polyfill-ready');\n } catch (error) {\n // IE11 does not support using CustomEvent as a constructor directly:\n event = document.createEvent('CustomEvent');\n event.initCustomEvent('focus-visible-polyfill-ready', false, false, {});\n }\n\n window.dispatchEvent(event);\n }\n\n if (typeof document !== 'undefined') {\n // Apply the polyfill to the global document, so that no JavaScript\n // coordination is required to use the polyfill in the top-level document:\n applyFocusVisiblePolyfill(document);\n }\n\n})));\n", "/*!\n * clipboard.js v2.0.11\n * https://clipboardjs.com/\n *\n * Licensed MIT \u00A9 Zeno Rocha\n */\n(function webpackUniversalModuleDefinition(root, factory) {\n\tif(typeof exports === 'object' && typeof module === 'object')\n\t\tmodule.exports = factory();\n\telse if(typeof define === 'function' && define.amd)\n\t\tdefine([], factory);\n\telse if(typeof exports === 'object')\n\t\texports[\"ClipboardJS\"] = factory();\n\telse\n\t\troot[\"ClipboardJS\"] = factory();\n})(this, function() {\nreturn /******/ (function() { // webpackBootstrap\n/******/ \tvar __webpack_modules__ = ({\n\n/***/ 686:\n/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\n\n// EXPORTS\n__webpack_require__.d(__webpack_exports__, {\n \"default\": function() { return /* binding */ clipboard; }\n});\n\n// EXTERNAL MODULE: ./node_modules/tiny-emitter/index.js\nvar tiny_emitter = __webpack_require__(279);\nvar tiny_emitter_default = /*#__PURE__*/__webpack_require__.n(tiny_emitter);\n// EXTERNAL MODULE: ./node_modules/good-listener/src/listen.js\nvar listen = __webpack_require__(370);\nvar listen_default = /*#__PURE__*/__webpack_require__.n(listen);\n// EXTERNAL MODULE: ./node_modules/select/src/select.js\nvar src_select = __webpack_require__(817);\nvar select_default = /*#__PURE__*/__webpack_require__.n(src_select);\n;// CONCATENATED MODULE: ./src/common/command.js\n/**\n * Executes a given operation type.\n * @param {String} type\n * @return {Boolean}\n */\nfunction command(type) {\n try {\n return document.execCommand(type);\n } catch (err) {\n return false;\n }\n}\n;// CONCATENATED MODULE: ./src/actions/cut.js\n\n\n/**\n * Cut action wrapper.\n * @param {String|HTMLElement} target\n * @return {String}\n */\n\nvar ClipboardActionCut = function ClipboardActionCut(target) {\n var selectedText = select_default()(target);\n command('cut');\n return selectedText;\n};\n\n/* harmony default export */ var actions_cut = (ClipboardActionCut);\n;// CONCATENATED MODULE: ./src/common/create-fake-element.js\n/**\n * Creates a fake textarea element with a value.\n * @param {String} value\n * @return {HTMLElement}\n */\nfunction createFakeElement(value) {\n var isRTL = document.documentElement.getAttribute('dir') === 'rtl';\n var fakeElement = document.createElement('textarea'); // Prevent zooming on iOS\n\n fakeElement.style.fontSize = '12pt'; // Reset box model\n\n fakeElement.style.border = '0';\n fakeElement.style.padding = '0';\n fakeElement.style.margin = '0'; // Move element out of screen horizontally\n\n fakeElement.style.position = 'absolute';\n fakeElement.style[isRTL ? 'right' : 'left'] = '-9999px'; // Move element to the same position vertically\n\n var yPosition = window.pageYOffset || document.documentElement.scrollTop;\n fakeElement.style.top = \"\".concat(yPosition, \"px\");\n fakeElement.setAttribute('readonly', '');\n fakeElement.value = value;\n return fakeElement;\n}\n;// CONCATENATED MODULE: ./src/actions/copy.js\n\n\n\n/**\n * Create fake copy action wrapper using a fake element.\n * @param {String} target\n * @param {Object} options\n * @return {String}\n */\n\nvar fakeCopyAction = function fakeCopyAction(value, options) {\n var fakeElement = createFakeElement(value);\n options.container.appendChild(fakeElement);\n var selectedText = select_default()(fakeElement);\n command('copy');\n fakeElement.remove();\n return selectedText;\n};\n/**\n * Copy action wrapper.\n * @param {String|HTMLElement} target\n * @param {Object} options\n * @return {String}\n */\n\n\nvar ClipboardActionCopy = function ClipboardActionCopy(target) {\n var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {\n container: document.body\n };\n var selectedText = '';\n\n if (typeof target === 'string') {\n selectedText = fakeCopyAction(target, options);\n } else if (target instanceof HTMLInputElement && !['text', 'search', 'url', 'tel', 'password'].includes(target === null || target === void 0 ? void 0 : target.type)) {\n // If input type doesn't support `setSelectionRange`. Simulate it. https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement/setSelectionRange\n selectedText = fakeCopyAction(target.value, options);\n } else {\n selectedText = select_default()(target);\n command('copy');\n }\n\n return selectedText;\n};\n\n/* harmony default export */ var actions_copy = (ClipboardActionCopy);\n;// CONCATENATED MODULE: ./src/actions/default.js\nfunction _typeof(obj) { \"@babel/helpers - typeof\"; if (typeof Symbol === \"function\" && typeof Symbol.iterator === \"symbol\") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === \"function\" && obj.constructor === Symbol && obj !== Symbol.prototype ? \"symbol\" : typeof obj; }; } return _typeof(obj); }\n\n\n\n/**\n * Inner function which performs selection from either `text` or `target`\n * properties and then executes copy or cut operations.\n * @param {Object} options\n */\n\nvar ClipboardActionDefault = function ClipboardActionDefault() {\n var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n // Defines base properties passed from constructor.\n var _options$action = options.action,\n action = _options$action === void 0 ? 'copy' : _options$action,\n container = options.container,\n target = options.target,\n text = options.text; // Sets the `action` to be performed which can be either 'copy' or 'cut'.\n\n if (action !== 'copy' && action !== 'cut') {\n throw new Error('Invalid \"action\" value, use either \"copy\" or \"cut\"');\n } // Sets the `target` property using an element that will be have its content copied.\n\n\n if (target !== undefined) {\n if (target && _typeof(target) === 'object' && target.nodeType === 1) {\n if (action === 'copy' && target.hasAttribute('disabled')) {\n throw new Error('Invalid \"target\" attribute. Please use \"readonly\" instead of \"disabled\" attribute');\n }\n\n if (action === 'cut' && (target.hasAttribute('readonly') || target.hasAttribute('disabled'))) {\n throw new Error('Invalid \"target\" attribute. You can\\'t cut text from elements with \"readonly\" or \"disabled\" attributes');\n }\n } else {\n throw new Error('Invalid \"target\" value, use a valid Element');\n }\n } // Define selection strategy based on `text` property.\n\n\n if (text) {\n return actions_copy(text, {\n container: container\n });\n } // Defines which selection strategy based on `target` property.\n\n\n if (target) {\n return action === 'cut' ? actions_cut(target) : actions_copy(target, {\n container: container\n });\n }\n};\n\n/* harmony default export */ var actions_default = (ClipboardActionDefault);\n;// CONCATENATED MODULE: ./src/clipboard.js\nfunction clipboard_typeof(obj) { \"@babel/helpers - typeof\"; if (typeof Symbol === \"function\" && typeof Symbol.iterator === \"symbol\") { clipboard_typeof = function _typeof(obj) { return typeof obj; }; } else { clipboard_typeof = function _typeof(obj) { return obj && typeof Symbol === \"function\" && obj.constructor === Symbol && obj !== Symbol.prototype ? \"symbol\" : typeof obj; }; } return clipboard_typeof(obj); }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }\n\nfunction _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function\"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); }\n\nfunction _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }\n\nfunction _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; }\n\nfunction _possibleConstructorReturn(self, call) { if (call && (clipboard_typeof(call) === \"object\" || typeof call === \"function\")) { return call; } return _assertThisInitialized(self); }\n\nfunction _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return self; }\n\nfunction _isNativeReflectConstruct() { if (typeof Reflect === \"undefined\" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === \"function\") return true; try { Date.prototype.toString.call(Reflect.construct(Date, [], function () {})); return true; } catch (e) { return false; } }\n\nfunction _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }\n\n\n\n\n\n\n/**\n * Helper function to retrieve attribute value.\n * @param {String} suffix\n * @param {Element} element\n */\n\nfunction getAttributeValue(suffix, element) {\n var attribute = \"data-clipboard-\".concat(suffix);\n\n if (!element.hasAttribute(attribute)) {\n return;\n }\n\n return element.getAttribute(attribute);\n}\n/**\n * Base class which takes one or more elements, adds event listeners to them,\n * and instantiates a new `ClipboardAction` on each click.\n */\n\n\nvar Clipboard = /*#__PURE__*/function (_Emitter) {\n _inherits(Clipboard, _Emitter);\n\n var _super = _createSuper(Clipboard);\n\n /**\n * @param {String|HTMLElement|HTMLCollection|NodeList} trigger\n * @param {Object} options\n */\n function Clipboard(trigger, options) {\n var _this;\n\n _classCallCheck(this, Clipboard);\n\n _this = _super.call(this);\n\n _this.resolveOptions(options);\n\n _this.listenClick(trigger);\n\n return _this;\n }\n /**\n * Defines if attributes would be resolved using internal setter functions\n * or custom functions that were passed in the constructor.\n * @param {Object} options\n */\n\n\n _createClass(Clipboard, [{\n key: \"resolveOptions\",\n value: function resolveOptions() {\n var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n this.action = typeof options.action === 'function' ? options.action : this.defaultAction;\n this.target = typeof options.target === 'function' ? options.target : this.defaultTarget;\n this.text = typeof options.text === 'function' ? options.text : this.defaultText;\n this.container = clipboard_typeof(options.container) === 'object' ? options.container : document.body;\n }\n /**\n * Adds a click event listener to the passed trigger.\n * @param {String|HTMLElement|HTMLCollection|NodeList} trigger\n */\n\n }, {\n key: \"listenClick\",\n value: function listenClick(trigger) {\n var _this2 = this;\n\n this.listener = listen_default()(trigger, 'click', function (e) {\n return _this2.onClick(e);\n });\n }\n /**\n * Defines a new `ClipboardAction` on each click event.\n * @param {Event} e\n */\n\n }, {\n key: \"onClick\",\n value: function onClick(e) {\n var trigger = e.delegateTarget || e.currentTarget;\n var action = this.action(trigger) || 'copy';\n var text = actions_default({\n action: action,\n container: this.container,\n target: this.target(trigger),\n text: this.text(trigger)\n }); // Fires an event based on the copy operation result.\n\n this.emit(text ? 'success' : 'error', {\n action: action,\n text: text,\n trigger: trigger,\n clearSelection: function clearSelection() {\n if (trigger) {\n trigger.focus();\n }\n\n window.getSelection().removeAllRanges();\n }\n });\n }\n /**\n * Default `action` lookup function.\n * @param {Element} trigger\n */\n\n }, {\n key: \"defaultAction\",\n value: function defaultAction(trigger) {\n return getAttributeValue('action', trigger);\n }\n /**\n * Default `target` lookup function.\n * @param {Element} trigger\n */\n\n }, {\n key: \"defaultTarget\",\n value: function defaultTarget(trigger) {\n var selector = getAttributeValue('target', trigger);\n\n if (selector) {\n return document.querySelector(selector);\n }\n }\n /**\n * Allow fire programmatically a copy action\n * @param {String|HTMLElement} target\n * @param {Object} options\n * @returns Text copied.\n */\n\n }, {\n key: \"defaultText\",\n\n /**\n * Default `text` lookup function.\n * @param {Element} trigger\n */\n value: function defaultText(trigger) {\n return getAttributeValue('text', trigger);\n }\n /**\n * Destroy lifecycle.\n */\n\n }, {\n key: \"destroy\",\n value: function destroy() {\n this.listener.destroy();\n }\n }], [{\n key: \"copy\",\n value: function copy(target) {\n var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {\n container: document.body\n };\n return actions_copy(target, options);\n }\n /**\n * Allow fire programmatically a cut action\n * @param {String|HTMLElement} target\n * @returns Text cutted.\n */\n\n }, {\n key: \"cut\",\n value: function cut(target) {\n return actions_cut(target);\n }\n /**\n * Returns the support of the given action, or all actions if no action is\n * given.\n * @param {String} [action]\n */\n\n }, {\n key: \"isSupported\",\n value: function isSupported() {\n var action = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : ['copy', 'cut'];\n var actions = typeof action === 'string' ? [action] : action;\n var support = !!document.queryCommandSupported;\n actions.forEach(function (action) {\n support = support && !!document.queryCommandSupported(action);\n });\n return support;\n }\n }]);\n\n return Clipboard;\n}((tiny_emitter_default()));\n\n/* harmony default export */ var clipboard = (Clipboard);\n\n/***/ }),\n\n/***/ 828:\n/***/ (function(module) {\n\nvar DOCUMENT_NODE_TYPE = 9;\n\n/**\n * A polyfill for Element.matches()\n */\nif (typeof Element !== 'undefined' && !Element.prototype.matches) {\n var proto = Element.prototype;\n\n proto.matches = proto.matchesSelector ||\n proto.mozMatchesSelector ||\n proto.msMatchesSelector ||\n proto.oMatchesSelector ||\n proto.webkitMatchesSelector;\n}\n\n/**\n * Finds the closest parent that matches a selector.\n *\n * @param {Element} element\n * @param {String} selector\n * @return {Function}\n */\nfunction closest (element, selector) {\n while (element && element.nodeType !== DOCUMENT_NODE_TYPE) {\n if (typeof element.matches === 'function' &&\n element.matches(selector)) {\n return element;\n }\n element = element.parentNode;\n }\n}\n\nmodule.exports = closest;\n\n\n/***/ }),\n\n/***/ 438:\n/***/ (function(module, __unused_webpack_exports, __webpack_require__) {\n\nvar closest = __webpack_require__(828);\n\n/**\n * Delegates event to a selector.\n *\n * @param {Element} element\n * @param {String} selector\n * @param {String} type\n * @param {Function} callback\n * @param {Boolean} useCapture\n * @return {Object}\n */\nfunction _delegate(element, selector, type, callback, useCapture) {\n var listenerFn = listener.apply(this, arguments);\n\n element.addEventListener(type, listenerFn, useCapture);\n\n return {\n destroy: function() {\n element.removeEventListener(type, listenerFn, useCapture);\n }\n }\n}\n\n/**\n * Delegates event to a selector.\n *\n * @param {Element|String|Array} [elements]\n * @param {String} selector\n * @param {String} type\n * @param {Function} callback\n * @param {Boolean} useCapture\n * @return {Object}\n */\nfunction delegate(elements, selector, type, callback, useCapture) {\n // Handle the regular Element usage\n if (typeof elements.addEventListener === 'function') {\n return _delegate.apply(null, arguments);\n }\n\n // Handle Element-less usage, it defaults to global delegation\n if (typeof type === 'function') {\n // Use `document` as the first parameter, then apply arguments\n // This is a short way to .unshift `arguments` without running into deoptimizations\n return _delegate.bind(null, document).apply(null, arguments);\n }\n\n // Handle Selector-based usage\n if (typeof elements === 'string') {\n elements = document.querySelectorAll(elements);\n }\n\n // Handle Array-like based usage\n return Array.prototype.map.call(elements, function (element) {\n return _delegate(element, selector, type, callback, useCapture);\n });\n}\n\n/**\n * Finds closest match and invokes callback.\n *\n * @param {Element} element\n * @param {String} selector\n * @param {String} type\n * @param {Function} callback\n * @return {Function}\n */\nfunction listener(element, selector, type, callback) {\n return function(e) {\n e.delegateTarget = closest(e.target, selector);\n\n if (e.delegateTarget) {\n callback.call(element, e);\n }\n }\n}\n\nmodule.exports = delegate;\n\n\n/***/ }),\n\n/***/ 879:\n/***/ (function(__unused_webpack_module, exports) {\n\n/**\n * Check if argument is a HTML element.\n *\n * @param {Object} value\n * @return {Boolean}\n */\nexports.node = function(value) {\n return value !== undefined\n && value instanceof HTMLElement\n && value.nodeType === 1;\n};\n\n/**\n * Check if argument is a list of HTML elements.\n *\n * @param {Object} value\n * @return {Boolean}\n */\nexports.nodeList = function(value) {\n var type = Object.prototype.toString.call(value);\n\n return value !== undefined\n && (type === '[object NodeList]' || type === '[object HTMLCollection]')\n && ('length' in value)\n && (value.length === 0 || exports.node(value[0]));\n};\n\n/**\n * Check if argument is a string.\n *\n * @param {Object} value\n * @return {Boolean}\n */\nexports.string = function(value) {\n return typeof value === 'string'\n || value instanceof String;\n};\n\n/**\n * Check if argument is a function.\n *\n * @param {Object} value\n * @return {Boolean}\n */\nexports.fn = function(value) {\n var type = Object.prototype.toString.call(value);\n\n return type === '[object Function]';\n};\n\n\n/***/ }),\n\n/***/ 370:\n/***/ (function(module, __unused_webpack_exports, __webpack_require__) {\n\nvar is = __webpack_require__(879);\nvar delegate = __webpack_require__(438);\n\n/**\n * Validates all params and calls the right\n * listener function based on its target type.\n *\n * @param {String|HTMLElement|HTMLCollection|NodeList} target\n * @param {String} type\n * @param {Function} callback\n * @return {Object}\n */\nfunction listen(target, type, callback) {\n if (!target && !type && !callback) {\n throw new Error('Missing required arguments');\n }\n\n if (!is.string(type)) {\n throw new TypeError('Second argument must be a String');\n }\n\n if (!is.fn(callback)) {\n throw new TypeError('Third argument must be a Function');\n }\n\n if (is.node(target)) {\n return listenNode(target, type, callback);\n }\n else if (is.nodeList(target)) {\n return listenNodeList(target, type, callback);\n }\n else if (is.string(target)) {\n return listenSelector(target, type, callback);\n }\n else {\n throw new TypeError('First argument must be a String, HTMLElement, HTMLCollection, or NodeList');\n }\n}\n\n/**\n * Adds an event listener to a HTML element\n * and returns a remove listener function.\n *\n * @param {HTMLElement} node\n * @param {String} type\n * @param {Function} callback\n * @return {Object}\n */\nfunction listenNode(node, type, callback) {\n node.addEventListener(type, callback);\n\n return {\n destroy: function() {\n node.removeEventListener(type, callback);\n }\n }\n}\n\n/**\n * Add an event listener to a list of HTML elements\n * and returns a remove listener function.\n *\n * @param {NodeList|HTMLCollection} nodeList\n * @param {String} type\n * @param {Function} callback\n * @return {Object}\n */\nfunction listenNodeList(nodeList, type, callback) {\n Array.prototype.forEach.call(nodeList, function(node) {\n node.addEventListener(type, callback);\n });\n\n return {\n destroy: function() {\n Array.prototype.forEach.call(nodeList, function(node) {\n node.removeEventListener(type, callback);\n });\n }\n }\n}\n\n/**\n * Add an event listener to a selector\n * and returns a remove listener function.\n *\n * @param {String} selector\n * @param {String} type\n * @param {Function} callback\n * @return {Object}\n */\nfunction listenSelector(selector, type, callback) {\n return delegate(document.body, selector, type, callback);\n}\n\nmodule.exports = listen;\n\n\n/***/ }),\n\n/***/ 817:\n/***/ (function(module) {\n\nfunction select(element) {\n var selectedText;\n\n if (element.nodeName === 'SELECT') {\n element.focus();\n\n selectedText = element.value;\n }\n else if (element.nodeName === 'INPUT' || element.nodeName === 'TEXTAREA') {\n var isReadOnly = element.hasAttribute('readonly');\n\n if (!isReadOnly) {\n element.setAttribute('readonly', '');\n }\n\n element.select();\n element.setSelectionRange(0, element.value.length);\n\n if (!isReadOnly) {\n element.removeAttribute('readonly');\n }\n\n selectedText = element.value;\n }\n else {\n if (element.hasAttribute('contenteditable')) {\n element.focus();\n }\n\n var selection = window.getSelection();\n var range = document.createRange();\n\n range.selectNodeContents(element);\n selection.removeAllRanges();\n selection.addRange(range);\n\n selectedText = selection.toString();\n }\n\n return selectedText;\n}\n\nmodule.exports = select;\n\n\n/***/ }),\n\n/***/ 279:\n/***/ (function(module) {\n\nfunction E () {\n // Keep this empty so it's easier to inherit from\n // (via https://github.com/lipsmack from https://github.com/scottcorgan/tiny-emitter/issues/3)\n}\n\nE.prototype = {\n on: function (name, callback, ctx) {\n var e = this.e || (this.e = {});\n\n (e[name] || (e[name] = [])).push({\n fn: callback,\n ctx: ctx\n });\n\n return this;\n },\n\n once: function (name, callback, ctx) {\n var self = this;\n function listener () {\n self.off(name, listener);\n callback.apply(ctx, arguments);\n };\n\n listener._ = callback\n return this.on(name, listener, ctx);\n },\n\n emit: function (name) {\n var data = [].slice.call(arguments, 1);\n var evtArr = ((this.e || (this.e = {}))[name] || []).slice();\n var i = 0;\n var len = evtArr.length;\n\n for (i; i < len; i++) {\n evtArr[i].fn.apply(evtArr[i].ctx, data);\n }\n\n return this;\n },\n\n off: function (name, callback) {\n var e = this.e || (this.e = {});\n var evts = e[name];\n var liveEvents = [];\n\n if (evts && callback) {\n for (var i = 0, len = evts.length; i < len; i++) {\n if (evts[i].fn !== callback && evts[i].fn._ !== callback)\n liveEvents.push(evts[i]);\n }\n }\n\n // Remove event from queue to prevent memory leak\n // Suggested by https://github.com/lazd\n // Ref: https://github.com/scottcorgan/tiny-emitter/commit/c6ebfaa9bc973b33d110a84a307742b7cf94c953#commitcomment-5024910\n\n (liveEvents.length)\n ? e[name] = liveEvents\n : delete e[name];\n\n return this;\n }\n};\n\nmodule.exports = E;\nmodule.exports.TinyEmitter = E;\n\n\n/***/ })\n\n/******/ \t});\n/************************************************************************/\n/******/ \t// The module cache\n/******/ \tvar __webpack_module_cache__ = {};\n/******/ \t\n/******/ \t// The require function\n/******/ \tfunction __webpack_require__(moduleId) {\n/******/ \t\t// Check if module is in cache\n/******/ \t\tif(__webpack_module_cache__[moduleId]) {\n/******/ \t\t\treturn __webpack_module_cache__[moduleId].exports;\n/******/ \t\t}\n/******/ \t\t// Create a new module (and put it into the cache)\n/******/ \t\tvar module = __webpack_module_cache__[moduleId] = {\n/******/ \t\t\t// no module.id needed\n/******/ \t\t\t// no module.loaded needed\n/******/ \t\t\texports: {}\n/******/ \t\t};\n/******/ \t\n/******/ \t\t// Execute the module function\n/******/ \t\t__webpack_modules__[moduleId](module, module.exports, __webpack_require__);\n/******/ \t\n/******/ \t\t// Return the exports of the module\n/******/ \t\treturn module.exports;\n/******/ \t}\n/******/ \t\n/************************************************************************/\n/******/ \t/* webpack/runtime/compat get default export */\n/******/ \t!function() {\n/******/ \t\t// getDefaultExport function for compatibility with non-harmony modules\n/******/ \t\t__webpack_require__.n = function(module) {\n/******/ \t\t\tvar getter = module && module.__esModule ?\n/******/ \t\t\t\tfunction() { return module['default']; } :\n/******/ \t\t\t\tfunction() { return module; };\n/******/ \t\t\t__webpack_require__.d(getter, { a: getter });\n/******/ \t\t\treturn getter;\n/******/ \t\t};\n/******/ \t}();\n/******/ \t\n/******/ \t/* webpack/runtime/define property getters */\n/******/ \t!function() {\n/******/ \t\t// define getter functions for harmony exports\n/******/ \t\t__webpack_require__.d = function(exports, definition) {\n/******/ \t\t\tfor(var key in definition) {\n/******/ \t\t\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n/******/ \t\t\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n/******/ \t\t\t\t}\n/******/ \t\t\t}\n/******/ \t\t};\n/******/ \t}();\n/******/ \t\n/******/ \t/* webpack/runtime/hasOwnProperty shorthand */\n/******/ \t!function() {\n/******/ \t\t__webpack_require__.o = function(obj, prop) { return Object.prototype.hasOwnProperty.call(obj, prop); }\n/******/ \t}();\n/******/ \t\n/************************************************************************/\n/******/ \t// module exports must be returned from runtime so entry inlining is disabled\n/******/ \t// startup\n/******/ \t// Load entry module and return exports\n/******/ \treturn __webpack_require__(686);\n/******/ })()\n.default;\n});", "/*!\n * escape-html\n * Copyright(c) 2012-2013 TJ Holowaychuk\n * Copyright(c) 2015 Andreas Lubbe\n * Copyright(c) 2015 Tiancheng \"Timothy\" Gu\n * MIT Licensed\n */\n\n'use strict';\n\n/**\n * Module variables.\n * @private\n */\n\nvar matchHtmlRegExp = /[\"'&<>]/;\n\n/**\n * Module exports.\n * @public\n */\n\nmodule.exports = escapeHtml;\n\n/**\n * Escape special characters in the given string of html.\n *\n * @param {string} string The string to escape for inserting into HTML\n * @return {string}\n * @public\n */\n\nfunction escapeHtml(string) {\n var str = '' + string;\n var match = matchHtmlRegExp.exec(str);\n\n if (!match) {\n return str;\n }\n\n var escape;\n var html = '';\n var index = 0;\n var lastIndex = 0;\n\n for (index = match.index; index < str.length; index++) {\n switch (str.charCodeAt(index)) {\n case 34: // \"\n escape = '"';\n break;\n case 38: // &\n escape = '&';\n break;\n case 39: // '\n escape = ''';\n break;\n case 60: // <\n escape = '<';\n break;\n case 62: // >\n escape = '>';\n break;\n default:\n continue;\n }\n\n if (lastIndex !== index) {\n html += str.substring(lastIndex, index);\n }\n\n lastIndex = index + 1;\n html += escape;\n }\n\n return lastIndex !== index\n ? html + str.substring(lastIndex, index)\n : html;\n}\n", "/*\n * Copyright (c) 2016-2024 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport \"focus-visible\"\n\nimport {\n EMPTY,\n NEVER,\n Observable,\n Subject,\n defer,\n delay,\n filter,\n map,\n merge,\n mergeWith,\n shareReplay,\n switchMap\n} from \"rxjs\"\n\nimport { configuration, feature } from \"./_\"\nimport {\n at,\n getActiveElement,\n getOptionalElement,\n requestJSON,\n setLocation,\n setToggle,\n watchDocument,\n watchKeyboard,\n watchLocation,\n watchLocationTarget,\n watchMedia,\n watchPrint,\n watchScript,\n watchViewport\n} from \"./browser\"\nimport {\n getComponentElement,\n getComponentElements,\n mountAnnounce,\n mountBackToTop,\n mountConsent,\n mountContent,\n mountDialog,\n mountHeader,\n mountHeaderTitle,\n mountPalette,\n mountProgress,\n mountSearch,\n mountSearchHiglight,\n mountSidebar,\n mountSource,\n mountTableOfContents,\n mountTabs,\n watchHeader,\n watchMain\n} from \"./components\"\nimport {\n SearchIndex,\n setupClipboardJS,\n setupInstantNavigation,\n setupVersionSelector\n} from \"./integrations\"\nimport {\n patchEllipsis,\n patchIndeterminate,\n patchScrollfix,\n patchScrolllock\n} from \"./patches\"\nimport \"./polyfills\"\n\n/* ----------------------------------------------------------------------------\n * Functions - @todo refactor\n * ------------------------------------------------------------------------- */\n\n/**\n * Fetch search index\n *\n * @returns Search index observable\n */\nfunction fetchSearchIndex(): Observable {\n if (location.protocol === \"file:\") {\n return watchScript(\n `${new URL(\"search/search_index.js\", config.base)}`\n )\n .pipe(\n // @ts-ignore - @todo fix typings\n map(() => __index),\n shareReplay(1)\n )\n } else {\n return requestJSON(\n new URL(\"search/search_index.json\", config.base)\n )\n }\n}\n\n/* ----------------------------------------------------------------------------\n * Application\n * ------------------------------------------------------------------------- */\n\n/* Yay, JavaScript is available */\ndocument.documentElement.classList.remove(\"no-js\")\ndocument.documentElement.classList.add(\"js\")\n\n/* Set up navigation observables and subjects */\nconst document$ = watchDocument()\nconst location$ = watchLocation()\nconst target$ = watchLocationTarget(location$)\nconst keyboard$ = watchKeyboard()\n\n/* Set up media observables */\nconst viewport$ = watchViewport()\nconst tablet$ = watchMedia(\"(min-width: 960px)\")\nconst screen$ = watchMedia(\"(min-width: 1220px)\")\nconst print$ = watchPrint()\n\n/* Retrieve search index, if search is enabled */\nconst config = configuration()\nconst index$ = document.forms.namedItem(\"search\")\n ? fetchSearchIndex()\n : NEVER\n\n/* Set up Clipboard.js integration */\nconst alert$ = new Subject()\nsetupClipboardJS({ alert$ })\n\n/* Set up progress indicator */\nconst progress$ = new Subject()\n\n/* Set up instant navigation, if enabled */\nif (feature(\"navigation.instant\"))\n setupInstantNavigation({ location$, viewport$, progress$ })\n .subscribe(document$)\n\n/* Set up version selector */\nif (config.version?.provider === \"mike\")\n setupVersionSelector({ document$ })\n\n/* Always close drawer and search on navigation */\nmerge(location$, target$)\n .pipe(\n delay(125)\n )\n .subscribe(() => {\n setToggle(\"drawer\", false)\n setToggle(\"search\", false)\n })\n\n/* Set up global keyboard handlers */\nkeyboard$\n .pipe(\n filter(({ mode }) => mode === \"global\")\n )\n .subscribe(key => {\n switch (key.type) {\n\n /* Go to previous page */\n case \"p\":\n case \",\":\n const prev = getOptionalElement(\"link[rel=prev]\")\n if (typeof prev !== \"undefined\")\n setLocation(prev)\n break\n\n /* Go to next page */\n case \"n\":\n case \".\":\n const next = getOptionalElement(\"link[rel=next]\")\n if (typeof next !== \"undefined\")\n setLocation(next)\n break\n\n /* Expand navigation, see https://bit.ly/3ZjG5io */\n case \"Enter\":\n const active = getActiveElement()\n if (active instanceof HTMLLabelElement)\n active.click()\n }\n })\n\n/* Set up patches */\npatchEllipsis({ viewport$, document$ })\npatchIndeterminate({ document$, tablet$ })\npatchScrollfix({ document$ })\npatchScrolllock({ viewport$, tablet$ })\n\n/* Set up header and main area observable */\nconst header$ = watchHeader(getComponentElement(\"header\"), { viewport$ })\nconst main$ = document$\n .pipe(\n map(() => getComponentElement(\"main\")),\n switchMap(el => watchMain(el, { viewport$, header$ })),\n shareReplay(1)\n )\n\n/* Set up control component observables */\nconst control$ = merge(\n\n /* Consent */\n ...getComponentElements(\"consent\")\n .map(el => mountConsent(el, { target$ })),\n\n /* Dialog */\n ...getComponentElements(\"dialog\")\n .map(el => mountDialog(el, { alert$ })),\n\n /* Header */\n ...getComponentElements(\"header\")\n .map(el => mountHeader(el, { viewport$, header$, main$ })),\n\n /* Color palette */\n ...getComponentElements(\"palette\")\n .map(el => mountPalette(el)),\n\n /* Progress bar */\n ...getComponentElements(\"progress\")\n .map(el => mountProgress(el, { progress$ })),\n\n /* Search */\n ...getComponentElements(\"search\")\n .map(el => mountSearch(el, { index$, keyboard$ })),\n\n /* Repository information */\n ...getComponentElements(\"source\")\n .map(el => mountSource(el))\n)\n\n/* Set up content component observables */\nconst content$ = defer(() => merge(\n\n /* Announcement bar */\n ...getComponentElements(\"announce\")\n .map(el => mountAnnounce(el)),\n\n /* Content */\n ...getComponentElements(\"content\")\n .map(el => mountContent(el, { viewport$, target$, print$ })),\n\n /* Search highlighting */\n ...getComponentElements(\"content\")\n .map(el => feature(\"search.highlight\")\n ? mountSearchHiglight(el, { index$, location$ })\n : EMPTY\n ),\n\n /* Header title */\n ...getComponentElements(\"header-title\")\n .map(el => mountHeaderTitle(el, { viewport$, header$ })),\n\n /* Sidebar */\n ...getComponentElements(\"sidebar\")\n .map(el => el.getAttribute(\"data-md-type\") === \"navigation\"\n ? at(screen$, () => mountSidebar(el, { viewport$, header$, main$ }))\n : at(tablet$, () => mountSidebar(el, { viewport$, header$, main$ }))\n ),\n\n /* Navigation tabs */\n ...getComponentElements(\"tabs\")\n .map(el => mountTabs(el, { viewport$, header$ })),\n\n /* Table of contents */\n ...getComponentElements(\"toc\")\n .map(el => mountTableOfContents(el, {\n viewport$, header$, main$, target$\n })),\n\n /* Back-to-top button */\n ...getComponentElements(\"top\")\n .map(el => mountBackToTop(el, { viewport$, header$, main$, target$ }))\n))\n\n/* Set up component observables */\nconst component$ = document$\n .pipe(\n switchMap(() => content$),\n mergeWith(control$),\n shareReplay(1)\n )\n\n/* Subscribe to all components */\ncomponent$.subscribe()\n\n/* ----------------------------------------------------------------------------\n * Exports\n * ------------------------------------------------------------------------- */\n\nwindow.document$ = document$ /* Document observable */\nwindow.location$ = location$ /* Location subject */\nwindow.target$ = target$ /* Location target observable */\nwindow.keyboard$ = keyboard$ /* Keyboard observable */\nwindow.viewport$ = viewport$ /* Viewport observable */\nwindow.tablet$ = tablet$ /* Media tablet observable */\nwindow.screen$ = screen$ /* Media screen observable */\nwindow.print$ = print$ /* Media print observable */\nwindow.alert$ = alert$ /* Alert subject */\nwindow.progress$ = progress$ /* Progress indicator subject */\nwindow.component$ = component$ /* Component observable */\n", "/*! *****************************************************************************\r\nCopyright (c) Microsoft Corporation.\r\n\r\nPermission to use, copy, modify, and/or distribute this software for any\r\npurpose with or without fee is hereby granted.\r\n\r\nTHE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH\r\nREGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY\r\nAND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,\r\nINDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM\r\nLOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR\r\nOTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR\r\nPERFORMANCE OF THIS SOFTWARE.\r\n***************************************************************************** */\r\n/* global Reflect, Promise */\r\n\r\nvar extendStatics = function(d, b) {\r\n extendStatics = Object.setPrototypeOf ||\r\n ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||\r\n function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };\r\n return extendStatics(d, b);\r\n};\r\n\r\nexport function __extends(d, b) {\r\n if (typeof b !== \"function\" && b !== null)\r\n throw new TypeError(\"Class extends value \" + String(b) + \" is not a constructor or null\");\r\n extendStatics(d, b);\r\n function __() { this.constructor = d; }\r\n d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());\r\n}\r\n\r\nexport var __assign = function() {\r\n __assign = Object.assign || function __assign(t) {\r\n for (var s, i = 1, n = arguments.length; i < n; i++) {\r\n s = arguments[i];\r\n for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];\r\n }\r\n return t;\r\n }\r\n return __assign.apply(this, arguments);\r\n}\r\n\r\nexport function __rest(s, e) {\r\n var t = {};\r\n for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)\r\n t[p] = s[p];\r\n if (s != null && typeof Object.getOwnPropertySymbols === \"function\")\r\n for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {\r\n if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))\r\n t[p[i]] = s[p[i]];\r\n }\r\n return t;\r\n}\r\n\r\nexport function __decorate(decorators, target, key, desc) {\r\n var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;\r\n if (typeof Reflect === \"object\" && typeof Reflect.decorate === \"function\") r = Reflect.decorate(decorators, target, key, desc);\r\n else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;\r\n return c > 3 && r && Object.defineProperty(target, key, r), r;\r\n}\r\n\r\nexport function __param(paramIndex, decorator) {\r\n return function (target, key) { decorator(target, key, paramIndex); }\r\n}\r\n\r\nexport function __metadata(metadataKey, metadataValue) {\r\n if (typeof Reflect === \"object\" && typeof Reflect.metadata === \"function\") return Reflect.metadata(metadataKey, metadataValue);\r\n}\r\n\r\nexport function __awaiter(thisArg, _arguments, P, generator) {\r\n function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }\r\n return new (P || (P = Promise))(function (resolve, reject) {\r\n function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }\r\n function rejected(value) { try { step(generator[\"throw\"](value)); } catch (e) { reject(e); } }\r\n function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }\r\n step((generator = generator.apply(thisArg, _arguments || [])).next());\r\n });\r\n}\r\n\r\nexport function __generator(thisArg, body) {\r\n var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;\r\n return g = { next: verb(0), \"throw\": verb(1), \"return\": verb(2) }, typeof Symbol === \"function\" && (g[Symbol.iterator] = function() { return this; }), g;\r\n function verb(n) { return function (v) { return step([n, v]); }; }\r\n function step(op) {\r\n if (f) throw new TypeError(\"Generator is already executing.\");\r\n while (_) try {\r\n if (f = 1, y && (t = op[0] & 2 ? y[\"return\"] : op[0] ? y[\"throw\"] || ((t = y[\"return\"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;\r\n if (y = 0, t) op = [op[0] & 2, t.value];\r\n switch (op[0]) {\r\n case 0: case 1: t = op; break;\r\n case 4: _.label++; return { value: op[1], done: false };\r\n case 5: _.label++; y = op[1]; op = [0]; continue;\r\n case 7: op = _.ops.pop(); _.trys.pop(); continue;\r\n default:\r\n if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }\r\n if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }\r\n if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }\r\n if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }\r\n if (t[2]) _.ops.pop();\r\n _.trys.pop(); continue;\r\n }\r\n op = body.call(thisArg, _);\r\n } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }\r\n if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };\r\n }\r\n}\r\n\r\nexport var __createBinding = Object.create ? (function(o, m, k, k2) {\r\n if (k2 === undefined) k2 = k;\r\n Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });\r\n}) : (function(o, m, k, k2) {\r\n if (k2 === undefined) k2 = k;\r\n o[k2] = m[k];\r\n});\r\n\r\nexport function __exportStar(m, o) {\r\n for (var p in m) if (p !== \"default\" && !Object.prototype.hasOwnProperty.call(o, p)) __createBinding(o, m, p);\r\n}\r\n\r\nexport function __values(o) {\r\n var s = typeof Symbol === \"function\" && Symbol.iterator, m = s && o[s], i = 0;\r\n if (m) return m.call(o);\r\n if (o && typeof o.length === \"number\") return {\r\n next: function () {\r\n if (o && i >= o.length) o = void 0;\r\n return { value: o && o[i++], done: !o };\r\n }\r\n };\r\n throw new TypeError(s ? \"Object is not iterable.\" : \"Symbol.iterator is not defined.\");\r\n}\r\n\r\nexport function __read(o, n) {\r\n var m = typeof Symbol === \"function\" && o[Symbol.iterator];\r\n if (!m) return o;\r\n var i = m.call(o), r, ar = [], e;\r\n try {\r\n while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);\r\n }\r\n catch (error) { e = { error: error }; }\r\n finally {\r\n try {\r\n if (r && !r.done && (m = i[\"return\"])) m.call(i);\r\n }\r\n finally { if (e) throw e.error; }\r\n }\r\n return ar;\r\n}\r\n\r\n/** @deprecated */\r\nexport function __spread() {\r\n for (var ar = [], i = 0; i < arguments.length; i++)\r\n ar = ar.concat(__read(arguments[i]));\r\n return ar;\r\n}\r\n\r\n/** @deprecated */\r\nexport function __spreadArrays() {\r\n for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length;\r\n for (var r = Array(s), k = 0, i = 0; i < il; i++)\r\n for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++)\r\n r[k] = a[j];\r\n return r;\r\n}\r\n\r\nexport function __spreadArray(to, from, pack) {\r\n if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {\r\n if (ar || !(i in from)) {\r\n if (!ar) ar = Array.prototype.slice.call(from, 0, i);\r\n ar[i] = from[i];\r\n }\r\n }\r\n return to.concat(ar || Array.prototype.slice.call(from));\r\n}\r\n\r\nexport function __await(v) {\r\n return this instanceof __await ? (this.v = v, this) : new __await(v);\r\n}\r\n\r\nexport function __asyncGenerator(thisArg, _arguments, generator) {\r\n if (!Symbol.asyncIterator) throw new TypeError(\"Symbol.asyncIterator is not defined.\");\r\n var g = generator.apply(thisArg, _arguments || []), i, q = [];\r\n return i = {}, verb(\"next\"), verb(\"throw\"), verb(\"return\"), i[Symbol.asyncIterator] = function () { return this; }, i;\r\n function verb(n) { if (g[n]) i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; }\r\n function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(q[0][3], e); } }\r\n function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); }\r\n function fulfill(value) { resume(\"next\", value); }\r\n function reject(value) { resume(\"throw\", value); }\r\n function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); }\r\n}\r\n\r\nexport function __asyncDelegator(o) {\r\n var i, p;\r\n return i = {}, verb(\"next\"), verb(\"throw\", function (e) { throw e; }), verb(\"return\"), i[Symbol.iterator] = function () { return this; }, i;\r\n function verb(n, f) { i[n] = o[n] ? function (v) { return (p = !p) ? { value: __await(o[n](v)), done: n === \"return\" } : f ? f(v) : v; } : f; }\r\n}\r\n\r\nexport function __asyncValues(o) {\r\n if (!Symbol.asyncIterator) throw new TypeError(\"Symbol.asyncIterator is not defined.\");\r\n var m = o[Symbol.asyncIterator], i;\r\n return m ? m.call(o) : (o = typeof __values === \"function\" ? __values(o) : o[Symbol.iterator](), i = {}, verb(\"next\"), verb(\"throw\"), verb(\"return\"), i[Symbol.asyncIterator] = function () { return this; }, i);\r\n function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }\r\n function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }\r\n}\r\n\r\nexport function __makeTemplateObject(cooked, raw) {\r\n if (Object.defineProperty) { Object.defineProperty(cooked, \"raw\", { value: raw }); } else { cooked.raw = raw; }\r\n return cooked;\r\n};\r\n\r\nvar __setModuleDefault = Object.create ? (function(o, v) {\r\n Object.defineProperty(o, \"default\", { enumerable: true, value: v });\r\n}) : function(o, v) {\r\n o[\"default\"] = v;\r\n};\r\n\r\nexport function __importStar(mod) {\r\n if (mod && mod.__esModule) return mod;\r\n var result = {};\r\n if (mod != null) for (var k in mod) if (k !== \"default\" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);\r\n __setModuleDefault(result, mod);\r\n return result;\r\n}\r\n\r\nexport function __importDefault(mod) {\r\n return (mod && mod.__esModule) ? mod : { default: mod };\r\n}\r\n\r\nexport function __classPrivateFieldGet(receiver, state, kind, f) {\r\n if (kind === \"a\" && !f) throw new TypeError(\"Private accessor was defined without a getter\");\r\n if (typeof state === \"function\" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError(\"Cannot read private member from an object whose class did not declare it\");\r\n return kind === \"m\" ? f : kind === \"a\" ? f.call(receiver) : f ? f.value : state.get(receiver);\r\n}\r\n\r\nexport function __classPrivateFieldSet(receiver, state, value, kind, f) {\r\n if (kind === \"m\") throw new TypeError(\"Private method is not writable\");\r\n if (kind === \"a\" && !f) throw new TypeError(\"Private accessor was defined without a setter\");\r\n if (typeof state === \"function\" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError(\"Cannot write private member to an object whose class did not declare it\");\r\n return (kind === \"a\" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;\r\n}\r\n", "/**\n * Returns true if the object is a function.\n * @param value The value to check\n */\nexport function isFunction(value: any): value is (...args: any[]) => any {\n return typeof value === 'function';\n}\n", "/**\n * Used to create Error subclasses until the community moves away from ES5.\n *\n * This is because compiling from TypeScript down to ES5 has issues with subclassing Errors\n * as well as other built-in types: https://github.com/Microsoft/TypeScript/issues/12123\n *\n * @param createImpl A factory function to create the actual constructor implementation. The returned\n * function should be a named function that calls `_super` internally.\n */\nexport function createErrorClass(createImpl: (_super: any) => any): T {\n const _super = (instance: any) => {\n Error.call(instance);\n instance.stack = new Error().stack;\n };\n\n const ctorFunc = createImpl(_super);\n ctorFunc.prototype = Object.create(Error.prototype);\n ctorFunc.prototype.constructor = ctorFunc;\n return ctorFunc;\n}\n", "import { createErrorClass } from './createErrorClass';\n\nexport interface UnsubscriptionError extends Error {\n readonly errors: any[];\n}\n\nexport interface UnsubscriptionErrorCtor {\n /**\n * @deprecated Internal implementation detail. Do not construct error instances.\n * Cannot be tagged as internal: https://github.com/ReactiveX/rxjs/issues/6269\n */\n new (errors: any[]): UnsubscriptionError;\n}\n\n/**\n * An error thrown when one or more errors have occurred during the\n * `unsubscribe` of a {@link Subscription}.\n */\nexport const UnsubscriptionError: UnsubscriptionErrorCtor = createErrorClass(\n (_super) =>\n function UnsubscriptionErrorImpl(this: any, errors: (Error | string)[]) {\n _super(this);\n this.message = errors\n ? `${errors.length} errors occurred during unsubscription:\n${errors.map((err, i) => `${i + 1}) ${err.toString()}`).join('\\n ')}`\n : '';\n this.name = 'UnsubscriptionError';\n this.errors = errors;\n }\n);\n", "/**\n * Removes an item from an array, mutating it.\n * @param arr The array to remove the item from\n * @param item The item to remove\n */\nexport function arrRemove(arr: T[] | undefined | null, item: T) {\n if (arr) {\n const index = arr.indexOf(item);\n 0 <= index && arr.splice(index, 1);\n }\n}\n", "import { isFunction } from './util/isFunction';\nimport { UnsubscriptionError } from './util/UnsubscriptionError';\nimport { SubscriptionLike, TeardownLogic, Unsubscribable } from './types';\nimport { arrRemove } from './util/arrRemove';\n\n/**\n * Represents a disposable resource, such as the execution of an Observable. A\n * Subscription has one important method, `unsubscribe`, that takes no argument\n * and just disposes the resource held by the subscription.\n *\n * Additionally, subscriptions may be grouped together through the `add()`\n * method, which will attach a child Subscription to the current Subscription.\n * When a Subscription is unsubscribed, all its children (and its grandchildren)\n * will be unsubscribed as well.\n *\n * @class Subscription\n */\nexport class Subscription implements SubscriptionLike {\n /** @nocollapse */\n public static EMPTY = (() => {\n const empty = new Subscription();\n empty.closed = true;\n return empty;\n })();\n\n /**\n * A flag to indicate whether this Subscription has already been unsubscribed.\n */\n public closed = false;\n\n private _parentage: Subscription[] | Subscription | null = null;\n\n /**\n * The list of registered finalizers to execute upon unsubscription. Adding and removing from this\n * list occurs in the {@link #add} and {@link #remove} methods.\n */\n private _finalizers: Exclude[] | null = null;\n\n /**\n * @param initialTeardown A function executed first as part of the finalization\n * process that is kicked off when {@link #unsubscribe} is called.\n */\n constructor(private initialTeardown?: () => void) {}\n\n /**\n * Disposes the resources held by the subscription. May, for instance, cancel\n * an ongoing Observable execution or cancel any other type of work that\n * started when the Subscription was created.\n * @return {void}\n */\n unsubscribe(): void {\n let errors: any[] | undefined;\n\n if (!this.closed) {\n this.closed = true;\n\n // Remove this from it's parents.\n const { _parentage } = this;\n if (_parentage) {\n this._parentage = null;\n if (Array.isArray(_parentage)) {\n for (const parent of _parentage) {\n parent.remove(this);\n }\n } else {\n _parentage.remove(this);\n }\n }\n\n const { initialTeardown: initialFinalizer } = this;\n if (isFunction(initialFinalizer)) {\n try {\n initialFinalizer();\n } catch (e) {\n errors = e instanceof UnsubscriptionError ? e.errors : [e];\n }\n }\n\n const { _finalizers } = this;\n if (_finalizers) {\n this._finalizers = null;\n for (const finalizer of _finalizers) {\n try {\n execFinalizer(finalizer);\n } catch (err) {\n errors = errors ?? [];\n if (err instanceof UnsubscriptionError) {\n errors = [...errors, ...err.errors];\n } else {\n errors.push(err);\n }\n }\n }\n }\n\n if (errors) {\n throw new UnsubscriptionError(errors);\n }\n }\n }\n\n /**\n * Adds a finalizer to this subscription, so that finalization will be unsubscribed/called\n * when this subscription is unsubscribed. If this subscription is already {@link #closed},\n * because it has already been unsubscribed, then whatever finalizer is passed to it\n * will automatically be executed (unless the finalizer itself is also a closed subscription).\n *\n * Closed Subscriptions cannot be added as finalizers to any subscription. Adding a closed\n * subscription to a any subscription will result in no operation. (A noop).\n *\n * Adding a subscription to itself, or adding `null` or `undefined` will not perform any\n * operation at all. (A noop).\n *\n * `Subscription` instances that are added to this instance will automatically remove themselves\n * if they are unsubscribed. Functions and {@link Unsubscribable} objects that you wish to remove\n * will need to be removed manually with {@link #remove}\n *\n * @param teardown The finalization logic to add to this subscription.\n */\n add(teardown: TeardownLogic): void {\n // Only add the finalizer if it's not undefined\n // and don't add a subscription to itself.\n if (teardown && teardown !== this) {\n if (this.closed) {\n // If this subscription is already closed,\n // execute whatever finalizer is handed to it automatically.\n execFinalizer(teardown);\n } else {\n if (teardown instanceof Subscription) {\n // We don't add closed subscriptions, and we don't add the same subscription\n // twice. Subscription unsubscribe is idempotent.\n if (teardown.closed || teardown._hasParent(this)) {\n return;\n }\n teardown._addParent(this);\n }\n (this._finalizers = this._finalizers ?? []).push(teardown);\n }\n }\n }\n\n /**\n * Checks to see if a this subscription already has a particular parent.\n * This will signal that this subscription has already been added to the parent in question.\n * @param parent the parent to check for\n */\n private _hasParent(parent: Subscription) {\n const { _parentage } = this;\n return _parentage === parent || (Array.isArray(_parentage) && _parentage.includes(parent));\n }\n\n /**\n * Adds a parent to this subscription so it can be removed from the parent if it\n * unsubscribes on it's own.\n *\n * NOTE: THIS ASSUMES THAT {@link _hasParent} HAS ALREADY BEEN CHECKED.\n * @param parent The parent subscription to add\n */\n private _addParent(parent: Subscription) {\n const { _parentage } = this;\n this._parentage = Array.isArray(_parentage) ? (_parentage.push(parent), _parentage) : _parentage ? [_parentage, parent] : parent;\n }\n\n /**\n * Called on a child when it is removed via {@link #remove}.\n * @param parent The parent to remove\n */\n private _removeParent(parent: Subscription) {\n const { _parentage } = this;\n if (_parentage === parent) {\n this._parentage = null;\n } else if (Array.isArray(_parentage)) {\n arrRemove(_parentage, parent);\n }\n }\n\n /**\n * Removes a finalizer from this subscription that was previously added with the {@link #add} method.\n *\n * Note that `Subscription` instances, when unsubscribed, will automatically remove themselves\n * from every other `Subscription` they have been added to. This means that using the `remove` method\n * is not a common thing and should be used thoughtfully.\n *\n * If you add the same finalizer instance of a function or an unsubscribable object to a `Subscription` instance\n * more than once, you will need to call `remove` the same number of times to remove all instances.\n *\n * All finalizer instances are removed to free up memory upon unsubscription.\n *\n * @param teardown The finalizer to remove from this subscription\n */\n remove(teardown: Exclude): void {\n const { _finalizers } = this;\n _finalizers && arrRemove(_finalizers, teardown);\n\n if (teardown instanceof Subscription) {\n teardown._removeParent(this);\n }\n }\n}\n\nexport const EMPTY_SUBSCRIPTION = Subscription.EMPTY;\n\nexport function isSubscription(value: any): value is Subscription {\n return (\n value instanceof Subscription ||\n (value && 'closed' in value && isFunction(value.remove) && isFunction(value.add) && isFunction(value.unsubscribe))\n );\n}\n\nfunction execFinalizer(finalizer: Unsubscribable | (() => void)) {\n if (isFunction(finalizer)) {\n finalizer();\n } else {\n finalizer.unsubscribe();\n }\n}\n", "import { Subscriber } from './Subscriber';\nimport { ObservableNotification } from './types';\n\n/**\n * The {@link GlobalConfig} object for RxJS. It is used to configure things\n * like how to react on unhandled errors.\n */\nexport const config: GlobalConfig = {\n onUnhandledError: null,\n onStoppedNotification: null,\n Promise: undefined,\n useDeprecatedSynchronousErrorHandling: false,\n useDeprecatedNextContext: false,\n};\n\n/**\n * The global configuration object for RxJS, used to configure things\n * like how to react on unhandled errors. Accessible via {@link config}\n * object.\n */\nexport interface GlobalConfig {\n /**\n * A registration point for unhandled errors from RxJS. These are errors that\n * cannot were not handled by consuming code in the usual subscription path. For\n * example, if you have this configured, and you subscribe to an observable without\n * providing an error handler, errors from that subscription will end up here. This\n * will _always_ be called asynchronously on another job in the runtime. This is because\n * we do not want errors thrown in this user-configured handler to interfere with the\n * behavior of the library.\n */\n onUnhandledError: ((err: any) => void) | null;\n\n /**\n * A registration point for notifications that cannot be sent to subscribers because they\n * have completed, errored or have been explicitly unsubscribed. By default, next, complete\n * and error notifications sent to stopped subscribers are noops. However, sometimes callers\n * might want a different behavior. For example, with sources that attempt to report errors\n * to stopped subscribers, a caller can configure RxJS to throw an unhandled error instead.\n * This will _always_ be called asynchronously on another job in the runtime. This is because\n * we do not want errors thrown in this user-configured handler to interfere with the\n * behavior of the library.\n */\n onStoppedNotification: ((notification: ObservableNotification, subscriber: Subscriber) => void) | null;\n\n /**\n * The promise constructor used by default for {@link Observable#toPromise toPromise} and {@link Observable#forEach forEach}\n * methods.\n *\n * @deprecated As of version 8, RxJS will no longer support this sort of injection of a\n * Promise constructor. If you need a Promise implementation other than native promises,\n * please polyfill/patch Promise as you see appropriate. Will be removed in v8.\n */\n Promise?: PromiseConstructorLike;\n\n /**\n * If true, turns on synchronous error rethrowing, which is a deprecated behavior\n * in v6 and higher. This behavior enables bad patterns like wrapping a subscribe\n * call in a try/catch block. It also enables producer interference, a nasty bug\n * where a multicast can be broken for all observers by a downstream consumer with\n * an unhandled error. DO NOT USE THIS FLAG UNLESS IT'S NEEDED TO BUY TIME\n * FOR MIGRATION REASONS.\n *\n * @deprecated As of version 8, RxJS will no longer support synchronous throwing\n * of unhandled errors. All errors will be thrown on a separate call stack to prevent bad\n * behaviors described above. Will be removed in v8.\n */\n useDeprecatedSynchronousErrorHandling: boolean;\n\n /**\n * If true, enables an as-of-yet undocumented feature from v5: The ability to access\n * `unsubscribe()` via `this` context in `next` functions created in observers passed\n * to `subscribe`.\n *\n * This is being removed because the performance was severely problematic, and it could also cause\n * issues when types other than POJOs are passed to subscribe as subscribers, as they will likely have\n * their `this` context overwritten.\n *\n * @deprecated As of version 8, RxJS will no longer support altering the\n * context of next functions provided as part of an observer to Subscribe. Instead,\n * you will have access to a subscription or a signal or token that will allow you to do things like\n * unsubscribe and test closed status. Will be removed in v8.\n */\n useDeprecatedNextContext: boolean;\n}\n", "import type { TimerHandle } from './timerHandle';\ntype SetTimeoutFunction = (handler: () => void, timeout?: number, ...args: any[]) => TimerHandle;\ntype ClearTimeoutFunction = (handle: TimerHandle) => void;\n\ninterface TimeoutProvider {\n setTimeout: SetTimeoutFunction;\n clearTimeout: ClearTimeoutFunction;\n delegate:\n | {\n setTimeout: SetTimeoutFunction;\n clearTimeout: ClearTimeoutFunction;\n }\n | undefined;\n}\n\nexport const timeoutProvider: TimeoutProvider = {\n // When accessing the delegate, use the variable rather than `this` so that\n // the functions can be called without being bound to the provider.\n setTimeout(handler: () => void, timeout?: number, ...args) {\n const { delegate } = timeoutProvider;\n if (delegate?.setTimeout) {\n return delegate.setTimeout(handler, timeout, ...args);\n }\n return setTimeout(handler, timeout, ...args);\n },\n clearTimeout(handle) {\n const { delegate } = timeoutProvider;\n return (delegate?.clearTimeout || clearTimeout)(handle as any);\n },\n delegate: undefined,\n};\n", "import { config } from '../config';\nimport { timeoutProvider } from '../scheduler/timeoutProvider';\n\n/**\n * Handles an error on another job either with the user-configured {@link onUnhandledError},\n * or by throwing it on that new job so it can be picked up by `window.onerror`, `process.on('error')`, etc.\n *\n * This should be called whenever there is an error that is out-of-band with the subscription\n * or when an error hits a terminal boundary of the subscription and no error handler was provided.\n *\n * @param err the error to report\n */\nexport function reportUnhandledError(err: any) {\n timeoutProvider.setTimeout(() => {\n const { onUnhandledError } = config;\n if (onUnhandledError) {\n // Execute the user-configured error handler.\n onUnhandledError(err);\n } else {\n // Throw so it is picked up by the runtime's uncaught error mechanism.\n throw err;\n }\n });\n}\n", "/* tslint:disable:no-empty */\nexport function noop() { }\n", "import { CompleteNotification, NextNotification, ErrorNotification } from './types';\n\n/**\n * A completion object optimized for memory use and created to be the\n * same \"shape\" as other notifications in v8.\n * @internal\n */\nexport const COMPLETE_NOTIFICATION = (() => createNotification('C', undefined, undefined) as CompleteNotification)();\n\n/**\n * Internal use only. Creates an optimized error notification that is the same \"shape\"\n * as other notifications.\n * @internal\n */\nexport function errorNotification(error: any): ErrorNotification {\n return createNotification('E', undefined, error) as any;\n}\n\n/**\n * Internal use only. Creates an optimized next notification that is the same \"shape\"\n * as other notifications.\n * @internal\n */\nexport function nextNotification(value: T) {\n return createNotification('N', value, undefined) as NextNotification;\n}\n\n/**\n * Ensures that all notifications created internally have the same \"shape\" in v8.\n *\n * TODO: This is only exported to support a crazy legacy test in `groupBy`.\n * @internal\n */\nexport function createNotification(kind: 'N' | 'E' | 'C', value: any, error: any) {\n return {\n kind,\n value,\n error,\n };\n}\n", "import { config } from '../config';\n\nlet context: { errorThrown: boolean; error: any } | null = null;\n\n/**\n * Handles dealing with errors for super-gross mode. Creates a context, in which\n * any synchronously thrown errors will be passed to {@link captureError}. Which\n * will record the error such that it will be rethrown after the call back is complete.\n * TODO: Remove in v8\n * @param cb An immediately executed function.\n */\nexport function errorContext(cb: () => void) {\n if (config.useDeprecatedSynchronousErrorHandling) {\n const isRoot = !context;\n if (isRoot) {\n context = { errorThrown: false, error: null };\n }\n cb();\n if (isRoot) {\n const { errorThrown, error } = context!;\n context = null;\n if (errorThrown) {\n throw error;\n }\n }\n } else {\n // This is the general non-deprecated path for everyone that\n // isn't crazy enough to use super-gross mode (useDeprecatedSynchronousErrorHandling)\n cb();\n }\n}\n\n/**\n * Captures errors only in super-gross mode.\n * @param err the error to capture\n */\nexport function captureError(err: any) {\n if (config.useDeprecatedSynchronousErrorHandling && context) {\n context.errorThrown = true;\n context.error = err;\n }\n}\n", "import { isFunction } from './util/isFunction';\nimport { Observer, ObservableNotification } from './types';\nimport { isSubscription, Subscription } from './Subscription';\nimport { config } from './config';\nimport { reportUnhandledError } from './util/reportUnhandledError';\nimport { noop } from './util/noop';\nimport { nextNotification, errorNotification, COMPLETE_NOTIFICATION } from './NotificationFactories';\nimport { timeoutProvider } from './scheduler/timeoutProvider';\nimport { captureError } from './util/errorContext';\n\n/**\n * Implements the {@link Observer} interface and extends the\n * {@link Subscription} class. While the {@link Observer} is the public API for\n * consuming the values of an {@link Observable}, all Observers get converted to\n * a Subscriber, in order to provide Subscription-like capabilities such as\n * `unsubscribe`. Subscriber is a common type in RxJS, and crucial for\n * implementing operators, but it is rarely used as a public API.\n *\n * @class Subscriber\n */\nexport class Subscriber extends Subscription implements Observer {\n /**\n * A static factory for a Subscriber, given a (potentially partial) definition\n * of an Observer.\n * @param next The `next` callback of an Observer.\n * @param error The `error` callback of an\n * Observer.\n * @param complete The `complete` callback of an\n * Observer.\n * @return A Subscriber wrapping the (partially defined)\n * Observer represented by the given arguments.\n * @nocollapse\n * @deprecated Do not use. Will be removed in v8. There is no replacement for this\n * method, and there is no reason to be creating instances of `Subscriber` directly.\n * If you have a specific use case, please file an issue.\n */\n static create(next?: (x?: T) => void, error?: (e?: any) => void, complete?: () => void): Subscriber {\n return new SafeSubscriber(next, error, complete);\n }\n\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n protected isStopped: boolean = false;\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n protected destination: Subscriber | Observer; // this `any` is the escape hatch to erase extra type param (e.g. R)\n\n /**\n * @deprecated Internal implementation detail, do not use directly. Will be made internal in v8.\n * There is no reason to directly create an instance of Subscriber. This type is exported for typings reasons.\n */\n constructor(destination?: Subscriber | Observer) {\n super();\n if (destination) {\n this.destination = destination;\n // Automatically chain subscriptions together here.\n // if destination is a Subscription, then it is a Subscriber.\n if (isSubscription(destination)) {\n destination.add(this);\n }\n } else {\n this.destination = EMPTY_OBSERVER;\n }\n }\n\n /**\n * The {@link Observer} callback to receive notifications of type `next` from\n * the Observable, with a value. The Observable may call this method 0 or more\n * times.\n * @param {T} [value] The `next` value.\n * @return {void}\n */\n next(value?: T): void {\n if (this.isStopped) {\n handleStoppedNotification(nextNotification(value), this);\n } else {\n this._next(value!);\n }\n }\n\n /**\n * The {@link Observer} callback to receive notifications of type `error` from\n * the Observable, with an attached `Error`. Notifies the Observer that\n * the Observable has experienced an error condition.\n * @param {any} [err] The `error` exception.\n * @return {void}\n */\n error(err?: any): void {\n if (this.isStopped) {\n handleStoppedNotification(errorNotification(err), this);\n } else {\n this.isStopped = true;\n this._error(err);\n }\n }\n\n /**\n * The {@link Observer} callback to receive a valueless notification of type\n * `complete` from the Observable. Notifies the Observer that the Observable\n * has finished sending push-based notifications.\n * @return {void}\n */\n complete(): void {\n if (this.isStopped) {\n handleStoppedNotification(COMPLETE_NOTIFICATION, this);\n } else {\n this.isStopped = true;\n this._complete();\n }\n }\n\n unsubscribe(): void {\n if (!this.closed) {\n this.isStopped = true;\n super.unsubscribe();\n this.destination = null!;\n }\n }\n\n protected _next(value: T): void {\n this.destination.next(value);\n }\n\n protected _error(err: any): void {\n try {\n this.destination.error(err);\n } finally {\n this.unsubscribe();\n }\n }\n\n protected _complete(): void {\n try {\n this.destination.complete();\n } finally {\n this.unsubscribe();\n }\n }\n}\n\n/**\n * This bind is captured here because we want to be able to have\n * compatibility with monoid libraries that tend to use a method named\n * `bind`. In particular, a library called Monio requires this.\n */\nconst _bind = Function.prototype.bind;\n\nfunction bind any>(fn: Fn, thisArg: any): Fn {\n return _bind.call(fn, thisArg);\n}\n\n/**\n * Internal optimization only, DO NOT EXPOSE.\n * @internal\n */\nclass ConsumerObserver implements Observer {\n constructor(private partialObserver: Partial>) {}\n\n next(value: T): void {\n const { partialObserver } = this;\n if (partialObserver.next) {\n try {\n partialObserver.next(value);\n } catch (error) {\n handleUnhandledError(error);\n }\n }\n }\n\n error(err: any): void {\n const { partialObserver } = this;\n if (partialObserver.error) {\n try {\n partialObserver.error(err);\n } catch (error) {\n handleUnhandledError(error);\n }\n } else {\n handleUnhandledError(err);\n }\n }\n\n complete(): void {\n const { partialObserver } = this;\n if (partialObserver.complete) {\n try {\n partialObserver.complete();\n } catch (error) {\n handleUnhandledError(error);\n }\n }\n }\n}\n\nexport class SafeSubscriber extends Subscriber {\n constructor(\n observerOrNext?: Partial> | ((value: T) => void) | null,\n error?: ((e?: any) => void) | null,\n complete?: (() => void) | null\n ) {\n super();\n\n let partialObserver: Partial>;\n if (isFunction(observerOrNext) || !observerOrNext) {\n // The first argument is a function, not an observer. The next\n // two arguments *could* be observers, or they could be empty.\n partialObserver = {\n next: (observerOrNext ?? undefined) as (((value: T) => void) | undefined),\n error: error ?? undefined,\n complete: complete ?? undefined,\n };\n } else {\n // The first argument is a partial observer.\n let context: any;\n if (this && config.useDeprecatedNextContext) {\n // This is a deprecated path that made `this.unsubscribe()` available in\n // next handler functions passed to subscribe. This only exists behind a flag\n // now, as it is *very* slow.\n context = Object.create(observerOrNext);\n context.unsubscribe = () => this.unsubscribe();\n partialObserver = {\n next: observerOrNext.next && bind(observerOrNext.next, context),\n error: observerOrNext.error && bind(observerOrNext.error, context),\n complete: observerOrNext.complete && bind(observerOrNext.complete, context),\n };\n } else {\n // The \"normal\" path. Just use the partial observer directly.\n partialObserver = observerOrNext;\n }\n }\n\n // Wrap the partial observer to ensure it's a full observer, and\n // make sure proper error handling is accounted for.\n this.destination = new ConsumerObserver(partialObserver);\n }\n}\n\nfunction handleUnhandledError(error: any) {\n if (config.useDeprecatedSynchronousErrorHandling) {\n captureError(error);\n } else {\n // Ideal path, we report this as an unhandled error,\n // which is thrown on a new call stack.\n reportUnhandledError(error);\n }\n}\n\n/**\n * An error handler used when no error handler was supplied\n * to the SafeSubscriber -- meaning no error handler was supplied\n * do the `subscribe` call on our observable.\n * @param err The error to handle\n */\nfunction defaultErrorHandler(err: any) {\n throw err;\n}\n\n/**\n * A handler for notifications that cannot be sent to a stopped subscriber.\n * @param notification The notification being sent\n * @param subscriber The stopped subscriber\n */\nfunction handleStoppedNotification(notification: ObservableNotification, subscriber: Subscriber) {\n const { onStoppedNotification } = config;\n onStoppedNotification && timeoutProvider.setTimeout(() => onStoppedNotification(notification, subscriber));\n}\n\n/**\n * The observer used as a stub for subscriptions where the user did not\n * pass any arguments to `subscribe`. Comes with the default error handling\n * behavior.\n */\nexport const EMPTY_OBSERVER: Readonly> & { closed: true } = {\n closed: true,\n next: noop,\n error: defaultErrorHandler,\n complete: noop,\n};\n", "/**\n * Symbol.observable or a string \"@@observable\". Used for interop\n *\n * @deprecated We will no longer be exporting this symbol in upcoming versions of RxJS.\n * Instead polyfill and use Symbol.observable directly *or* use https://www.npmjs.com/package/symbol-observable\n */\nexport const observable: string | symbol = (() => (typeof Symbol === 'function' && Symbol.observable) || '@@observable')();\n", "/**\n * This function takes one parameter and just returns it. Simply put,\n * this is like `(x: T): T => x`.\n *\n * ## Examples\n *\n * This is useful in some cases when using things like `mergeMap`\n *\n * ```ts\n * import { interval, take, map, range, mergeMap, identity } from 'rxjs';\n *\n * const source$ = interval(1000).pipe(take(5));\n *\n * const result$ = source$.pipe(\n * map(i => range(i)),\n * mergeMap(identity) // same as mergeMap(x => x)\n * );\n *\n * result$.subscribe({\n * next: console.log\n * });\n * ```\n *\n * Or when you want to selectively apply an operator\n *\n * ```ts\n * import { interval, take, identity } from 'rxjs';\n *\n * const shouldLimit = () => Math.random() < 0.5;\n *\n * const source$ = interval(1000);\n *\n * const result$ = source$.pipe(shouldLimit() ? take(5) : identity);\n *\n * result$.subscribe({\n * next: console.log\n * });\n * ```\n *\n * @param x Any value that is returned by this function\n * @returns The value passed as the first parameter to this function\n */\nexport function identity(x: T): T {\n return x;\n}\n", "import { identity } from './identity';\nimport { UnaryFunction } from '../types';\n\nexport function pipe(): typeof identity;\nexport function pipe(fn1: UnaryFunction): UnaryFunction;\nexport function pipe(fn1: UnaryFunction, fn2: UnaryFunction): UnaryFunction;\nexport function pipe(fn1: UnaryFunction, fn2: UnaryFunction, fn3: UnaryFunction): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction\n): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction,\n fn5: UnaryFunction\n): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction,\n fn5: UnaryFunction,\n fn6: UnaryFunction\n): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction,\n fn5: UnaryFunction,\n fn6: UnaryFunction,\n fn7: UnaryFunction\n): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction,\n fn5: UnaryFunction,\n fn6: UnaryFunction,\n fn7: UnaryFunction,\n fn8: UnaryFunction\n): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction,\n fn5: UnaryFunction,\n fn6: UnaryFunction,\n fn7: UnaryFunction,\n fn8: UnaryFunction,\n fn9: UnaryFunction\n): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction,\n fn5: UnaryFunction,\n fn6: UnaryFunction,\n fn7: UnaryFunction,\n fn8: UnaryFunction,\n fn9: UnaryFunction,\n ...fns: UnaryFunction[]\n): UnaryFunction;\n\n/**\n * pipe() can be called on one or more functions, each of which can take one argument (\"UnaryFunction\")\n * and uses it to return a value.\n * It returns a function that takes one argument, passes it to the first UnaryFunction, and then\n * passes the result to the next one, passes that result to the next one, and so on. \n */\nexport function pipe(...fns: Array>): UnaryFunction {\n return pipeFromArray(fns);\n}\n\n/** @internal */\nexport function pipeFromArray(fns: Array>): UnaryFunction {\n if (fns.length === 0) {\n return identity as UnaryFunction;\n }\n\n if (fns.length === 1) {\n return fns[0];\n }\n\n return function piped(input: T): R {\n return fns.reduce((prev: any, fn: UnaryFunction) => fn(prev), input as any);\n };\n}\n", "import { Operator } from './Operator';\nimport { SafeSubscriber, Subscriber } from './Subscriber';\nimport { isSubscription, Subscription } from './Subscription';\nimport { TeardownLogic, OperatorFunction, Subscribable, Observer } from './types';\nimport { observable as Symbol_observable } from './symbol/observable';\nimport { pipeFromArray } from './util/pipe';\nimport { config } from './config';\nimport { isFunction } from './util/isFunction';\nimport { errorContext } from './util/errorContext';\n\n/**\n * A representation of any set of values over any amount of time. This is the most basic building block\n * of RxJS.\n *\n * @class Observable\n */\nexport class Observable implements Subscribable {\n /**\n * @deprecated Internal implementation detail, do not use directly. Will be made internal in v8.\n */\n source: Observable | undefined;\n\n /**\n * @deprecated Internal implementation detail, do not use directly. Will be made internal in v8.\n */\n operator: Operator | undefined;\n\n /**\n * @constructor\n * @param {Function} subscribe the function that is called when the Observable is\n * initially subscribed to. This function is given a Subscriber, to which new values\n * can be `next`ed, or an `error` method can be called to raise an error, or\n * `complete` can be called to notify of a successful completion.\n */\n constructor(subscribe?: (this: Observable, subscriber: Subscriber) => TeardownLogic) {\n if (subscribe) {\n this._subscribe = subscribe;\n }\n }\n\n // HACK: Since TypeScript inherits static properties too, we have to\n // fight against TypeScript here so Subject can have a different static create signature\n /**\n * Creates a new Observable by calling the Observable constructor\n * @owner Observable\n * @method create\n * @param {Function} subscribe? the subscriber function to be passed to the Observable constructor\n * @return {Observable} a new observable\n * @nocollapse\n * @deprecated Use `new Observable()` instead. Will be removed in v8.\n */\n static create: (...args: any[]) => any = (subscribe?: (subscriber: Subscriber) => TeardownLogic) => {\n return new Observable(subscribe);\n };\n\n /**\n * Creates a new Observable, with this Observable instance as the source, and the passed\n * operator defined as the new observable's operator.\n * @method lift\n * @param operator the operator defining the operation to take on the observable\n * @return a new observable with the Operator applied\n * @deprecated Internal implementation detail, do not use directly. Will be made internal in v8.\n * If you have implemented an operator using `lift`, it is recommended that you create an\n * operator by simply returning `new Observable()` directly. See \"Creating new operators from\n * scratch\" section here: https://rxjs.dev/guide/operators\n */\n lift(operator?: Operator): Observable {\n const observable = new Observable();\n observable.source = this;\n observable.operator = operator;\n return observable;\n }\n\n subscribe(observerOrNext?: Partial> | ((value: T) => void)): Subscription;\n /** @deprecated Instead of passing separate callback arguments, use an observer argument. Signatures taking separate callback arguments will be removed in v8. Details: https://rxjs.dev/deprecations/subscribe-arguments */\n subscribe(next?: ((value: T) => void) | null, error?: ((error: any) => void) | null, complete?: (() => void) | null): Subscription;\n /**\n * Invokes an execution of an Observable and registers Observer handlers for notifications it will emit.\n *\n * Use it when you have all these Observables, but still nothing is happening.\n *\n * `subscribe` is not a regular operator, but a method that calls Observable's internal `subscribe` function. It\n * might be for example a function that you passed to Observable's constructor, but most of the time it is\n * a library implementation, which defines what will be emitted by an Observable, and when it be will emitted. This means\n * that calling `subscribe` is actually the moment when Observable starts its work, not when it is created, as it is often\n * the thought.\n *\n * Apart from starting the execution of an Observable, this method allows you to listen for values\n * that an Observable emits, as well as for when it completes or errors. You can achieve this in two\n * of the following ways.\n *\n * The first way is creating an object that implements {@link Observer} interface. It should have methods\n * defined by that interface, but note that it should be just a regular JavaScript object, which you can create\n * yourself in any way you want (ES6 class, classic function constructor, object literal etc.). In particular, do\n * not attempt to use any RxJS implementation details to create Observers - you don't need them. Remember also\n * that your object does not have to implement all methods. If you find yourself creating a method that doesn't\n * do anything, you can simply omit it. Note however, if the `error` method is not provided and an error happens,\n * it will be thrown asynchronously. Errors thrown asynchronously cannot be caught using `try`/`catch`. Instead,\n * use the {@link onUnhandledError} configuration option or use a runtime handler (like `window.onerror` or\n * `process.on('error)`) to be notified of unhandled errors. Because of this, it's recommended that you provide\n * an `error` method to avoid missing thrown errors.\n *\n * The second way is to give up on Observer object altogether and simply provide callback functions in place of its methods.\n * This means you can provide three functions as arguments to `subscribe`, where the first function is equivalent\n * of a `next` method, the second of an `error` method and the third of a `complete` method. Just as in case of an Observer,\n * if you do not need to listen for something, you can omit a function by passing `undefined` or `null`,\n * since `subscribe` recognizes these functions by where they were placed in function call. When it comes\n * to the `error` function, as with an Observer, if not provided, errors emitted by an Observable will be thrown asynchronously.\n *\n * You can, however, subscribe with no parameters at all. This may be the case where you're not interested in terminal events\n * and you also handled emissions internally by using operators (e.g. using `tap`).\n *\n * Whichever style of calling `subscribe` you use, in both cases it returns a Subscription object.\n * This object allows you to call `unsubscribe` on it, which in turn will stop the work that an Observable does and will clean\n * up all resources that an Observable used. Note that cancelling a subscription will not call `complete` callback\n * provided to `subscribe` function, which is reserved for a regular completion signal that comes from an Observable.\n *\n * Remember that callbacks provided to `subscribe` are not guaranteed to be called asynchronously.\n * It is an Observable itself that decides when these functions will be called. For example {@link of}\n * by default emits all its values synchronously. Always check documentation for how given Observable\n * will behave when subscribed and if its default behavior can be modified with a `scheduler`.\n *\n * #### Examples\n *\n * Subscribe with an {@link guide/observer Observer}\n *\n * ```ts\n * import { of } from 'rxjs';\n *\n * const sumObserver = {\n * sum: 0,\n * next(value) {\n * console.log('Adding: ' + value);\n * this.sum = this.sum + value;\n * },\n * error() {\n * // We actually could just remove this method,\n * // since we do not really care about errors right now.\n * },\n * complete() {\n * console.log('Sum equals: ' + this.sum);\n * }\n * };\n *\n * of(1, 2, 3) // Synchronously emits 1, 2, 3 and then completes.\n * .subscribe(sumObserver);\n *\n * // Logs:\n * // 'Adding: 1'\n * // 'Adding: 2'\n * // 'Adding: 3'\n * // 'Sum equals: 6'\n * ```\n *\n * Subscribe with functions ({@link deprecations/subscribe-arguments deprecated})\n *\n * ```ts\n * import { of } from 'rxjs'\n *\n * let sum = 0;\n *\n * of(1, 2, 3).subscribe(\n * value => {\n * console.log('Adding: ' + value);\n * sum = sum + value;\n * },\n * undefined,\n * () => console.log('Sum equals: ' + sum)\n * );\n *\n * // Logs:\n * // 'Adding: 1'\n * // 'Adding: 2'\n * // 'Adding: 3'\n * // 'Sum equals: 6'\n * ```\n *\n * Cancel a subscription\n *\n * ```ts\n * import { interval } from 'rxjs';\n *\n * const subscription = interval(1000).subscribe({\n * next(num) {\n * console.log(num)\n * },\n * complete() {\n * // Will not be called, even when cancelling subscription.\n * console.log('completed!');\n * }\n * });\n *\n * setTimeout(() => {\n * subscription.unsubscribe();\n * console.log('unsubscribed!');\n * }, 2500);\n *\n * // Logs:\n * // 0 after 1s\n * // 1 after 2s\n * // 'unsubscribed!' after 2.5s\n * ```\n *\n * @param {Observer|Function} observerOrNext (optional) Either an observer with methods to be called,\n * or the first of three possible handlers, which is the handler for each value emitted from the subscribed\n * Observable.\n * @param {Function} error (optional) A handler for a terminal event resulting from an error. If no error handler is provided,\n * the error will be thrown asynchronously as unhandled.\n * @param {Function} complete (optional) A handler for a terminal event resulting from successful completion.\n * @return {Subscription} a subscription reference to the registered handlers\n * @method subscribe\n */\n subscribe(\n observerOrNext?: Partial> | ((value: T) => void) | null,\n error?: ((error: any) => void) | null,\n complete?: (() => void) | null\n ): Subscription {\n const subscriber = isSubscriber(observerOrNext) ? observerOrNext : new SafeSubscriber(observerOrNext, error, complete);\n\n errorContext(() => {\n const { operator, source } = this;\n subscriber.add(\n operator\n ? // We're dealing with a subscription in the\n // operator chain to one of our lifted operators.\n operator.call(subscriber, source)\n : source\n ? // If `source` has a value, but `operator` does not, something that\n // had intimate knowledge of our API, like our `Subject`, must have\n // set it. We're going to just call `_subscribe` directly.\n this._subscribe(subscriber)\n : // In all other cases, we're likely wrapping a user-provided initializer\n // function, so we need to catch errors and handle them appropriately.\n this._trySubscribe(subscriber)\n );\n });\n\n return subscriber;\n }\n\n /** @internal */\n protected _trySubscribe(sink: Subscriber): TeardownLogic {\n try {\n return this._subscribe(sink);\n } catch (err) {\n // We don't need to return anything in this case,\n // because it's just going to try to `add()` to a subscription\n // above.\n sink.error(err);\n }\n }\n\n /**\n * Used as a NON-CANCELLABLE means of subscribing to an observable, for use with\n * APIs that expect promises, like `async/await`. You cannot unsubscribe from this.\n *\n * **WARNING**: Only use this with observables you *know* will complete. If the source\n * observable does not complete, you will end up with a promise that is hung up, and\n * potentially all of the state of an async function hanging out in memory. To avoid\n * this situation, look into adding something like {@link timeout}, {@link take},\n * {@link takeWhile}, or {@link takeUntil} amongst others.\n *\n * #### Example\n *\n * ```ts\n * import { interval, take } from 'rxjs';\n *\n * const source$ = interval(1000).pipe(take(4));\n *\n * async function getTotal() {\n * let total = 0;\n *\n * await source$.forEach(value => {\n * total += value;\n * console.log('observable -> ' + value);\n * });\n *\n * return total;\n * }\n *\n * getTotal().then(\n * total => console.log('Total: ' + total)\n * );\n *\n * // Expected:\n * // 'observable -> 0'\n * // 'observable -> 1'\n * // 'observable -> 2'\n * // 'observable -> 3'\n * // 'Total: 6'\n * ```\n *\n * @param next a handler for each value emitted by the observable\n * @return a promise that either resolves on observable completion or\n * rejects with the handled error\n */\n forEach(next: (value: T) => void): Promise;\n\n /**\n * @param next a handler for each value emitted by the observable\n * @param promiseCtor a constructor function used to instantiate the Promise\n * @return a promise that either resolves on observable completion or\n * rejects with the handled error\n * @deprecated Passing a Promise constructor will no longer be available\n * in upcoming versions of RxJS. This is because it adds weight to the library, for very\n * little benefit. If you need this functionality, it is recommended that you either\n * polyfill Promise, or you create an adapter to convert the returned native promise\n * to whatever promise implementation you wanted. Will be removed in v8.\n */\n forEach(next: (value: T) => void, promiseCtor: PromiseConstructorLike): Promise;\n\n forEach(next: (value: T) => void, promiseCtor?: PromiseConstructorLike): Promise {\n promiseCtor = getPromiseCtor(promiseCtor);\n\n return new promiseCtor((resolve, reject) => {\n const subscriber = new SafeSubscriber({\n next: (value) => {\n try {\n next(value);\n } catch (err) {\n reject(err);\n subscriber.unsubscribe();\n }\n },\n error: reject,\n complete: resolve,\n });\n this.subscribe(subscriber);\n }) as Promise;\n }\n\n /** @internal */\n protected _subscribe(subscriber: Subscriber): TeardownLogic {\n return this.source?.subscribe(subscriber);\n }\n\n /**\n * An interop point defined by the es7-observable spec https://github.com/zenparsing/es-observable\n * @method Symbol.observable\n * @return {Observable} this instance of the observable\n */\n [Symbol_observable]() {\n return this;\n }\n\n /* tslint:disable:max-line-length */\n pipe(): Observable;\n pipe(op1: OperatorFunction): Observable;\n pipe(op1: OperatorFunction, op2: OperatorFunction): Observable;\n pipe(op1: OperatorFunction, op2: OperatorFunction, op3: OperatorFunction): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction\n ): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction,\n op5: OperatorFunction\n ): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction,\n op5: OperatorFunction,\n op6: OperatorFunction\n ): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction,\n op5: OperatorFunction,\n op6: OperatorFunction,\n op7: OperatorFunction\n ): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction,\n op5: OperatorFunction,\n op6: OperatorFunction,\n op7: OperatorFunction,\n op8: OperatorFunction\n ): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction,\n op5: OperatorFunction,\n op6: OperatorFunction,\n op7: OperatorFunction,\n op8: OperatorFunction,\n op9: OperatorFunction\n ): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction,\n op5: OperatorFunction,\n op6: OperatorFunction,\n op7: OperatorFunction,\n op8: OperatorFunction,\n op9: OperatorFunction,\n ...operations: OperatorFunction[]\n ): Observable;\n /* tslint:enable:max-line-length */\n\n /**\n * Used to stitch together functional operators into a chain.\n * @method pipe\n * @return {Observable} the Observable result of all of the operators having\n * been called in the order they were passed in.\n *\n * ## Example\n *\n * ```ts\n * import { interval, filter, map, scan } from 'rxjs';\n *\n * interval(1000)\n * .pipe(\n * filter(x => x % 2 === 0),\n * map(x => x + x),\n * scan((acc, x) => acc + x)\n * )\n * .subscribe(x => console.log(x));\n * ```\n */\n pipe(...operations: OperatorFunction[]): Observable {\n return pipeFromArray(operations)(this);\n }\n\n /* tslint:disable:max-line-length */\n /** @deprecated Replaced with {@link firstValueFrom} and {@link lastValueFrom}. Will be removed in v8. Details: https://rxjs.dev/deprecations/to-promise */\n toPromise(): Promise;\n /** @deprecated Replaced with {@link firstValueFrom} and {@link lastValueFrom}. Will be removed in v8. Details: https://rxjs.dev/deprecations/to-promise */\n toPromise(PromiseCtor: typeof Promise): Promise;\n /** @deprecated Replaced with {@link firstValueFrom} and {@link lastValueFrom}. Will be removed in v8. Details: https://rxjs.dev/deprecations/to-promise */\n toPromise(PromiseCtor: PromiseConstructorLike): Promise;\n /* tslint:enable:max-line-length */\n\n /**\n * Subscribe to this Observable and get a Promise resolving on\n * `complete` with the last emission (if any).\n *\n * **WARNING**: Only use this with observables you *know* will complete. If the source\n * observable does not complete, you will end up with a promise that is hung up, and\n * potentially all of the state of an async function hanging out in memory. To avoid\n * this situation, look into adding something like {@link timeout}, {@link take},\n * {@link takeWhile}, or {@link takeUntil} amongst others.\n *\n * @method toPromise\n * @param [promiseCtor] a constructor function used to instantiate\n * the Promise\n * @return A Promise that resolves with the last value emit, or\n * rejects on an error. If there were no emissions, Promise\n * resolves with undefined.\n * @deprecated Replaced with {@link firstValueFrom} and {@link lastValueFrom}. Will be removed in v8. Details: https://rxjs.dev/deprecations/to-promise\n */\n toPromise(promiseCtor?: PromiseConstructorLike): Promise {\n promiseCtor = getPromiseCtor(promiseCtor);\n\n return new promiseCtor((resolve, reject) => {\n let value: T | undefined;\n this.subscribe(\n (x: T) => (value = x),\n (err: any) => reject(err),\n () => resolve(value)\n );\n }) as Promise;\n }\n}\n\n/**\n * Decides between a passed promise constructor from consuming code,\n * A default configured promise constructor, and the native promise\n * constructor and returns it. If nothing can be found, it will throw\n * an error.\n * @param promiseCtor The optional promise constructor to passed by consuming code\n */\nfunction getPromiseCtor(promiseCtor: PromiseConstructorLike | undefined) {\n return promiseCtor ?? config.Promise ?? Promise;\n}\n\nfunction isObserver(value: any): value is Observer {\n return value && isFunction(value.next) && isFunction(value.error) && isFunction(value.complete);\n}\n\nfunction isSubscriber(value: any): value is Subscriber {\n return (value && value instanceof Subscriber) || (isObserver(value) && isSubscription(value));\n}\n", "import { Observable } from '../Observable';\nimport { Subscriber } from '../Subscriber';\nimport { OperatorFunction } from '../types';\nimport { isFunction } from './isFunction';\n\n/**\n * Used to determine if an object is an Observable with a lift function.\n */\nexport function hasLift(source: any): source is { lift: InstanceType['lift'] } {\n return isFunction(source?.lift);\n}\n\n/**\n * Creates an `OperatorFunction`. Used to define operators throughout the library in a concise way.\n * @param init The logic to connect the liftedSource to the subscriber at the moment of subscription.\n */\nexport function operate(\n init: (liftedSource: Observable, subscriber: Subscriber) => (() => void) | void\n): OperatorFunction {\n return (source: Observable) => {\n if (hasLift(source)) {\n return source.lift(function (this: Subscriber, liftedSource: Observable) {\n try {\n return init(liftedSource, this);\n } catch (err) {\n this.error(err);\n }\n });\n }\n throw new TypeError('Unable to lift unknown Observable type');\n };\n}\n", "import { Subscriber } from '../Subscriber';\n\n/**\n * Creates an instance of an `OperatorSubscriber`.\n * @param destination The downstream subscriber.\n * @param onNext Handles next values, only called if this subscriber is not stopped or closed. Any\n * error that occurs in this function is caught and sent to the `error` method of this subscriber.\n * @param onError Handles errors from the subscription, any errors that occur in this handler are caught\n * and send to the `destination` error handler.\n * @param onComplete Handles completion notification from the subscription. Any errors that occur in\n * this handler are sent to the `destination` error handler.\n * @param onFinalize Additional teardown logic here. This will only be called on teardown if the\n * subscriber itself is not already closed. This is called after all other teardown logic is executed.\n */\nexport function createOperatorSubscriber(\n destination: Subscriber,\n onNext?: (value: T) => void,\n onComplete?: () => void,\n onError?: (err: any) => void,\n onFinalize?: () => void\n): Subscriber {\n return new OperatorSubscriber(destination, onNext, onComplete, onError, onFinalize);\n}\n\n/**\n * A generic helper for allowing operators to be created with a Subscriber and\n * use closures to capture necessary state from the operator function itself.\n */\nexport class OperatorSubscriber extends Subscriber {\n /**\n * Creates an instance of an `OperatorSubscriber`.\n * @param destination The downstream subscriber.\n * @param onNext Handles next values, only called if this subscriber is not stopped or closed. Any\n * error that occurs in this function is caught and sent to the `error` method of this subscriber.\n * @param onError Handles errors from the subscription, any errors that occur in this handler are caught\n * and send to the `destination` error handler.\n * @param onComplete Handles completion notification from the subscription. Any errors that occur in\n * this handler are sent to the `destination` error handler.\n * @param onFinalize Additional finalization logic here. This will only be called on finalization if the\n * subscriber itself is not already closed. This is called after all other finalization logic is executed.\n * @param shouldUnsubscribe An optional check to see if an unsubscribe call should truly unsubscribe.\n * NOTE: This currently **ONLY** exists to support the strange behavior of {@link groupBy}, where unsubscription\n * to the resulting observable does not actually disconnect from the source if there are active subscriptions\n * to any grouped observable. (DO NOT EXPOSE OR USE EXTERNALLY!!!)\n */\n constructor(\n destination: Subscriber,\n onNext?: (value: T) => void,\n onComplete?: () => void,\n onError?: (err: any) => void,\n private onFinalize?: () => void,\n private shouldUnsubscribe?: () => boolean\n ) {\n // It's important - for performance reasons - that all of this class's\n // members are initialized and that they are always initialized in the same\n // order. This will ensure that all OperatorSubscriber instances have the\n // same hidden class in V8. This, in turn, will help keep the number of\n // hidden classes involved in property accesses within the base class as\n // low as possible. If the number of hidden classes involved exceeds four,\n // the property accesses will become megamorphic and performance penalties\n // will be incurred - i.e. inline caches won't be used.\n //\n // The reasons for ensuring all instances have the same hidden class are\n // further discussed in this blog post from Benedikt Meurer:\n // https://benediktmeurer.de/2018/03/23/impact-of-polymorphism-on-component-based-frameworks-like-react/\n super(destination);\n this._next = onNext\n ? function (this: OperatorSubscriber, value: T) {\n try {\n onNext(value);\n } catch (err) {\n destination.error(err);\n }\n }\n : super._next;\n this._error = onError\n ? function (this: OperatorSubscriber, err: any) {\n try {\n onError(err);\n } catch (err) {\n // Send any errors that occur down stream.\n destination.error(err);\n } finally {\n // Ensure finalization.\n this.unsubscribe();\n }\n }\n : super._error;\n this._complete = onComplete\n ? function (this: OperatorSubscriber) {\n try {\n onComplete();\n } catch (err) {\n // Send any errors that occur down stream.\n destination.error(err);\n } finally {\n // Ensure finalization.\n this.unsubscribe();\n }\n }\n : super._complete;\n }\n\n unsubscribe() {\n if (!this.shouldUnsubscribe || this.shouldUnsubscribe()) {\n const { closed } = this;\n super.unsubscribe();\n // Execute additional teardown if we have any and we didn't already do so.\n !closed && this.onFinalize?.();\n }\n }\n}\n", "import { Subscription } from '../Subscription';\n\ninterface AnimationFrameProvider {\n schedule(callback: FrameRequestCallback): Subscription;\n requestAnimationFrame: typeof requestAnimationFrame;\n cancelAnimationFrame: typeof cancelAnimationFrame;\n delegate:\n | {\n requestAnimationFrame: typeof requestAnimationFrame;\n cancelAnimationFrame: typeof cancelAnimationFrame;\n }\n | undefined;\n}\n\nexport const animationFrameProvider: AnimationFrameProvider = {\n // When accessing the delegate, use the variable rather than `this` so that\n // the functions can be called without being bound to the provider.\n schedule(callback) {\n let request = requestAnimationFrame;\n let cancel: typeof cancelAnimationFrame | undefined = cancelAnimationFrame;\n const { delegate } = animationFrameProvider;\n if (delegate) {\n request = delegate.requestAnimationFrame;\n cancel = delegate.cancelAnimationFrame;\n }\n const handle = request((timestamp) => {\n // Clear the cancel function. The request has been fulfilled, so\n // attempting to cancel the request upon unsubscription would be\n // pointless.\n cancel = undefined;\n callback(timestamp);\n });\n return new Subscription(() => cancel?.(handle));\n },\n requestAnimationFrame(...args) {\n const { delegate } = animationFrameProvider;\n return (delegate?.requestAnimationFrame || requestAnimationFrame)(...args);\n },\n cancelAnimationFrame(...args) {\n const { delegate } = animationFrameProvider;\n return (delegate?.cancelAnimationFrame || cancelAnimationFrame)(...args);\n },\n delegate: undefined,\n};\n", "import { createErrorClass } from './createErrorClass';\n\nexport interface ObjectUnsubscribedError extends Error {}\n\nexport interface ObjectUnsubscribedErrorCtor {\n /**\n * @deprecated Internal implementation detail. Do not construct error instances.\n * Cannot be tagged as internal: https://github.com/ReactiveX/rxjs/issues/6269\n */\n new (): ObjectUnsubscribedError;\n}\n\n/**\n * An error thrown when an action is invalid because the object has been\n * unsubscribed.\n *\n * @see {@link Subject}\n * @see {@link BehaviorSubject}\n *\n * @class ObjectUnsubscribedError\n */\nexport const ObjectUnsubscribedError: ObjectUnsubscribedErrorCtor = createErrorClass(\n (_super) =>\n function ObjectUnsubscribedErrorImpl(this: any) {\n _super(this);\n this.name = 'ObjectUnsubscribedError';\n this.message = 'object unsubscribed';\n }\n);\n", "import { Operator } from './Operator';\nimport { Observable } from './Observable';\nimport { Subscriber } from './Subscriber';\nimport { Subscription, EMPTY_SUBSCRIPTION } from './Subscription';\nimport { Observer, SubscriptionLike, TeardownLogic } from './types';\nimport { ObjectUnsubscribedError } from './util/ObjectUnsubscribedError';\nimport { arrRemove } from './util/arrRemove';\nimport { errorContext } from './util/errorContext';\n\n/**\n * A Subject is a special type of Observable that allows values to be\n * multicasted to many Observers. Subjects are like EventEmitters.\n *\n * Every Subject is an Observable and an Observer. You can subscribe to a\n * Subject, and you can call next to feed values as well as error and complete.\n */\nexport class Subject extends Observable implements SubscriptionLike {\n closed = false;\n\n private currentObservers: Observer[] | null = null;\n\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n observers: Observer[] = [];\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n isStopped = false;\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n hasError = false;\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n thrownError: any = null;\n\n /**\n * Creates a \"subject\" by basically gluing an observer to an observable.\n *\n * @nocollapse\n * @deprecated Recommended you do not use. Will be removed at some point in the future. Plans for replacement still under discussion.\n */\n static create: (...args: any[]) => any = (destination: Observer, source: Observable): AnonymousSubject => {\n return new AnonymousSubject(destination, source);\n };\n\n constructor() {\n // NOTE: This must be here to obscure Observable's constructor.\n super();\n }\n\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n lift(operator: Operator): Observable {\n const subject = new AnonymousSubject(this, this);\n subject.operator = operator as any;\n return subject as any;\n }\n\n /** @internal */\n protected _throwIfClosed() {\n if (this.closed) {\n throw new ObjectUnsubscribedError();\n }\n }\n\n next(value: T) {\n errorContext(() => {\n this._throwIfClosed();\n if (!this.isStopped) {\n if (!this.currentObservers) {\n this.currentObservers = Array.from(this.observers);\n }\n for (const observer of this.currentObservers) {\n observer.next(value);\n }\n }\n });\n }\n\n error(err: any) {\n errorContext(() => {\n this._throwIfClosed();\n if (!this.isStopped) {\n this.hasError = this.isStopped = true;\n this.thrownError = err;\n const { observers } = this;\n while (observers.length) {\n observers.shift()!.error(err);\n }\n }\n });\n }\n\n complete() {\n errorContext(() => {\n this._throwIfClosed();\n if (!this.isStopped) {\n this.isStopped = true;\n const { observers } = this;\n while (observers.length) {\n observers.shift()!.complete();\n }\n }\n });\n }\n\n unsubscribe() {\n this.isStopped = this.closed = true;\n this.observers = this.currentObservers = null!;\n }\n\n get observed() {\n return this.observers?.length > 0;\n }\n\n /** @internal */\n protected _trySubscribe(subscriber: Subscriber): TeardownLogic {\n this._throwIfClosed();\n return super._trySubscribe(subscriber);\n }\n\n /** @internal */\n protected _subscribe(subscriber: Subscriber): Subscription {\n this._throwIfClosed();\n this._checkFinalizedStatuses(subscriber);\n return this._innerSubscribe(subscriber);\n }\n\n /** @internal */\n protected _innerSubscribe(subscriber: Subscriber) {\n const { hasError, isStopped, observers } = this;\n if (hasError || isStopped) {\n return EMPTY_SUBSCRIPTION;\n }\n this.currentObservers = null;\n observers.push(subscriber);\n return new Subscription(() => {\n this.currentObservers = null;\n arrRemove(observers, subscriber);\n });\n }\n\n /** @internal */\n protected _checkFinalizedStatuses(subscriber: Subscriber) {\n const { hasError, thrownError, isStopped } = this;\n if (hasError) {\n subscriber.error(thrownError);\n } else if (isStopped) {\n subscriber.complete();\n }\n }\n\n /**\n * Creates a new Observable with this Subject as the source. You can do this\n * to create custom Observer-side logic of the Subject and conceal it from\n * code that uses the Observable.\n * @return {Observable} Observable that the Subject casts to\n */\n asObservable(): Observable {\n const observable: any = new Observable();\n observable.source = this;\n return observable;\n }\n}\n\n/**\n * @class AnonymousSubject\n */\nexport class AnonymousSubject extends Subject {\n constructor(\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n public destination?: Observer,\n source?: Observable\n ) {\n super();\n this.source = source;\n }\n\n next(value: T) {\n this.destination?.next?.(value);\n }\n\n error(err: any) {\n this.destination?.error?.(err);\n }\n\n complete() {\n this.destination?.complete?.();\n }\n\n /** @internal */\n protected _subscribe(subscriber: Subscriber): Subscription {\n return this.source?.subscribe(subscriber) ?? EMPTY_SUBSCRIPTION;\n }\n}\n", "import { Subject } from './Subject';\nimport { Subscriber } from './Subscriber';\nimport { Subscription } from './Subscription';\n\n/**\n * A variant of Subject that requires an initial value and emits its current\n * value whenever it is subscribed to.\n *\n * @class BehaviorSubject\n */\nexport class BehaviorSubject extends Subject {\n constructor(private _value: T) {\n super();\n }\n\n get value(): T {\n return this.getValue();\n }\n\n /** @internal */\n protected _subscribe(subscriber: Subscriber): Subscription {\n const subscription = super._subscribe(subscriber);\n !subscription.closed && subscriber.next(this._value);\n return subscription;\n }\n\n getValue(): T {\n const { hasError, thrownError, _value } = this;\n if (hasError) {\n throw thrownError;\n }\n this._throwIfClosed();\n return _value;\n }\n\n next(value: T): void {\n super.next((this._value = value));\n }\n}\n", "import { TimestampProvider } from '../types';\n\ninterface DateTimestampProvider extends TimestampProvider {\n delegate: TimestampProvider | undefined;\n}\n\nexport const dateTimestampProvider: DateTimestampProvider = {\n now() {\n // Use the variable rather than `this` so that the function can be called\n // without being bound to the provider.\n return (dateTimestampProvider.delegate || Date).now();\n },\n delegate: undefined,\n};\n", "import { Subject } from './Subject';\nimport { TimestampProvider } from './types';\nimport { Subscriber } from './Subscriber';\nimport { Subscription } from './Subscription';\nimport { dateTimestampProvider } from './scheduler/dateTimestampProvider';\n\n/**\n * A variant of {@link Subject} that \"replays\" old values to new subscribers by emitting them when they first subscribe.\n *\n * `ReplaySubject` has an internal buffer that will store a specified number of values that it has observed. Like `Subject`,\n * `ReplaySubject` \"observes\" values by having them passed to its `next` method. When it observes a value, it will store that\n * value for a time determined by the configuration of the `ReplaySubject`, as passed to its constructor.\n *\n * When a new subscriber subscribes to the `ReplaySubject` instance, it will synchronously emit all values in its buffer in\n * a First-In-First-Out (FIFO) manner. The `ReplaySubject` will also complete, if it has observed completion; and it will\n * error if it has observed an error.\n *\n * There are two main configuration items to be concerned with:\n *\n * 1. `bufferSize` - This will determine how many items are stored in the buffer, defaults to infinite.\n * 2. `windowTime` - The amount of time to hold a value in the buffer before removing it from the buffer.\n *\n * Both configurations may exist simultaneously. So if you would like to buffer a maximum of 3 values, as long as the values\n * are less than 2 seconds old, you could do so with a `new ReplaySubject(3, 2000)`.\n *\n * ### Differences with BehaviorSubject\n *\n * `BehaviorSubject` is similar to `new ReplaySubject(1)`, with a couple of exceptions:\n *\n * 1. `BehaviorSubject` comes \"primed\" with a single value upon construction.\n * 2. `ReplaySubject` will replay values, even after observing an error, where `BehaviorSubject` will not.\n *\n * @see {@link Subject}\n * @see {@link BehaviorSubject}\n * @see {@link shareReplay}\n */\nexport class ReplaySubject extends Subject {\n private _buffer: (T | number)[] = [];\n private _infiniteTimeWindow = true;\n\n /**\n * @param bufferSize The size of the buffer to replay on subscription\n * @param windowTime The amount of time the buffered items will stay buffered\n * @param timestampProvider An object with a `now()` method that provides the current timestamp. This is used to\n * calculate the amount of time something has been buffered.\n */\n constructor(\n private _bufferSize = Infinity,\n private _windowTime = Infinity,\n private _timestampProvider: TimestampProvider = dateTimestampProvider\n ) {\n super();\n this._infiniteTimeWindow = _windowTime === Infinity;\n this._bufferSize = Math.max(1, _bufferSize);\n this._windowTime = Math.max(1, _windowTime);\n }\n\n next(value: T): void {\n const { isStopped, _buffer, _infiniteTimeWindow, _timestampProvider, _windowTime } = this;\n if (!isStopped) {\n _buffer.push(value);\n !_infiniteTimeWindow && _buffer.push(_timestampProvider.now() + _windowTime);\n }\n this._trimBuffer();\n super.next(value);\n }\n\n /** @internal */\n protected _subscribe(subscriber: Subscriber): Subscription {\n this._throwIfClosed();\n this._trimBuffer();\n\n const subscription = this._innerSubscribe(subscriber);\n\n const { _infiniteTimeWindow, _buffer } = this;\n // We use a copy here, so reentrant code does not mutate our array while we're\n // emitting it to a new subscriber.\n const copy = _buffer.slice();\n for (let i = 0; i < copy.length && !subscriber.closed; i += _infiniteTimeWindow ? 1 : 2) {\n subscriber.next(copy[i] as T);\n }\n\n this._checkFinalizedStatuses(subscriber);\n\n return subscription;\n }\n\n private _trimBuffer() {\n const { _bufferSize, _timestampProvider, _buffer, _infiniteTimeWindow } = this;\n // If we don't have an infinite buffer size, and we're over the length,\n // use splice to truncate the old buffer values off. Note that we have to\n // double the size for instances where we're not using an infinite time window\n // because we're storing the values and the timestamps in the same array.\n const adjustedBufferSize = (_infiniteTimeWindow ? 1 : 2) * _bufferSize;\n _bufferSize < Infinity && adjustedBufferSize < _buffer.length && _buffer.splice(0, _buffer.length - adjustedBufferSize);\n\n // Now, if we're not in an infinite time window, remove all values where the time is\n // older than what is allowed.\n if (!_infiniteTimeWindow) {\n const now = _timestampProvider.now();\n let last = 0;\n // Search the array for the first timestamp that isn't expired and\n // truncate the buffer up to that point.\n for (let i = 1; i < _buffer.length && (_buffer[i] as number) <= now; i += 2) {\n last = i;\n }\n last && _buffer.splice(0, last + 1);\n }\n }\n}\n", "import { Scheduler } from '../Scheduler';\nimport { Subscription } from '../Subscription';\nimport { SchedulerAction } from '../types';\n\n/**\n * A unit of work to be executed in a `scheduler`. An action is typically\n * created from within a {@link SchedulerLike} and an RxJS user does not need to concern\n * themselves about creating and manipulating an Action.\n *\n * ```ts\n * class Action extends Subscription {\n * new (scheduler: Scheduler, work: (state?: T) => void);\n * schedule(state?: T, delay: number = 0): Subscription;\n * }\n * ```\n *\n * @class Action\n */\nexport class Action extends Subscription {\n constructor(scheduler: Scheduler, work: (this: SchedulerAction, state?: T) => void) {\n super();\n }\n /**\n * Schedules this action on its parent {@link SchedulerLike} for execution. May be passed\n * some context object, `state`. May happen at some point in the future,\n * according to the `delay` parameter, if specified.\n * @param {T} [state] Some contextual data that the `work` function uses when\n * called by the Scheduler.\n * @param {number} [delay] Time to wait before executing the work, where the\n * time unit is implicit and defined by the Scheduler.\n * @return {void}\n */\n public schedule(state?: T, delay: number = 0): Subscription {\n return this;\n }\n}\n", "import type { TimerHandle } from './timerHandle';\ntype SetIntervalFunction = (handler: () => void, timeout?: number, ...args: any[]) => TimerHandle;\ntype ClearIntervalFunction = (handle: TimerHandle) => void;\n\ninterface IntervalProvider {\n setInterval: SetIntervalFunction;\n clearInterval: ClearIntervalFunction;\n delegate:\n | {\n setInterval: SetIntervalFunction;\n clearInterval: ClearIntervalFunction;\n }\n | undefined;\n}\n\nexport const intervalProvider: IntervalProvider = {\n // When accessing the delegate, use the variable rather than `this` so that\n // the functions can be called without being bound to the provider.\n setInterval(handler: () => void, timeout?: number, ...args) {\n const { delegate } = intervalProvider;\n if (delegate?.setInterval) {\n return delegate.setInterval(handler, timeout, ...args);\n }\n return setInterval(handler, timeout, ...args);\n },\n clearInterval(handle) {\n const { delegate } = intervalProvider;\n return (delegate?.clearInterval || clearInterval)(handle as any);\n },\n delegate: undefined,\n};\n", "import { Action } from './Action';\nimport { SchedulerAction } from '../types';\nimport { Subscription } from '../Subscription';\nimport { AsyncScheduler } from './AsyncScheduler';\nimport { intervalProvider } from './intervalProvider';\nimport { arrRemove } from '../util/arrRemove';\nimport { TimerHandle } from './timerHandle';\n\nexport class AsyncAction extends Action {\n public id: TimerHandle | undefined;\n public state?: T;\n // @ts-ignore: Property has no initializer and is not definitely assigned\n public delay: number;\n protected pending: boolean = false;\n\n constructor(protected scheduler: AsyncScheduler, protected work: (this: SchedulerAction, state?: T) => void) {\n super(scheduler, work);\n }\n\n public schedule(state?: T, delay: number = 0): Subscription {\n if (this.closed) {\n return this;\n }\n\n // Always replace the current state with the new state.\n this.state = state;\n\n const id = this.id;\n const scheduler = this.scheduler;\n\n //\n // Important implementation note:\n //\n // Actions only execute once by default, unless rescheduled from within the\n // scheduled callback. This allows us to implement single and repeat\n // actions via the same code path, without adding API surface area, as well\n // as mimic traditional recursion but across asynchronous boundaries.\n //\n // However, JS runtimes and timers distinguish between intervals achieved by\n // serial `setTimeout` calls vs. a single `setInterval` call. An interval of\n // serial `setTimeout` calls can be individually delayed, which delays\n // scheduling the next `setTimeout`, and so on. `setInterval` attempts to\n // guarantee the interval callback will be invoked more precisely to the\n // interval period, regardless of load.\n //\n // Therefore, we use `setInterval` to schedule single and repeat actions.\n // If the action reschedules itself with the same delay, the interval is not\n // canceled. If the action doesn't reschedule, or reschedules with a\n // different delay, the interval will be canceled after scheduled callback\n // execution.\n //\n if (id != null) {\n this.id = this.recycleAsyncId(scheduler, id, delay);\n }\n\n // Set the pending flag indicating that this action has been scheduled, or\n // has recursively rescheduled itself.\n this.pending = true;\n\n this.delay = delay;\n // If this action has already an async Id, don't request a new one.\n this.id = this.id ?? this.requestAsyncId(scheduler, this.id, delay);\n\n return this;\n }\n\n protected requestAsyncId(scheduler: AsyncScheduler, _id?: TimerHandle, delay: number = 0): TimerHandle {\n return intervalProvider.setInterval(scheduler.flush.bind(scheduler, this), delay);\n }\n\n protected recycleAsyncId(_scheduler: AsyncScheduler, id?: TimerHandle, delay: number | null = 0): TimerHandle | undefined {\n // If this action is rescheduled with the same delay time, don't clear the interval id.\n if (delay != null && this.delay === delay && this.pending === false) {\n return id;\n }\n // Otherwise, if the action's delay time is different from the current delay,\n // or the action has been rescheduled before it's executed, clear the interval id\n if (id != null) {\n intervalProvider.clearInterval(id);\n }\n\n return undefined;\n }\n\n /**\n * Immediately executes this action and the `work` it contains.\n * @return {any}\n */\n public execute(state: T, delay: number): any {\n if (this.closed) {\n return new Error('executing a cancelled action');\n }\n\n this.pending = false;\n const error = this._execute(state, delay);\n if (error) {\n return error;\n } else if (this.pending === false && this.id != null) {\n // Dequeue if the action didn't reschedule itself. Don't call\n // unsubscribe(), because the action could reschedule later.\n // For example:\n // ```\n // scheduler.schedule(function doWork(counter) {\n // /* ... I'm a busy worker bee ... */\n // var originalAction = this;\n // /* wait 100ms before rescheduling the action */\n // setTimeout(function () {\n // originalAction.schedule(counter + 1);\n // }, 100);\n // }, 1000);\n // ```\n this.id = this.recycleAsyncId(this.scheduler, this.id, null);\n }\n }\n\n protected _execute(state: T, _delay: number): any {\n let errored: boolean = false;\n let errorValue: any;\n try {\n this.work(state);\n } catch (e) {\n errored = true;\n // HACK: Since code elsewhere is relying on the \"truthiness\" of the\n // return here, we can't have it return \"\" or 0 or false.\n // TODO: Clean this up when we refactor schedulers mid-version-8 or so.\n errorValue = e ? e : new Error('Scheduled action threw falsy error');\n }\n if (errored) {\n this.unsubscribe();\n return errorValue;\n }\n }\n\n unsubscribe() {\n if (!this.closed) {\n const { id, scheduler } = this;\n const { actions } = scheduler;\n\n this.work = this.state = this.scheduler = null!;\n this.pending = false;\n\n arrRemove(actions, this);\n if (id != null) {\n this.id = this.recycleAsyncId(scheduler, id, null);\n }\n\n this.delay = null!;\n super.unsubscribe();\n }\n }\n}\n", "import { Action } from './scheduler/Action';\nimport { Subscription } from './Subscription';\nimport { SchedulerLike, SchedulerAction } from './types';\nimport { dateTimestampProvider } from './scheduler/dateTimestampProvider';\n\n/**\n * An execution context and a data structure to order tasks and schedule their\n * execution. Provides a notion of (potentially virtual) time, through the\n * `now()` getter method.\n *\n * Each unit of work in a Scheduler is called an `Action`.\n *\n * ```ts\n * class Scheduler {\n * now(): number;\n * schedule(work, delay?, state?): Subscription;\n * }\n * ```\n *\n * @class Scheduler\n * @deprecated Scheduler is an internal implementation detail of RxJS, and\n * should not be used directly. Rather, create your own class and implement\n * {@link SchedulerLike}. Will be made internal in v8.\n */\nexport class Scheduler implements SchedulerLike {\n public static now: () => number = dateTimestampProvider.now;\n\n constructor(private schedulerActionCtor: typeof Action, now: () => number = Scheduler.now) {\n this.now = now;\n }\n\n /**\n * A getter method that returns a number representing the current time\n * (at the time this function was called) according to the scheduler's own\n * internal clock.\n * @return {number} A number that represents the current time. May or may not\n * have a relation to wall-clock time. May or may not refer to a time unit\n * (e.g. milliseconds).\n */\n public now: () => number;\n\n /**\n * Schedules a function, `work`, for execution. May happen at some point in\n * the future, according to the `delay` parameter, if specified. May be passed\n * some context object, `state`, which will be passed to the `work` function.\n *\n * The given arguments will be processed an stored as an Action object in a\n * queue of actions.\n *\n * @param {function(state: ?T): ?Subscription} work A function representing a\n * task, or some unit of work to be executed by the Scheduler.\n * @param {number} [delay] Time to wait before executing the work, where the\n * time unit is implicit and defined by the Scheduler itself.\n * @param {T} [state] Some contextual data that the `work` function uses when\n * called by the Scheduler.\n * @return {Subscription} A subscription in order to be able to unsubscribe\n * the scheduled work.\n */\n public schedule(work: (this: SchedulerAction, state?: T) => void, delay: number = 0, state?: T): Subscription {\n return new this.schedulerActionCtor(this, work).schedule(state, delay);\n }\n}\n", "import { Scheduler } from '../Scheduler';\nimport { Action } from './Action';\nimport { AsyncAction } from './AsyncAction';\nimport { TimerHandle } from './timerHandle';\n\nexport class AsyncScheduler extends Scheduler {\n public actions: Array> = [];\n /**\n * A flag to indicate whether the Scheduler is currently executing a batch of\n * queued actions.\n * @type {boolean}\n * @internal\n */\n public _active: boolean = false;\n /**\n * An internal ID used to track the latest asynchronous task such as those\n * coming from `setTimeout`, `setInterval`, `requestAnimationFrame`, and\n * others.\n * @type {any}\n * @internal\n */\n public _scheduled: TimerHandle | undefined;\n\n constructor(SchedulerAction: typeof Action, now: () => number = Scheduler.now) {\n super(SchedulerAction, now);\n }\n\n public flush(action: AsyncAction): void {\n const { actions } = this;\n\n if (this._active) {\n actions.push(action);\n return;\n }\n\n let error: any;\n this._active = true;\n\n do {\n if ((error = action.execute(action.state, action.delay))) {\n break;\n }\n } while ((action = actions.shift()!)); // exhaust the scheduler queue\n\n this._active = false;\n\n if (error) {\n while ((action = actions.shift()!)) {\n action.unsubscribe();\n }\n throw error;\n }\n }\n}\n", "import { AsyncAction } from './AsyncAction';\nimport { AsyncScheduler } from './AsyncScheduler';\n\n/**\n *\n * Async Scheduler\n *\n * Schedule task as if you used setTimeout(task, duration)\n *\n * `async` scheduler schedules tasks asynchronously, by putting them on the JavaScript\n * event loop queue. It is best used to delay tasks in time or to schedule tasks repeating\n * in intervals.\n *\n * If you just want to \"defer\" task, that is to perform it right after currently\n * executing synchronous code ends (commonly achieved by `setTimeout(deferredTask, 0)`),\n * better choice will be the {@link asapScheduler} scheduler.\n *\n * ## Examples\n * Use async scheduler to delay task\n * ```ts\n * import { asyncScheduler } from 'rxjs';\n *\n * const task = () => console.log('it works!');\n *\n * asyncScheduler.schedule(task, 2000);\n *\n * // After 2 seconds logs:\n * // \"it works!\"\n * ```\n *\n * Use async scheduler to repeat task in intervals\n * ```ts\n * import { asyncScheduler } from 'rxjs';\n *\n * function task(state) {\n * console.log(state);\n * this.schedule(state + 1, 1000); // `this` references currently executing Action,\n * // which we reschedule with new state and delay\n * }\n *\n * asyncScheduler.schedule(task, 3000, 0);\n *\n * // Logs:\n * // 0 after 3s\n * // 1 after 4s\n * // 2 after 5s\n * // 3 after 6s\n * ```\n */\n\nexport const asyncScheduler = new AsyncScheduler(AsyncAction);\n\n/**\n * @deprecated Renamed to {@link asyncScheduler}. Will be removed in v8.\n */\nexport const async = asyncScheduler;\n", "import { AsyncAction } from './AsyncAction';\nimport { Subscription } from '../Subscription';\nimport { QueueScheduler } from './QueueScheduler';\nimport { SchedulerAction } from '../types';\nimport { TimerHandle } from './timerHandle';\n\nexport class QueueAction extends AsyncAction {\n constructor(protected scheduler: QueueScheduler, protected work: (this: SchedulerAction, state?: T) => void) {\n super(scheduler, work);\n }\n\n public schedule(state?: T, delay: number = 0): Subscription {\n if (delay > 0) {\n return super.schedule(state, delay);\n }\n this.delay = delay;\n this.state = state;\n this.scheduler.flush(this);\n return this;\n }\n\n public execute(state: T, delay: number): any {\n return delay > 0 || this.closed ? super.execute(state, delay) : this._execute(state, delay);\n }\n\n protected requestAsyncId(scheduler: QueueScheduler, id?: TimerHandle, delay: number = 0): TimerHandle {\n // If delay exists and is greater than 0, or if the delay is null (the\n // action wasn't rescheduled) but was originally scheduled as an async\n // action, then recycle as an async action.\n\n if ((delay != null && delay > 0) || (delay == null && this.delay > 0)) {\n return super.requestAsyncId(scheduler, id, delay);\n }\n\n // Otherwise flush the scheduler starting with this action.\n scheduler.flush(this);\n\n // HACK: In the past, this was returning `void`. However, `void` isn't a valid\n // `TimerHandle`, and generally the return value here isn't really used. So the\n // compromise is to return `0` which is both \"falsy\" and a valid `TimerHandle`,\n // as opposed to refactoring every other instanceo of `requestAsyncId`.\n return 0;\n }\n}\n", "import { AsyncScheduler } from './AsyncScheduler';\n\nexport class QueueScheduler extends AsyncScheduler {\n}\n", "import { QueueAction } from './QueueAction';\nimport { QueueScheduler } from './QueueScheduler';\n\n/**\n *\n * Queue Scheduler\n *\n * Put every next task on a queue, instead of executing it immediately\n *\n * `queue` scheduler, when used with delay, behaves the same as {@link asyncScheduler} scheduler.\n *\n * When used without delay, it schedules given task synchronously - executes it right when\n * it is scheduled. However when called recursively, that is when inside the scheduled task,\n * another task is scheduled with queue scheduler, instead of executing immediately as well,\n * that task will be put on a queue and wait for current one to finish.\n *\n * This means that when you execute task with `queue` scheduler, you are sure it will end\n * before any other task scheduled with that scheduler will start.\n *\n * ## Examples\n * Schedule recursively first, then do something\n * ```ts\n * import { queueScheduler } from 'rxjs';\n *\n * queueScheduler.schedule(() => {\n * queueScheduler.schedule(() => console.log('second')); // will not happen now, but will be put on a queue\n *\n * console.log('first');\n * });\n *\n * // Logs:\n * // \"first\"\n * // \"second\"\n * ```\n *\n * Reschedule itself recursively\n * ```ts\n * import { queueScheduler } from 'rxjs';\n *\n * queueScheduler.schedule(function(state) {\n * if (state !== 0) {\n * console.log('before', state);\n * this.schedule(state - 1); // `this` references currently executing Action,\n * // which we reschedule with new state\n * console.log('after', state);\n * }\n * }, 0, 3);\n *\n * // In scheduler that runs recursively, you would expect:\n * // \"before\", 3\n * // \"before\", 2\n * // \"before\", 1\n * // \"after\", 1\n * // \"after\", 2\n * // \"after\", 3\n *\n * // But with queue it logs:\n * // \"before\", 3\n * // \"after\", 3\n * // \"before\", 2\n * // \"after\", 2\n * // \"before\", 1\n * // \"after\", 1\n * ```\n */\n\nexport const queueScheduler = new QueueScheduler(QueueAction);\n\n/**\n * @deprecated Renamed to {@link queueScheduler}. Will be removed in v8.\n */\nexport const queue = queueScheduler;\n", "import { AsyncAction } from './AsyncAction';\nimport { AnimationFrameScheduler } from './AnimationFrameScheduler';\nimport { SchedulerAction } from '../types';\nimport { animationFrameProvider } from './animationFrameProvider';\nimport { TimerHandle } from './timerHandle';\n\nexport class AnimationFrameAction extends AsyncAction {\n constructor(protected scheduler: AnimationFrameScheduler, protected work: (this: SchedulerAction, state?: T) => void) {\n super(scheduler, work);\n }\n\n protected requestAsyncId(scheduler: AnimationFrameScheduler, id?: TimerHandle, delay: number = 0): TimerHandle {\n // If delay is greater than 0, request as an async action.\n if (delay !== null && delay > 0) {\n return super.requestAsyncId(scheduler, id, delay);\n }\n // Push the action to the end of the scheduler queue.\n scheduler.actions.push(this);\n // If an animation frame has already been requested, don't request another\n // one. If an animation frame hasn't been requested yet, request one. Return\n // the current animation frame request id.\n return scheduler._scheduled || (scheduler._scheduled = animationFrameProvider.requestAnimationFrame(() => scheduler.flush(undefined)));\n }\n\n protected recycleAsyncId(scheduler: AnimationFrameScheduler, id?: TimerHandle, delay: number = 0): TimerHandle | undefined {\n // If delay exists and is greater than 0, or if the delay is null (the\n // action wasn't rescheduled) but was originally scheduled as an async\n // action, then recycle as an async action.\n if (delay != null ? delay > 0 : this.delay > 0) {\n return super.recycleAsyncId(scheduler, id, delay);\n }\n // If the scheduler queue has no remaining actions with the same async id,\n // cancel the requested animation frame and set the scheduled flag to\n // undefined so the next AnimationFrameAction will request its own.\n const { actions } = scheduler;\n if (id != null && actions[actions.length - 1]?.id !== id) {\n animationFrameProvider.cancelAnimationFrame(id as number);\n scheduler._scheduled = undefined;\n }\n // Return undefined so the action knows to request a new async id if it's rescheduled.\n return undefined;\n }\n}\n", "import { AsyncAction } from './AsyncAction';\nimport { AsyncScheduler } from './AsyncScheduler';\n\nexport class AnimationFrameScheduler extends AsyncScheduler {\n public flush(action?: AsyncAction): void {\n this._active = true;\n // The async id that effects a call to flush is stored in _scheduled.\n // Before executing an action, it's necessary to check the action's async\n // id to determine whether it's supposed to be executed in the current\n // flush.\n // Previous implementations of this method used a count to determine this,\n // but that was unsound, as actions that are unsubscribed - i.e. cancelled -\n // are removed from the actions array and that can shift actions that are\n // scheduled to be executed in a subsequent flush into positions at which\n // they are executed within the current flush.\n const flushId = this._scheduled;\n this._scheduled = undefined;\n\n const { actions } = this;\n let error: any;\n action = action || actions.shift()!;\n\n do {\n if ((error = action.execute(action.state, action.delay))) {\n break;\n }\n } while ((action = actions[0]) && action.id === flushId && actions.shift());\n\n this._active = false;\n\n if (error) {\n while ((action = actions[0]) && action.id === flushId && actions.shift()) {\n action.unsubscribe();\n }\n throw error;\n }\n }\n}\n", "import { AnimationFrameAction } from './AnimationFrameAction';\nimport { AnimationFrameScheduler } from './AnimationFrameScheduler';\n\n/**\n *\n * Animation Frame Scheduler\n *\n * Perform task when `window.requestAnimationFrame` would fire\n *\n * When `animationFrame` scheduler is used with delay, it will fall back to {@link asyncScheduler} scheduler\n * behaviour.\n *\n * Without delay, `animationFrame` scheduler can be used to create smooth browser animations.\n * It makes sure scheduled task will happen just before next browser content repaint,\n * thus performing animations as efficiently as possible.\n *\n * ## Example\n * Schedule div height animation\n * ```ts\n * // html:
\n * import { animationFrameScheduler } from 'rxjs';\n *\n * const div = document.querySelector('div');\n *\n * animationFrameScheduler.schedule(function(height) {\n * div.style.height = height + \"px\";\n *\n * this.schedule(height + 1); // `this` references currently executing Action,\n * // which we reschedule with new state\n * }, 0, 0);\n *\n * // You will see a div element growing in height\n * ```\n */\n\nexport const animationFrameScheduler = new AnimationFrameScheduler(AnimationFrameAction);\n\n/**\n * @deprecated Renamed to {@link animationFrameScheduler}. Will be removed in v8.\n */\nexport const animationFrame = animationFrameScheduler;\n", "import { Observable } from '../Observable';\nimport { SchedulerLike } from '../types';\n\n/**\n * A simple Observable that emits no items to the Observer and immediately\n * emits a complete notification.\n *\n * Just emits 'complete', and nothing else.\n *\n * ![](empty.png)\n *\n * A simple Observable that only emits the complete notification. It can be used\n * for composing with other Observables, such as in a {@link mergeMap}.\n *\n * ## Examples\n *\n * Log complete notification\n *\n * ```ts\n * import { EMPTY } from 'rxjs';\n *\n * EMPTY.subscribe({\n * next: () => console.log('Next'),\n * complete: () => console.log('Complete!')\n * });\n *\n * // Outputs\n * // Complete!\n * ```\n *\n * Emit the number 7, then complete\n *\n * ```ts\n * import { EMPTY, startWith } from 'rxjs';\n *\n * const result = EMPTY.pipe(startWith(7));\n * result.subscribe(x => console.log(x));\n *\n * // Outputs\n * // 7\n * ```\n *\n * Map and flatten only odd numbers to the sequence `'a'`, `'b'`, `'c'`\n *\n * ```ts\n * import { interval, mergeMap, of, EMPTY } from 'rxjs';\n *\n * const interval$ = interval(1000);\n * const result = interval$.pipe(\n * mergeMap(x => x % 2 === 1 ? of('a', 'b', 'c') : EMPTY),\n * );\n * result.subscribe(x => console.log(x));\n *\n * // Results in the following to the console:\n * // x is equal to the count on the interval, e.g. (0, 1, 2, 3, ...)\n * // x will occur every 1000ms\n * // if x % 2 is equal to 1, print a, b, c (each on its own)\n * // if x % 2 is not equal to 1, nothing will be output\n * ```\n *\n * @see {@link Observable}\n * @see {@link NEVER}\n * @see {@link of}\n * @see {@link throwError}\n */\nexport const EMPTY = new Observable((subscriber) => subscriber.complete());\n\n/**\n * @param scheduler A {@link SchedulerLike} to use for scheduling\n * the emission of the complete notification.\n * @deprecated Replaced with the {@link EMPTY} constant or {@link scheduled} (e.g. `scheduled([], scheduler)`). Will be removed in v8.\n */\nexport function empty(scheduler?: SchedulerLike) {\n return scheduler ? emptyScheduled(scheduler) : EMPTY;\n}\n\nfunction emptyScheduled(scheduler: SchedulerLike) {\n return new Observable((subscriber) => scheduler.schedule(() => subscriber.complete()));\n}\n", "import { SchedulerLike } from '../types';\nimport { isFunction } from './isFunction';\n\nexport function isScheduler(value: any): value is SchedulerLike {\n return value && isFunction(value.schedule);\n}\n", "import { SchedulerLike } from '../types';\nimport { isFunction } from './isFunction';\nimport { isScheduler } from './isScheduler';\n\nfunction last(arr: T[]): T | undefined {\n return arr[arr.length - 1];\n}\n\nexport function popResultSelector(args: any[]): ((...args: unknown[]) => unknown) | undefined {\n return isFunction(last(args)) ? args.pop() : undefined;\n}\n\nexport function popScheduler(args: any[]): SchedulerLike | undefined {\n return isScheduler(last(args)) ? args.pop() : undefined;\n}\n\nexport function popNumber(args: any[], defaultValue: number): number {\n return typeof last(args) === 'number' ? args.pop()! : defaultValue;\n}\n", "export const isArrayLike = ((x: any): x is ArrayLike => x && typeof x.length === 'number' && typeof x !== 'function');", "import { isFunction } from \"./isFunction\";\n\n/**\n * Tests to see if the object is \"thennable\".\n * @param value the object to test\n */\nexport function isPromise(value: any): value is PromiseLike {\n return isFunction(value?.then);\n}\n", "import { InteropObservable } from '../types';\nimport { observable as Symbol_observable } from '../symbol/observable';\nimport { isFunction } from './isFunction';\n\n/** Identifies an input as being Observable (but not necessary an Rx Observable) */\nexport function isInteropObservable(input: any): input is InteropObservable {\n return isFunction(input[Symbol_observable]);\n}\n", "import { isFunction } from './isFunction';\n\nexport function isAsyncIterable(obj: any): obj is AsyncIterable {\n return Symbol.asyncIterator && isFunction(obj?.[Symbol.asyncIterator]);\n}\n", "/**\n * Creates the TypeError to throw if an invalid object is passed to `from` or `scheduled`.\n * @param input The object that was passed.\n */\nexport function createInvalidObservableTypeError(input: any) {\n // TODO: We should create error codes that can be looked up, so this can be less verbose.\n return new TypeError(\n `You provided ${\n input !== null && typeof input === 'object' ? 'an invalid object' : `'${input}'`\n } where a stream was expected. You can provide an Observable, Promise, ReadableStream, Array, AsyncIterable, or Iterable.`\n );\n}\n", "export function getSymbolIterator(): symbol {\n if (typeof Symbol !== 'function' || !Symbol.iterator) {\n return '@@iterator' as any;\n }\n\n return Symbol.iterator;\n}\n\nexport const iterator = getSymbolIterator();\n", "import { iterator as Symbol_iterator } from '../symbol/iterator';\nimport { isFunction } from './isFunction';\n\n/** Identifies an input as being an Iterable */\nexport function isIterable(input: any): input is Iterable {\n return isFunction(input?.[Symbol_iterator]);\n}\n", "import { ReadableStreamLike } from '../types';\nimport { isFunction } from './isFunction';\n\nexport async function* readableStreamLikeToAsyncGenerator(readableStream: ReadableStreamLike): AsyncGenerator {\n const reader = readableStream.getReader();\n try {\n while (true) {\n const { value, done } = await reader.read();\n if (done) {\n return;\n }\n yield value!;\n }\n } finally {\n reader.releaseLock();\n }\n}\n\nexport function isReadableStreamLike(obj: any): obj is ReadableStreamLike {\n // We don't want to use instanceof checks because they would return\n // false for instances from another Realm, like an