master 973b65d48b2c cached
16 files
158.0 KB
46.6k tokens
514 symbols
1 requests
Download .txt
Repository: dastergon/wheel-of-misfortune
Branch: master
Commit: 973b65d48b2c
Files: 16
Total size: 158.0 KB

Directory structure:
gitextract_7tkj533i/

├── .github/
│   ├── FUNDING.yml
│   └── workflows/
│       └── deploy_to_gh_pages.yml
├── .gitignore
├── LICENSE
├── README.md
├── incidents/
│   ├── general_incidents.json.sample
│   ├── general_incidents.jsonnet.sample
│   └── redis-story.json.sample
├── index.html
├── instructions.html
└── static/
    ├── ink.js
    ├── mainink.js
    ├── site.webmanifest
    ├── stopwatch.js
    ├── styles.css
    └── wheel.js

================================================
FILE CONTENTS
================================================

================================================
FILE: .github/FUNDING.yml
================================================
github: dastergon


================================================
FILE: .github/workflows/deploy_to_gh_pages.yml
================================================
name: Deploy Wheel of Misfortune to GitHub Pages

on:
  push:
    branches:
      - master

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - name: Copy needed files and dirs into the public/ directory
        run: |
          mkdir -p public/incidents
          for source in *.html static; do cp -r $source public/; done
          ls incidents/ | while read i; do cp incidents/$i public/incidents/${i/.sample/}; done
      - uses: crazy-max/ghaction-github-pages@v2
        with:
          commit_message: Deploy from ${{ github.ref }}@${{ github.sha }}
          build_dir: public
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}


================================================
FILE: .gitignore
================================================
incidents/*.json
incidents/*.jsonnet


================================================
FILE: LICENSE
================================================
MIT License

Copyright (c) 2018 Pavlos Ratis

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


================================================
FILE: README.md
================================================
# Wheel of Misfortune
**Wheel of Misfortune** is a game that aims to build confidence in on-call engineers via simulated outage scenarios.
With the game, you practice problem debugging under stress, understanding the incident management protocol,
and effective communication with other engineers of your team and organization.
It is a great way to train new hires, interns, and seasoned engineers to become well-rounded on-call engineers.

The game is inspired by the [Site Reliability Engineering](https://landing.google.com/sre/book/chapters/accelerating-sre-on-call.html#xref_training_disaster-rpg) book.

[Demo website](https://dastergon.gr/wheel-of-misfortune)

## Instructions
### Terminology

*   **Scenario**: A past or fictional incident case.
*   **Game Master**: The host-coordinator of the session.
*   **Volunteer**: The trainee on-call engineer.

Feel free to fork the [repository](https://github.com/dastergon/wheel-of-misfortune) or [download](https://github.com/dastergon/wheel-of-misfortune/releases) the stable release.
Copy the [general\_incidents.json.sample](incidents/general\_incidents.json.sample) file to *general_incidents.json*, inside the [incidents/](incidents/) directory, and insert your incident scenarios into it.

To run the game locally on your computer please navigate the the main directory of the downloaded project i.e. `wheel-of-misfortune-5.0/` and from here start a web server i.e. `python -m SimpleHTTPServer` after that open the http://localhost:8000 using your web browser.

The file has the following format:
- **ID**: the unique ID of the outage (you can just auto-increment).
- **title**: the title of the incident.
- **scenario**: the description of the incident. It is useful to include URLs from monitoring systems, dashboards, time-series databases and playbooks.
- **inkstory**: the path to an [Ink](https://www.inklestudios.com/ink/) story file in JSON format.

You can also use [general\_incidents.jsonnet](incidents/general_incidents.jsonnet.sample) as an example, in case you want to generate your incident scenarios using [Jsonnet](https://jsonnet.org/).

### Ink
[Ink](https://github.com/inkle/ink) is a scripting language for writing interactive narrative stories. It enables us to write interactive incident response narratives for team or individual trainings. You can use [Inky](https://github.com/inkle/inky) to write an interactive narrative for an incident and then export the story as JSON. Then, you can store the story file inside the [incidents/](incidents/) folder and associate the Ink story file with an Incident scenario using the **inkstory** key. You can read an example incident narrative [here](https://github.com/dastergon/wheel-of-misfortune/tree/master/incidents/redis-story.json).

### Role Playing
#### Game Master

1.  Choose a volunteer to be the primary on-call engineer in front of the group.
2.  Find a balance between the volunteer's experience and the incident's difficulty.
3.  Assist volunteer by answering questions that may arise in each theoretical action or dashboard observation.
  * Engage with the rest of the team and ask for different ways to debug the problem following the volunteer's explanation.
  * Team members may be made available over time for assistance in various topics.
5.  At the end, have a debrief on the learnings of the session.


#### Volunteer

1.  Spin the wheel and attempt to fix the theoretical outage scenario.
2.  Explain to the Game Master and the rest of the group what actions you would take (lookup queries, checks in dashboards, etc.) to find the root causes, and eventually solve the incident.
3.  Always keep an eye on the time, since it is a simulated incident response scenario and not a routine troubleshooting process. During a real incident, you might have an SLA or SLO breach and therefore you should take timing into account.
4.  Engage with the rest of the group. Keep them in the loop. Ask questions to different members depending on their expertise.

Most importantly, **have fun!**

You can [read](https://landing.google.com/sre/book/chapters/accelerating-sre-on-call.html#xref_training_disaster-rpg) a comprehensive example on how to conduct the exercise in the Google SRE book.

### Featured

The Wheel of Misfortune was established as a practice in [Open Practice Library](https://openpracticelibrary.com/) and this project was [featured](https://openpracticelibrary.com/practice/wheel-of-misfortune/) there.

### Resources

*   [Disaster Role Playing](https://landing.google.com/sre/book/chapters/accelerating-sre-on-call.html#xref_training_disaster-rpg)
*   [Managing Misfortune for Best Results](https://www.usenix.org/conference/srecon18europe/presentation/barry)
*   [Postmortem Culture: Learning from Failure](https://landing.google.com/sre/book/chapters/postmortem-culture.html)
*   [Postmortem Templates](https://github.com/dastergon/postmortem-templates)
*   [Postmortems Metadata Index](https://postmortems.app)
*   [Site Reliability Engineering Resources](https://github.com/dastergon/awesome-sre)


================================================
FILE: incidents/general_incidents.json.sample
================================================
[
    {
        "ID": "1",
        "title": "Incident 1",
        "scenario": "You received alerts that your MySQL master does not accept writes..."
    },
    {
        "ID": "2",
        "title": "Incident 2",
        "scenario": "You've received alerts that your load balancer is down..."
    },
    {
        "ID": "3",
        "title": "Incident 3",
        "scenario": "You've received an alert that your Blob storage system is unable to store objects..."
    },
    {
        "ID": "4",
        "title": "Incident 4",
        "scenario": "You've received alerts that your DC eu-west-1 has a power outage..."
    },
    {
        "ID": "5",
        "title": "Incident 5",
        "inkstory": "incidents/redis-story.json",
        "scenario": "You've received an alert that your Redis server is killed..."
    },
    {
        "ID": "6",
        "title": "Incident 6",
        "scenario": "You've received alerts of high HTTP 5xx error rate..."
    },
    {
        "ID": "7",
        "title": "Incident 7",
        "scenario": "You've received alerts that there is high query latency in the RPC servers..."
    },
    {
        "ID": "8",
        "title": "Incident 8",
        "scenario": "You've received an alert that an LDAP client cannot query LDAP server"
    }
]


================================================
FILE: incidents/general_incidents.jsonnet.sample
================================================
local incidents = [
  { scenario: "You've received an alert that your " + database + ' server is killed...' }
  for database in [
    'Redis',
    'MySQL',
    'MongoDB',
  ]
] + [
  // Generates DC is down incidents
  { scenario: "You've received alerts that your DC " + dc + ' has a power outage...' }
  for dc in [
    'eu-west-1',
    'eu-north-1',
    'us-east-2',
  ]
] + [
  // Static incidents
  {
    scenario: 'You received alerts that your MySQL master does not accept writes...',
  },
  {
    scenario: "You've received alerts that your load balancer is down...",
  },
  {
    scenario: "You've received an alert that your Blob storage system is unable to store objects...",
  },
  {
    scenario: "You've received an alert that your Redis server is killed...",
  },
  {
    scenario: "You've received alerts of high HTTP 5xx error rate...",
  },
  {
    scenario: "You've received alerts that there is high query latency in the RPC servers...",
  },
  {
    scenario: "You've received an alert that an LDAP client cannot query LDAP server",
  },
];

# Generates incident title and id
[incidents[i] { id: '%d' % [i + 1], title: 'Incident %d' % [i + 1] } for i in std.range(0, std.length(incidents) - 1)]


================================================
FILE: incidents/redis-story.json.sample
================================================
{"inkVersion":19,"root":[[["^You've received an alert that your Redis server is killed...","\n",["ev",{"^->":"0.g-0.2.$r1"},{"temp=":"$r"},"str",{"->":".^.s"},[{"#n":"$r1"}],"/str","/ev",{"*":".^.^.c-0","flg":18},{"s":["^Acknowledge the alert",{"->":"$r","var":true},null]}],"ev","str","^Wait for the incident to autoresolve","/str","/ev",{"*":".^.c-1","flg":4},"ev","str","^Do Nothing","/str","/ev",{"*":".^.c-2","flg":4},{"c-0":["ev",{"^->":"0.g-0.c-0.$r2"},"/ev",{"temp=":"$r"},{"->":".^.^.2.s"},[{"#n":"$r2"}],"\n","^How do you proceed?","\n",[["ev",{"^->":"0.g-0.c-0.9.0.$r1"},{"temp=":"$r"},"str",{"->":".^.s"},[{"#n":"$r1"}],"/str","/ev",{"*":".^.^.c-0","flg":18},{"s":["^Restart the service",{"->":"$r","var":true},null]}],["ev",{"^->":"0.g-0.c-0.9.1.$r1"},{"temp=":"$r"},"str",{"->":".^.s"},[{"#n":"$r1"}],"/str","/ev",{"*":".^.^.c-1","flg":18},{"s":["^Check Grafana ",{"->":"$r","var":true},null]}],{"c-0":["ev",{"^->":"0.g-0.c-0.9.c-0.$r2"},"/ev",{"temp=":"$r"},{"->":".^.^.0.s"},[{"#n":"$r2"}],"\n","^\"It looks like the restart didn't help the situation\"","\n",[["^And now, what?","\n",["ev",{"^->":"0.g-0.c-0.9.c-0.9.g-0.2.$r1"},{"temp=":"$r"},"str",{"->":".^.s"},[{"#n":"$r1"}],"/str","/ev",{"*":".^.^.c-0","flg":18},{"s":["^Restart again",{"->":"$r","var":true},null]}],["ev",{"^->":"0.g-0.c-0.9.c-0.9.g-0.3.$r1"},{"temp=":"$r"},"str",{"->":".^.s"},[{"#n":"$r1"}],"/str","/ev",{"*":".^.^.c-1","flg":18},{"s":["^Scale out",{"->":"$r","var":true},null]}],{"c-0":["ev",{"^->":"0.g-0.c-0.9.c-0.9.g-0.c-0.$r2"},"/ev",{"temp=":"$r"},{"->":".^.^.2.s"},[{"#n":"$r2"}],"\n",{"->":".^.^.^.g-1"},{"#f":5}],"c-1":["ev",{"^->":"0.g-0.c-0.9.c-0.9.g-0.c-1.$r2"},"/ev",{"temp=":"$r"},{"->":".^.^.3.s"},[{"#n":"$r2"}],"\n",{"->":".^.^.^.g-1"},{"#f":5}],"#f":5,"#n":"g-0"}],{"g-1":["^\"It seems that the alert is not getting resolved\"","\n",["ev",{"^->":"0.g-0.c-0.9.c-0.9.g-1.2.$r1"},{"temp=":"$r"},"str",{"->":".^.s"},[{"#n":"$r1"}],"/str","/ev",{"*":".^.^.c-2","flg":18},{"s":["^Check Dashboard for service X",{"->":"$r","var":true},null]}],["ev",{"^->":"0.g-0.c-0.9.c-0.9.g-1.3.$r1"},{"temp=":"$r"},"str",{"->":".^.s"},[{"#n":"$r1"}],"/str","/ev",{"*":".^.^.c-3","flg":18},{"s":["^Check Dashboard for service Y",{"->":"$r","var":true},null]}],{"c-2":["ev",{"^->":"0.g-0.c-0.9.c-0.9.g-1.c-2.$r2"},"/ev",{"temp=":"$r"},{"->":".^.^.2.s"},[{"#n":"$r2"}],"\n","^\"There's the URL for service X\" ",{"->":"inspect"},"\n",{"->":"0.g-1"},{"#f":5}],"c-3":["ev",{"^->":"0.g-0.c-0.9.c-0.9.g-1.c-3.$r2"},"/ev",{"temp=":"$r"},{"->":".^.^.3.s"},[{"#n":"$r2"}],"\n","^\"There's the URL for service Y\" ",{"->":"inspect"},"\n",{"->":"0.g-1"},{"#f":5}],"#f":5}]}],{"#f":5}],"c-1":["ev",{"^->":"0.g-0.c-0.9.c-1.$r2"},"/ev",{"temp=":"$r"},{"->":".^.^.1.s"},[{"#n":"$r2"}],{"->":"grafana"},"\n",{"->":"0.g-1"},{"#f":5}]}],{"#f":5}],"c-1":["^ ",{"->":"chill"},"\n",{"->":"0.g-1"},{"#f":5}],"c-2":["^ ",{"->":"chill"},"\n",{"->":"0.g-1"},{"#f":5}],"#f":5,"#n":"g-0"}],{"g-1":["end",["done",{"#f":5,"#n":"g-2"}],{"#f":5}]}],"done",{"grafana":["^There's the dashboard for Redis:  URL ",{"->":"inspect"},"\n",{"#f":1}],"inspect":[[["^Do you see anything suspicious?","\n",[["ev",{"^->":"inspect.0.g-0.2.0.$r1"},{"temp=":"$r"},"str",{"->":".^.s"},[{"#n":"$r1"}],"/str","/ev",{"*":".^.^.c-0","flg":18},{"s":["^Nope ",{"->":"$r","var":true},null]}],["ev",{"^->":"inspect.0.g-0.2.1.$r1"},{"temp=":"$r"},"str",{"->":".^.s"},[{"#n":"$r1"}],"/str","/ev",{"*":".^.^.c-1","flg":18},{"s":["^Too many open connections",{"->":"$r","var":true},null]}],["ev",{"^->":"inspect.0.g-0.2.2.$r1"},{"temp=":"$r"},"str",{"->":".^.s"},[{"#n":"$r1"}],"/str","/ev",{"*":".^.^.c-2","flg":18},{"s":["^System is running out of memory",{"->":"$r","var":true},null]}],{"c-0":["ev",{"^->":"inspect.0.g-0.2.c-0.$r2"},"/ev",{"temp=":"$r"},{"->":".^.^.0.s"},[{"#n":"$r2"}],{"->":"grafana"},"\n",{"->":".^.^.g-0"},{"#f":5}],"c-1":["ev",{"^->":"inspect.0.g-0.2.c-1.$r2"},"/ev",{"temp=":"$r"},{"->":".^.^.1.s"},[{"#n":"$r2"}],"\n",{"->":".^.^.g-0"},{"#f":5}],"c-2":["ev",{"^->":"inspect.0.g-0.2.c-2.$r2"},"/ev",{"temp=":"$r"},{"->":".^.^.2.s"},[{"#n":"$r2"}],"\n",{"->":".^.^.g-0"},{"#f":5}],"g-0":["^And now what?","\n",["ev",{"^->":"inspect.0.g-0.2.g-0.2.$r1"},{"temp=":"$r"},"str",{"->":".^.s"},[{"#n":"$r1"}],"/str","/ev",{"*":".^.^.c-3","flg":18},{"s":["^Restart the service",{"->":"$r","var":true},null]}],["ev",{"^->":"inspect.0.g-0.2.g-0.3.$r1"},{"temp=":"$r"},"str",{"->":".^.s"},[{"#n":"$r1"}],"/str","/ev",{"*":".^.^.c-4","flg":18},{"s":["^Restart service X (that connects to Redis) ",{"->":"$r","var":true},null]}],["ev",{"^->":"inspect.0.g-0.2.g-0.4.$r1"},{"temp=":"$r"},"str",{"->":".^.s"},[{"#n":"$r1"}],"/str","/ev",{"*":".^.^.c-5","flg":18},{"s":["^Call your secondary to ask for help ","<>","^ ",{"->":"$r","var":true},null]}],["ev",{"^->":"inspect.0.g-0.2.g-0.5.$r1"},{"temp=":"$r"},"str",{"->":".^.s"},[{"#n":"$r1"}],"/str","/ev",{"*":".^.^.c-6","flg":18},{"s":["^Call your boss ",{"->":"$r","var":true},null]}],{"c-3":["ev",{"^->":"inspect.0.g-0.2.g-0.c-3.$r2"},"/ev",{"temp=":"$r"},{"->":".^.^.2.s"},[{"#n":"$r2"}],"\n","^\"It looks like the restart didn't help the situation\" ",{"->":"grafana"},"\n",{"#f":5}],"c-4":["ev",{"^->":"inspect.0.g-0.2.g-0.c-4.$r2"},"/ev",{"temp=":"$r"},{"->":".^.^.3.s"},[{"#n":"$r2"}],"\n","^\" ",{"->":"resolve"},"\n",{"#f":5}],"c-5":["ev",{"^->":"inspect.0.g-0.2.g-0.c-5.$r2"},"/ev",{"temp=":"$r"},{"->":".^.^.4.s"},[{"#n":"$r2"}],{"->":"chill"},"\n",{"#f":5}],"c-6":["ev",{"^->":"inspect.0.g-0.2.g-0.c-6.$r2"},"/ev",{"temp=":"$r"},{"->":".^.^.5.s"},[{"#n":"$r2"}],{"->":"escalate"},"\n",{"#f":5}],"#f":5}]}],{"#f":5,"#n":"g-0"}],null],{"#f":1}],"resolve":[["^\"Congrats, the Redis started to get back to normal\"","\n","^How do you proceed?","\n",["ev",{"^->":"resolve.0.4.$r1"},{"temp=":"$r"},"str",{"->":".^.s"},[{"#n":"$r1"}],"/str","/ev",{"*":".^.^.c-0","flg":18},{"s":["^Did you forget to open a ticket?",{"->":"$r","var":true},null]}],["ev",{"^->":"resolve.0.5.$r1"},{"temp=":"$r"},"str",{"->":".^.s"},[{"#n":"$r1"}],"/str","/ev",{"*":".^.^.c-1","flg":18},{"s":["^Mark the open ticket as resolved",{"->":"$r","var":true},null]}],{"c-0":["ev",{"^->":"resolve.0.c-0.$r2"},"/ev",{"temp=":"$r"},{"->":".^.^.4.s"},[{"#n":"$r2"}],"\n",[["ev",{"^->":"resolve.0.c-0.7.0.$r1"},{"temp=":"$r"},"str",{"->":".^.s"},[{"#n":"$r1"}],"/str","/ev",{"*":".^.^.c-0","flg":18},{"s":["^Create a new ticket, fill out the details and mark it as resolved ",{"->":"$r","var":true},null]}],{"c-0":["ev",{"^->":"resolve.0.c-0.7.c-0.$r2"},"/ev",{"temp=":"$r"},{"->":".^.^.0.s"},[{"#n":"$r2"}],"end","\n",{"#f":5}]}],{"#f":5}],"c-1":["ev",{"^->":"resolve.0.c-1.$r2"},"/ev",{"temp=":"$r"},{"->":".^.^.5.s"},[{"#n":"$r2"}],"\n","^\"Awesome, nice work!\"","\n","end",{"#f":5}]}],{"#f":1}],"escalate":["^\"Good luck with that\" ","end","\n",{"#f":1}],"chill":["^\"Have fun, doing nothing.\" ","end","\n",{"#f":1}],"#f":1}],"listDefs":{}}


================================================
FILE: index.html
================================================
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0">

    <title>Wheel of Misfortune</title>
    <meta name="author" content="Pavlos Ratis">
    <meta name="description" content="A role-playing game for incident management training">
    <meta name="keywords" content="Incident Response,Trainng,Site Reliability Engineering,SRE,Oncall">
    <link rel="stylesheet" href="static/styles.css">

    <link rel="apple-touch-icon" sizes="180x180" href="static/apple-touch-icon.png">
    <link rel="icon" type="image/png" sizes="32x32" href="static/favicon-32x32.png">
    <link rel="icon" type="image/png" sizes="16x16" href="static/favicon-16x16.png">
    <link rel="manifest" href="static/site.webmanifest">
</head>

<body>
    <a href="https://github.com/dastergon/wheel-of-misfortune" class="github-corner"
        aria-label="View source on GitHub"><svg width="80" height="80" viewBox="0 0 250 250"
            style="fill:#151513; color:#fff; position: absolute; top: 0; border: 0; right: 0;" aria-hidden="true">
            <path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path>
            <path
                d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2"
                fill="currentColor" style="transform-origin: 130px 106px;" class="octo-arm"></path>
            <path
                d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z"
                fill="currentColor" class="octo-body"></path>
        </svg></a>
    <style>
        .github-corner:hover .octo-arm {
            animation: octocat-wave 560ms ease-in-out
        }

        @keyframes octocat-wave {

            0%,
            100% {
                transform: rotate(0)
            }

            20%,
            60% {
                transform: rotate(-25deg)
            }

            40%,
            80% {
                transform: rotate(10deg)
            }
        }

        @media (max-width:500px) {
            .github-corner:hover .octo-arm {
                animation: none
            }

            .github-corner .octo-arm {
                animation: octocat-wave 560ms ease-in-out
            }
        }
    </style>

    <header class="tc pv4 bg-blue">
        <a href="index.html">
            <h1 class="athelas i white-ft f1">Wheel of Misfortune</h1>
        </a>
        <h1 class="white-ft f4">A role-playing game for incident management training</h1>
        <small class="white-ft ">Inspired by the
            <a
                href="https://landing.google.com/sre/book/chapters/accelerating-sre-on-call.html#xref_training_disaster-rpg">Site
                Reliability Engineering book</a>
        </small>
    </header>
    <div class="flex items-center justify-center bg-lightest-blue navy pa3">
        <svg class="w1" data-icon="info" viewBox="0 0 32 32" style="fill:currentcolor">
            <title>info icon</title>
            <path
                d="M16 0 A16 16 0 0 1 16 32 A16 16 0 0 1 16 0 M19 15 L13 15 L13 26 L19 26 z M16 6 A3 3 0 0 0 16 12 A3 3 0 0 0 16 6">
            </path>
        </svg>
        <div class="ml3"><a class="navy" href="instructions.html">Instructions</a></div>
    </div>

    <article class="cf mw mw-h center bg-white br3 mv4 ba b--black-10">
        <div class="fl w-100 w-50-ns tc pa3">
            <h4 class="f4 center mw6">Spin the Wheel!</h4> &#128223;
            <hr class="mw3 bb bw1 b--black-10">
            <div id="wheel"></div>
            <script src="https://d3js.org/d3.v3.min.js" charset="utf-8"></script>
            <script src="static/wheel.js"></script>
        </div>
        <div class="fl w-100 w-50-ns tc pa3">
            <article id="incident">
                <p></p>
            </article>
        </div>
    </article>

    <article class="cf">
        <div class="mw mw-h center">
            <div class="fl w-100 w-30-ns tc center bg-white br3 pa3 mv3 ba b--black-10 mbr">
                <ul class="list">
                    <h4 class="f4 center mw6">Incident Actions</h4> &#128203;
                    <hr class="mw3 bb bw1 b--black-10">
                    <li class="pv3 ba bl-0 bt-0 br-0 b--dotted b--black-30">
                        <input type="checkbox" class="magic-checkbox" name="progress" value="first" id="first"
                            onClick="stopwatch.lap();changeProgress(this, 0, 25);unlockCheckbox(this);" />
                        <label for="first">ACKNOWLEDGE</label>
                    </li>
                    <li class="pv3 ba bl-0 bt-0 br-0 b--dotted b--black-30"><input type="checkbox"
                            class="magic-checkbox" name="progress" value="second" id="second"
                            onClick="stopwatch.lap();changeProgress(this, 25, 50);unlockCheckbox(this);" disabled>
                        <label for="second">MITIGATE</label></li>
                    <li class="pv3 ba bl-0 bt-0 br-0 b--dotted b--black-30">
                        <input type="checkbox" class="magic-checkbox" name="progress" value="third" id="third"
                            onClick="stopwatch.lap();changeProgress(this, 50, 75);unlockCheckbox(this);" disabled>
                        <label for="third">TRIAGE</label></li>
                    <li class="pv3 ba bl-0 bt-0 br-0 b--dotted b--black-30">
                        <input type="checkbox" class="magic-checkbox" name="progress" value="fourth" id="fourth"
                            onClick="stopwatch.lap();stopwatch.stop();changeProgress(this, 75, 100);" disabled>
                        <label for="fourth">RESOLVE</label></li>
                </ul>
            </div>
            <div class="fl w-100 w-30-ns tc center bg-white br3 pa3 mv3 ba b--black-10 mbr">
                <h4 class="f4 center mw6">Incident Progress</h4>&#128202;
                <hr class="mw3 bb bw1 b--black-10">
                <div class="sz pa3">
                    <div id="progress" class="pie-wrapper">
                        <span id="percent" class="label">0<span class="smaller">%</span></span>
                        <div class="pie">
                            <div class="left-side half-circle"></div>
                            <div class="right-side half-circle"></div>
                        </div>
                        <div class="shadow"></div>
                    </div>
                </div>
            </div>
            <div class="fl w-100 w-30-ns tc center bg-white br3 pa3 mv3 ba b--black-10 mbr">
                <div class="timing">
                    <h4 class="f4 center mw6">Timing Controls</h4>&#9203;
                    <hr class="mw3 bb bw1 b--black-10">
                    <div class="stopwatch pa3"></div>
                    <button id="start" class="button" onClick="stopwatch.start();changeControls();">▶</button>
                    <button id="restart" class="button"
                        onClick="stopwatch.restart();stopwatch.clear();changeControls();">↻</button>
                    <button id="lap" class="button" onClick="stopwatch.lap();">Lap</button>
                    <ul class="results"></ul>
                    <script src="static/stopwatch.js"></script>
                    <script src="static/ink.js"></script>
                    <script src="static/mainink.js"></script>
                </div>
            </div>
        </div>
    </article>

    <footer class="pv4 ph4">
        <small class="f4 db tc"><a href="https://dastergon.gr" class="link">Pavlos Ratis</a></small>
    </footer>
</body>

</html>

<script>

    function unlockCheckbox(c) {
        if (c.value == "first") {
            document.getElementById("first").disabled = true;
            document.getElementById("second").disabled = false;
        }
        else if (c.value == "second") {
            document.getElementById("second").disabled = true;
            document.getElementById("third").disabled = false;
        }
        else if (c.value == "third") {
            document.getElementById("third").disabled = true;
            document.getElementById("fourth").disabled = false;
        }
        else {
            document.getElementById("fourth").disabled = true;
        }

    }

    function changeProgress(c, p, n) {
        if (c.checked) {
            var element = document.getElementById("progress");
            element.classList.remove("progress-" + p);
            element.classList.add("progress-" + n);
            document.getElementById('percent').childNodes[0].nodeValue = n;
        } else {
            var element = document.getElementById("progress");
            element.classList.remove("progress-" + n);
            element.classList.add("progress-" + p);
            document.getElementById('percent').childNodes[0].nodeValue = p;
        }
    }

    function changeControls() {
        var timingBtn = document.getElementById("start");
        if (timingBtn) {
            timingBtn.setAttribute('onclick', 'stopwatch.stop();changeControls();');
            timingBtn.id = "stop";
            timingBtn.innerText = "||";
        } else {
            var timingBtn = document.getElementById("stop");
            timingBtn.setAttribute('onclick', 'stopwatch.start();changeControls();');
            timingBtn.id = "start";
            timingBtn.innerText = "▶";
        }


    }
</script>


================================================
FILE: instructions.html
================================================
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0">

    <title>Wheel of Misfortune</title>
    <meta name="author" content="Pavlos Ratis">
    <meta name="description" content="A role-playing game for incident management training">
    <meta name="keywords" content="Incident Response,Trainng,Site Reliability Engineering,SRE,Oncall">
    <link rel="stylesheet" href="static/styles.css">

    <link rel="apple-touch-icon" sizes="180x180" href="static/apple-touch-icon.png">
    <link rel="icon" type="image/png" sizes="32x32" href="static/favicon-32x32.png">
    <link rel="icon" type="image/png" sizes="16x16" href="static/favicon-16x16.png">
    <link rel="manifest" href="static/site.webmanifest">
</head>

<body>
    <a href="https://github.com/dastergon/wheel-of-misfortune" class="github-corner"
        aria-label="View source on GitHub"><svg width="80" height="80" viewBox="0 0 250 250"
            style="fill:#151513; color:#fff; position: absolute; top: 0; border: 0; right: 0;" aria-hidden="true">
            <path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path>
            <path
                d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2"
                fill="currentColor" style="transform-origin: 130px 106px;" class="octo-arm"></path>
            <path
                d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z"
                fill="currentColor" class="octo-body"></path>
        </svg></a>
    <style>
        .github-corner:hover .octo-arm {
            animation: octocat-wave 560ms ease-in-out
        }

        @keyframes octocat-wave {

            0%,
            100% {
                transform: rotate(0)
            }

            20%,
            60% {
                transform: rotate(-25deg)
            }

            40%,
            80% {
                transform: rotate(10deg)
            }
        }

        @media (max-width:500px) {
            .github-corner:hover .octo-arm {
                animation: none
            }

            .github-corner .octo-arm {
                animation: octocat-wave 560ms ease-in-out
            }
        }
    </style>

    <header class="tc pv4 bg-blue">
        <a href="/">
            <h1 class="athelas i white-ft f1">Wheel of Misfortune</h1>
        </a>
        <h1 class="white-ft f4">A role-playing game for incident management training</h1>
        <small class="white-ft ">Inspired by the
            <a
                href="https://landing.google.com/sre/book/chapters/accelerating-sre-on-call.html#xref_training_disaster-rpg">Site
                Reliability Engineering book</a>
        </small>
    </header>
    <div class="flex items-center justify-center bg-lightest-blue navy pa3">
        <svg class="w1" data-icon="info" viewBox="0 0 32 32" style="fill:currentcolor">
            <title>info icon</title>
            <path
                d="M16 0 A16 16 0 0 1 16 32 A16 16 0 0 1 16 0 M19 15 L13 15 L13 26 L19 26 z M16 6 A3 3 0 0 0 16 12 A3 3 0 0 0 16 6">
            </path>
        </svg>
        <div class="ml3"><a class="navy" href="instructions.html">Instructions</a></div>
    </div>

    <article class="cf pa4 mw center bg-white br3 pa3 mv4 ba b--black-10">
        <div class="fl w-100 mw mw-h center">
            <h2 class="f6 center mw6 tc">Instructions</h2>
            <hr class="mw3 bb bw1 b--black-10">
            <p>Wheel of Misfortune is a game that aims to build confidence to oncall engineers via simulated
                outage scenarios.
                With the game, you practice problem debugging under stress, the understanding of the incident
                management protocol, and effective communication with other engineers
                of your team and organization. It is a great way to train new hires, interns, and seasoned
                engineers to become well-rounded oncall engineers.</p>
            <h4>Terminology</h4>
            <ul>
                <li><strong>Scenario</strong>: A past or fictional incident case.</li>
                <li><strong>Game Master</strong>: The host-coordinator of the session.</li>
                <li><strong>Volunteer</strong>: The trainee oncall engineer.</li>
            </ul>
            <p>Feel free to fork the <a href="https://github.com/dastergon/wheel-of-misfortune">repository</a> or <a
                    href="https://github.com/dastergon/wheel-of-misfortune/releases">download</a> the stable
                release.<br />
                Insert your incident scenarios into the <a
                    href="https://github.com/dastergon/wheel-of-misfortune/blob/master/incidents/general_incidents.json">general_incidents.json</a>
                file inside the <a
                    href="https://github.com/dastergon/wheel-of-misfortune/tree/master/incidents">incidents/</a>
                folder. The file has the following format:
            <table>
                <tr>
                    <td><b>title</b></td>
                    <td>the title of the incident.</td>
                </tr>
                <tr>
                    <td><b>scenario</b></td>
                    <td>the description of the incident. It is useful to include URLs from monitoring
                        systems, dashboards, time-series databases and playbooks.</td>
                </tr>
                <tr>
                    <td><b>ID</b></td>
                    <td>the unique ID of the outage (you can just auto-increment).</td>
                </tr>
                <tr>
                    <td><b>inkstory</b></td>
                    <td>the path to an <a href="https://www.inklestudios.com/ink/">Ink</a> story file in JSON format.
                    </td>
                </tr>
            </table>
            <p>You could use <a
                    href="https://github.com/dastergon/wheel-of-misfortune/blob/master/incidents/general_incidents.jsonnet">general_incidents.jsonnet</a>
                as an example in case you want to generate your incident scenarios using <a
                    href="https://jsonnet.org/">Jsonnet.</a>
            <p>Wheel of Misfortune also supports the <a href="https://github.com/inkle/ink">Ink</a> scripting language
                for writing incident response narratives, for team and invdividual training. <a
                    href="https://github.com/inkle/ink">Ink</a> is a scripting language for writing interactive
                narrative stories. It enables us to write interactive incident response narratives for
                team or individual trainings. You can use <a href="https://github.com/inkle/inky">Inky</a> to write an
                interactive narrative for an
                incident and then export the story as JSON. Then, you can store the story file inside the
                <a href="https://github.com/dastergon/wheel-of-misfortune/tree/master/incidents">incidents/</a> folder and associate the Ink story file with an Incident scenario using the <b>inkstory</b> key. 
                You can have a look at the <a
                    href="https://github.com/dastergon/wheel-of-misfortune/tree/master/incidentsredis-story.json">incident
                    narrative example</a>.</p>
            </p>
            <h4>Game Master</h4>
            <ol>
                <li>Choose a volunteer to be the primary oncall engineer in front of the group.</li>
                <li>Find a balance between volunteer's experience and incident's difficulty.</li>
                <li>Assist volunteer by answering questions that may arise in each theoretical action or
                    dashboard observation.</li>
                <ul>
                    <li>Engage with the rest of the team and ask for different ways to debug the problem
                        following the volunteer's explanation.</li>
                    <li>Team members may be made available over time for assistance in various topics.</li>
                </ul>
                <li>At the end, have a debrief on the learnings of the session.</li>
            </ol>

            <h4>Volunteer</h4>
            <ol>
                <li>Spin the wheel and attempt to fix the theoretical outage scenario.</li>
                <li>Explain to the Game Master and the rest of the group what actions you would take (lookup
                    queries, checks in dashboards, etc.) to find the root causes, and eventually solve the
                    incident.</li>
                <li>Always keep an eye on the time, since it is simulated incident response scenario and not a
                    routine troubleshooting process. During a real incident you might have an SLA or SLO
                    breach and therefore you should take timing into account.</li>
                <li>Engage with the rest of the group. Keep them in the loop. Ask questions to different
                    members depending on their expertise.</li>
            </ol>
            <p>Most importantly, <strong>have fun!</strong></p>
            <p>You can read a comprehensive example on how to conduct the exercise <a
                    href="https://landing.google.com/sre/book/chapters/accelerating-sre-on-call.html#xref_training_disaster-rpg">here</a>.
            </p>

            <h4>Resources</h4>
            <ul>
                <li>
                    <a
                        href="https://landing.google.com/sre/book/chapters/accelerating-sre-on-call.html#xref_training_disaster-rpg">Disaster
                        Role Playing</a>
                </li>
                <li>
                    <a href="https://www.usenix.org/conference/srecon18europe/presentation/barry">Managing
                        Misfortune for Best
                        Results</a>
                </li>
                <li>
                    <a href="https://landing.google.com/sre/book/chapters/postmortem-culture.html">Postmortem
                        Culture: Learning
                        from Failure</a>
                </li>
                <li>
                    <a href="https://github.com/dastergon/postmortem-templates">Postmortem Templates</a>
                </li>
                <li>
                    <a href="https://postmortems.app">Postmortems Metadata Index</a>
                </li>
                <li>
                    <a href="https://github.com/dastergon/awesome-sre">Site Reliability Engineering Resources</a>
                </li>
            </ul>
        </div>
    </article>

    <footer class="pv4 ph4">
        <small class="f4 db tc"><a href="https://dastergon.gr" class="link">Pavlos Ratis</a> | 2020</small>
    </footer>
</body>

</html>


================================================
FILE: static/ink.js
================================================
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e((t=t||self).inkjs={})}(this,function(t){"use strict";class e{constructor(){if(this._components=[],this._componentsString=null,this._isRelative=!1,"string"==typeof arguments[0]){let t=arguments[0];this.componentsString=t}else if(arguments[0]instanceof e.Component&&arguments[1]instanceof e){let t=arguments[0],e=arguments[1];this._components.push(t),this._components=this._components.concat(e._components)}else if(arguments[0]instanceof Array){let t=arguments[0],e=!!arguments[1];this._components=this._components.concat(t),this._isRelative=e}}get isRelative(){return this._isRelative}get componentCount(){return this._components.length}get head(){return this._components.length>0?this._components[0]:null}get tail(){if(this._components.length>=2){let t=this._components.slice(1,this._components.length);return new e(t)}return e.self}get length(){return this._components.length}get lastComponent(){let t=this._components.length-1;return t>=0?this._components[t]:null}get containsNamedComponent(){for(let t=0,e=this._components.length;t<e;t++)if(!this._components[t].isIndex)return!0;return!1}static get self(){let t=new e;return t._isRelative=!0,t}GetComponent(t){return this._components[t]}PathByAppendingPath(t){let n=new e,i=0;for(let e=0;e<t._components.length&&t._components[e].isParent;++e)i++;for(let t=0;t<this._components.length-i;++t)n._components.push(this._components[t]);for(let e=i;e<t._components.length;++e)n._components.push(t._components[e]);return n}get componentsString(){return null==this._componentsString&&(this._componentsString=this._components.join("."),this.isRelative&&(this._componentsString="."+this._componentsString)),this._componentsString}set componentsString(t){if(this._components.length=0,this._componentsString=t,null==this._componentsString||""==this._componentsString)return;"."==this._componentsString[0]&&(this._isRelative=!0,this._componentsString=this._componentsString.substring(1));let n=this._componentsString.split(".");for(let t of n)/^(\-|\+)?([0-9]+|Infinity)$/.test(t)?this._components.push(new e.Component(parseInt(t))):this._components.push(new e.Component(t))}toString(){return this.componentsString}Equals(t){if(null==t)return!1;if(t._components.length!=this._components.length)return!1;if(t.isRelative!=this.isRelative)return!1;for(let e=0,n=t._components.length;e<n;e++)if(!t._components[e].Equals(this._components[e]))return!1;return!0}PathByAppendingComponent(t){let n=new e;return n._components.push.apply(n._components,this._components),n._components.push(t),n}}var n,i,a;function r(t,e){return t instanceof e?h(t):null}function s(t,e){if(t instanceof e)return h(t);throw new Error(`${t} is not of type ${e}`)}function l(t){if("number"==typeof t)return t;throw new Error(`${t} is not a number`)}function o(t){return t.hasValidName&&t.name?t:null}function u(t){return void 0===t?null:t}function h(t,e){return t}e.parentId="^",function(t){class e{constructor(t){this.index=-1,this.name=null,"string"==typeof t?this.name=t:this.index=t}get isIndex(){return this.index>=0}get isParent(){return this.name==t.parentId}static ToParent(){return new e(t.parentId)}toString(){return this.isIndex?this.index.toString():this.name}Equals(t){return null!=t&&t.isIndex==this.isIndex&&(this.isIndex?this.index==t.index:this.name==t.name)}}t.Component=e}(e||(e={})),function(t){function e(t,e){if(!t)throw void 0!==e&&console.warn(e),console.trace&&console.trace(),""}t.AssertType=function(t,n,i){e(t instanceof n,i)},t.Assert=e}(n||(n={}));class c extends Error{}function d(t){throw new c(`${t} is null or undefined`)}class p{constructor(){this.parent=null,this._debugMetadata=null,this._path=null}get debugMetadata(){return null===this._debugMetadata&&this.parent?this.parent.debugMetadata:this._debugMetadata}set debugMetadata(t){this._debugMetadata=t}get ownDebugMetadata(){return this._debugMetadata}DebugLineNumberOfPath(t){if(null===t)return null;let e=this.rootContentContainer;if(e){let n=e.ContentAtPath(t).obj;if(n){let t=n.debugMetadata;if(null!==t)return t.startLineNumber}}return null}get path(){if(null==this._path)if(null==this.parent)this._path=new e;else{let t=[],n=this,i=r(n.parent,N);for(;null!==i;){let a=o(n);null!=a&&a.hasValidName?t.unshift(new e.Component(a.name)):t.unshift(new e.Component(i.content.indexOf(n))),n=i,i=r(i.parent,N)}this._path=new e(t)}return this._path}ResolvePath(t){if(null===t)return d("path");if(t.isRelative){let e=r(this,N);return null===e&&(n.Assert(null!==this.parent,"Can't resolve relative path because we don't have a parent"),e=r(this.parent,N),n.Assert(null!==e,"Expected parent to be a container"),n.Assert(t.GetComponent(0).isParent),t=t.tail),null===e?d("nearestContainer"):e.ContentAtPath(t)}{let e=this.rootContentContainer;return null===e?d("contentContainer"):e.ContentAtPath(t)}}ConvertPathToRelative(t){let n=this.path,i=Math.min(t.length,n.length),a=-1;for(let e=0;e<i;++e){let i=n.GetComponent(e),r=t.GetComponent(e);if(!i.Equals(r))break;a=e}if(-1==a)return t;let r=n.componentCount-1-a,s=[];for(let t=0;t<r;++t)s.push(e.Component.ToParent());for(let e=a+1;e<t.componentCount;++e)s.push(t.GetComponent(e));return new e(s,!0)}CompactPathString(t){let e=null,n=null;if(t.isRelative)n=t.componentsString,e=this.path.PathByAppendingPath(t).componentsString;else{n=this.ConvertPathToRelative(t).componentsString,e=t.componentsString}return n.length<e.length?n:e}get rootContentContainer(){let t=this;for(;t.parent;)t=t.parent;return r(t,N)}Copy(){throw Error("Not Implemented: Doesn't support copying")}SetChild(t,e,n){t[e]&&(t[e]=null),t[e]=n,t[e]&&(t[e].parent=this)}}class m{constructor(t){t=void 0!==t?t.toString():"",this.string=t}get Length(){return this.string.length}Append(t){null!==t&&(this.string+=t)}AppendLine(t){void 0!==t&&this.Append(t),this.string+="\n"}AppendFormat(t,...e){this.string+=t.replace(/{(\d+)}/g,(t,n)=>void 0!==e[n]?e[n]:t)}toString(){return this.string}}class f{constructor(){if(this.originName=null,this.itemName=null,void 0!==arguments[1]){let t=arguments[0],e=arguments[1];this.originName=t,this.itemName=e}else if(arguments[0]){let t=arguments[0].toString().split(".");this.originName=t[0],this.itemName=t[1]}}static get Null(){return new f(null,null)}get isNull(){return null==this.originName&&null==this.itemName}get fullName(){return(null!==this.originName?this.originName:"?")+"."+this.itemName}toString(){return this.fullName}Equals(t){if(t instanceof f){let e=t;return e.itemName==this.itemName&&e.originName==this.originName}return!1}copy(){return new f(this.originName,this.itemName)}serialized(){return JSON.stringify({originName:this.originName,itemName:this.itemName})}static fromSerializedKey(t){let e=JSON.parse(t);if(!f.isLikeInkListItem(e))return f.Null;let n=e;return new f(n.originName,n.itemName)}static isLikeInkListItem(t){return"object"==typeof t&&(!(!t.hasOwnProperty("originName")||!t.hasOwnProperty("itemName"))&&(("string"==typeof t.originName||null===typeof t.originName)&&("string"==typeof t.itemName||null===typeof t.itemName)))}}class g extends Map{constructor(){if(super((()=>arguments[0]instanceof g?arguments[0]:void 0)()),this.origins=null,this._originNames=[],arguments[0]instanceof g){let t=arguments[0];t._originNames&&(this._originNames=t._originNames.slice())}else if("string"==typeof arguments[0]){let t=arguments[0],e=arguments[1];this.SetInitialOriginName(t);let n=e.listDefinitions.TryListGetDefinition(t,null);if(!n.exists)throw new Error("InkList origin could not be found in story when constructing new list: "+t);this.origins=[n.result]}else if("object"==typeof arguments[0]&&arguments[0].hasOwnProperty("Key")&&arguments[0].hasOwnProperty("Value")){let t=arguments[0];this.Add(t.Key,t.Value)}}AddItem(t){if(t instanceof f){let e=t;if(null==e.originName)return void this.AddItem(e.itemName);if(null===this.origins)return d("this.origins");for(let t of this.origins)if(t.name==e.originName){let n=t.TryGetValueForItem(e,0);if(n.exists)return void this.Add(e,n.result);throw new Error("Could not add the item "+e+" to this list because it doesn't exist in the original list definition in ink.")}throw new Error("Failed to add item to list because the item was from a new list definition that wasn't previously known to this list. Only items from previously known lists can be used, so that the int value can be found.")}{let e=t,n=null;if(null===this.origins)return d("this.origins");for(let t of this.origins){if(null===e)return d("itemName");if(t.ContainsItemWithName(e)){if(null!=n)throw new Error("Could not add the item "+e+" to this list because it could come from either "+t.name+" or "+n.name);n=t}}if(null==n)throw new Error("Could not add the item "+e+" to this list because it isn't known to any list definitions previously associated with this list.");let i=new f(n.name,e),a=n.ValueForItem(i);this.Add(i,a)}}ContainsItemNamed(t){for(let[e,n]of this){if(f.fromSerializedKey(e).itemName==t)return!0}return!1}ContainsKey(t){return this.has(t.serialized())}Add(t,e){let n=t.serialized();if(this.has(n))throw new Error(`The Map already contains an entry for ${t}`);this.set(n,e)}Remove(t){return this.delete(t.serialized())}get Count(){return this.size}get originOfMaxItem(){if(null==this.origins)return null;let t=this.maxItem.Key.originName,e=null;return this.origins.every(n=>n.name!=t||(e=n,!1)),e}get originNames(){if(this.Count>0){null==this._originNames&&this.Count>0?this._originNames=[]:(this._originNames||(this._originNames=[]),this._originNames.length=0);for(let[t,e]of this){let e=f.fromSerializedKey(t);if(null===e.originName)return d("item.originName");this._originNames.push(e.originName)}}return this._originNames}SetInitialOriginName(t){this._originNames=[t]}SetInitialOriginNames(t){this._originNames=null==t?null:t.slice()}get maxItem(){let t={Key:f.Null,Value:0};for(let[e,n]of this){let i=f.fromSerializedKey(e);(t.Key.isNull||n>t.Value)&&(t={Key:i,Value:n})}return t}get minItem(){let t={Key:f.Null,Value:0};for(let[e,n]of this){let i=f.fromSerializedKey(e);(t.Key.isNull||n<t.Value)&&(t={Key:i,Value:n})}return t}get inverse(){let t=new g;if(null!=this.origins)for(let e of this.origins)for(let[n,i]of e.items){let e=f.fromSerializedKey(n);this.ContainsKey(e)||t.Add(e,i)}return t}get all(){let t=new g;if(null!=this.origins)for(let e of this.origins)for(let[n,i]of e.items){let e=f.fromSerializedKey(n);t.set(e.serialized(),i)}return t}Union(t){let e=new g(this);for(let[n,i]of t)e.set(n,i);return e}Intersect(t){let e=new g;for(let[n,i]of this)t.has(n)&&e.set(n,i);return e}Without(t){let e=new g(this);for(let[n,i]of t)e.delete(n);return e}Contains(t){for(let[e,n]of t)if(!this.has(e))return!1;return!0}GreaterThan(t){return 0!=this.Count&&(0==t.Count||this.minItem.Value>t.maxItem.Value)}GreaterThanOrEquals(t){return 0!=this.Count&&(0==t.Count||this.minItem.Value>=t.minItem.Value&&this.maxItem.Value>=t.maxItem.Value)}LessThan(t){return 0!=t.Count&&(0==this.Count||this.maxItem.Value<t.minItem.Value)}LessThanOrEquals(t){return 0!=t.Count&&(0==this.Count||this.maxItem.Value<=t.maxItem.Value&&this.minItem.Value<=t.minItem.Value)}MaxAsList(){return this.Count>0?new g(this.maxItem):new g}MinAsList(){return this.Count>0?new g(this.minItem):new g}ListWithSubRange(t,e){if(0==this.Count)return new g;let n=this.orderedItems,i=0,a=Number.MAX_SAFE_INTEGER;Number.isInteger(t)?i=t:t instanceof g&&t.Count>0&&(i=t.minItem.Value),Number.isInteger(e)?a=e:t instanceof g&&t.Count>0&&(a=e.maxItem.Value);let r=new g;r.SetInitialOriginNames(this.originNames);for(let t of n)t.Value>=i&&t.Value<=a&&r.Add(t.Key,t.Value);return r}Equals(t){if(t instanceof g==!1)return!1;if(t.Count!=this.Count)return!1;for(let[e,n]of this)if(!t.has(e))return!1;return!0}get orderedItems(){let t=new Array;for(let[e,n]of this){let i=f.fromSerializedKey(e);t.push({Key:i,Value:n})}return t.sort((t,e)=>null===t.Key.originName?d("x.Key.originName"):null===e.Key.originName?d("y.Key.originName"):t.Value==e.Value?t.Key.originName.localeCompare(e.Key.originName):t.Value<e.Value?-1:t.Value>e.Value?1:0),t}toString(){let t=this.orderedItems,e=new m;for(let n=0;n<t.length;n++){n>0&&e.Append(", ");let i=t[n].Key;if(null===i.itemName)return d("item.itemName");e.Append(i.itemName)}return e.toString()}valueOf(){return NaN}}class C extends Error{constructor(t){super(t),this.useEndLineNumber=!1,this.message=t,this.name="StoryException"}}function v(t,e,n){if(null===t)return{result:n,exists:!1};let i=t.get(e);return i?{result:i,exists:!0}:{result:n,exists:!1}}class S extends p{static Create(t){if("boolean"==typeof t){t=!!t?1:0}return Number.isInteger(Number(t))?new T(Number(t)):isNaN(t)?"string"==typeof t?new _(String(t)):t instanceof e?new w(s(t,e)):t instanceof g?new O(s(t,g)):null:new b(Number(t))}Copy(){return s(S.Create(this),p)}BadCastException(t){return new C("Can't cast "+this.valueObject+" from "+this.valueType+" to "+t)}}class y extends S{constructor(t){super(),this.value=t}get valueObject(){return this.value}toString(){return null===this.value?d("Value.value"):this.value.toString()}}class T extends y{constructor(t){super(t||0)}get isTruthy(){return 0!=this.value}get valueType(){return i.Int}Cast(t){if(null===this.value)return d("Value.value");if(t==this.valueType)return this;if(t==i.Float)return new b(this.value);if(t==i.String)return new _(""+this.value);throw this.BadCastException(t)}}class b extends y{constructor(t){super(t||0)}get isTruthy(){return 0!=this.value}get valueType(){return i.Float}Cast(t){if(null===this.value)return d("Value.value");if(t==this.valueType)return this;if(t==i.Int)return new T(this.value);if(t==i.String)return new _(""+this.value);throw this.BadCastException(t)}}class _ extends y{constructor(t){if(super(t||""),this._isNewline="\n"==this.value,this._isInlineWhitespace=!0,null===this.value)return d("Value.value");this.value.length>0&&this.value.split("").every(t=>" "==t||"\t"==t||(this._isInlineWhitespace=!1,!1))}get valueType(){return i.String}get isTruthy(){return null===this.value?d("Value.value"):this.value.length>0}get isNewline(){return this._isNewline}get isInlineWhitespace(){return this._isInlineWhitespace}get isNonWhitespace(){return!this.isNewline&&!this.isInlineWhitespace}Cast(t){if(t==this.valueType)return this;if(t==i.Int){let e=function(t,e=0){let n=parseInt(t);return Number.isNaN(n)?{result:e,exists:!1}:{result:n,exists:!0}}(this.value);if(e.exists)return new T(e.result);throw this.BadCastException(t)}if(t==i.Float){let e=function(t,e=0){let n=parseFloat(t);return Number.isNaN(n)?{result:e,exists:!1}:{result:n,exists:!0}}(this.value);if(e.exists)return new b(e.result);throw this.BadCastException(t)}throw this.BadCastException(t)}}class w extends y{constructor(t){super(t)}get valueType(){return i.DivertTarget}get targetPath(){return null===this.value?d("Value.value"):this.value}set targetPath(t){this.value=t}get isTruthy(){throw new Error("Shouldn't be checking the truthiness of a divert target")}Cast(t){if(t==this.valueType)return this;throw this.BadCastException(t)}toString(){return"DivertTargetValue("+this.targetPath+")"}}class E extends y{constructor(t,e=-1){super(t),this._contextIndex=e}get contextIndex(){return this._contextIndex}set contextIndex(t){this._contextIndex=t}get variableName(){return null===this.value?d("Value.value"):this.value}set variableName(t){this.value=t}get valueType(){return i.VariablePointer}get isTruthy(){throw new Error("Shouldn't be checking the truthiness of a variable pointer")}Cast(t){if(t==this.valueType)return this;throw this.BadCastException(t)}toString(){return"VariablePointerValue("+this.variableName+")"}Copy(){return new E(this.variableName,this.contextIndex)}}class O extends y{get isTruthy(){return null===this.value?d("this.value"):this.value.Count>0}get valueType(){return i.List}Cast(t){if(null===this.value)return d("Value.value");if(t==i.Int){let t=this.value.maxItem;return t.Key.isNull?new T(0):new T(t.Value)}if(t==i.Float){let t=this.value.maxItem;return t.Key.isNull?new b(0):new b(t.Value)}if(t==i.String){let t=this.value.maxItem;return t.Key.isNull?new _(""):new _(t.Key.toString())}if(t==this.valueType)return this;throw this.BadCastException(t)}constructor(t,e){super(null),t||e?t instanceof g?this.value=new g(t):t instanceof f&&"number"==typeof e&&(this.value=new g({Key:t,Value:e})):this.value=new g}static RetainListOriginsForAssignment(t,e){let n=r(t,O),i=r(e,O);return i&&null===i.value?d("newList.value"):n&&null===n.value?d("oldList.value"):void(n&&i&&0==i.value.Count&&i.value.SetInitialOriginNames(n.value.originNames))}}!function(t){t[t.Int=0]="Int",t[t.Float=1]="Float",t[t.List=2]="List",t[t.String=3]="String",t[t.DivertTarget=4]="DivertTarget",t[t.VariablePointer=5]="VariablePointer"}(i||(i={}));class x{constructor(){this.obj=null,this.approximate=!1}get correctObj(){return this.approximate?null:this.obj}get container(){return this.obj instanceof N?this.obj:null}copy(){let t=new x;return t.obj=this.obj,t.approximate=this.approximate,t}}class N extends p{constructor(){super(...arguments),this.name="",this._content=[],this.namedContent=new Map,this.visitsShouldBeCounted=!1,this.turnIndexShouldBeCounted=!1,this.countingAtStartOnly=!1,this._pathToFirstLeafContent=null}get hasValidName(){return null!=this.name&&this.name.length>0}get content(){return this._content}set content(t){this.AddContent(t)}get namedOnlyContent(){let t=new Map;for(let[e,n]of this.namedContent){let i=s(n,p);t.set(e,i)}for(let e of this.content){let n=o(e);null!=n&&n.hasValidName&&t.delete(n.name)}return 0==t.size&&(t=null),t}set namedOnlyContent(t){let e=this.namedOnlyContent;if(null!=e)for(let[t,n]of e)this.namedContent.delete(t);if(null!=t)for(let[e,n]of t){let t=o(n);null!=t&&this.AddToNamedContentOnly(t)}}get countFlags(){let t=0;return this.visitsShouldBeCounted&&(t|=N.CountFlags.Visits),this.turnIndexShouldBeCounted&&(t|=N.CountFlags.Turns),this.countingAtStartOnly&&(t|=N.CountFlags.CountStartOnly),t==N.CountFlags.CountStartOnly&&(t=0),t}set countFlags(t){let e=t;(e&N.CountFlags.Visits)>0&&(this.visitsShouldBeCounted=!0),(e&N.CountFlags.Turns)>0&&(this.turnIndexShouldBeCounted=!0),(e&N.CountFlags.CountStartOnly)>0&&(this.countingAtStartOnly=!0)}get pathToFirstLeafContent(){return null==this._pathToFirstLeafContent&&(this._pathToFirstLeafContent=this.path.PathByAppendingPath(this.internalPathToFirstLeafContent)),this._pathToFirstLeafContent}get internalPathToFirstLeafContent(){let t=[],n=this;for(;n instanceof N;)n.content.length>0&&(t.push(new e.Component(0)),n=n.content[0]);return new e(t)}AddContent(t){if(t instanceof Array){let e=t;for(let t of e)this.AddContent(t)}else{let e=t;if(this._content.push(e),e.parent)throw new Error("content is already in "+e.parent);e.parent=this,this.TryAddNamedContent(e)}}TryAddNamedContent(t){let e=o(t);null!=e&&e.hasValidName&&this.AddToNamedContentOnly(e)}AddToNamedContentOnly(t){n.AssertType(t,p,"Can only add Runtime.Objects to a Runtime.Container"),s(t,p).parent=this,this.namedContent.set(t.name,t)}ContentAtPath(t,e=0,n=-1){-1==n&&(n=t.length);let i=new x;i.approximate=!1;let a=this,s=this;for(let l=e;l<n;++l){let e=t.GetComponent(l);if(null==a){i.approximate=!0;break}let n=a.ContentWithPathComponent(e);if(null==n){i.approximate=!0;break}s=n,a=r(n,N)}return i.obj=s,i}InsertContent(t,e){if(this.content[e]=t,t.parent)throw new Error("content is already in "+t.parent);t.parent=this,this.TryAddNamedContent(t)}AddContentsOfContainer(t){this.content=this.content.concat(t.content);for(let e of t.content)e.parent=this,this.TryAddNamedContent(e)}ContentWithPathComponent(t){if(t.isIndex)return t.index>=0&&t.index<this.content.length?this.content[t.index]:null;if(t.isParent)return this.parent;{if(null===t.name)return d("component.name");let e=v(this.namedContent,t.name,null);return e.exists?s(e.result,p):null}}BuildStringOfHierarchy(){let t;if(0==arguments.length)return t=new m,this.BuildStringOfHierarchy(t,0,null),t.toString();t=arguments[0];let e=arguments[1],i=arguments[2];function a(){for(let n=0;n<4*e;++n)t.Append(" ")}a(),t.Append("["),this.hasValidName&&t.AppendFormat(" ({0})",this.name),this==i&&t.Append("  <---"),t.AppendLine(),e++;for(let n=0;n<this.content.length;++n){let r=this.content[n];if(r instanceof N){r.BuildStringOfHierarchy(t,e,i)}else a(),r instanceof _?(t.Append('"'),t.Append(r.toString().replace("\n","\\n")),t.Append('"')):t.Append(r.toString());n!=this.content.length-1&&t.Append(","),r instanceof N||r!=i||t.Append("  <---"),t.AppendLine()}let r=new Map;for(let[t,e]of this.namedContent)this.content.indexOf(s(e,p))>=0||r.set(t,e);if(r.size>0){a(),t.AppendLine("-- named: --");for(let[a,s]of r){n.AssertType(s,N,"Can only print out named Containers"),s.BuildStringOfHierarchy(t,e,i),t.AppendLine()}}e--,a(),t.Append("]")}}!function(t){let e;!function(t){t[t.Visits=1]="Visits",t[t.Turns=2]="Turns",t[t.CountStartOnly=4]="CountStartOnly"}(e=t.CountFlags||(t.CountFlags={}))}(N||(N={}));class P extends p{toString(){return"Glue"}}class A extends p{get commandType(){return this._commandType}constructor(t=A.CommandType.NotSet){super(),this._commandType=t}Copy(){return new A(this.commandType)}static EvalStart(){return new A(A.CommandType.EvalStart)}static EvalOutput(){return new A(A.CommandType.EvalOutput)}static EvalEnd(){return new A(A.CommandType.EvalEnd)}static Duplicate(){return new A(A.CommandType.Duplicate)}static PopEvaluatedValue(){return new A(A.CommandType.PopEvaluatedValue)}static PopFunction(){return new A(A.CommandType.PopFunction)}static PopTunnel(){return new A(A.CommandType.PopTunnel)}static BeginString(){return new A(A.CommandType.BeginString)}static EndString(){return new A(A.CommandType.EndString)}static NoOp(){return new A(A.CommandType.NoOp)}static ChoiceCount(){return new A(A.CommandType.ChoiceCount)}static Turns(){return new A(A.CommandType.Turns)}static TurnsSince(){return new A(A.CommandType.TurnsSince)}static ReadCount(){return new A(A.CommandType.ReadCount)}static Random(){return new A(A.CommandType.Random)}static SeedRandom(){return new A(A.CommandType.SeedRandom)}static VisitIndex(){return new A(A.CommandType.VisitIndex)}static SequenceShuffleIndex(){return new A(A.CommandType.SequenceShuffleIndex)}static StartThread(){return new A(A.CommandType.StartThread)}static Done(){return new A(A.CommandType.Done)}static End(){return new A(A.CommandType.End)}static ListFromInt(){return new A(A.CommandType.ListFromInt)}static ListRange(){return new A(A.CommandType.ListRange)}static ListRandom(){return new A(A.CommandType.ListRandom)}toString(){return this.commandType.toString()}}!function(t){let e;!function(t){t[t.NotSet=-1]="NotSet",t[t.EvalStart=0]="EvalStart",t[t.EvalOutput=1]="EvalOutput",t[t.EvalEnd=2]="EvalEnd",t[t.Duplicate=3]="Duplicate",t[t.PopEvaluatedValue=4]="PopEvaluatedValue",t[t.PopFunction=5]="PopFunction",t[t.PopTunnel=6]="PopTunnel",t[t.BeginString=7]="BeginString",t[t.EndString=8]="EndString",t[t.NoOp=9]="NoOp",t[t.ChoiceCount=10]="ChoiceCount",t[t.Turns=11]="Turns",t[t.TurnsSince=12]="TurnsSince",t[t.Random=13]="Random",t[t.SeedRandom=14]="SeedRandom",t[t.VisitIndex=15]="VisitIndex",t[t.SequenceShuffleIndex=16]="SequenceShuffleIndex",t[t.StartThread=17]="StartThread",t[t.Done=18]="Done",t[t.End=19]="End",t[t.ListFromInt=20]="ListFromInt",t[t.ListRange=21]="ListRange",t[t.ListRandom=22]="ListRandom",t[t.ReadCount=23]="ReadCount",t[t.TOTAL_VALUES=24]="TOTAL_VALUES"}(e=t.CommandType||(t.CommandType={}))}(A||(A={})),function(t){t[t.Tunnel=0]="Tunnel",t[t.Function=1]="Function",t[t.FunctionEvaluationFromGame=2]="FunctionEvaluationFromGame"}(a||(a={}));class k{constructor(){this.container=null,this.index=-1,2===arguments.length&&(this.container=arguments[0],this.index=arguments[1])}Resolve(){return this.index<0?this.container:null==this.container?null:0==this.container.content.length?this.container:this.index>=this.container.content.length?null:this.container.content[this.index]}get isNull(){return null==this.container}get path(){return this.isNull?null:this.index>=0?this.container.path.PathByAppendingComponent(new e.Component(this.index)):this.container.path}toString(){return this.container?"Ink Pointer -> "+this.container.path.toString()+" -- index "+this.index:"Ink Pointer (null)"}copy(){return new k(this.container,this.index)}static StartOf(t){return new k(t,0)}static get Null(){return new k(null,-1)}}class I extends p{constructor(t){super(),this._targetPath=null,this._targetPointer=k.Null,this.variableDivertName=null,this.pushesToStack=!1,this.stackPushType=0,this.isExternal=!1,this.externalArgs=0,this.isConditional=!1,this.pushesToStack=!1,void 0!==t&&(this.pushesToStack=!0,this.stackPushType=t)}get targetPath(){if(null!=this._targetPath&&this._targetPath.isRelative){let t=this.targetPointer.Resolve();t&&(this._targetPath=t.path)}return this._targetPath}set targetPath(t){this._targetPath=t,this._targetPointer=k.Null}get targetPointer(){if(this._targetPointer.isNull){let t=this.ResolvePath(this._targetPath).obj;if(null===this._targetPath)return d("this._targetPath");if(null===this._targetPath.lastComponent)return d("this._targetPath.lastComponent");if(this._targetPath.lastComponent.isIndex){if(null===t)return d("targetObj");this._targetPointer.container=t.parent instanceof N?t.parent:null,this._targetPointer.index=this._targetPath.lastComponent.index}else this._targetPointer=k.StartOf(t instanceof N?t:null)}return this._targetPointer.copy()}get targetPathString(){return null==this.targetPath?null:this.CompactPathString(this.targetPath)}set targetPathString(t){this.targetPath=null==t?null:new e(t)}get hasVariableTarget(){return null!=this.variableDivertName}Equals(t){let e=t;return e instanceof I&&this.hasVariableTarget==e.hasVariableTarget&&(this.hasVariableTarget?this.variableDivertName==e.variableDivertName:null===this.targetPath?d("this.targetPath"):this.targetPath.Equals(e.targetPath))}toString(){if(this.hasVariableTarget)return"Divert(variable: "+this.variableDivertName+")";if(null==this.targetPath)return"Divert(null)";{let t=new m,e=this.targetPath.toString();return t.Append("Divert"),this.isConditional&&t.Append("?"),this.pushesToStack&&(this.stackPushType==a.Function?t.Append(" function"):t.Append(" tunnel")),t.Append(" -> "),t.Append(this.targetPathString),t.Append(" ("),t.Append(e),t.Append(")"),t.toString()}}}class F extends p{constructor(t=!0){super(),this._pathOnChoice=null,this.hasCondition=!1,this.hasStartContent=!1,this.hasChoiceOnlyContent=!1,this.isInvisibleDefault=!1,this.onceOnly=!0,this.onceOnly=t}get pathOnChoice(){if(null!=this._pathOnChoice&&this._pathOnChoice.isRelative){let t=this.choiceTarget;t&&(this._pathOnChoice=t.path)}return this._pathOnChoice}set pathOnChoice(t){this._pathOnChoice=t}get choiceTarget(){return null===this._pathOnChoice?d("ChoicePoint._pathOnChoice"):this.ResolvePath(this._pathOnChoice).container}get pathStringOnChoice(){return null===this.pathOnChoice?d("ChoicePoint.pathOnChoice"):this.CompactPathString(this.pathOnChoice)}set pathStringOnChoice(t){this.pathOnChoice=new e(t)}get flags(){let t=0;return this.hasCondition&&(t|=1),this.hasStartContent&&(t|=2),this.hasChoiceOnlyContent&&(t|=4),this.isInvisibleDefault&&(t|=8),this.onceOnly&&(t|=16),t}set flags(t){this.hasCondition=(1&t)>0,this.hasStartContent=(2&t)>0,this.hasChoiceOnlyContent=(4&t)>0,this.isInvisibleDefault=(8&t)>0,this.onceOnly=(16&t)>0}toString(){if(null===this.pathOnChoice)return d("ChoicePoint.pathOnChoice");return"Choice: -> "+this.pathOnChoice.toString()}}class V extends p{constructor(t=null){super(),this.pathForCount=null,this.name=t}get containerForCount(){return null===this.pathForCount?null:this.ResolvePath(this.pathForCount).container}get pathStringForCount(){return null===this.pathForCount?null:this.CompactPathString(this.pathForCount)}set pathStringForCount(t){this.pathForCount=null===t?null:new e(t)}toString(){if(null!=this.name)return"var("+this.name+")";return"read_count("+this.pathStringForCount+")"}}class L extends p{constructor(t,e){super(),this.variableName=t||null,this.isNewDeclaration=!!e,this.isGlobal=!1}toString(){return"VarAssign to "+this.variableName}}class R extends p{}class D extends p{constructor(){if(super(),this._name=null,this._numberOfParameters=0,this._prototype=null,this._isPrototype=!1,this._operationFuncs=null,0===arguments.length)D.GenerateNativeFunctionsIfNecessary();else if(1===arguments.length){let t=arguments[0];D.GenerateNativeFunctionsIfNecessary(),this.name=t}else if(2===arguments.length){let t=arguments[0],e=arguments[1];this._isPrototype=!0,this.name=t,this.numberOfParameters=e}}static CallWithName(t){return new D(t)}static CallExistsWithName(t){return this.GenerateNativeFunctionsIfNecessary(),this._nativeFunctions.get(t)}get name(){return null===this._name?d("NativeFunctionCall._name"):this._name}set name(t){this._name=t,this._isPrototype||(null===D._nativeFunctions?d("NativeFunctionCall._nativeFunctions"):this._prototype=D._nativeFunctions.get(this._name)||null)}get numberOfParameters(){return this._prototype?this._prototype.numberOfParameters:this._numberOfParameters}set numberOfParameters(t){this._numberOfParameters=t}Call(t){if(this._prototype)return this._prototype.Call(t);if(this.numberOfParameters!=t.length)throw new Error("Unexpected number of parameters");let e=!1;for(let n of t){if(n instanceof R)throw new C('Attempting to perform operation on a void value. Did you forget to "return" a value from a function you called here?');n instanceof O&&(e=!0)}if(2==t.length&&e)return this.CallBinaryListOperation(t);let n=this.CoerceValuesToSingleType(t),a=n[0].valueType;return a==i.Int?this.CallType(n):a==i.Float?this.CallType(n):a==i.String?this.CallType(n):a==i.DivertTarget?this.CallType(n):a==i.List?this.CallType(n):null}CallType(t){let e=s(t[0],y),n=e.valueType,i=e,a=t.length;if(2==a||1==a){if(null===this._operationFuncs)return d("NativeFunctionCall._operationFuncs");let e=this._operationFuncs.get(n);if(!e)throw new C("Cannot perform operation "+this.name+" on "+n);if(2==a){let n=s(t[1],y),a=e;if(null===i.value||null===n.value)return d("NativeFunctionCall.Call BinaryOp values");let r=a(i.value,n.value);return y.Create(r)}{let t=e;if(null===i.value)return d("NativeFunctionCall.Call UnaryOp value");let n=t(i.value);return y.Create(n)}}throw new Error("Unexpected number of parameters to NativeFunctionCall: "+t.length)}CallBinaryListOperation(t){if(("+"==this.name||"-"==this.name)&&t[0]instanceof O&&t[1]instanceof T)return this.CallListIncrementOperation(t);let e=s(t[0],y),n=s(t[1],y);if(!("&&"!=this.name&&"||"!=this.name||e.valueType==i.List&&n.valueType==i.List)){if(null===this._operationFuncs)return d("NativeFunctionCall._operationFuncs");let t=this._operationFuncs.get(i.Int);if(null===t)return d("NativeFunctionCall.CallBinaryListOperation op");let a=t(e.isTruthy?1:0,n.isTruthy?1:0);return new T(a)}if(e.valueType==i.List&&n.valueType==i.List)return this.CallType([e,n]);throw new C("Can not call use "+this.name+" operation on "+e.valueType+" and "+n.valueType)}CallListIncrementOperation(t){let e=s(t[0],O),n=s(t[1],T),a=new g;if(null===e.value)return d("NativeFunctionCall.CallListIncrementOperation listVal.value");for(let[t,r]of e.value){let s=f.fromSerializedKey(t);if(null===this._operationFuncs)return d("NativeFunctionCall._operationFuncs");let l=this._operationFuncs.get(i.Int);if(null===n.value)return d("NativeFunctionCall.CallListIncrementOperation intVal.value");let o=l(r,n.value),u=null;if(null===e.value.origins)return d("NativeFunctionCall.CallListIncrementOperation listVal.value.origins");for(let t of e.value.origins)if(t.name==s.originName){u=t;break}if(null!=u){let t=u.TryGetItemWithValue(o,f.Null);t.exists&&a.Add(t.result,o)}}return new O(a)}CoerceValuesToSingleType(t){let e=i.Int,n=null;for(let a of t){let t=s(a,y);t.valueType>e&&(e=t.valueType),t.valueType==i.List&&(n=r(t,O))}let a=[];if(i[e]==i[i.List])for(let e of t){let t=s(e,y);if(t.valueType==i.List)a.push(t);else{if(t.valueType!=i.Int)throw new C("Cannot mix Lists and "+t.valueType+" values in this operation");{let e=parseInt(t.valueObject);if(null===(n=s(n,O)).value)return d("NativeFunctionCall.CoerceValuesToSingleType specialCaseList.value");let i=n.value.originOfMaxItem;if(null===i)return d("NativeFunctionCall.CoerceValuesToSingleType list");let r=i.TryGetItemWithValue(e,f.Null);if(!r.exists)throw new C("Could not find List item with the value "+e+" in "+i.name);{let t=new O(r.result,e);a.push(t)}}}}else for(let n of t){let t=s(n,y).Cast(e);a.push(t)}return a}static Identity(t){return t}static GenerateNativeFunctionsIfNecessary(){if(null==this._nativeFunctions){this._nativeFunctions=new Map,this.AddIntBinaryOp(this.Add,(t,e)=>t+e),this.AddIntBinaryOp(this.Subtract,(t,e)=>t-e),this.AddIntBinaryOp(this.Multiply,(t,e)=>t*e),this.AddIntBinaryOp(this.Divide,(t,e)=>Math.round(t/e)),this.AddIntBinaryOp(this.Mod,(t,e)=>t%e),this.AddIntUnaryOp(this.Negate,t=>-t),this.AddIntBinaryOp(this.Equal,(t,e)=>t==e?1:0),this.AddIntBinaryOp(this.Greater,(t,e)=>t>e?1:0),this.AddIntBinaryOp(this.Less,(t,e)=>t<e?1:0),this.AddIntBinaryOp(this.GreaterThanOrEquals,(t,e)=>t>=e?1:0),this.AddIntBinaryOp(this.LessThanOrEquals,(t,e)=>t<=e?1:0),this.AddIntBinaryOp(this.NotEquals,(t,e)=>t!=e?1:0),this.AddIntUnaryOp(this.Not,t=>0==t?1:0),this.AddIntBinaryOp(this.And,(t,e)=>0!=t&&0!=e?1:0),this.AddIntBinaryOp(this.Or,(t,e)=>0!=t||0!=e?1:0),this.AddIntBinaryOp(this.Max,(t,e)=>Math.max(t,e)),this.AddIntBinaryOp(this.Min,(t,e)=>Math.min(t,e)),this.AddIntBinaryOp(this.Pow,(t,e)=>Math.pow(t,e)),this.AddIntUnaryOp(this.Floor,D.Identity),this.AddIntUnaryOp(this.Ceiling,D.Identity),this.AddIntUnaryOp(this.Int,D.Identity),this.AddIntUnaryOp(this.Float,t=>t),this.AddFloatBinaryOp(this.Add,(t,e)=>t+e),this.AddFloatBinaryOp(this.Subtract,(t,e)=>t-e),this.AddFloatBinaryOp(this.Multiply,(t,e)=>t*e),this.AddFloatBinaryOp(this.Divide,(t,e)=>t/e),this.AddFloatBinaryOp(this.Mod,(t,e)=>t%e),this.AddFloatUnaryOp(this.Negate,t=>-t),this.AddFloatBinaryOp(this.Equal,(t,e)=>t==e?1:0),this.AddFloatBinaryOp(this.Greater,(t,e)=>t>e?1:0),this.AddFloatBinaryOp(this.Less,(t,e)=>t<e?1:0),this.AddFloatBinaryOp(this.GreaterThanOrEquals,(t,e)=>t>=e?1:0),this.AddFloatBinaryOp(this.LessThanOrEquals,(t,e)=>t<=e?1:0),this.AddFloatBinaryOp(this.NotEquals,(t,e)=>t!=e?1:0),this.AddFloatUnaryOp(this.Not,t=>0==t?1:0),this.AddFloatBinaryOp(this.And,(t,e)=>0!=t&&0!=e?1:0),this.AddFloatBinaryOp(this.Or,(t,e)=>0!=t||0!=e?1:0),this.AddFloatBinaryOp(this.Max,(t,e)=>Math.max(t,e)),this.AddFloatBinaryOp(this.Min,(t,e)=>Math.min(t,e)),this.AddFloatBinaryOp(this.Pow,(t,e)=>Math.pow(t,e)),this.AddFloatUnaryOp(this.Floor,t=>Math.floor(t)),this.AddFloatUnaryOp(this.Ceiling,t=>Math.ceil(t)),this.AddFloatUnaryOp(this.Int,t=>Math.floor(t)),this.AddFloatUnaryOp(this.Float,D.Identity),this.AddStringBinaryOp(this.Add,(t,e)=>t+e),this.AddStringBinaryOp(this.Equal,(t,e)=>t===e?1:0),this.AddStringBinaryOp(this.NotEquals,(t,e)=>t!==e?1:0),this.AddStringBinaryOp(this.Has,(t,e)=>t.includes(e)?1:0),this.AddStringBinaryOp(this.Hasnt,(t,e)=>t.includes(e)?0:1),this.AddListBinaryOp(this.Add,(t,e)=>t.Union(e)),this.AddListBinaryOp(this.Subtract,(t,e)=>t.Without(e)),this.AddListBinaryOp(this.Has,(t,e)=>t.Contains(e)?1:0),this.AddListBinaryOp(this.Hasnt,(t,e)=>t.Contains(e)?0:1),this.AddListBinaryOp(this.Intersect,(t,e)=>t.Intersect(e)),this.AddListBinaryOp(this.Equal,(t,e)=>t.Equals(e)?1:0),this.AddListBinaryOp(this.Greater,(t,e)=>t.GreaterThan(e)?1:0),this.AddListBinaryOp(this.Less,(t,e)=>t.LessThan(e)?1:0),this.AddListBinaryOp(this.GreaterThanOrEquals,(t,e)=>t.GreaterThanOrEquals(e)?1:0),this.AddListBinaryOp(this.LessThanOrEquals,(t,e)=>t.LessThanOrEquals(e)?1:0),this.AddListBinaryOp(this.NotEquals,(t,e)=>t.Equals(e)?0:1),this.AddListBinaryOp(this.And,(t,e)=>t.Count>0&&e.Count>0?1:0),this.AddListBinaryOp(this.Or,(t,e)=>t.Count>0||e.Count>0?1:0),this.AddListUnaryOp(this.Not,t=>0==t.Count?1:0),this.AddListUnaryOp(this.Invert,t=>t.inverse),this.AddListUnaryOp(this.All,t=>t.all),this.AddListUnaryOp(this.ListMin,t=>t.MinAsList()),this.AddListUnaryOp(this.ListMax,t=>t.MaxAsList()),this.AddListUnaryOp(this.Count,t=>t.Count),this.AddListUnaryOp(this.ValueOfList,t=>t.maxItem.Value);let t=(t,e)=>t.Equals(e)?1:0,e=(t,e)=>t.Equals(e)?0:1;this.AddOpToNativeFunc(this.Equal,2,i.DivertTarget,t),this.AddOpToNativeFunc(this.NotEquals,2,i.DivertTarget,e)}}AddOpFuncForType(t,e){null==this._operationFuncs&&(this._operationFuncs=new Map),this._operationFuncs.set(t,e)}static AddOpToNativeFunc(t,e,n,i){if(null===this._nativeFunctions)return d("NativeFunctionCall._nativeFunctions");let a=this._nativeFunctions.get(t);a||(a=new D(t,e),this._nativeFunctions.set(t,a)),a.AddOpFuncForType(n,i)}static AddIntBinaryOp(t,e){this.AddOpToNativeFunc(t,2,i.Int,e)}static AddIntUnaryOp(t,e){this.AddOpToNativeFunc(t,1,i.Int,e)}static AddFloatBinaryOp(t,e){this.AddOpToNativeFunc(t,2,i.Float,e)}static AddFloatUnaryOp(t,e){this.AddOpToNativeFunc(t,1,i.Float,e)}static AddStringBinaryOp(t,e){this.AddOpToNativeFunc(t,2,i.String,e)}static AddListBinaryOp(t,e){this.AddOpToNativeFunc(t,2,i.List,e)}static AddListUnaryOp(t,e){this.AddOpToNativeFunc(t,1,i.List,e)}toString(){return'Native "'+this.name+'"'}}D.Add="+",D.Subtract="-",D.Divide="/",D.Multiply="*",D.Mod="%",D.Negate="_",D.Equal="==",D.Greater=">",D.Less="<",D.GreaterThanOrEquals=">=",D.LessThanOrEquals="<=",D.NotEquals="!=",D.Not="!",D.And="&&",D.Or="||",D.Min="MIN",D.Max="MAX",D.Pow="POW",D.Floor="FLOOR",D.Ceiling="CEILING",D.Int="INT",D.Float="FLOAT",D.Has="?",D.Hasnt="!?",D.Intersect="^",D.ListMin="LIST_MIN",D.ListMax="LIST_MAX",D.All="LIST_ALL",D.Count="LIST_COUNT",D.ValueOfList="LIST_VALUE",D.Invert="LIST_INVERT",D._nativeFunctions=null;class M extends p{constructor(t){super(),this.text=t.toString()||""}toString(){return"# "+this.text}}class G extends p{constructor(){super(...arguments),this.text="",this.index=0,this.threadAtGeneration=null,this.sourcePath="",this.targetPath=null,this.isInvisibleDefault=!1,this.originalThreadIndex=0}get pathStringOnChoice(){return null===this.targetPath?d("Choice.targetPath"):this.targetPath.toString()}set pathStringOnChoice(t){this.targetPath=new e(t)}}class B{constructor(t,e){this._name=t||"",this._items=null,this._itemNameToValues=e||new Map}get name(){return this._name}get items(){if(null==this._items){this._items=new Map;for(let[t,e]of this._itemNameToValues){let n=new f(this.name,t);this._items.set(n.serialized(),e)}}return this._items}ValueForItem(t){if(!t.itemName)return 0;let e=this._itemNameToValues.get(t.itemName);return void 0!==e?e:0}ContainsItem(t){return!!t.itemName&&(t.originName==this.name&&this._itemNameToValues.has(t.itemName))}ContainsItemWithName(t){return this._itemNameToValues.has(t)}TryGetItemWithValue(t,e){for(let[e,n]of this._itemNameToValues)if(n==t)return{result:new f(this.name,e),exists:!0};return{result:f.Null,exists:!1}}TryGetValueForItem(t,e){if(!t.itemName)return{result:0,exists:!1};let n=this._itemNameToValues.get(t.itemName);return n?{result:n,exists:!0}:{result:0,exists:!1}}}class W{constructor(t){this._lists=new Map,this._allUnambiguousListValueCache=new Map;for(let e of t){this._lists.set(e.name,e);for(let[t,n]of e.items){let e=f.fromSerializedKey(t),i=new O(e,n);if(!e.itemName)throw new Error("item.itemName is null or undefined.");this._allUnambiguousListValueCache.set(e.itemName,i),this._allUnambiguousListValueCache.set(e.fullName,i)}}}get lists(){let t=[];for(let[e,n]of this._lists)t.push(n);return t}TryListGetDefinition(t,e){if(null===t)return{result:e,exists:!1};let n=this._lists.get(t);return n?{result:n,exists:!0}:{result:e,exists:!1}}FindSingleItemListWithName(t){if(null===t)return d("name");let e=this._allUnambiguousListValueCache.get(t);return void 0!==e?e:null}}class j{static ListToJArray(t){let e=[];for(let n of t)e.push(this.RuntimeObjectToJToken(n));return e}static JArrayToRuntimeObjList(t,e=!1){let n=t.length;e&&n--;let i=[];for(let e=0;e<n;e++){let n=t[e],a=this.JTokenToRuntimeObject(n);if(null===a)return d("runtimeObj");i.push(a)}return i}static DictionaryRuntimeObjsToJObject(t){let e={};for(let[n,i]of t){let t=r(i,p);null!=t&&(e[n]=this.RuntimeObjectToJToken(t))}return e}static JObjectToDictionaryRuntimeObjs(t){let e=new Map;for(let n in t)if(t.hasOwnProperty(n)){let i=this.JTokenToRuntimeObject(t[n]);if(null===i)return d("inkObject");e.set(n,i)}return e}static JObjectToIntDictionary(t){let e=new Map;for(let n in t)t.hasOwnProperty(n)&&e.set(n,parseInt(t[n]));return e}static IntDictionaryToJObject(t){let e={};for(let[n,i]of t)e[n]=l(i);return e}static JTokenToRuntimeObject(t){if("number"==typeof t&&!isNaN(t))return y.Create(t);if("string"==typeof t){let e=t.toString(),n=e[0];if("^"==n)return new _(e.substring(1));if("\n"==n&&1==e.length)return new _("\n");if("<>"==e)return new P;for(let t=0;t<j._controlCommandNames.length;++t){if(e==j._controlCommandNames[t])return new A(t)}if("L^"==e&&(e="^"),D.CallExistsWithName(e))return D.CallWithName(e);if("->->"==e)return A.PopTunnel();if("~ret"==e)return A.PopFunction();if("void"==e)return new R}if("object"==typeof t&&!Array.isArray(t)){let n,i=t;if(i["^->"])return n=i["^->"],new w(new e(n.toString()));if(i["^var"]){n=i["^var"];let t=new E(n.toString());return"ci"in i&&(n=i.ci,t.contextIndex=parseInt(n)),t}let r=!1,s=!1,l=a.Function,o=!1;if((n=i["->"])?r=!0:(n=i["f()"])?(r=!0,s=!0,l=a.Function):(n=i["->t->"])?(r=!0,s=!0,l=a.Tunnel):(n=i["x()"])&&(r=!0,o=!0,s=!1,l=a.Function),r){let t=new I;t.pushesToStack=s,t.stackPushType=l,t.isExternal=o;let e=n.toString();return(n=i.var)?t.variableDivertName=e:t.targetPathString=e,t.isConditional=!!i.c,o&&(n=i.exArgs)&&(t.externalArgs=parseInt(n)),t}if(n=i["*"]){let t=new F;return t.pathStringOnChoice=n.toString(),(n=i.flg)&&(t.flags=parseInt(n)),t}if(n=i["VAR?"])return new V(n.toString());if(n=i["CNT?"]){let t=new V;return t.pathStringForCount=n.toString(),t}let u=!1,h=!1;if((n=i["VAR="])?(u=!0,h=!0):(n=i["temp="])&&(u=!0,h=!1),u){let t=n.toString(),e=!i.re,a=new L(t,e);return a.isGlobal=h,a}if(void 0!==i["#"])return n=i["#"],new M(n.toString());if(n=i.list){let t=n,e=new g;if(n=i.origins){let t=n;e.SetInitialOriginNames(t)}for(let n in t)if(t.hasOwnProperty(n)){let i=t[n],a=new f(n),r=parseInt(i);e.Add(a,r)}return new O(e)}if(null!=i.originalChoicePath)return this.JObjectToChoice(i)}if(Array.isArray(t))return this.JArrayToContainer(t);if(null==t)return null;throw new Error("Failed to convert token to runtime object: "+JSON.stringify(t))}static RuntimeObjectToJToken(t){let e=r(t,N);if(e)return this.ContainerToJArray(e);let n=r(t,I);if(n){let t,e="->";n.isExternal?e="x()":n.pushesToStack&&(n.stackPushType==a.Function?e="f()":n.stackPushType==a.Tunnel&&(e="->t->")),t=n.hasVariableTarget?n.variableDivertName:n.targetPathString;let i={};return i[e]=t,n.hasVariableTarget&&(i.var=!0),n.isConditional&&(i.c=!0),n.externalArgs>0&&(i.exArgs=n.externalArgs),i}let i=r(t,F);if(i){let t={};return t["*"]=i.pathStringOnChoice,t.flg=i.flags,t}let s=r(t,T);if(s)return s.value;let l=r(t,b);if(l)return l.value;let o=r(t,_);if(o)return o.isNewline?"\n":"^"+o.value;let u=r(t,O);if(u)return this.InkListToJObject(u);let h=r(t,w);if(h){let t={};return null===h.value?d("divTargetVal.value"):(t["^->"]=h.value.componentsString,t)}let c=r(t,E);if(c){let t={};return t["^var"]=c.value,t.ci=c.contextIndex,t}if(r(t,P))return"<>";let p=r(t,A);if(p)return j._controlCommandNames[p.commandType];let m=r(t,D);if(m){let t=m.name;return"^"==t&&(t="L^"),t}let f=r(t,V);if(f){let t={},e=f.pathStringForCount;return null!=e?t["CNT?"]=e:t["VAR?"]=f.name,t}let g=r(t,L);if(g){let t={};return t[g.isGlobal?"VAR=":"temp="]=g.variableName,g.isNewDeclaration||(t.re=!0),t}if(r(t,R))return"void";let C=r(t,M);if(C){let t={};return t["#"]=C.text,t}let v=r(t,G);if(v)return this.ChoiceToJObject(v);throw new Error("Failed to convert runtime object to Json token: "+t)}static ContainerToJArray(t){let e=this.ListToJArray(t.content),n=t.namedOnlyContent,i=t.countFlags;if(null!=n&&n.size>0||i>0||null!=t.name){let a;if(null!=n){a=this.DictionaryRuntimeObjsToJObject(n);for(let t in a)if(a.hasOwnProperty(t)){let e=a[t];if(null!=e){let t=e[e.length-1];null!=t&&(delete t["#n"],0==Object.keys(t).length&&(e[e.length-1]=null))}}}else a={};i>0&&(a["#f"]=i),null!=t.name&&(a["#n"]=t.name),e.push(a)}else e.push(null);return e}static JArrayToContainer(t){let e=new N;e.content=this.JArrayToRuntimeObjList(t,!0);let n=t[t.length-1];if(null!=n){let t=new Map;for(let i in n)if("#f"==i)e.countFlags=parseInt(n[i]);else if("#n"==i)e.name=n[i].toString();else{let e=this.JTokenToRuntimeObject(n[i]),a=r(e,N);a&&(a.name=i),t.set(i,e)}e.namedOnlyContent=t}return e}static JObjectToChoice(t){let e=new G;return e.text=t.text.toString(),e.index=parseInt(t.index),e.sourcePath=t.originalChoicePath.toString(),e.originalThreadIndex=parseInt(t.originalThreadIndex),e.pathStringOnChoice=t.targetPath.toString(),e}static ChoiceToJObject(t){let e={};return e.text=t.text,e.index=t.index,e.originalChoicePath=t.sourcePath,e.originalThreadIndex=t.originalThreadIndex,e.targetPath=t.pathStringOnChoice,e}static InkListToJObject(t){let e=t.value;if(null===e)return d("rawList");let n={},i={};for(let[t,n]of e){i[f.fromSerializedKey(t).toString()]=n}return n.list=i,0==e.Count&&null!=e.originNames&&e.originNames.length>0&&(n.origins=e.originNames),n}static ListDefinitionsToJToken(t){let e={};for(let n of t.lists){let t={};for(let[e,i]of n.items){let n=f.fromSerializedKey(e);if(null===n.itemName)return d("item.itemName");t[n.itemName]=i}e[n.name]=t}return e}static JTokenToListDefinitions(t){let e=t,n=[];for(let t in e)if(e.hasOwnProperty(t)){let i=t.toString(),a=e[t],r=new Map;for(let n in a)if(e.hasOwnProperty(t)){let t=a[n];r.set(n,parseInt(t))}let s=new B(i,r);n.push(s)}return new W(n)}}j._controlCommandNames=(()=>{let t=[];t[A.CommandType.EvalStart]="ev",t[A.CommandType.EvalOutput]="out",t[A.CommandType.EvalEnd]="/ev",t[A.CommandType.Duplicate]="du",t[A.CommandType.PopEvaluatedValue]="pop",t[A.CommandType.PopFunction]="~ret",t[A.CommandType.PopTunnel]="->->",t[A.CommandType.BeginString]="str",t[A.CommandType.EndString]="/str",t[A.CommandType.NoOp]="nop",t[A.CommandType.ChoiceCount]="choiceCnt",t[A.CommandType.Turns]="turn",t[A.CommandType.TurnsSince]="turns",t[A.CommandType.ReadCount]="readc",t[A.CommandType.Random]="rnd",t[A.CommandType.SeedRandom]="srnd",t[A.CommandType.VisitIndex]="visit",t[A.CommandType.SequenceShuffleIndex]="seq",t[A.CommandType.StartThread]="thread",t[A.CommandType.Done]="done",t[A.CommandType.End]="end",t[A.CommandType.ListFromInt]="listInt",t[A.CommandType.ListRange]="range",t[A.CommandType.ListRandom]="lrnd";for(let e=0;e<A.CommandType.TOTAL_VALUES;++e)if(null==t[e])throw new Error("Control command not accounted for in serialisation");return t})();class J{constructor(){if(this._threadCounter=0,this._startOfRoot=k.Null,arguments[0]instanceof H){let t=arguments[0];this._startOfRoot=k.StartOf(t.rootContentContainer),this.Reset()}else{let t=arguments[0];this._threads=[];for(let e of t._threads)this._threads.push(e.Copy());this._startOfRoot=t._startOfRoot}}get elements(){return this.callStack}get depth(){return this.elements.length}get currentElement(){let t=this._threads[this._threads.length-1].callstack;return t[t.length-1]}get currentElementIndex(){return this.callStack.length-1}get currentThread(){return this._threads[this._threads.length-1]}set currentThread(t){n.Assert(1==this._threads.length,"Shouldn't be directly setting the current thread when we have a stack of them"),this._threads.length=0,this._threads.push(t)}get canPop(){return this.callStack.length>1}Reset(){this._threads=[],this._threads.push(new J.Thread),this._threads[0].callstack.push(new J.Element(a.Tunnel,this._startOfRoot))}SetJsonToken(t,e){this._threads.length=0;let n=t.threads;for(let t of n){let n=t,i=new J.Thread(n,e);this._threads.push(i)}this._threadCounter=parseInt(t.threadCounter),this._startOfRoot=k.StartOf(e.rootContentContainer)}GetJsonToken(){let t={},e=[];for(let t of this._threads)e.push(t.jsonToken);return t.threads=e,t.threadCounter=this._threadCounter,t}PushThread(){let t=this.currentThread.Copy();this._threadCounter++,t.threadIndex=this._threadCounter,this._threads.push(t)}ForkThread(){let t=this.currentThread.Copy();return this._threadCounter++,t.threadIndex=this._threadCounter,t}PopThread(){if(!this.canPopThread)throw new Error("Can't pop thread");this._threads.splice(this._threads.indexOf(this.currentThread),1)}get canPopThread(){return this._threads.length>1&&!this.elementIsEvaluateFromGame}get elementIsEvaluateFromGame(){return this.currentElement.type==a.FunctionEvaluationFromGame}Push(t,e=0,n=0){let i=new J.Element(t,this.currentElement.currentPointer,!1);i.evaluationStackHeightWhenPushed=e,i.functionStartInOutputStream=n,this.callStack.push(i)}CanPop(t=null){return!!this.canPop&&(null==t||this.currentElement.type==t)}Pop(t=null){if(!this.CanPop(t))throw new Error("Mismatched push/pop in Callstack");this.callStack.pop()}GetTemporaryVariableWithName(t,e=-1){-1==e&&(e=this.currentElementIndex+1);let n=v(this.callStack[e-1].temporaryVariables,t,null);return n.exists?n.result:null}SetTemporaryVariable(t,e,n,i=-1){-1==i&&(i=this.currentElementIndex+1);let a=this.callStack[i-1];if(!n&&!a.temporaryVariables.get(t))throw new C("Could not find temporary variable to set: "+t);let r=v(a.temporaryVariables,t,null);r.exists&&O.RetainListOriginsForAssignment(r.result,e),a.temporaryVariables.set(t,e)}ContextForVariableNamed(t){return this.currentElement.temporaryVariables.get(t)?this.currentElementIndex+1:0}ThreadWithIndex(t){return this._threads.filter(e=>{if(e.threadIndex==t)return e})[0]}get callStack(){return this.currentThread.callstack}get callStackTrace(){let t=new m;for(let e=0;e<this._threads.length;e++){let n=this._threads[e],i=e==this._threads.length-1;t.AppendFormat("=== THREAD {0}/{1} {2}===\n",e+1,this._threads.length,i?"(current) ":"");for(let e=0;e<n.callstack.length;e++){n.callstack[e].type==a.Function?t.Append("  [FUNCTION] "):t.Append("  [TUNNEL] ");let i=n.callstack[e].currentPointer;if(!i.isNull){if(t.Append("<SOMEWHERE IN "),null===i.container)return d("pointer.container");t.Append(i.container.path.toString()),t.AppendLine(">")}}}return t.toString()}}!function(t){class n{constructor(t,e,n=!1){this.evaluationStackHeightWhenPushed=0,this.functionStartInOutputStream=0,this.currentPointer=e.copy(),this.inExpressionEvaluation=n,this.temporaryVariables=new Map,this.type=t}Copy(){let t=new n(this.type,this.currentPointer,this.inExpressionEvaluation);return t.temporaryVariables=new Map(this.temporaryVariables),t.evaluationStackHeightWhenPushed=this.evaluationStackHeightWhenPushed,t.functionStartInOutputStream=this.functionStartInOutputStream,t}}t.Element=n;class i{constructor(){if(this.threadIndex=0,this.previousPointer=k.Null,this.callstack=[],arguments[0]&&arguments[1]){let t=arguments[0],i=arguments[1];this.threadIndex=parseInt(t.threadIndex);let a=t.callstack;for(let t of a){let a,r=t,s=parseInt(r.type),l=k.Null,o=r.cPath;if(void 0!==o){a=o.toString();let t=i.ContentAtPath(new e(a));if(l.container=t.container,l.index=parseInt(r.idx),null==t.obj)throw new Error("When loading state, internal story location couldn't be found: "+a+". Has the story changed since this save data was created?");if(t.approximate){if(null===l.container)return d("pointer.container");i.Warning("When loading state, exact internal story location couldn't be found: '"+a+"', so it was approximated to '"+l.container.path.toString()+"' to recover. Has the story changed since this save data was created?")}}let u=!!r.exp,h=new n(s,l,u),c=r.temp;h.temporaryVariables=j.JObjectToDictionaryRuntimeObjs(c),this.callstack.push(h)}let r=t.previousContentObject;if(void 0!==r){let t=new e(r.toString());this.previousPointer=i.PointerAtPath(t)}}}Copy(){let t=new i;t.threadIndex=this.threadIndex;for(let e of this.callstack)t.callstack.push(e.Copy());return t.previousPointer=this.previousPointer.copy(),t}get jsonToken(){let t={},e=[];for(let t of this.callstack){let n={};if(!t.currentPointer.isNull){if(null===t.currentPointer.container)return d("el.currentPointer.container");n.cPath=t.currentPointer.container.path.componentsString,n.idx=t.currentPointer.index}n.exp=t.inExpressionEvaluation,n.type=t.type,n.temp=j.DictionaryRuntimeObjsToJObject(t.temporaryVariables),e.push(n)}if(t.callstack=e,t.threadIndex=this.threadIndex,!this.previousPointer.isNull){let e=this.previousPointer.Resolve();if(null===e)return d("this.previousPointer.Resolve()");t.previousContentObject=e.path.toString()}return t}}t.Thread=i}(J||(J={}));class q{constructor(t,e){this.variableChangedEventCallbacks=[],this._batchObservingVariableChanges=!1,this._defaultGlobalVariables=new Map,this._changedVariables=new Set,this._globalVariables=new Map,this._callStack=t,this._listDefsOrigin=e;try{return new Proxy(this,{get:(t,e)=>e in t?t[e]:t.$(e),set:(t,e,n)=>(e in t?t[e]=n:t.$(e,n),!0)})}catch(t){}}variableChangedEvent(t,e){for(let n of this.variableChangedEventCallbacks)n(t,e)}get batchObservingVariableChanges(){return this._batchObservingVariableChanges}set batchObservingVariableChanges(t){if(this._batchObservingVariableChanges=t,t)this._changedVariables=new Set;else if(null!=this._changedVariables)for(let t of this._changedVariables){let e=this._globalVariables.get(t);e?this.variableChangedEvent(t,e):d("currentValue")}}get callStack(){return this._callStack}set callStack(t){this._callStack=t}$(t,e){if(void 0===e){let e=this._globalVariables.get(t);return void 0===e&&(e=this._defaultGlobalVariables.get(t)),void 0!==e?e.valueObject:null}{if(void 0===this._defaultGlobalVariables.get(t))throw new C("Cannot assign to a variable ("+t+") that hasn't been declared in the story");let n=y.Create(e);if(null==n)throw new C(null==e?"Cannot pass null to VariableState":"Invalid value passed to VariableState: "+e.toString());this.SetGlobal(t,n)}}CopyFrom(t){if(this._globalVariables=new Map(t._globalVariables),this._defaultGlobalVariables=new Map(t._defaultGlobalVariables),this.variableChangedEvent=t.variableChangedEvent,t.batchObservingVariableChanges!=this.batchObservingVariableChanges)if(t.batchObservingVariableChanges){if(this._batchObservingVariableChanges=!0,null===t._changedVariables)return d("toCopy._changedVariables");this._changedVariables=new Set(t._changedVariables)}else this._batchObservingVariableChanges=!1,this._changedVariables=null}get jsonToken(){return j.DictionaryRuntimeObjsToJObject(this._globalVariables)}set jsonToken(t){this._globalVariables=j.JObjectToDictionaryRuntimeObjs(t)}TryGetDefaultVariableValue(t){let e=v(this._defaultGlobalVariables,t,null);return e.exists?e.result:null}GlobalVariableExistsWithName(t){return this._globalVariables.has(t)}GetVariableWithName(t,e=-1){let n=this.GetRawVariableWithName(t,e),i=r(n,E);return null!==i&&(n=this.ValueAtVariablePointer(i)),n}GetRawVariableWithName(t,e){let n=null;if(0==e||-1==e){let e=v(this._globalVariables,t,null);if(e.exists)return e.result;if(null===this._listDefsOrigin)return d("VariablesState._listDefsOrigin");let n=this._listDefsOrigin.FindSingleItemListWithName(t);if(n)return n}return n=this._callStack.GetTemporaryVariableWithName(t,e)}ValueAtVariablePointer(t){return this.GetVariableWithName(t.variableName,t.contextIndex)}Assign(t,e){let n=t.variableName;if(null===n)return d("name");let i=-1,a=!1;if(a=t.isNewDeclaration?t.isGlobal:this._globalVariables.has(n),t.isNewDeclaration){let t=r(e,E);if(null!==t){e=this.ResolveVariablePointer(t)}}else{let t=null;do{null!=(t=r(this.GetRawVariableWithName(n,i),E))&&(n=t.variableName,a=0==(i=t.contextIndex))}while(null!=t)}a?this.SetGlobal(n,e):this._callStack.SetTemporaryVariable(n,e,t.isNewDeclaration,i)}SnapshotDefaultGlobals(){this._defaultGlobalVariables=new Map(this._globalVariables)}RetainListOriginsForAssignment(t,e){let n=s(t,O),i=s(e,O);n.value&&i.value&&0==i.value.Count&&i.value.SetInitialOriginNames(n.value.originNames)}SetGlobal(t,e){let n=v(this._globalVariables,t,null);if(n.exists&&O.RetainListOriginsForAssignment(n.result,e),null===t)return d("variableName");if(this._globalVariables.set(t,e),null!=this.variableChangedEvent&&e!==n.result)if(this.batchObservingVariableChanges){if(null===this._changedVariables)return d("this._changedVariables");this._changedVariables.add(t)}else this.variableChangedEvent(t,e)}ResolveVariablePointer(t){let e=t.contextIndex;-1==e&&(e=this.GetContextIndexOfVariableNamed(t.variableName));let n=r(this.GetRawVariableWithName(t.variableName,e),E);return null!=n?n:new E(t.variableName,e)}GetContextIndexOfVariableNamed(t){return this._globalVariables.get(t)?0:this._callStack.currentElementIndex}ObserveVariableChange(t){this.variableChangedEventCallbacks.push(t)}}class K{constructor(t){this.seed=t%2147483647,this.seed<=0&&(this.seed+=2147483646)}next(){return this.seed=16807*this.seed%2147483647}nextFloat(){return(this.next()-1)/2147483646}}class U{constructor(t){this.kInkSaveStateVersion=8,this.kMinCompatibleLoadVersion=8,this._currentErrors=null,this._currentWarnings=null,this.divertedPointer=k.Null,this._currentTurnIndex=0,this.storySeed=0,this.previousRandom=0,this.didSafeExit=!1,this._currentText=null,this._currentTags=null,this._outputStreamTextDirty=!0,this._outputStreamTagsDirty=!0,this.story=t,this._outputStream=[],this.OutputStreamDirty(),this._evaluationStack=[],this.callStack=new J(t),this._variablesState=new q(this.callStack,t.listDefinitions),this._visitCounts=new Map,this._turnIndices=new Map,this._currentTurnIndex=-1;let e=(new Date).getTime();this.storySeed=new K(e).next()%100,this.previousRandom=0,this._currentChoices=[],this.GoToStart()}ToJson(t=!1){return JSON.stringify(this.jsonToken,null,t?2:0)}toJson(t=!1){return this.ToJson(t)}LoadJson(t){this.jsonToken=JSON.parse(t)}VisitCountAtPathString(t){let e=v(this.visitCounts,t,null);return e.exists?e.result:0}get callstackDepth(){return this.callStack.depth}get outputStream(){return this._outputStream}get currentChoices(){return this.canContinue?[]:this._currentChoices}get generatedChoices(){return this._currentChoices}get currentErrors(){return this._currentErrors}get currentWarnings(){return this._currentWarnings}get variablesState(){return this._variablesState}get evaluationStack(){return this._evaluationStack}get visitCounts(){return this._visitCounts}get turnIndices(){return this._turnIndices}get currentTurnIndex(){return this._currentTurnIndex}get currentPathString(){let t=this.currentPointer;return t.isNull?null:null===t.path?d("pointer.path"):t.path.toString()}get currentPointer(){return this.callStack.currentElement.currentPointer.copy()}set currentPointer(t){this.callStack.currentElement.currentPointer=t.copy()}get previousPointer(){return this.callStack.currentThread.previousPointer.copy()}set previousPointer(t){this.callStack.currentThread.previousPointer=t.copy()}get canContinue(){return!this.currentPointer.isNull&&!this.hasError}get hasError(){return null!=this.currentErrors&&this.currentErrors.length>0}get hasWarning(){return null!=this.currentWarnings&&this.currentWarnings.length>0}get currentText(){if(this._outputStreamTextDirty){let t=new m;for(let e of this._outputStream){let n=r(e,_);null!==n&&t.Append(n.value)}this._currentText=this.CleanOutputWhitespace(t.toString()),this._outputStreamTextDirty=!1}return this._currentText}CleanOutputWhitespace(t){let e=new m,n=-1,i=0;for(let a=0;a<t.length;a++){let r=t.charAt(a),s=" "==r||"\t"==r;s&&-1==n&&(n=a),s||("\n"!=r&&n>0&&n!=i&&e.Append(" "),n=-1),"\n"==r&&(i=a+1),s||e.Append(r)}return e.toString()}get currentTags(){if(this._outputStreamTagsDirty){this._currentTags=[];for(let t of this._outputStream){let e=r(t,M);null!==e&&this._currentTags.push(e.text)}this._outputStreamTagsDirty=!1}return this._currentTags}get inExpressionEvaluation(){return this.callStack.currentElement.inExpressionEvaluation}set inExpressionEvaluation(t){this.callStack.currentElement.inExpressionEvaluation=t}GoToStart(){this.callStack.currentElement.currentPointer=k.StartOf(this.story.mainContentContainer)}Copy(){let t=new U(this.story);return t.outputStream.push.apply(t.outputStream,this._outputStream),this.OutputStreamDirty(),t._currentChoices.push.apply(t._currentChoices,this._currentChoices),this.hasError&&(t._currentErrors=[],t._currentErrors.push.apply(t._currentErrors,this.currentErrors||[])),this.hasWarning&&(t._currentWarnings=[],t._currentWarnings.push.apply(t._currentWarnings,this.currentWarnings||[])),t.callStack=new J(this.callStack),t._variablesState=new q(t.callStack,this.story.listDefinitions),t.variablesState.CopyFrom(this.variablesState),t.evaluationStack.push.apply(t.evaluationStack,this.evaluationStack),this.divertedPointer.isNull||(t.divertedPointer=this.divertedPointer.copy()),t.previousPointer=this.previousPointer.copy(),t._visitCounts=new Map(this.visitCounts),t._turnIndices=new Map(this.turnIndices),t._currentTurnIndex=this.currentTurnIndex,t.storySeed=this.storySeed,t.previousRandom=this.previousRandom,t.didSafeExit=this.didSafeExit,t}get jsonToken(){let t,e={};for(let e of this._currentChoices){if(null===e.threadAtGeneration)return d("c.threadAtGeneration");e.originalThreadIndex=e.threadAtGeneration.threadIndex,null==this.callStack.ThreadWithIndex(e.originalThreadIndex)&&(null==t&&(t=new Map),t[e.originalThreadIndex.toString()]=e.threadAtGeneration.jsonToken)}if(null!=t&&(e.choiceThreads=t),e.callstackThreads=this.callStack.GetJsonToken(),e.variablesState=this.variablesState.jsonToken,e.evalStack=j.ListToJArray(this.evaluationStack),e.outputStream=j.ListToJArray(this._outputStream),e.currentChoices=j.ListToJArray(this._currentChoices),!this.divertedPointer.isNull){if(null===this.divertedPointer.path)return d("this.divertedPointer.path");e.currentDivertTarget=this.divertedPointer.path.componentsString}return e.visitCounts=j.IntDictionaryToJObject(this.visitCounts),e.turnIndices=j.IntDictionaryToJObject(this.turnIndices),e.turnIdx=this.currentTurnIndex,e.storySeed=this.storySeed,e.previousRandom=this.previousRandom,e.inkSaveVersion=this.kInkSaveStateVersion,e.inkFormatVersion=this.story.inkVersionCurrent,e}set jsonToken(t){let n=t,i=n.inkSaveVersion;if(null==i)throw new C("ink save format incorrect, can't load.");if(parseInt(i)<this.kMinCompatibleLoadVersion)throw new C("Ink save format isn't compatible with the current version (saw '"+i+"', but minimum is "+this.kMinCompatibleLoadVersion+"), so can't load.");this.callStack.SetJsonToken(n.callstackThreads,this.story),this.variablesState.jsonToken=n.variablesState,this._evaluationStack=j.JArrayToRuntimeObjList(n.evalStack),this._outputStream=j.JArrayToRuntimeObjList(n.outputStream),this.OutputStreamDirty(),this._currentChoices=j.JArrayToRuntimeObjList(n.currentChoices);let a=n.currentDivertTarget;if(null!=a){let t=new e(a.toString());this.divertedPointer=this.story.PointerAtPath(t)}this._visitCounts=j.JObjectToIntDictionary(n.visitCounts),this._turnIndices=j.JObjectToIntDictionary(n.turnIndices),this._currentTurnIndex=parseInt(n.turnIdx),this.storySeed=parseInt(n.storySeed),this.previousRandom=parseInt(n.previousRandom);let r=n.choiceThreads;for(let t of this._currentChoices){let e=this.callStack.ThreadWithIndex(t.originalThreadIndex);if(null!=e)t.threadAtGeneration=e.Copy();else{let e=r[t.originalThreadIndex.toString()];t.threadAtGeneration=new J.Thread(e,this.story)}}}ResetErrors(){this._currentErrors=null,this._currentWarnings=null}ResetOutput(t=null){this._outputStream.length=0,null!==t&&this._outputStream.push.apply(this._outputStream,t),this.OutputStreamDirty()}PushToOutputStream(t){let e=r(t,_);if(null!==e){let t=this.TrySplittingHeadTailWhitespace(e);if(null!==t){for(let e of t)this.PushToOutputStreamIndividual(e);return void this.OutputStreamDirty()}}this.PushToOutputStreamIndividual(t),this.OutputStreamDirty()}PopFromOutputStream(t){this.outputStream.splice(this.outputStream.length-t,t),this.OutputStreamDirty()}TrySplittingHeadTailWhitespace(t){let e=t.value;if(null===e)return d("single.value");let n=-1,i=-1;for(let t=0;t<e.length;++t){let a=e[t];if("\n"!=a){if(" "==a||"\t"==a)continue;break}-1==n&&(n=t),i=t}let a=-1,r=-1;for(let t=0;t<e.length;++t){let n=e[t];if("\n"!=n){if(" "==n||"\t"==n)continue;break}-1==a&&(a=t),r=t}if(-1==n&&-1==a)return null;let s=[],l=0,o=e.length;if(-1!=n){if(n>0){let t=new _(e.substring(0,n));s.push(t)}s.push(new _("\n")),l=i+1}if(-1!=a&&(o=r),o>l){let t=e.substring(l,o-l);s.push(new _(t))}if(-1!=a&&r>i&&(s.push(new _("\n")),a<e.length-1)){let t=e.length-a-1,n=new _(e.substring(a+1,t));s.push(n)}return s}PushToOutputStreamIndividual(t){let e=r(t,P),n=r(t,_),i=!0;if(e)this.TrimNewlinesFromOutputStream(),i=!0;else if(n){let t=-1,e=this.callStack.currentElement;e.type==a.Function&&(t=e.functionStartInOutputStream);let r=-1;for(let e=this._outputStream.length-1;e>=0;e--){let n=this._outputStream[e],i=n instanceof A?n:null;if(null!=(n instanceof P?n:null)){r=e;break}if(null!=i&&i.commandType==A.CommandType.BeginString){e>=t&&(t=-1);break}}let s=-1;if(-1!=(s=-1!=r&&-1!=t?Math.min(t,r):-1!=r?r:t)){if(n.isNewline)i=!1;else if(n.isNonWhitespace&&(r>-1&&this.RemoveExistingGlue(),t>-1)){let t=this.callStack.elements;for(let e=t.length-1;e>=0;e--){let n=t[e];if(n.type!=a.Function)break;n.functionStartInOutputStream=-1}}}else n.isNewline&&(!this.outputStreamEndsInNewline&&this.outputStreamContainsContent||(i=!1))}if(i){if(null===t)return d("obj");this._outputStream.push(t),this.OutputStreamDirty()}}TrimNewlinesFromOutputStream(){let t=-1,e=this._outputStream.length-1;for(;e>=0;){let n=this._outputStream[e],i=r(n,A),a=r(n,_);if(null!=i||null!=a&&a.isNonWhitespace)break;null!=a&&a.isNewline&&(t=e),e--}if(t>=0)for(e=t;e<this._outputStream.length;){r(this._outputStream[e],_)?this._outputStream.splice(e,1):e++}this.OutputStreamDirty()}RemoveExistingGlue(){for(let t=this._outputStream.length-1;t>=0;t--){let e=this._outputStream[t];if(e instanceof P)this._outputStream.splice(t,1);else if(e instanceof A)break}this.OutputStreamDirty()}get outputStreamEndsInNewline(){if(this._outputStream.length>0)for(let t=this._outputStream.length-1;t>=0;t--){if(this._outputStream[t]instanceof A)break;let e=this._outputStream[t];if(e instanceof _){if(e.isNewline)return!0;if(e.isNonWhitespace)break}}return!1}get outputStreamContainsContent(){for(let t=0;t<this._outputStream.length;t++)if(this._outputStream[t]instanceof _)return!0;return!1}get inStringEvaluation(){for(let t=this._outputStream.length-1;t>=0;t--){let e=r(this._outputStream[t],A);if(e instanceof A&&e.commandType==A.CommandType.BeginString)return!0}return!1}PushEvaluationStack(t){let e=r(t,O);if(e){let t=e.value;if(null===t)return d("rawList");if(null!=t.originNames){t.origins||(t.origins=[]),t.origins.length=0;for(let e of t.originNames){if(null===this.story.listDefinitions)return d("StoryState.story.listDefinitions");let n=this.story.listDefinitions.TryListGetDefinition(e,null);if(null===n.result)return d("StoryState def.result");t.origins.indexOf(n.result)<0&&t.origins.push(n.result)}}}if(null===t)return d("obj");this.evaluationStack.push(t)}PopEvaluationStack(t){if(void 0===t){return u(this.evaluationStack.pop())}if(t>this.evaluationStack.length)throw new Error("trying to pop too many objects");return u(this.evaluationStack.splice(this.evaluationStack.length-t,t))}PeekEvaluationStack(){return this.evaluationStack[this.evaluationStack.length-1]}ForceEnd(){this.callStack.Reset(),this._currentChoices.length=0,this.currentPointer=k.Null,this.previousPointer=k.Null,this.didSafeExit=!0}TrimWhitespaceFromFunctionEnd(){n.Assert(this.callStack.currentElement.type==a.Function);let t=this.callStack.currentElement.functionStartInOutputStream;-1==t&&(t=0);for(let e=this._outputStream.length-1;e>=t;e--){let t=this._outputStream[e],n=r(t,_),i=r(t,A);if(null!=n){if(i)break;if(!n.isNewline&&!n.isInlineWhitespace)break;this._outputStream.splice(e,1),this.OutputStreamDirty()}}}PopCallStack(t=null){this.callStack.currentElement.type==a.Function&&this.TrimWhitespaceFromFunctionEnd(),this.callStack.Pop(t)}SetChosenPath(t,e){this._currentChoices.length=0;let n=this.story.PointerAtPath(t);n.isNull||-1!=n.index||(n.index=0),this.currentPointer=n,e&&this._currentTurnIndex++}StartFunctionEvaluationFromGame(t,e){this.callStack.Push(a.FunctionEvaluationFromGame,this.evaluationStack.length),this.callStack.currentElement.currentPointer=k.StartOf(t),this.PassArgumentsToEvaluationStack(e)}PassArgumentsToEvaluationStack(t){if(null!=t)for(let e=0;e<t.length;e++){if("number"!=typeof t[e]&&"string"!=typeof t[e])throw new Error("ink arguments when calling EvaluateFunction / ChoosePathStringWithParameters  must be int, float or string");this.PushEvaluationStack(y.Create(t[e]))}}TryExitFunctionEvaluationFromGame(){return this.callStack.currentElement.type==a.FunctionEvaluationFromGame&&(this.currentPointer=k.Null,this.didSafeExit=!0,!0)}CompleteFunctionEvaluationFromGame(){if(this.callStack.currentElement.type!=a.FunctionEvaluationFromGame)throw new C("Expected external function evaluation to be complete. Stack trace: "+this.callStack.callStackTrace);let t=this.callStack.currentElement.evaluationStackHeightWhenPushed,e=null;for(;this.evaluationStack.length>t;){let t=this.PopEvaluationStack();null===e&&(e=t)}if(this.PopCallStack(a.FunctionEvaluationFromGame),e){if(e instanceof R)return null;let t=s(e,y);return t.valueType==i.DivertTarget?t.valueObject.toString():t.valueObject}return null}AddError(t,e){e?(null==this._currentWarnings&&(this._currentWarnings=[]),this._currentWarnings.push(t)):(null==this._currentErrors&&(this._currentErrors=[]),this._currentErrors.push(t))}OutputStreamDirty(){this._outputStreamTextDirty=!0,this._outputStreamTagsDirty=!0}}class z{constructor(){this.startTime=void 0}get ElapsedMilliseconds(){return void 0===this.startTime?0:(new Date).getTime()-this.startTime}Start(){this.startTime=(new Date).getTime()}Stop(){this.startTime=void 0}}Number.isInteger||(Number.isInteger=function(t){return"number"==typeof t&&isFinite(t)&&t>-9007199254740992&&t<9007199254740992&&Math.floor(t)===t});class H extends p{constructor(){let t;super(),this.inkVersionCurrent=19,this.inkVersionMinimumCompatible=18,this._prevContainers=[],this.allowExternalFunctionFallbacks=!1,this._listDefinitions=null,this._variableObservers=null,this._hasValidatedExternals=!1,this._temporaryEvaluationContainer=null,this._asyncContinueActive=!1,this._stateAtLastNewline=null,this._recursiveContinueCount=0,this._profiler=null;let e=null,n=null;if(arguments[0]instanceof N)t=arguments[0],void 0!==arguments[1]&&(e=arguments[1]),this._mainContentContainer=t;else if("string"==typeof arguments[0]){let t=arguments[0];n=JSON.parse(t)}else n=arguments[0];if(null!=e&&(this._listDefinitions=new W(e)),this._externals=new Map,null!==n){let t=n,e=t.inkVersion;if(null==e)throw new Error("ink version number not found. Are you sure it's a valid .ink.json file?");let i=parseInt(e);if(i>this.inkVersionCurrent)throw new Error("Version of ink used to build story was newer than the current version of the engine");if(i<this.inkVersionMinimumCompatible)throw new Error("Version of ink used to build story is too old to be loaded by this version of the engine");i!=this.inkVersionCurrent&&console.warn("WARNING: Version of ink used to build story doesn't match current version of engine. Non-critical, but recommend synchronising.");let a,r=t.root;if(null==r)throw new Error("Root node for ink not found. Are you sure it's a valid .ink.json file?");(a=t.listDefs)&&(this._listDefinitions=j.JTokenToListDefinitions(a)),this._mainContentContainer=s(j.JTokenToRuntimeObject(r),N),this.ResetState()}}get currentChoices(){let t=[];if(null===this._state)return d("this._state");for(let e of this._state.currentChoices)e.isInvisibleDefault||(e.index=t.length,t.push(e));return t}get currentText(){return this.IfAsyncWeCant("call currentText since it's a work in progress"),this.state.currentText}get currentTags(){return this.IfAsyncWeCant("call currentTags since it's a work in progress"),this.state.currentTags}get currentErrors(){return this.state.currentErrors}get currentWarnings(){return this.state.currentWarnings}get hasError(){return this.state.hasError}get hasWarning(){return this.state.hasWarning}get variablesState(){return this.state.variablesState}get listDefinitions(){return this._listDefinitions}get state(){return this._state}StartProfiling(){}EndProfiling(){}ToJsonString(){let t=j.RuntimeObjectToJToken(this._mainContentContainer),e={};return e.inkVersion=this.inkVersionCurrent,e.root=t,null!=this._listDefinitions&&(e.listDefs=j.ListDefinitionsToJToken(this._listDefinitions)),JSON.stringify(e)}ResetState(){this.IfAsyncWeCant("ResetState"),this._state=new U(this),this._state.variablesState.ObserveVariableChange(this.VariableStateDidChangeEvent.bind(this)),this.ResetGlobals()}ResetErrors(){if(null===this._state)return d("this._state");this._state.ResetErrors()}ResetCallstack(){if(this.IfAsyncWeCant("ResetCallstack"),null===this._state)return d("this._state");this._state.ForceEnd()}ResetGlobals(){if(this._mainContentContainer.namedContent.get("global decl")){let t=this.state.currentPointer.copy();this.ChoosePath(new e("global decl"),!1),this.ContinueInternal(),this.state.currentPointer=t}this.state.variablesState.SnapshotDefaultGlobals()}Continue(){return this.ContinueAsync(0),this.currentText}get canContinue(){return this.state.canContinue}get asyncContinueComplete(){return!this._asyncContinueActive}ContinueAsync(t){this._hasValidatedExternals||this.ValidateExternalBindings(),this.ContinueInternal(t)}ContinueInternal(t=0){null!=this._profiler&&this._profiler.PreContinue();let e=t>0;if(this._recursiveContinueCount++,!this._asyncContinueActive){if(this._asyncContinueActive=e,!this.canContinue)throw new C("Can't continue - should check canContinue before calling Continue");this._state.didSafeExit=!1,this._state.ResetOutput(),1==this._recursiveContinueCount&&(this._state.variablesState.batchObservingVariableChanges=!0)}let n=new z;n.Start();let i=!1;do{try{i=this.ContinueSingleStep()}catch(t){if(!(t instanceof C))throw t;this.AddError(t.message,void 0,t.useEndLineNumber);break}if(i)break;if(this._asyncContinueActive&&n.ElapsedMilliseconds>t)break}while(this.canContinue);n.Stop(),!i&&this.canContinue||(null!=this._stateAtLastNewline&&(this.RestoreStateSnapshot(this._stateAtLastNewline),this._stateAtLastNewline=null),this.canContinue||(this.state.callStack.canPopThread&&this.AddError("Thread available to pop, threads should always be flat by the end of evaluation?"),0!=this.state.generatedChoices.length||this.state.didSafeExit||null!=this._temporaryEvaluationContainer||(this.state.callStack.CanPop(a.Tunnel)?this.AddError("unexpectedly reached end of content. Do you need a '->->' to return from a tunnel?"):this.state.callStack.CanPop(a.Function)?this.AddError("unexpectedly reached end of content. Do you need a '~ return'?"):this.state.callStack.canPop?this.AddError("unexpectedly reached end of content for unknown reason. Please debug compiler!"):this.AddError("ran out of content. Do you need a '-> DONE' or '-> END'?"))),this.state.didSafeExit=!1,1==this._recursiveContinueCount&&(this._state.variablesState.batchObservingVariableChanges=!1),this._asyncContinueActive=!1),this._recursiveContinueCount--,null!=this._profiler&&this._profiler.PostContinue()}ContinueSingleStep(){if(null!=this._profiler&&this._profiler.PreStep(),this.Step(),null!=this._profiler&&this._profiler.PostStep(),this.canContinue||this.state.callStack.elementIsEvaluateFromGame||this.TryFollowDefaultInvisibleChoice(),null!=this._profiler&&this._profiler.PreSnapshot(),!this.state.inStringEvaluation){if(null!=this._stateAtLastNewline){if(null===this._stateAtLastNewline.currentTags)return d("this._stateAtLastNewline.currentTags");if(null===this.state.currentTags)return d("this.state.currentTags");let t=this.CalculateNewlineOutputStateChange(this._stateAtLastNewline.currentText,this.state.currentText,this._stateAtLastNewline.currentTags.length,this.state.currentTags.length);if(t==H.OutputStateChange.ExtendedBeyondNewline)return this.RestoreStateSnapshot(this._stateAtLastNewline),!0;t==H.OutputStateChange.NewlineRemoved&&(this._stateAtLastNewline=null)}this.state.outputStreamEndsInNewline&&(this.canContinue?null==this._stateAtLastNewline&&(this._stateAtLastNewline=this.StateSnapshot()):this._stateAtLastNewline=null)}return null!=this._profiler&&this._profiler.PostSnapshot(),!1}CalculateNewlineOutputStateChange(t,e,n,i){if(null===t)return d("prevText");if(null===e)return d("currText");let a=e.length>=t.length&&"\n"==e.charAt(t.length-1);if(n==i&&t.length==e.length&&a)return H.OutputStateChange.NoChange;if(!a)return H.OutputStateChange.NewlineRemoved;if(i>n)return H.OutputStateChange.ExtendedBeyondNewline;for(let n=t.length;n<e.length;n++){let t=e.charAt(n);if(" "!=t&&"\t"!=t)return H.OutputStateChange.ExtendedBeyondNewline}return H.OutputStateChange.NoChange}ContinueMaximally(){this.IfAsyncWeCant("ContinueMaximally");let t=new m;for(;this.canContinue;)t.Append(this.Continue());return t.toString()}ContentAtPath(t){return this.mainContentContainer.ContentAtPath(t)}KnotContainerWithName(t){let e=this.mainContentContainer.namedContent.get(t);return e instanceof N?e:null}PointerAtPath(t){if(0==t.length)return k.Null;let e=new k,n=t.length,i=null;return null===t.lastComponent?d("path.lastComponent"):(t.lastComponent.isIndex?(n=t.length-1,i=this.mainContentContainer.ContentAtPath(t,void 0,n),e.container=i.container,e.index=t.lastComponent.index):(i=this.mainContentContainer.ContentAtPath(t),e.container=i.container,e.index=-1),null==i.obj||i.obj==this.mainContentContainer&&n>0?this.Error("Failed to find content at path '"+t+"', and no approximation of it was possible."):i.approximate&&this.Warning("Failed to find content at path '"+t+"', so it was approximated to: '"+i.obj.path+"'."),e)}StateSnapshot(){return this.state.Copy()}RestoreStateSnapshot(t){this._state=t}Step(){let t=!0,e=this.state.currentPointer.copy();if(e.isNull)return;let n=r(e.Resolve(),N);for(;n&&(this.VisitContainer(n,!0),0!=n.content.length);)n=r((e=k.StartOf(n)).Resolve(),N);this.state.currentPointer=e.copy(),null!=this._profiler&&this._profiler.Step(this.state.callStack);let i=e.Resolve(),a=this.PerformLogicAndFlowControl(i);if(this.state.currentPointer.isNull)return;a&&(t=!1);let s=r(i,F);if(s){let e=this.ProcessChoice(s);e&&this.state.generatedChoices.push(e),i=null,t=!1}if(i instanceof N&&(t=!1),t){let t=r(i,E);if(t&&-1==t.contextIndex){let e=this.state.callStack.ContextForVariableNamed(t.variableName);i=new E(t.variableName,e)}this.state.inExpressionEvaluation?this.state.PushEvaluationStack(i):this.state.PushToOutputStream(i)}this.NextContent();let l=r(i,A);l&&l.commandType==A.CommandType.StartThread&&this.state.callStack.PushThread()}VisitContainer(t,e){t.countingAtStartOnly&&!e||(t.visitsShouldBeCounted&&this.IncrementVisitCountForContainer(t),t.turnIndexShouldBeCounted&&this.RecordTurnIndexVisitToContainer(t))}VisitChangedContainersDueToDivert(){let t=this.state.previousPointer.copy(),e=this.state.currentPointer.copy();if(e.isNull||-1==e.index)return;if(this._prevContainers.length=0,!t.isNull){let e=r(t.Resolve(),N)||r(t.container,N);for(;e;)this._prevContainers.push(e),e=r(e.parent,N)}let n=e.Resolve();if(null==n)return;let i=r(n.parent,N);for(;i&&(this._prevContainers.indexOf(i)<0||i.countingAtStartOnly);){let t=i.content.length>0&&n==i.content[0];this.VisitContainer(i,t),n=i,i=r(i.parent,N)}}ProcessChoice(t){let e=!0;if(t.hasCondition){let t=this.state.PopEvaluationStack();this.IsTruthy(t)||(e=!1)}let n="",i="";if(t.hasChoiceOnlyContent){i=s(this.state.PopEvaluationStack(),_).value||""}if(t.hasStartContent){n=s(this.state.PopEvaluationStack(),_).value||""}if(t.onceOnly){this.VisitCountForContainer(t.choiceTarget)>0&&(e=!1)}if(!e)return null;let a=new G;return a.targetPath=t.pathOnChoice,a.sourcePath=t.path.toString(),a.isInvisibleDefault=t.isInvisibleDefault,a.threadAtGeneration=this.state.callStack.ForkThread(),a.text=(n+i).replace(/^[ \t]+|[ \t]+$/g,""),a}IsTruthy(t){if(t instanceof y){let e=t;if(e instanceof w){let t=e;return this.Error("Shouldn't use a divert target (to "+t.targetPath+") as a conditional value. Did you intend a function call 'likeThis()' or a read count check 'likeThis'? (no arrows)"),!1}return e.isTruthy}return!1}PerformLogicAndFlowControl(t){if(null==t)return!1;if(t instanceof I){let e=t;if(e.isConditional){let t=this.state.PopEvaluationStack();if(!this.IsTruthy(t))return!0}if(e.hasVariableTarget){let t=e.variableDivertName,n=this.state.variablesState.GetVariableWithName(t);if(null==n)this.Error("Tried to divert using a target from a variable that could not be found ("+t+")");else if(!(n instanceof w)){let e=r(n,T),i="Tried to divert to a target from a variable, but the variable ("+t+") didn't contain a divert target, it ";e instanceof T&&0==e.value?i+="was empty/null (the value 0).":i+="contained '"+n+"'.",this.Error(i)}let i=s(n,w);this.state.divertedPointer=this.PointerAtPath(i.targetPath)}else{if(e.isExternal)return this.CallExternalFunction(e.targetPathString,e.externalArgs),!0;this.state.divertedPointer=e.targetPointer.copy()}return e.pushesToStack&&this.state.callStack.Push(e.stackPushType,void 0,this.state.outputStream.length),this.state.divertedPointer.isNull&&!e.isExternal&&(e&&e.debugMetadata&&null!=e.debugMetadata.sourceName?this.Error("Divert target doesn't exist: "+e.debugMetadata.sourceName):this.Error("Divert resolution failed: "+e)),!0}if(t instanceof A){let e=t;switch(e.commandType){case A.CommandType.EvalStart:this.Assert(!1===this.state.inExpressionEvaluation,"Already in expression evaluation?"),this.state.inExpressionEvaluation=!0;break;case A.CommandType.EvalEnd:this.Assert(!0===this.state.inExpressionEvaluation,"Not in expression evaluation mode"),this.state.inExpressionEvaluation=!1;break;case A.CommandType.EvalOutput:if(this.state.evaluationStack.length>0){let t=this.state.PopEvaluationStack();if(!(t instanceof R)){let e=new _(t.toString());this.state.PushToOutputStream(e)}}break;case A.CommandType.NoOp:break;case A.CommandType.Duplicate:this.state.PushEvaluationStack(this.state.PeekEvaluationStack());break;case A.CommandType.PopEvaluatedValue:this.state.PopEvaluationStack();break;case A.CommandType.PopFunction:case A.CommandType.PopTunnel:let t=e.commandType==A.CommandType.PopFunction?a.Function:a.Tunnel,n=null;if(t==a.Tunnel){let t=this.state.PopEvaluationStack();null===(n=r(t,w))&&this.Assert(t instanceof R,"Expected void if ->-> doesn't override target")}if(this.state.TryExitFunctionEvaluationFromGame())break;if(this.state.callStack.currentElement.type==t&&this.state.callStack.canPop)this.state.PopCallStack(),n&&(this.state.divertedPointer=this.PointerAtPath(n.targetPath));else{let e=new Map;e.set(a.Function,"function return statement (~ return)"),e.set(a.Tunnel,"tunnel onwards statement (->->)");let n=e.get(this.state.callStack.currentElement.type);this.state.callStack.canPop||(n="end of flow (-> END or choice)");let i="Found "+e.get(t)+", when expected "+n;this.Error(i)}break;case A.CommandType.BeginString:this.state.PushToOutputStream(e),this.Assert(!0===this.state.inExpressionEvaluation,"Expected to be in an expression when evaluating a string"),this.state.inExpressionEvaluation=!1;break;case A.CommandType.EndString:let i=[],l=0;for(let t=this.state.outputStream.length-1;t>=0;--t){let e=this.state.outputStream[t];l++;let n=r(e,A);if(n&&n.commandType==A.CommandType.BeginString)break;e instanceof _&&i.push(e)}this.state.PopFromOutputStream(l),i=i.reverse();let o=new m;for(let t of i)o.Append(t.toString());this.state.inExpressionEvaluation=!0,this.state.PushEvaluationStack(new _(o.toString()));break;case A.CommandType.ChoiceCount:let u=this.state.generatedChoices.length;this.state.PushEvaluationStack(new T(u));break;case A.CommandType.Turns:this.state.PushEvaluationStack(new T(this.state.currentTurnIndex+1));break;case A.CommandType.TurnsSince:case A.CommandType.ReadCount:let h=this.state.PopEvaluationStack();if(!(h instanceof w)){let t="";h instanceof T&&(t=". Did you accidentally pass a read count ('knot_name') instead of a target ('-> knot_name')?"),this.Error("TURNS_SINCE / READ_COUNT expected a divert target (knot, stitch, label name), but saw "+h+t);break}let c,p=s(h,w),v=r(this.ContentAtPath(p.targetPath).correctObj,N);null!=v?c=e.commandType==A.CommandType.TurnsSince?this.TurnsSinceForContainer(v):this.VisitCountForContainer(v):(c=e.commandType==A.CommandType.TurnsSince?-1:0,this.Warning("Failed to find container for "+e.toString()+" lookup at "+p.targetPath.toString())),this.state.PushEvaluationStack(new T(c));break;case A.CommandType.Random:{let t=r(this.state.PopEvaluationStack(),T),e=r(this.state.PopEvaluationStack(),T);if(null==e||e instanceof T==!1)return this.Error("Invalid value for minimum parameter of RANDOM(min, max)");if(null==t||e instanceof T==!1)return this.Error("Invalid value for maximum parameter of RANDOM(min, max)");if(null===t.value)return d("maxInt.value");if(null===e.value)return d("minInt.value");let n=t.value-e.value+1;n<=0&&this.Error("RANDOM was called with minimum as "+e.value+" and maximum as "+t.value+". The maximum must be larger");let i=this.state.storySeed+this.state.previousRandom,a=new K(i).next(),s=a%n+e.value;this.state.PushEvaluationStack(new T(s)),this.state.previousRandom=a;break}case A.CommandType.SeedRandom:let S=r(this.state.PopEvaluationStack(),T);if(null==S||S instanceof T==!1)return this.Error("Invalid value passed to SEED_RANDOM");if(null===S.value)return d("minInt.value");this.state.storySeed=S.value,this.state.previousRandom=0,this.state.PushEvaluationStack(new R);break;case A.CommandType.VisitIndex:let b=this.VisitCountForContainer(this.state.currentPointer.container)-1;this.state.PushEvaluationStack(new T(b));break;case A.CommandType.SequenceShuffleIndex:let E=this.NextSequenceShuffleIndex();this.state.PushEvaluationStack(new T(E));break;case A.CommandType.StartThread:break;case A.CommandType.Done:this.state.callStack.canPopThread?this.state.callStack.PopThread():(this.state.didSafeExit=!0,this.state.currentPointer=k.Null);break;case A.CommandType.End:this.state.ForceEnd();break;case A.CommandType.ListFromInt:let x=r(this.state.PopEvaluationStack(),T),P=s(this.state.PopEvaluationStack(),_);if(null===x)throw new C("Passed non-integer when creating a list element from a numerical value.");let I=null;if(null===this.listDefinitions)return d("this.listDefinitions");let F=this.listDefinitions.TryListGetDefinition(P.value,null);if(!F.exists)throw new C("Failed to find LIST called "+P.value);{if(null===x.value)return d("minInt.value");let t=F.result.TryGetItemWithValue(x.value,f.Null);t.exists&&(I=new O(t.result,x.value))}null==I&&(I=new O),this.state.PushEvaluationStack(I);break;case A.CommandType.ListRange:let V=r(this.state.PopEvaluationStack(),y),L=r(this.state.PopEvaluationStack(),y),D=r(this.state.PopEvaluationStack(),O);if(null===D||null===L||null===V)throw new C("Expected list, minimum and maximum for LIST_RANGE");if(null===D.value)return d("targetList.value");let M=D.value.ListWithSubRange(L.valueObject,V.valueObject);this.state.PushEvaluationStack(new O(M));break;case A.CommandType.ListRandom:{let t=this.state.PopEvaluationStack();if(null===t)throw new C("Expected list for LIST_RANDOM");let e=t.value,n=null;if(null===e)throw d("list");if(0==e.Count)n=new g;else{let t=this.state.storySeed+this.state.previousRandom,i=new K(t).next(),a=i%e.Count,r=e.entries();for(let t=0;t<=a-1;t++)r.next();let s=r.next().value,l={Key:f.fromSerializedKey(s[0]),Value:s[1]};if(null===l.Key.originName)return d("randomItem.Key.originName");(n=new g(l.Key.originName,this)).Add(l.Key,l.Value),this.state.previousRandom=i}this.state.PushEvaluationStack(new O(n));break}default:this.Error("unhandled ControlCommand: "+e)}return!0}if(t instanceof L){let e=t,n=this.state.PopEvaluationStack();return this.state.variablesState.Assign(e,n),!0}if(t instanceof V){let e=t,n=null;if(null!=e.pathForCount){let t=e.containerForCount,i=this.VisitCountForContainer(t);n=new T(i)}else if(null==(n=this.state.variablesState.GetVariableWithName(e.name))){let t=this.state.variablesState.TryGetDefaultVariableValue(e.name);null!=t?(this.Warning("Variable not found in save state: '"+e.name+"', but seems to have been newly created. Assigning value from latest ink's declaration: "+t),n=t,this.state.variablesState.SetGlobal(e.name,n)):(this.Warning("Variable not found: '"+e.name+"'. Using default value of 0 (false). This can happen with temporary variables if the declaration hasn't yet been hit."),n=new T(0))}return this.state.PushEvaluationStack(n),!0}if(t instanceof D){let e=t,n=this.state.PopEvaluationStack(e.numberOfParameters),i=e.Call(n);return this.state.PushEvaluationStack(i),!0}return!1}ChoosePathString(t,n=!0,i=[]){if(this.IfAsyncWeCant("call ChoosePathString right now"),n)this.ResetCallstack();else if(this.state.callStack.currentElement.type==a.Function){let e="",n=this.state.callStack.currentElement.currentPointer.container;throw null!=n&&(e="("+n.path.toString()+") "),new Error("Story was running a function "+e+"when you called ChoosePathString("+t+") - this is almost certainly not not what you want! Full stack trace: \n"+this.state.callStack.callStackTrace)}this.state.PassArgumentsToEvaluationStack(i),this.ChoosePath(new e(t))}IfAsyncWeCant(t){if(this._asyncContinueActive)throw new Error("Can't "+t+". Story is in the middle of a ContinueAsync(). Make more ContinueAsync() calls or a single Continue() call beforehand.")}ChoosePath(t,e=!0){this.state.SetChosenPath(t,e),this.VisitChangedContainersDueToDivert()}ChooseChoiceIndex(t){t=t;let e=this.currentChoices;this.Assert(t>=0&&t<e.length,"choice out of range");let n=e[t];return null===n.threadAtGeneration?d("choiceToChoose.threadAtGeneration"):null===n.targetPath?d("choiceToChoose.targetPath"):(this.state.callStack.currentThread=n.threadAtGeneration,void this.ChoosePath(n.targetPath))}HasFunction(t){try{return null!=this.KnotContainerWithName(t)}catch(t){return!1}}EvaluateFunction(t,e=[],n=!1){if(this.IfAsyncWeCant("evaluate a function"),null==t)throw new Error("Function is null");if(""==t||""==t.trim())throw new Error("Function is empty or white space.");let i=this.KnotContainerWithName(t);if(null==i)throw new Error("Function doesn't exist: '"+t+"'");let a=[];a.push.apply(a,this.state.outputStream),this._state.ResetOutput(),this.state.StartFunctionEvaluationFromGame(i,e);let r=new m;for(;this.canContinue;)r.Append(this.Continue());let s=r.toString();this._state.ResetOutput(a);let l=this.state.CompleteFunctionEvaluationFromGame();return n?{returned:l,output:s}:l}EvaluateExpression(t){let e=this.state.callStack.elements.length;this.state.callStack.Push(a.Tunnel),this._temporaryEvaluationContainer=t,this.state.GoToStart();let n=this.state.evaluationStack.length;return this.Continue(),this._temporaryEvaluationContainer=null,this.state.callStack.elements.length>e&&this.state.PopCallStack(),this.state.evaluationStack.length>n?this.state.PopEvaluationStack():null}CallExternalFunction(t,e){if(null===t)return d("funcName");let n=this._externals.get(t),i=null;if(!(void 0!==n)){if(this.allowExternalFunctionFallbacks)return i=this.KnotContainerWithName(t),this.Assert(null!==i,"Trying to call EXTERNAL function '"+t+"' which has not been bound, and fallback ink function could not be found."),this.state.callStack.Push(a.Function,void 0,this.state.outputStream.length),void(this.state.divertedPointer=k.StartOf(i));this.Assert(!1,"Trying to call EXTERNAL function '"+t+"' which has not been bound (and ink fallbacks disabled).")}let r=[];for(let t=0;t<e;++t){let t=s(this.state.PopEvaluationStack(),y).valueObject;r.push(t)}r.reverse();let l=n(r),o=null;null!=l?(o=y.Create(l),this.Assert(null!==o,"Could not create ink value from returned object of type "+typeof l)):o=new R,this.state.PushEvaluationStack(o)}BindExternalFunctionGeneral(t,e){this.IfAsyncWeCant("bind an external function"),this.Assert(!this._externals.has(t),"Function '"+t+"' has already been bound."),this._externals.set(t,e)}TryCoerce(t){return t}BindExternalFunction(t,e){this.Assert(null!=e,"Can't bind a null function"),this.BindExternalFunctionGeneral(t,t=>{this.Assert(t.length>=e.length,"External function expected "+e.length+" arguments");let n=[];for(let e=0,i=t.length;e<i;e++)n[e]=this.TryCoerce(t[e]);return e.apply(null,n)})}UnbindExternalFunction(t){this.IfAsyncWeCant("unbind an external a function"),this.Assert(this._externals.has(t),"Function '"+t+"' has not been bound."),this._externals.delete(t)}ValidateExternalBindings(){let t=null,e=null,n=arguments[1]||new Set;if(arguments[0]instanceof N&&(t=arguments[0]),arguments[0]instanceof p&&(e=arguments[0]),null===t&&null===e)if(this.ValidateExternalBindings(this._mainContentContainer,n),this._hasValidatedExternals=!0,0==n.size)this._hasValidatedExternals=!0;else{let t="Error: Missing function binding for external";t+=n.size>1?"s":"",t+=": '",t+=Array.from(n).join("', '"),t+="' ",t+=this.allowExternalFunctionFallbacks?", and no fallback ink function found.":" (ink fallbacks disabled)",this.Error(t)}else if(null!=t){for(let e of t.content){let t=e;null!=t&&t.hasValidName||this.ValidateExternalBindings(e,n)}for(let[e,i]of t.namedContent)this.ValidateExternalBindings(r(i,p),n)}else if(null!=e){let t=r(e,I);if(t&&t.isExternal){let e=t.targetPathString;if(null===e)return d("name");if(!this._externals.has(e))if(this.allowExternalFunctionFallbacks){this.mainContentContainer.namedContent.has(e)||n.add(e)}else n.add(e)}}}ObserveVariable(t,e){if(this.IfAsyncWeCant("observe a new variable"),null===this._variableObservers&&(this._variableObservers=new Map),!this.state.variablesState.GlobalVariableExistsWithName(t))throw new C("Cannot observe variable '"+t+"' because it wasn't declared in the ink story.");this._variableObservers.has(t)?this._variableObservers.get(t).push(e):this._variableObservers.set(t,[e])}ObserveVariables(t,e){for(let n=0,i=t.length;n<i;n++)this.ObserveVariable(t[n],e[n])}RemoveVariableObserver(t,e){if(this.IfAsyncWeCant("remove a variable observer"),null!==this._variableObservers)if(void 0!==e){if(this._variableObservers.has(e)){let n=this._variableObservers.get(e);n.splice(n.indexOf(t),1)}}else{let e=this._variableObservers.keys();for(let n of e){let e=this._variableObservers.get(n);e.splice(e.indexOf(t),1)}}}VariableStateDidChangeEvent(t,e){if(null===this._variableObservers)return;let n=this._variableObservers.get(t);if(void 0!==n){if(!(e instanceof y))throw new Error("Tried to get the value of a variable that isn't a standard type");let i=s(e,y);for(let e of n)e(t,i.valueObject)}}get globalTags(){return this.TagsAtStartOfFlowContainerWithPathString("")}TagsForContentAtPath(t){return this.TagsAtStartOfFlowContainerWithPathString(t)}TagsAtStartOfFlowContainerWithPathString(t){let n=new e(t),i=this.ContentAtPath(n).container;if(null===i)return d("flowContainer");for(;;){let t=i.content[0];if(!(t instanceof N))break;i=t}let a=null;for(let t of i.content){let e=r(t,M);if(!e)break;null==a&&(a=[]),a.push(e.text)}return a}BuildStringOfHierarchy(){let t=new m;return this.mainContentContainer.BuildStringOfHierarchy(t,0,this.state.currentPointer.Resolve()),t.toString()}BuildStringOfContainer(t){let e=new m;return t.BuildStringOfHierarchy(e,0,this.state.currentPointer.Resolve()),e.toString()}NextContent(){if(this.state.previousPointer=this.state.currentPointer.copy(),!this.state.divertedPointer.isNull&&(this.state.currentPointer=this.state.divertedPointer.copy(),this.state.divertedPointer=k.Null,this.VisitChangedContainersDueToDivert(),!this.state.currentPointer.isNull))return;if(!this.IncrementContentPointer()){let t=!1;this.state.callStack.CanPop(a.Function)?(this.state.PopCallStack(a.Function),this.state.inExpressionEvaluation&&this.state.PushEvaluationStack(new R),t=!0):this.state.callStack.canPopThread?(this.state.callStack.PopThread(),t=!0):this.state.TryExitFunctionEvaluationFromGame(),t&&!this.state.currentPointer.isNull&&this.NextContent()}}IncrementContentPointer(){let t=!0,e=this.state.callStack.currentElement.currentPointer.copy();if(e.index++,null===e.container)return d("pointer.container");for(;e.index>=e.container.content.length;){t=!1;let n=r(e.container.parent,N);if(n instanceof N==!1)break;let i=n.content.indexOf(e.container);if(-1==i)break;if((e=new k(n,i)).index++,t=!0,null===e.container)return d("pointer.container")}return t||(e=k.Null),this.state.callStack.currentElement.currentPointer=e.copy(),t}TryFollowDefaultInvisibleChoice(){let t=this._state.currentChoices,e=t.filter(t=>t.isInvisibleDefault);if(0==e.length||t.length>e.length)return!1;let n=e[0];return null===n.targetPath?d("choice.targetPath"):null===n.threadAtGeneration?d("choice.threadAtGeneration"):(this.state.callStack.currentThread=n.threadAtGeneration,this.ChoosePath(n.targetPath,!1),!0)}VisitCountForContainer(t){if(null===t)return d("container");if(!t.visitsShouldBeCounted)return console.warn("Read count for target ("+t.name+" - on "+t.debugMetadata+") unknown. The story may need to be compiled with countAllVisits flag (-c)."),0;let e=0,n=t.path.toString();return e=this.state.visitCounts.get(n)||e}IncrementVisitCountForContainer(t){let e=0,n=t.path.toString();this.state.visitCounts.has(n)&&(e=this.state.visitCounts.get(n)),e++,this.state.visitCounts.set(n,e)}RecordTurnIndexVisitToContainer(t){let e=t.path.toString();this.state.turnIndices.set(e,this.state.currentTurnIndex)}TurnsSinceForContainer(t){t.turnIndexShouldBeCounted||this.Error("TURNS_SINCE() for target ("+t.name+" - on "+t.debugMetadata+") unknown. The story may need to be compiled with countAllVisits flag (-c).");let e=t.path.toString(),n=this.state.turnIndices.get(e);return void 0!==n?this.state.currentTurnIndex-n:-1}NextSequenceShuffleIndex(){let t=r(this.state.PopEvaluationStack(),T);if(!(t instanceof T))return this.Error("expected number of elements in sequence for shuffle index"),0;let e=this.state.currentPointer.container;if(null===e)return d("seqContainer");if(null===t.value)return d("numElementsIntVal.value");let n=t.value,i=s(this.state.PopEvaluationStack(),T).value;if(null===i)return d("seqCount");let a=i/n,l=i%n,o=e.path.toString(),u=0;for(let t=0,e=o.length;t<e;t++)u+=o.charCodeAt(t)||0;let h=u+a+this.state.storySeed,c=new K(Math.floor(h)),p=[];for(let t=0;t<n;++t)p.push(t);for(let t=0;t<=l;++t){let e=c.next()%p.length,n=p[e];if(p.splice(e,1),t==l)return n}throw new Error("Should never reach here")}Error(t,e=!1){let n=new C(t);throw n.useEndLineNumber=e,n}Warning(t){this.AddError(t,!0)}AddError(t,e=!1,n=!1){let i=this.currentDebugMetadata,a=e?"WARNING":"ERROR";if(null!=i){let e=n?i.endLineNumber:i.startLineNumber;t="RUNTIME "+a+": '"+i.fileName+"' line "+e+": "+t}else t=this.state.currentPointer.isNull?"RUNTIME "+a+": "+t:"RUNTIME "+a+": ("+this.state.currentPointer+"): "+t;this.state.AddError(t,e),e||this.state.ForceEnd()}Assert(t,e=null){if(0==t)throw null==e&&(e="Story assert"),new Error(e+" "+this.currentDebugMetadata)}get currentDebugMetadata(){let t,e=this.state.currentPointer;if(!e.isNull&&null!==e.Resolve()&&null!==(t=e.Resolve().debugMetadata))return t;for(let n=this.state.callStack.elements.length-1;n>=0;--n)if(!(e=this.state.callStack.elements[n].currentPointer).isNull&&null!==e.Resolve()&&null!==(t=e.Resolve().debugMetadata))return t;for(let e=this.state.outputStream.length-1;e>=0;--e){if(null!==(t=this.state.outputStream[e].debugMetadata))return t}return null}get mainContentContainer(){return this._temporaryEvaluationContainer?this._temporaryEvaluationContainer:this._mainContentContainer}}!function(t){let e;!function(t){t[t.NoChange=0]="NoChange",t[t.ExtendedBeyondNewline=1]="ExtendedBeyondNewline",t[t.NewlineRemoved=2]="NewlineRemoved"}(e=t.OutputStateChange||(t.OutputStateChange={}))}(H||(H={})),t.InkList=g,t.Story=H,Object.defineProperty(t,"__esModule",{value:!0})});
//# sourceMappingURL=ink-es2015.js.map


================================================
FILE: static/mainink.js
================================================
var story;

var storyContainer = document.querySelectorAll('#incident')[0];

function loadStory(storyName) {
    fetch(storyName)
        .then(function (response) {
            return response.text();
        })
        .then(function (storyContent) {
            story = new inkjs.Story(storyContent);
            continueStory();
        });
}

function showAfter(delay, el) {
    setTimeout(function () { el.classList.add("show") }, delay);
}

function scrollToBottom() {
    var progress = 0.0;
    var start = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0;
    var dist = document.body.scrollHeight - window.innerHeight - start;
    if (dist < 0) return;

    var duration = 300 + 300 * dist / 100;
    var startTime = null;
    function step(time) {
        if (startTime == null) startTime = time;
        var t = (time - startTime) / duration;
        var lerp = 3 * t * t - 2 * t * t * t;
        window.scrollTo(0, start + lerp * dist);
        if (t < 1) requestAnimationFrame(step);
    }
    requestAnimationFrame(step);
}

function continueStory() {

    var paragraphIndex = 0;
    var delay = 0.0;

    // Generate story text - loop through available content
    while (story.canContinue) {

        // Get ink to generate the next paragraph
        var paragraphText = story.Continue();

        // Create paragraph element
        var paragraphElement = document.createElement('p');
        paragraphElement.innerHTML = paragraphText;
        storyContainer.appendChild(paragraphElement);

        // Fade in paragraph after a short delay
        showAfter(delay, paragraphElement);

        delay += 200.0;
    }

    // Create HTML choices from ink choices
    story.currentChoices.forEach(function (choice) {

        // Create paragraph with anchor element
        var choiceParagraphElement = document.createElement('p');
        choiceParagraphElement.classList.add("choice");
        choiceParagraphElement.innerHTML = `<a href='#'>${choice.text}</a>`
        storyContainer.appendChild(choiceParagraphElement);

        // Fade choice in after a short delay
        showAfter(delay, choiceParagraphElement);
        delay += 200.0;

        // Click on choice
        var choiceAnchorEl = choiceParagraphElement.querySelectorAll("a")[0];
        choiceAnchorEl.addEventListener("click", function (event) {

            // Don't follow <a> link
            event.preventDefault();

            // Remove all existing choices
            var existingChoices = storyContainer.querySelectorAll('p.choice');
            for (var i = 0; i < existingChoices.length; i++) {
                var c = existingChoices[i];
                c.parentNode.removeChild(c);
            }

            // Tell the story where to go next
            story.ChooseChoiceIndex(choice.index);

            // Aaand loop
            continueStory();
        });
    });

    //scrollToBottom();
}


================================================
FILE: static/site.webmanifest
================================================
{"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"}

================================================
FILE: static/stopwatch.js
================================================
/* Kudos to Billy Brown: https://codepen.io/_Billy_Brown/pen/dbJeh */
var _createClass = function () { function 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); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } var Stopwatch = function () {
    function Stopwatch(display, results) {
        _classCallCheck(this, Stopwatch);
        this.running = false;
        this.display = display;
        this.results = results;
        this.laps = [];
        this.reset();
        this.print(this.times);
    } _createClass(Stopwatch, [{
        key: 'reset', value: function reset() {
            this.times = [0, 0, 0];
        }
    }, {
        key: 'start', value: function start() {
            if (!this.time) this.time = performance.now();
            if (!this.running) {
                this.running = true;
                requestAnimationFrame(this.step.bind(this));
            }
        }
    }, {
        key: 'lap', value: function lap() {
            if (!this.running) return;
            var times = this.times;
            var li = document.createElement('li');
            li.innerText = this.format(times);
            this.results.appendChild(li);
        }
    }, {
        key: 'stop', value: function stop() {
            this.running = false;
            this.time = null;
        }
    }, {
        key: 'restart', value: function restart() {
            if (!this.time) this.time = performance.now();
            if (!this.running) {
                this.running = true;
                requestAnimationFrame(this.step.bind(this));
            }
            this.reset();
        }
    }, {
        key: 'clear', value: function clear() {
            clearChildren(this.results);
        }
    }, {
        key: 'step', value: function step(

            timestamp) {
            if (!this.running) return;
            this.calculate(timestamp);
            this.time = timestamp;
            this.print();
            requestAnimationFrame(this.step.bind(this));
        }
    }, {
        key: 'calculate', value: function calculate(

            timestamp) {
            var diff = timestamp - this.time;
            // Hundredths of a second are 100 ms
            this.times[2] += diff / 10;
            // Seconds are 100 hundredths of a second
            if (this.times[2] >= 100) {
                this.times[1] += 1;
                this.times[2] -= 100;
            }
            // Minutes are 60 seconds
            if (this.times[1] >= 60) {
                this.times[0] += 1;
                this.times[1] -= 60;
            }
        }
    }, {
        key: 'print', value: function print() {
            this.display.innerText = this.format(this.times);
        }
    }, {
        key: 'format', value: function format(

            times) {
            return (
                pad0(times[0], 2) + ':' +
                pad0(times[1], 2) + ':' +
                pad0(Math.floor(times[2]), 2));
        }
    }]); return Stopwatch;
}();


function pad0(value, count) {
    var result = value.toString();
    for (; result.length < count; --count) {
        result = '0' + result;
    }
    return result;
}

function clearChildren(node) {
    while (node.lastChild) {
        node.removeChild(node.lastChild);
    }
}

var stopwatch = new Stopwatch(
    document.querySelector('.stopwatch'),
    document.querySelector('.results'));


================================================
FILE: static/styles.css
================================================
html,
body {
  width: 100%;
  margin: 0;
  padding: 0;

}

body {
    font-family: "Roboto", Helvetica, sans-serif;
    font-weight: 350;
    background-color: #f5f5f5;
}

button {
  border:solid 2px #590F20;
  border-radius:5px;
  background-color: #e7e7e7;
  letter-spacing: 1px;
  border: none;
  margin: 3px 1px;
  text-align: center;
  text-decoration: none;
  display: inline-block;
  cursor: pointer;
  font-size: 15px;
  padding: 12px 12px;
}

button:first-child {
    margin-left: 0;
}

button:last-child {
    margin-right: 0;
}

button:hover {
  color: rgb(255, 127, 14);
}

table {
    border-collapse: collapse;
    background-color: white;
}

td, th {
    border: 1px solid #dddddd;
    text-align: left;
    padding: 5px;
}

li a {
    text-decoration: none;
}

li a:hover {
    text-decoration:underline;
}

header p {
    color: #0a0a0a;
}

header li {
  color: #0a0a0a;
}

small a {
    color: white;
    text-decoration: underline;
}

small a:hover {
    text-decoration: underline;
}

ul {
  margin: 0;
  padding: 0;
  list-style-type: none;
}

h1 {
  margin-bottom: 5px;
}

h2 {
  margin-bottom: 10px;
  color: rgba(0,0,0,.54);
}

h3, h4 {
  margin-top: 5px;
  margin-bottom: 5px;
  color: rgba(0,0,0,.54);
}

label {
  display: inline-block;
  max-width: 100%;
  padding: 5px;
  font-weight: bold;
}

text {
    pointer-events:none;
}

.center {
  margin-right: auto;
  margin-left: auto;
}

.results {
  font-size: 20px;
  margin: 15px;
  padding: 0;
  bottom: 0;
  left: 50%;
}

.results li {
  color: #666;
  position: relative;
  border-bottom: 0.5px solid #333;
}

.result li::before {
  visibility: hidden;
  color: inherit;
  line-height: 3em;
  position: absolute;
  left: 0;
  top: 0;
}

a {
  text-decoration: none;
}

a:hover {
  text-decoration: underline;
  color: rgb(255, 127, 14);
}

p a {
    color: rgb(255,64,129);;
}

ul a {
    color: rgb(255,64,129);;
}

.br3 {
    border-radius: .5rem;
    box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2);
}

.br-100 {
    border-radius: 100%;
}

.bw1 {
    border-width: .125rem;
}

.cf:before, .cf:after {
    content: " ";
    display: table;
}

.cf:after {
    clear: both;
}

.cf {
    *zoom: 1;
}

.fl {
    float: left;
    _display: inline;
}

.w-100 {
    width: 100%;
}

.tc {
    text-align: center;
}

@media screen and (min-width: 50em) {
    .w-50-ns {
        width: 50%;
    }
}

@media screen and (min-width: 50em) {
    .w-30-ns {
        width: 30%;
    }
}

@media screen and (min-width: 50em) {
    .w-40-ns {
        width: 40%;
    }
}

.f2 {
    font-size: 2.2rem;
    color: #721329;
}

@media screen and (min-width: 40em) and (max-width: 60em) {
    .f2-m {
        font-size: 2.2rem;
    }
}

@media screen and (min-width: 60em) {
    .f-headline-l2 {
        font-size: 2.2rem;
    }
}

.f3 {
    font-size: 1.5rem;
}

@media screen and (min-width: 50em) and (max-width: 60em) {
    .f1-m {
        font-size: 1.5rem;
    }
}

@media screen and (min-width: 60em) {
    .f-headline-l {
        font-size: 1.5rem;
    }
}

.white-ft {
    color: white;
}

.athelas {
    font-family: athelas, georgia, serif;
}

.pa4 {
    padding: 1rem;
}

.i {
    font-style: italic;
}

.fw1 {
    font-weight: 100;
}

.fw4 {
    font-weight: 400;
}

.f5 {
    font-size: 1rem;
}

.h2 {
    height: 2rem;
}

.black-80 {
    color: rgba(0, 0, 0, .8);
}

.pv4 {
    padding-top: 1rem;
    padding-bottom: 1rem;
}

.f1 {
    font-size: 2rem;
}

.ff2 {
    font-size: 1.15rem;
}

.db {
  display: block;
}

.mid-gray {
  color: #555;
}

.ph3 {
  padding-left: 1rem;
  padding-right: 1rem;
}

.container {
  position: relative;
  margin: 40px auto;
  font-size: 16px;
}

.ba {
  border-style: solid;
  border-width: 1px;
}

.bg-blue {
    background-color: rgb(63,81,181);
}

.bg-white {
    background-color: #fff;
}

.br3 {
    border-radius: .5rem;
}

.br-100 {
    border-radius: 100%;
}

.bw1 {
    border-width: .125rem;
}

.b--black-10 {
    border-color: rgba(0, 0, 0, .1);
}

.b--black-30 {
  border-color: rgba(0, 0, 0, .3);
}

.b--dotted {
  border-style: dotted;
}

.bt-0 {
  border-top-width: 0;
}

.br-0 {
  border-right-width: 0;
}

.bl-0 {
  border-left-width: 0;
}

.mw3 {
    max-width: 4rem;
}

.mw5 {
    max-width: 16rem;
}

.lh-copy {
  line-height: 1.5;
}

.list  {
  display:list-item;
  list-style-type: none;
}

.list label {
  display:list-item;
  list-style-type: none;
}

.pa3 {
  padding: 1rem;
}

.pv3 {
  padding-top: 1rem;
  padding-bottom: 0.5rem;
}

.pl0 {
  padding-left: 0;
}

.f4 {
  font-size: 1rem;
}

.mw6 {
  max-width: 32rem;
}

@media screen and (min-width: 30em) {
    .pa4-ns {
        padding: 2rem;
    }
}

.mb {
    margin-bottom: 20px;
}

.pv5 {
    padding-top: 4rem;
    padding-bottom: 4rem;
}

.ph4 {
    padding-left: 2rem;
    padding-right: 2rem;
}

.mv4 {
    margin-top: 2rem;
    margin-bottom: 0.5rem;
}

.mv3 {
    margin-top: 0.5rem;
    margin-bottom: 2rem;
}

.mbr {
    margin-right: 1rem;
}

@media screen and (min-width: 60em) {
    .pv6-l {
        padding-top: 8rem;
        padding-bottom: 8rem;
    }
}

.mw-h {
  max-width: 35rem;
}

@media screen and (min-width: 50em) {
  .mw {
    max-width: 55rem;
  }
}
.flex {
  display: flex;
}

.items-center {
  align-items: center;
}

.justify-center {
  justify-content: center;
}

.w1 {
  width: 1rem;
}

.navy {
  color: #001b44;
}

.bg-lightest-blue {
  background-color: #cdecff;
}

.pa4 {
  padding: 2rem;
}

.ml3 {
  margin-left: .5rem;
}

.timing {
  font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
  font-weight: 300;
  text-align: center;
}

.timing ul {
      list-style-type: none;
}

.timing button {
  padding: 12px 23px;
  cursor: pointer;
  font-size: 16px;
  border: 1px solid #353535;
  background-color: #353535;
  color: #ccc;
  border-radius: 100%;
  text-align: center;
  line-height: 35px;
}

.stopwatch {
  font-size: 2rem;
}

#start {
  background-color: #4caf50;
  color:white
}

#start:hover {
  background-color: #81c784;
  color:white
}

#stop {
  background-color: #f44336;
  color:white
}

#stop:hover {
  background-color: #e57373;
  color:white
}

#restart {
  background-color: #2196f3;
  color:white
}

#restart:hover {
  background-color: #b9d9f3;
  color:white
}

#wheel {
  width: 100%;
  margin-left: 5px;
}

#incident {
    margin-top: 10px;
    word-wrap: break-word;
    line-height: 1.5;
}

#incident p {
    font-size: .875rem
}

.link {
  text-decoration: none;
  color: white;
  transition: color .15s ease-in;
}

.link:link, .link:visited {
  transition: color .15s ease-in;
}

.link:hover {
  transition: color .15s ease-in;
  color: #f44336;
}

.link:active {
  transition: color .15s ease-in;
}

.link:focus {
  transition: color .15s ease-in;
  outline: 1px dotted currentColor;
}

@media screen and (min-width: 30em) and (max-width: 60em) {
  .ph5-m {
      padding-left: 4rem;
      padding-right: 4rem;
  }
}

@media screen and (min-width: 60em) {
  .ph6-l {
      padding-left: 8rem;
      padding-right: 8rem;
  }
}

@media screen and (min-width: 30em) {
  .w-40-ns {
      width: 40%;
  }
}

@media screen and (min-width: 30em) {
  .w-33-ns {
      width: 33.33333%;
  }
}

@media screen and (min-width: 30em) {
  .w-20-ns {
      width: 20%;
  }
}

@media screen and (min-width: 30em) {
  .w-10-ns {
      width: 10%;
  }
}

/* Slight modifications from:
* https://www.cssscript.com/beautiful-checkbox-and-radio-button-replacement-with-pure-css-magic-check/
*/
@keyframes hover-color {
  from {
    border-color: #c0c0c0; }
  to {
    border-color: #81c784; }
}

.magic-checkbox {
  position: absolute;
  display: none;
 }

.magic-checkbox[disabled] {
  cursor: not-allowed; }


.magic-checkbox + label {
  position: relative;
  display: block;
  cursor: pointer;
}

.magic-checkbox + label:hover:before {
    animation-duration: 0.4s;
    animation-fill-mode: both;
    animation-name: hover-color;
}

.magic-checkbox + label:before {
    position: absolute;
    top: 0;
    left: 0;
    display: inline-block;
    width: 30px;
    height: 30px;
    content: '';
    border: 1px solid #c0c0c0;
}

.magic-checkbox + label:after {
    position: absolute;
    display: none;
    content: '';
}

.magic-checkbox[disabled] + label {
  cursor: not-allowed;
  color: #e4e4e4;
}

.magic-checkbox[disabled] + label:hover,
.magic-checkbox[disabled] + label:before,
.magic-checkbox[disabled] + label:after {
  cursor: not-allowed;
}

.magic-checkbox[disabled] + label:hover:before {
  border: 1px solid #e4e4e4;
  animation-name: none;
}

.magic-checkbox[disabled] + label:before {
  border-color: #e4e4e4;
}

.magic-checkbox:checked + label:before {
  animation-name: none;
}

.magic-checkbox:checked + label:after {
  display: block;
}

.magic-checkbox + label:before {
  border-radius: 3px;
}

.magic-checkbox + label:after {
  top: 2px;
  left: 11px;
  box-sizing: border-box;
  width: 10px;
  height: 20px;
  transform: rotate(45deg);
  border-width: 2px;
  border-style: solid;
  border-color: #fff;
  border-top: 0;
  border-left: 0;
}

.magic-checkbox:checked + label:before {
  background: #388E3C;
}

.magic-checkbox:checked[disabled] + label:before {
  border:  #388E3C;
  background:  #388E3C;
}

.magic-checkbox + label {
    color: rgb(255,64,129);
    font-weight: bold;
}

.magic-checkbox + label:hover{
  color: #353535;
  -moz-transition: all 0.2s ease-in;
  -o-transition: all 0.2s ease-in;
  -webkit-transition: all 0.2s ease-in;
  transition: all 0.2s ease-in;
}

.magic-checkbox:checked + label {
  color: #353535;
}

/* Modified: https://codepen.io/jo-asakura/pen/stFHi */
.pie-wrapper {
  height: 1em;
  width: 1em;
  float: left;
  position: relative;
}

.pie-wrapper:nth-child(3n + 1) {
  clear: both;
}

.pie-wrapper .pie {
  height: 100%;
  width: 100%;
  clip: rect(0, 1em, 1em, 0.5em);
  left: 0;
  position: absolute;
  top: 0;
}
.pie-wrapper .pie .half-circle {
  height: 100%;
  width: 100%;
  border: 0.1em solid #3498db;
  border-radius: 50%;
  clip: rect(0, 0.5em, 1em, 0);
  left: 0;
  position: absolute;
  top: 0;
}
.pie-wrapper .label {
  border-radius: 50%;
  bottom: 0.4em;
  background: none;
  color: #7f8c8d;
  cursor: default;
  display: block;
  font-size: 0.25em;
  left: 0.4em;
  line-height: 2.8em;
  position: absolute;
  right: 0.4em;
  text-align: center;
  top: 0.4em;
}
.pie-wrapper .label .smaller {
  color: #bdc3c7;
  font-size: .45em;
  padding-bottom: 20px;
  vertical-align: super;
}
.pie-wrapper .shadow {
  height: 100%;
  width: 100%;
  border: 0.1em solid #bdc3c7;
  border-radius: 50%;
}

.pie-wrapper.progress-25 .pie .half-circle {
  border-color: #e65100;
}
.pie-wrapper.progress-25 .pie .left-side {
  -webkit-transform: rotate(90deg);
          transform: rotate(90deg);
}
.pie-wrapper.progress-25 .pie .right-side {
  display: none;
}

.pie-wrapper.progress-50 .pie .half-circle {
  border-color: #ffc107;
}
.pie-wrapper.progress-50 .pie .left-side {
  -webkit-transform: rotate(180deg);
          transform: rotate(180deg);
}
.pie-wrapper.progress-50 .pie .right-side {
  display: none;
}

.pie-wrapper.progress-75 .pie {
  clip: rect(auto, auto, auto, auto);
}
.pie-wrapper.progress-75 .pie .half-circle {
  border-color: #ffd54f;
}
.pie-wrapper.progress-75 .pie .left-side {
  -webkit-transform: rotate(270deg);
          transform: rotate(270deg);
}
.pie-wrapper.progress-75 .pie .right-side {
  -webkit-transform: rotate(180deg);
          transform: rotate(180deg);
}
.pie-wrapper.progress-100 .pie {
  clip: rect(auto, auto, auto, auto);
}
.pie-wrapper.progress-100 .pie .half-circle {
  border-color: #4caf50;
}
.pie-wrapper.progress-100 .pie .left-side {
  -webkit-transform: rotate(360deg);
          transform: rotate(360deg);
}
.pie-wrapper.progress-100 .pie .right-side {
  -webkit-transform: rotate(180deg);
          transform: rotate(180deg);
}

*,
*:before,
*:after {
  box-sizing: border-box;
}

.sz {
  font-size: 10em;
  display: inline-block;
}

footer {
  color: #9e9e9e;
  background-color: #424242;
  margin-top: 2rem;
}


================================================
FILE: static/wheel.js
================================================
/* This is a modified version of Jeremy Rue's Wheel of Fortune
 * http://bl.ocks.org/jrue/a2aaf36b3c096925ccbf */

function custom_colors(n) {
    var colors = ["#388E3C", "#1976D2", "#D32F2F", "#FFA000", "#388E3C", "#990099", "#0099c6", "#C2185B", "#81C784", "#D32F2F", "#1976D2", "#994499", "#4DD0E1", "#AED581", "#536DFE", "#FBC02D", "#C62828", "#AB47BC", "#66BB6A", "#90A4AE", "#0D47A1"];
    return colors[n % colors.length];
}

var padding = { top: 20, right: 40, bottom: 0, left: 0 },
    w = 500 - padding.left - padding.right,
    h = 500 - padding.top - padding.bottom,
    r = Math.min(w, h) / 2,
    rotation = 0,
    oldrotation = 0,
    picked = 100000,
    oldpick = [];

d3.json("./incidents/general_incidents.json", function (error, data) {
    if (error) throw error;
    var svg = d3.select("#wheel")
        .append("svg")
        .data([data])
        .attr("viewBox", "0 0 500 500");

    var container = svg.append("g")
        .attr("transform", "translate(" + (w / 2 + padding.left) + "," + (h / 2 + padding.top) + ")");

    var vis = container
        .append("g");

    var pie = d3.layout.pie().sort(null).value(function (d) { return 1; });

    // declare an arc generator function
    var arc = d3.svg.arc().outerRadius(r);

    // select paths, use arc generator to draw
    var arcs = vis.selectAll("g.slice")
        .data(pie)
        .enter()
        .append("g")
        .attr("class", "slice");


    arcs.append("path")
        .attr("fill", function (d, i) { return custom_colors(i); })
        .attr("d", function (d) { return arc(d); });

    // add the text
    arcs.append("text").attr("transform", function (d) {
        d.innerRadius = 0;
        d.outerRadius = r;
        d.angle = (d.startAngle + d.endAngle) / 2;
        return "rotate(" + (d.angle * 180 / Math.PI - 90) + ")translate(" + (d.outerRadius - 10) + ")";
    })
        .attr("text-anchor", "end")
        .text(function (d, i) {
            return data[i].title;
        });
    container.on("click", spin);

    function spin(d) {
        container.on("click", null);
        //all slices have been seen, all done
        if (oldpick.length == data.length) {
            container.on("click", null);
            return;
        }

        var ps = 360 / data.length,
            pieslice = Math.round(1440 / data.length),
            rng = Math.floor((Math.random() * 1440) + 360);

        rotation = (Math.round(rng / ps) * ps);
        picked = Math.round(data.length - (rotation % 360) / ps);
        picked = picked >= data.length ? (picked % data.length) : picked;

        if (oldpick.indexOf(picked) !== -1) {
            d3.select(this).call(spin);
            return;
        } else {
            oldpick.push(picked);
        }

        rotation += 90 - Math.round(ps / 2);
        vis.transition()
            .duration(3000)
            .attrTween("transform", rotTween)
            .each("end", function () {
                //mark incident as seen
                d3.select(".slice:nth-child(" + (picked + 1) + ") path")
                    .attr("fill", "#111");
                // if Ink story is provided, override the actual scenario.
                if (data[picked].inkstory != undefined) {
                    //populate incident
                    d3.select("#incident p")
                        .html("<h4 class=\"f4 center mw6\">" + data[picked].title + "</h4>" + "<div id=\"play-area\"> <ol id =\"choices\"><ol></div>");
                    container.on("click", spin, stopwatch.reset(), stopwatch.start(), changeControls(), loadStory(data[picked].inkstory));
                   } else {
                    //populate incident
                    d3.select("#incident p")
                        .html("<h4 class=\"f4 center mw6\">" + data[picked].title + "</h4>" + data[picked].scenario);
                    container.on("click", spin, stopwatch.reset(), stopwatch.start(), changeControls());
                }
                oldrotation = rotation;
            });
    }

    //make arrow
    svg.append("g")
        .attr("transform", "translate(" + (w + padding.left + padding.right) + "," + ((h / 2) + padding.top) + ")")
        .append("path")
        .attr("d", "M-" + (r * .15) + ",0L0," + (r * .05) + "L0,-" + (r * .05) + "Z")
        .style({ "fill": "black" });

    //draw spin circle
    container.append("circle")
        .attr("cx", 0)
        .attr("cy", 0)
        .attr("r", 30)
        .style({ "fill": "white", "cursor": "pointer" });

    //spin text
    container.append("text")
        .attr("x", 0)
        .attr("y", 10)
        .attr("text-anchor", "middle")
        .text("SPIN")
        .style({ "color": "white", "font-weight": "bold", "font-size": "18px" });

    function rotTween(to) {
        var i = d3.interpolate(oldrotation % 360, rotation);
        return function (t) {
            return "rotate(" + i(t) + ")";
        };
    }

});
Download .txt
gitextract_7tkj533i/

├── .github/
│   ├── FUNDING.yml
│   └── workflows/
│       └── deploy_to_gh_pages.yml
├── .gitignore
├── LICENSE
├── README.md
├── incidents/
│   ├── general_incidents.json.sample
│   ├── general_incidents.jsonnet.sample
│   └── redis-story.json.sample
├── index.html
├── instructions.html
└── static/
    ├── ink.js
    ├── mainink.js
    ├── site.webmanifest
    ├── stopwatch.js
    ├── styles.css
    └── wheel.js
Download .txt
SYMBOL INDEX (514 symbols across 4 files)

FILE: static/ink.js
  class e (line 1) | class e{constructor(){if(this._components=[],this._componentsString=null...
    method constructor (line 1) | constructor(){if(this._components=[],this._componentsString=null,this....
    method isRelative (line 1) | get isRelative(){return this._isRelative}
    method componentCount (line 1) | get componentCount(){return this._components.length}
    method head (line 1) | get head(){return this._components.length>0?this._components[0]:null}
    method tail (line 1) | get tail(){if(this._components.length>=2){let t=this._components.slice...
    method length (line 1) | get length(){return this._components.length}
    method lastComponent (line 1) | get lastComponent(){let t=this._components.length-1;return t>=0?this._...
    method containsNamedComponent (line 1) | get containsNamedComponent(){for(let t=0,e=this._components.length;t<e...
    method self (line 1) | static get self(){let t=new e;return t._isRelative=!0,t}
    method GetComponent (line 1) | GetComponent(t){return this._components[t]}
    method PathByAppendingPath (line 1) | PathByAppendingPath(t){let n=new e,i=0;for(let e=0;e<t._components.len...
    method componentsString (line 1) | get componentsString(){return null==this._componentsString&&(this._com...
    method componentsString (line 1) | set componentsString(t){if(this._components.length=0,this._componentsS...
    method toString (line 1) | toString(){return this.componentsString}
    method Equals (line 1) | Equals(t){if(null==t)return!1;if(t._components.length!=this._component...
    method PathByAppendingComponent (line 1) | PathByAppendingComponent(t){let n=new e;return n._components.push.appl...
    method constructor (line 1) | constructor(t){this.index=-1,this.name=null,"string"==typeof t?this.na...
    method isIndex (line 1) | get isIndex(){return this.index>=0}
    method isParent (line 1) | get isParent(){return this.name==t.parentId}
    method ToParent (line 1) | static ToParent(){return new e(t.parentId)}
    method toString (line 1) | toString(){return this.isIndex?this.index.toString():this.name}
    method Equals (line 1) | Equals(t){return null!=t&&t.isIndex==this.isIndex&&(this.isIndex?this....
  function r (line 1) | function r(t,e){return t instanceof e?h(t):null}
  function s (line 1) | function s(t,e){if(t instanceof e)return h(t);throw new Error(`${t} is n...
  function l (line 1) | function l(t){if("number"==typeof t)return t;throw new Error(`${t} is no...
  function o (line 1) | function o(t){return t.hasValidName&&t.name?t:null}
  function u (line 1) | function u(t){return void 0===t?null:t}
  function h (line 1) | function h(t,e){return t}
  class e (line 1) | class e{constructor(t){this.index=-1,this.name=null,"string"==typeof t?t...
    method constructor (line 1) | constructor(){if(this._components=[],this._componentsString=null,this....
    method isRelative (line 1) | get isRelative(){return this._isRelative}
    method componentCount (line 1) | get componentCount(){return this._components.length}
    method head (line 1) | get head(){return this._components.length>0?this._components[0]:null}
    method tail (line 1) | get tail(){if(this._components.length>=2){let t=this._components.slice...
    method length (line 1) | get length(){return this._components.length}
    method lastComponent (line 1) | get lastComponent(){let t=this._components.length-1;return t>=0?this._...
    method containsNamedComponent (line 1) | get containsNamedComponent(){for(let t=0,e=this._components.length;t<e...
    method self (line 1) | static get self(){let t=new e;return t._isRelative=!0,t}
    method GetComponent (line 1) | GetComponent(t){return this._components[t]}
    method PathByAppendingPath (line 1) | PathByAppendingPath(t){let n=new e,i=0;for(let e=0;e<t._components.len...
    method componentsString (line 1) | get componentsString(){return null==this._componentsString&&(this._com...
    method componentsString (line 1) | set componentsString(t){if(this._components.length=0,this._componentsS...
    method toString (line 1) | toString(){return this.componentsString}
    method Equals (line 1) | Equals(t){if(null==t)return!1;if(t._components.length!=this._component...
    method PathByAppendingComponent (line 1) | PathByAppendingComponent(t){let n=new e;return n._components.push.appl...
    method constructor (line 1) | constructor(t){this.index=-1,this.name=null,"string"==typeof t?this.na...
    method isIndex (line 1) | get isIndex(){return this.index>=0}
    method isParent (line 1) | get isParent(){return this.name==t.parentId}
    method ToParent (line 1) | static ToParent(){return new e(t.parentId)}
    method toString (line 1) | toString(){return this.isIndex?this.index.toString():this.name}
    method Equals (line 1) | Equals(t){return null!=t&&t.isIndex==this.isIndex&&(this.isIndex?this....
  function e (line 1) | function e(t,e){if(!t)throw void 0!==e&&console.warn(e),console.trace&&c...
    method constructor (line 1) | constructor(){if(this._components=[],this._componentsString=null,this....
    method isRelative (line 1) | get isRelative(){return this._isRelative}
    method componentCount (line 1) | get componentCount(){return this._components.length}
    method head (line 1) | get head(){return this._components.length>0?this._components[0]:null}
    method tail (line 1) | get tail(){if(this._components.length>=2){let t=this._components.slice...
    method length (line 1) | get length(){return this._components.length}
    method lastComponent (line 1) | get lastComponent(){let t=this._components.length-1;return t>=0?this._...
    method containsNamedComponent (line 1) | get containsNamedComponent(){for(let t=0,e=this._components.length;t<e...
    method self (line 1) | static get self(){let t=new e;return t._isRelative=!0,t}
    method GetComponent (line 1) | GetComponent(t){return this._components[t]}
    method PathByAppendingPath (line 1) | PathByAppendingPath(t){let n=new e,i=0;for(let e=0;e<t._components.len...
    method componentsString (line 1) | get componentsString(){return null==this._componentsString&&(this._com...
    method componentsString (line 1) | set componentsString(t){if(this._components.length=0,this._componentsS...
    method toString (line 1) | toString(){return this.componentsString}
    method Equals (line 1) | Equals(t){if(null==t)return!1;if(t._components.length!=this._component...
    method PathByAppendingComponent (line 1) | PathByAppendingComponent(t){let n=new e;return n._components.push.appl...
    method constructor (line 1) | constructor(t){this.index=-1,this.name=null,"string"==typeof t?this.na...
    method isIndex (line 1) | get isIndex(){return this.index>=0}
    method isParent (line 1) | get isParent(){return this.name==t.parentId}
    method ToParent (line 1) | static ToParent(){return new e(t.parentId)}
    method toString (line 1) | toString(){return this.isIndex?this.index.toString():this.name}
    method Equals (line 1) | Equals(t){return null!=t&&t.isIndex==this.isIndex&&(this.isIndex?this....
  class c (line 1) | class c extends Error{}
  function d (line 1) | function d(t){throw new c(`${t} is null or undefined`)}
  class p (line 1) | class p{constructor(){this.parent=null,this._debugMetadata=null,this._pa...
    method constructor (line 1) | constructor(){this.parent=null,this._debugMetadata=null,this._path=null}
    method debugMetadata (line 1) | get debugMetadata(){return null===this._debugMetadata&&this.parent?thi...
    method debugMetadata (line 1) | set debugMetadata(t){this._debugMetadata=t}
    method ownDebugMetadata (line 1) | get ownDebugMetadata(){return this._debugMetadata}
    method DebugLineNumberOfPath (line 1) | DebugLineNumberOfPath(t){if(null===t)return null;let e=this.rootConten...
    method path (line 1) | get path(){if(null==this._path)if(null==this.parent)this._path=new e;e...
    method ResolvePath (line 1) | ResolvePath(t){if(null===t)return d("path");if(t.isRelative){let e=r(t...
    method ConvertPathToRelative (line 1) | ConvertPathToRelative(t){let n=this.path,i=Math.min(t.length,n.length)...
    method CompactPathString (line 1) | CompactPathString(t){let e=null,n=null;if(t.isRelative)n=t.componentsS...
    method rootContentContainer (line 1) | get rootContentContainer(){let t=this;for(;t.parent;)t=t.parent;return...
    method Copy (line 1) | Copy(){throw Error("Not Implemented: Doesn't support copying")}
    method SetChild (line 1) | SetChild(t,e,n){t[e]&&(t[e]=null),t[e]=n,t[e]&&(t[e].parent=this)}
  class m (line 1) | class m{constructor(t){t=void 0!==t?t.toString():"",this.string=t}get Le...
    method constructor (line 1) | constructor(t){t=void 0!==t?t.toString():"",this.string=t}
    method Length (line 1) | get Length(){return this.string.length}
    method Append (line 1) | Append(t){null!==t&&(this.string+=t)}
    method AppendLine (line 1) | AppendLine(t){void 0!==t&&this.Append(t),this.string+="\n"}
    method AppendFormat (line 1) | AppendFormat(t,...e){this.string+=t.replace(/{(\d+)}/g,(t,n)=>void 0!=...
    method toString (line 1) | toString(){return this.string}
  class f (line 1) | class f{constructor(){if(this.originName=null,this.itemName=null,void 0!...
    method constructor (line 1) | constructor(){if(this.originName=null,this.itemName=null,void 0!==argu...
    method Null (line 1) | static get Null(){return new f(null,null)}
    method isNull (line 1) | get isNull(){return null==this.originName&&null==this.itemName}
    method fullName (line 1) | get fullName(){return(null!==this.originName?this.originName:"?")+"."+...
    method toString (line 1) | toString(){return this.fullName}
    method Equals (line 1) | Equals(t){if(t instanceof f){let e=t;return e.itemName==this.itemName&...
    method copy (line 1) | copy(){return new f(this.originName,this.itemName)}
    method serialized (line 1) | serialized(){return JSON.stringify({originName:this.originName,itemNam...
    method fromSerializedKey (line 1) | static fromSerializedKey(t){let e=JSON.parse(t);if(!f.isLikeInkListIte...
    method isLikeInkListItem (line 1) | static isLikeInkListItem(t){return"object"==typeof t&&(!(!t.hasOwnProp...
  class g (line 1) | class g extends Map{constructor(){if(super((()=>arguments[0]instanceof g...
    method constructor (line 1) | constructor(){if(super((()=>arguments[0]instanceof g?arguments[0]:void...
    method AddItem (line 1) | AddItem(t){if(t instanceof f){let e=t;if(null==e.originName)return voi...
    method ContainsItemNamed (line 1) | ContainsItemNamed(t){for(let[e,n]of this){if(f.fromSerializedKey(e).it...
    method ContainsKey (line 1) | ContainsKey(t){return this.has(t.serialized())}
    method Add (line 1) | Add(t,e){let n=t.serialized();if(this.has(n))throw new Error(`The Map ...
    method Remove (line 1) | Remove(t){return this.delete(t.serialized())}
    method Count (line 1) | get Count(){return this.size}
    method originOfMaxItem (line 1) | get originOfMaxItem(){if(null==this.origins)return null;let t=this.max...
    method originNames (line 1) | get originNames(){if(this.Count>0){null==this._originNames&&this.Count...
    method SetInitialOriginName (line 1) | SetInitialOriginName(t){this._originNames=[t]}
    method SetInitialOriginNames (line 1) | SetInitialOriginNames(t){this._originNames=null==t?null:t.slice()}
    method maxItem (line 1) | get maxItem(){let t={Key:f.Null,Value:0};for(let[e,n]of this){let i=f....
    method minItem (line 1) | get minItem(){let t={Key:f.Null,Value:0};for(let[e,n]of this){let i=f....
    method inverse (line 1) | get inverse(){let t=new g;if(null!=this.origins)for(let e of this.orig...
    method all (line 1) | get all(){let t=new g;if(null!=this.origins)for(let e of this.origins)...
    method Union (line 1) | Union(t){let e=new g(this);for(let[n,i]of t)e.set(n,i);return e}
    method Intersect (line 1) | Intersect(t){let e=new g;for(let[n,i]of this)t.has(n)&&e.set(n,i);retu...
    method Without (line 1) | Without(t){let e=new g(this);for(let[n,i]of t)e.delete(n);return e}
    method Contains (line 1) | Contains(t){for(let[e,n]of t)if(!this.has(e))return!1;return!0}
    method GreaterThan (line 1) | GreaterThan(t){return 0!=this.Count&&(0==t.Count||this.minItem.Value>t...
    method GreaterThanOrEquals (line 1) | GreaterThanOrEquals(t){return 0!=this.Count&&(0==t.Count||this.minItem...
    method LessThan (line 1) | LessThan(t){return 0!=t.Count&&(0==this.Count||this.maxItem.Value<t.mi...
    method LessThanOrEquals (line 1) | LessThanOrEquals(t){return 0!=t.Count&&(0==this.Count||this.maxItem.Va...
    method MaxAsList (line 1) | MaxAsList(){return this.Count>0?new g(this.maxItem):new g}
    method MinAsList (line 1) | MinAsList(){return this.Count>0?new g(this.minItem):new g}
    method ListWithSubRange (line 1) | ListWithSubRange(t,e){if(0==this.Count)return new g;let n=this.ordered...
    method Equals (line 1) | Equals(t){if(t instanceof g==!1)return!1;if(t.Count!=this.Count)return...
    method orderedItems (line 1) | get orderedItems(){let t=new Array;for(let[e,n]of this){let i=f.fromSe...
    method toString (line 1) | toString(){let t=this.orderedItems,e=new m;for(let n=0;n<t.length;n++)...
    method valueOf (line 1) | valueOf(){return NaN}
  class C (line 1) | class C extends Error{constructor(t){super(t),this.useEndLineNumber=!1,t...
    method constructor (line 1) | constructor(t){super(t),this.useEndLineNumber=!1,this.message=t,this.n...
  function v (line 1) | function v(t,e,n){if(null===t)return{result:n,exists:!1};let i=t.get(e);...
  class S (line 1) | class S extends p{static Create(t){if("boolean"==typeof t){t=!!t?1:0}ret...
    method Create (line 1) | static Create(t){if("boolean"==typeof t){t=!!t?1:0}return Number.isInt...
    method Copy (line 1) | Copy(){return s(S.Create(this),p)}
    method BadCastException (line 1) | BadCastException(t){return new C("Can't cast "+this.valueObject+" from...
  class y (line 1) | class y extends S{constructor(t){super(),this.value=t}get valueObject(){...
    method constructor (line 1) | constructor(t){super(),this.value=t}
    method valueObject (line 1) | get valueObject(){return this.value}
    method toString (line 1) | toString(){return null===this.value?d("Value.value"):this.value.toStri...
  class T (line 1) | class T extends y{constructor(t){super(t||0)}get isTruthy(){return 0!=th...
    method constructor (line 1) | constructor(t){super(t||0)}
    method isTruthy (line 1) | get isTruthy(){return 0!=this.value}
    method valueType (line 1) | get valueType(){return i.Int}
    method Cast (line 1) | Cast(t){if(null===this.value)return d("Value.value");if(t==this.valueT...
  class b (line 1) | class b extends y{constructor(t){super(t||0)}get isTruthy(){return 0!=th...
    method constructor (line 1) | constructor(t){super(t||0)}
    method isTruthy (line 1) | get isTruthy(){return 0!=this.value}
    method valueType (line 1) | get valueType(){return i.Float}
    method Cast (line 1) | Cast(t){if(null===this.value)return d("Value.value");if(t==this.valueT...
  class _ (line 1) | class _ extends y{constructor(t){if(super(t||""),this._isNewline="\n"==t...
    method constructor (line 1) | constructor(t){if(super(t||""),this._isNewline="\n"==this.value,this._...
    method valueType (line 1) | get valueType(){return i.String}
    method isTruthy (line 1) | get isTruthy(){return null===this.value?d("Value.value"):this.value.le...
    method isNewline (line 1) | get isNewline(){return this._isNewline}
    method isInlineWhitespace (line 1) | get isInlineWhitespace(){return this._isInlineWhitespace}
    method isNonWhitespace (line 1) | get isNonWhitespace(){return!this.isNewline&&!this.isInlineWhitespace}
    method Cast (line 1) | Cast(t){if(t==this.valueType)return this;if(t==i.Int){let e=function(t...
  class w (line 1) | class w extends y{constructor(t){super(t)}get valueType(){return i.Diver...
    method constructor (line 1) | constructor(t){super(t)}
    method valueType (line 1) | get valueType(){return i.DivertTarget}
    method targetPath (line 1) | get targetPath(){return null===this.value?d("Value.value"):this.value}
    method targetPath (line 1) | set targetPath(t){this.value=t}
    method isTruthy (line 1) | get isTruthy(){throw new Error("Shouldn't be checking the truthiness o...
    method Cast (line 1) | Cast(t){if(t==this.valueType)return this;throw this.BadCastException(t)}
    method toString (line 1) | toString(){return"DivertTargetValue("+this.targetPath+")"}
  class E (line 1) | class E extends y{constructor(t,e=-1){super(t),this._contextIndex=e}get ...
    method constructor (line 1) | constructor(t,e=-1){super(t),this._contextIndex=e}
    method contextIndex (line 1) | get contextIndex(){return this._contextIndex}
    method contextIndex (line 1) | set contextIndex(t){this._contextIndex=t}
    method variableName (line 1) | get variableName(){return null===this.value?d("Value.value"):this.value}
    method variableName (line 1) | set variableName(t){this.value=t}
    method valueType (line 1) | get valueType(){return i.VariablePointer}
    method isTruthy (line 1) | get isTruthy(){throw new Error("Shouldn't be checking the truthiness o...
    method Cast (line 1) | Cast(t){if(t==this.valueType)return this;throw this.BadCastException(t)}
    method toString (line 1) | toString(){return"VariablePointerValue("+this.variableName+")"}
    method Copy (line 1) | Copy(){return new E(this.variableName,this.contextIndex)}
  class O (line 1) | class O extends y{get isTruthy(){return null===this.value?d("this.value"...
    method isTruthy (line 1) | get isTruthy(){return null===this.value?d("this.value"):this.value.Cou...
    method valueType (line 1) | get valueType(){return i.List}
    method Cast (line 1) | Cast(t){if(null===this.value)return d("Value.value");if(t==i.Int){let ...
    method constructor (line 1) | constructor(t,e){super(null),t||e?t instanceof g?this.value=new g(t):t...
    method RetainListOriginsForAssignment (line 1) | static RetainListOriginsForAssignment(t,e){let n=r(t,O),i=r(e,O);retur...
  class x (line 1) | class x{constructor(){this.obj=null,this.approximate=!1}get correctObj()...
    method constructor (line 1) | constructor(){this.obj=null,this.approximate=!1}
    method correctObj (line 1) | get correctObj(){return this.approximate?null:this.obj}
    method container (line 1) | get container(){return this.obj instanceof N?this.obj:null}
    method copy (line 1) | copy(){let t=new x;return t.obj=this.obj,t.approximate=this.approximat...
  class N (line 1) | class N extends p{constructor(){super(...arguments),this.name="",this._c...
    method constructor (line 1) | constructor(){super(...arguments),this.name="",this._content=[],this.n...
    method hasValidName (line 1) | get hasValidName(){return null!=this.name&&this.name.length>0}
    method content (line 1) | get content(){return this._content}
    method content (line 1) | set content(t){this.AddContent(t)}
    method namedOnlyContent (line 1) | get namedOnlyContent(){let t=new Map;for(let[e,n]of this.namedContent)...
    method namedOnlyContent (line 1) | set namedOnlyContent(t){let e=this.namedOnlyContent;if(null!=e)for(let...
    method countFlags (line 1) | get countFlags(){let t=0;return this.visitsShouldBeCounted&&(t|=N.Coun...
    method countFlags (line 1) | set countFlags(t){let e=t;(e&N.CountFlags.Visits)>0&&(this.visitsShoul...
    method pathToFirstLeafContent (line 1) | get pathToFirstLeafContent(){return null==this._pathToFirstLeafContent...
    method internalPathToFirstLeafContent (line 1) | get internalPathToFirstLeafContent(){let t=[],n=this;for(;n instanceof...
    method AddContent (line 1) | AddContent(t){if(t instanceof Array){let e=t;for(let t of e)this.AddCo...
    method TryAddNamedContent (line 1) | TryAddNamedContent(t){let e=o(t);null!=e&&e.hasValidName&&this.AddToNa...
    method AddToNamedContentOnly (line 1) | AddToNamedContentOnly(t){n.AssertType(t,p,"Can only add Runtime.Object...
    method ContentAtPath (line 1) | ContentAtPath(t,e=0,n=-1){-1==n&&(n=t.length);let i=new x;i.approximat...
    method InsertContent (line 1) | InsertContent(t,e){if(this.content[e]=t,t.parent)throw new Error("cont...
    method AddContentsOfContainer (line 1) | AddContentsOfContainer(t){this.content=this.content.concat(t.content);...
    method ContentWithPathComponent (line 1) | ContentWithPathComponent(t){if(t.isIndex)return t.index>=0&&t.index<th...
    method BuildStringOfHierarchy (line 1) | BuildStringOfHierarchy(){let t;if(0==arguments.length)return t=new m,t...
  class P (line 1) | class P extends p{toString(){return"Glue"}}
    method toString (line 1) | toString(){return"Glue"}
  class A (line 1) | class A extends p{get commandType(){return this._commandType}constructor...
    method commandType (line 1) | get commandType(){return this._commandType}
    method constructor (line 1) | constructor(t=A.CommandType.NotSet){super(),this._commandType=t}
    method Copy (line 1) | Copy(){return new A(this.commandType)}
    method EvalStart (line 1) | static EvalStart(){return new A(A.CommandType.EvalStart)}
    method EvalOutput (line 1) | static EvalOutput(){return new A(A.CommandType.EvalOutput)}
    method EvalEnd (line 1) | static EvalEnd(){return new A(A.CommandType.EvalEnd)}
    method Duplicate (line 1) | static Duplicate(){return new A(A.CommandType.Duplicate)}
    method PopEvaluatedValue (line 1) | static PopEvaluatedValue(){return new A(A.CommandType.PopEvaluatedValue)}
    method PopFunction (line 1) | static PopFunction(){return new A(A.CommandType.PopFunction)}
    method PopTunnel (line 1) | static PopTunnel(){return new A(A.CommandType.PopTunnel)}
    method BeginString (line 1) | static BeginString(){return new A(A.CommandType.BeginString)}
    method EndString (line 1) | static EndString(){return new A(A.CommandType.EndString)}
    method NoOp (line 1) | static NoOp(){return new A(A.CommandType.NoOp)}
    method ChoiceCount (line 1) | static ChoiceCount(){return new A(A.CommandType.ChoiceCount)}
    method Turns (line 1) | static Turns(){return new A(A.CommandType.Turns)}
    method TurnsSince (line 1) | static TurnsSince(){return new A(A.CommandType.TurnsSince)}
    method ReadCount (line 1) | static ReadCount(){return new A(A.CommandType.ReadCount)}
    method Random (line 1) | static Random(){return new A(A.CommandType.Random)}
    method SeedRandom (line 1) | static SeedRandom(){return new A(A.CommandType.SeedRandom)}
    method VisitIndex (line 1) | static VisitIndex(){return new A(A.CommandType.VisitIndex)}
    method SequenceShuffleIndex (line 1) | static SequenceShuffleIndex(){return new A(A.CommandType.SequenceShuff...
    method StartThread (line 1) | static StartThread(){return new A(A.CommandType.StartThread)}
    method Done (line 1) | static Done(){return new A(A.CommandType.Done)}
    method End (line 1) | static End(){return new A(A.CommandType.End)}
    method ListFromInt (line 1) | static ListFromInt(){return new A(A.CommandType.ListFromInt)}
    method ListRange (line 1) | static ListRange(){return new A(A.CommandType.ListRange)}
    method ListRandom (line 1) | static ListRandom(){return new A(A.CommandType.ListRandom)}
    method toString (line 1) | toString(){return this.commandType.toString()}
  class k (line 1) | class k{constructor(){this.container=null,this.index=-1,2===arguments.le...
    method constructor (line 1) | constructor(){this.container=null,this.index=-1,2===arguments.length&&...
    method Resolve (line 1) | Resolve(){return this.index<0?this.container:null==this.container?null...
    method isNull (line 1) | get isNull(){return null==this.container}
    method path (line 1) | get path(){return this.isNull?null:this.index>=0?this.container.path.P...
    method toString (line 1) | toString(){return this.container?"Ink Pointer -> "+this.container.path...
    method copy (line 1) | copy(){return new k(this.container,this.index)}
    method StartOf (line 1) | static StartOf(t){return new k(t,0)}
    method Null (line 1) | static get Null(){return new k(null,-1)}
  class I (line 1) | class I extends p{constructor(t){super(),this._targetPath=null,this._tar...
    method constructor (line 1) | constructor(t){super(),this._targetPath=null,this._targetPointer=k.Nul...
    method targetPath (line 1) | get targetPath(){if(null!=this._targetPath&&this._targetPath.isRelativ...
    method targetPath (line 1) | set targetPath(t){this._targetPath=t,this._targetPointer=k.Null}
    method targetPointer (line 1) | get targetPointer(){if(this._targetPointer.isNull){let t=this.ResolveP...
    method targetPathString (line 1) | get targetPathString(){return null==this.targetPath?null:this.CompactP...
    method targetPathString (line 1) | set targetPathString(t){this.targetPath=null==t?null:new e(t)}
    method hasVariableTarget (line 1) | get hasVariableTarget(){return null!=this.variableDivertName}
    method Equals (line 1) | Equals(t){let e=t;return e instanceof I&&this.hasVariableTarget==e.has...
    method toString (line 1) | toString(){if(this.hasVariableTarget)return"Divert(variable: "+this.va...
  class F (line 1) | class F extends p{constructor(t=!0){super(),this._pathOnChoice=null,this...
    method constructor (line 1) | constructor(t=!0){super(),this._pathOnChoice=null,this.hasCondition=!1...
    method pathOnChoice (line 1) | get pathOnChoice(){if(null!=this._pathOnChoice&&this._pathOnChoice.isR...
    method pathOnChoice (line 1) | set pathOnChoice(t){this._pathOnChoice=t}
    method choiceTarget (line 1) | get choiceTarget(){return null===this._pathOnChoice?d("ChoicePoint._pa...
    method pathStringOnChoice (line 1) | get pathStringOnChoice(){return null===this.pathOnChoice?d("ChoicePoin...
    method pathStringOnChoice (line 1) | set pathStringOnChoice(t){this.pathOnChoice=new e(t)}
    method flags (line 1) | get flags(){let t=0;return this.hasCondition&&(t|=1),this.hasStartCont...
    method flags (line 1) | set flags(t){this.hasCondition=(1&t)>0,this.hasStartContent=(2&t)>0,th...
    method toString (line 1) | toString(){if(null===this.pathOnChoice)return d("ChoicePoint.pathOnCho...
  class V (line 1) | class V extends p{constructor(t=null){super(),this.pathForCount=null,thi...
    method constructor (line 1) | constructor(t=null){super(),this.pathForCount=null,this.name=t}
    method containerForCount (line 1) | get containerForCount(){return null===this.pathForCount?null:this.Reso...
    method pathStringForCount (line 1) | get pathStringForCount(){return null===this.pathForCount?null:this.Com...
    method pathStringForCount (line 1) | set pathStringForCount(t){this.pathForCount=null===t?null:new e(t)}
    method toString (line 1) | toString(){if(null!=this.name)return"var("+this.name+")";return"read_c...
  class L (line 1) | class L extends p{constructor(t,e){super(),this.variableName=t||null,thi...
    method constructor (line 1) | constructor(t,e){super(),this.variableName=t||null,this.isNewDeclarati...
    method toString (line 1) | toString(){return"VarAssign to "+this.variableName}
  class R (line 1) | class R extends p{}
  class D (line 1) | class D extends p{constructor(){if(super(),this._name=null,this._numberO...
    method constructor (line 1) | constructor(){if(super(),this._name=null,this._numberOfParameters=0,th...
    method CallWithName (line 1) | static CallWithName(t){return new D(t)}
    method CallExistsWithName (line 1) | static CallExistsWithName(t){return this.GenerateNativeFunctionsIfNece...
    method name (line 1) | get name(){return null===this._name?d("NativeFunctionCall._name"):this...
    method name (line 1) | set name(t){this._name=t,this._isPrototype||(null===D._nativeFunctions...
    method numberOfParameters (line 1) | get numberOfParameters(){return this._prototype?this._prototype.number...
    method numberOfParameters (line 1) | set numberOfParameters(t){this._numberOfParameters=t}
    method Call (line 1) | Call(t){if(this._prototype)return this._prototype.Call(t);if(this.numb...
    method CallType (line 1) | CallType(t){let e=s(t[0],y),n=e.valueType,i=e,a=t.length;if(2==a||1==a...
    method CallBinaryListOperation (line 1) | CallBinaryListOperation(t){if(("+"==this.name||"-"==this.name)&&t[0]in...
    method CallListIncrementOperation (line 1) | CallListIncrementOperation(t){let e=s(t[0],O),n=s(t[1],T),a=new g;if(n...
    method CoerceValuesToSingleType (line 1) | CoerceValuesToSingleType(t){let e=i.Int,n=null;for(let a of t){let t=s...
    method Identity (line 1) | static Identity(t){return t}
    method GenerateNativeFunctionsIfNecessary (line 1) | static GenerateNativeFunctionsIfNecessary(){if(null==this._nativeFunct...
    method AddOpFuncForType (line 1) | AddOpFuncForType(t,e){null==this._operationFuncs&&(this._operationFunc...
    method AddOpToNativeFunc (line 1) | static AddOpToNativeFunc(t,e,n,i){if(null===this._nativeFunctions)retu...
    method AddIntBinaryOp (line 1) | static AddIntBinaryOp(t,e){this.AddOpToNativeFunc(t,2,i.Int,e)}
    method AddIntUnaryOp (line 1) | static AddIntUnaryOp(t,e){this.AddOpToNativeFunc(t,1,i.Int,e)}
    method AddFloatBinaryOp (line 1) | static AddFloatBinaryOp(t,e){this.AddOpToNativeFunc(t,2,i.Float,e)}
    method AddFloatUnaryOp (line 1) | static AddFloatUnaryOp(t,e){this.AddOpToNativeFunc(t,1,i.Float,e)}
    method AddStringBinaryOp (line 1) | static AddStringBinaryOp(t,e){this.AddOpToNativeFunc(t,2,i.String,e)}
    method AddListBinaryOp (line 1) | static AddListBinaryOp(t,e){this.AddOpToNativeFunc(t,2,i.List,e)}
    method AddListUnaryOp (line 1) | static AddListUnaryOp(t,e){this.AddOpToNativeFunc(t,1,i.List,e)}
    method toString (line 1) | toString(){return'Native "'+this.name+'"'}
  class M (line 1) | class M extends p{constructor(t){super(),this.text=t.toString()||""}toSt...
    method constructor (line 1) | constructor(t){super(),this.text=t.toString()||""}
    method toString (line 1) | toString(){return"# "+this.text}
  class G (line 1) | class G extends p{constructor(){super(...arguments),this.text="",this.in...
    method constructor (line 1) | constructor(){super(...arguments),this.text="",this.index=0,this.threa...
    method pathStringOnChoice (line 1) | get pathStringOnChoice(){return null===this.targetPath?d("Choice.targe...
    method pathStringOnChoice (line 1) | set pathStringOnChoice(t){this.targetPath=new e(t)}
  class B (line 1) | class B{constructor(t,e){this._name=t||"",this._items=null,this._itemNam...
    method constructor (line 1) | constructor(t,e){this._name=t||"",this._items=null,this._itemNameToVal...
    method name (line 1) | get name(){return this._name}
    method items (line 1) | get items(){if(null==this._items){this._items=new Map;for(let[t,e]of t...
    method ValueForItem (line 1) | ValueForItem(t){if(!t.itemName)return 0;let e=this._itemNameToValues.g...
    method ContainsItem (line 1) | ContainsItem(t){return!!t.itemName&&(t.originName==this.name&&this._it...
    method ContainsItemWithName (line 1) | ContainsItemWithName(t){return this._itemNameToValues.has(t)}
    method TryGetItemWithValue (line 1) | TryGetItemWithValue(t,e){for(let[e,n]of this._itemNameToValues)if(n==t...
    method TryGetValueForItem (line 1) | TryGetValueForItem(t,e){if(!t.itemName)return{result:0,exists:!1};let ...
  class W (line 1) | class W{constructor(t){this._lists=new Map,this._allUnambiguousListValue...
    method constructor (line 1) | constructor(t){this._lists=new Map,this._allUnambiguousListValueCache=...
    method lists (line 1) | get lists(){let t=[];for(let[e,n]of this._lists)t.push(n);return t}
    method TryListGetDefinition (line 1) | TryListGetDefinition(t,e){if(null===t)return{result:e,exists:!1};let n...
    method FindSingleItemListWithName (line 1) | FindSingleItemListWithName(t){if(null===t)return d("name");let e=this....
  class j (line 1) | class j{static ListToJArray(t){let e=[];for(let n of t)e.push(this.Runti...
    method ListToJArray (line 1) | static ListToJArray(t){let e=[];for(let n of t)e.push(this.RuntimeObje...
    method JArrayToRuntimeObjList (line 1) | static JArrayToRuntimeObjList(t,e=!1){let n=t.length;e&&n--;let i=[];f...
    method DictionaryRuntimeObjsToJObject (line 1) | static DictionaryRuntimeObjsToJObject(t){let e={};for(let[n,i]of t){le...
    method JObjectToDictionaryRuntimeObjs (line 1) | static JObjectToDictionaryRuntimeObjs(t){let e=new Map;for(let n in t)...
    method JObjectToIntDictionary (line 1) | static JObjectToIntDictionary(t){let e=new Map;for(let n in t)t.hasOwn...
    method IntDictionaryToJObject (line 1) | static IntDictionaryToJObject(t){let e={};for(let[n,i]of t)e[n]=l(i);r...
    method JTokenToRuntimeObject (line 1) | static JTokenToRuntimeObject(t){if("number"==typeof t&&!isNaN(t))retur...
    method RuntimeObjectToJToken (line 1) | static RuntimeObjectToJToken(t){let e=r(t,N);if(e)return this.Containe...
    method ContainerToJArray (line 1) | static ContainerToJArray(t){let e=this.ListToJArray(t.content),n=t.nam...
    method JArrayToContainer (line 1) | static JArrayToContainer(t){let e=new N;e.content=this.JArrayToRuntime...
    method JObjectToChoice (line 1) | static JObjectToChoice(t){let e=new G;return e.text=t.text.toString(),...
    method ChoiceToJObject (line 1) | static ChoiceToJObject(t){let e={};return e.text=t.text,e.index=t.inde...
    method InkListToJObject (line 1) | static InkListToJObject(t){let e=t.value;if(null===e)return d("rawList...
    method ListDefinitionsToJToken (line 1) | static ListDefinitionsToJToken(t){let e={};for(let n of t.lists){let t...
    method JTokenToListDefinitions (line 1) | static JTokenToListDefinitions(t){let e=t,n=[];for(let t in e)if(e.has...
  class J (line 1) | class J{constructor(){if(this._threadCounter=0,this._startOfRoot=k.Null,...
    method constructor (line 1) | constructor(){if(this._threadCounter=0,this._startOfRoot=k.Null,argume...
    method elements (line 1) | get elements(){return this.callStack}
    method depth (line 1) | get depth(){return this.elements.length}
    method currentElement (line 1) | get currentElement(){let t=this._threads[this._threads.length-1].calls...
    method currentElementIndex (line 1) | get currentElementIndex(){return this.callStack.length-1}
    method currentThread (line 1) | get currentThread(){return this._threads[this._threads.length-1]}
    method currentThread (line 1) | set currentThread(t){n.Assert(1==this._threads.length,"Shouldn't be di...
    method canPop (line 1) | get canPop(){return this.callStack.length>1}
    method Reset (line 1) | Reset(){this._threads=[],this._threads.push(new J.Thread),this._thread...
    method SetJsonToken (line 1) | SetJsonToken(t,e){this._threads.length=0;let n=t.threads;for(let t of ...
    method GetJsonToken (line 1) | GetJsonToken(){let t={},e=[];for(let t of this._threads)e.push(t.jsonT...
    method PushThread (line 1) | PushThread(){let t=this.currentThread.Copy();this._threadCounter++,t.t...
    method ForkThread (line 1) | ForkThread(){let t=this.currentThread.Copy();return this._threadCounte...
    method PopThread (line 1) | PopThread(){if(!this.canPopThread)throw new Error("Can't pop thread");...
    method canPopThread (line 1) | get canPopThread(){return this._threads.length>1&&!this.elementIsEvalu...
    method elementIsEvaluateFromGame (line 1) | get elementIsEvaluateFromGame(){return this.currentElement.type==a.Fun...
    method Push (line 1) | Push(t,e=0,n=0){let i=new J.Element(t,this.currentElement.currentPoint...
    method CanPop (line 1) | CanPop(t=null){return!!this.canPop&&(null==t||this.currentElement.type...
    method Pop (line 1) | Pop(t=null){if(!this.CanPop(t))throw new Error("Mismatched push/pop in...
    method GetTemporaryVariableWithName (line 1) | GetTemporaryVariableWithName(t,e=-1){-1==e&&(e=this.currentElementInde...
    method SetTemporaryVariable (line 1) | SetTemporaryVariable(t,e,n,i=-1){-1==i&&(i=this.currentElementIndex+1)...
    method ContextForVariableNamed (line 1) | ContextForVariableNamed(t){return this.currentElement.temporaryVariabl...
    method ThreadWithIndex (line 1) | ThreadWithIndex(t){return this._threads.filter(e=>{if(e.threadIndex==t...
    method callStack (line 1) | get callStack(){return this.currentThread.callstack}
    method callStackTrace (line 1) | get callStackTrace(){let t=new m;for(let e=0;e<this._threads.length;e+...
  class n (line 1) | class n{constructor(t,e,n=!1){this.evaluationStackHeightWhenPushed=0,thi...
    method constructor (line 1) | constructor(t,e,n=!1){this.evaluationStackHeightWhenPushed=0,this.func...
    method Copy (line 1) | Copy(){let t=new n(this.type,this.currentPointer,this.inExpressionEval...
  class i (line 1) | class i{constructor(){if(this.threadIndex=0,this.previousPointer=k.Null,...
    method constructor (line 1) | constructor(){if(this.threadIndex=0,this.previousPointer=k.Null,this.c...
    method Copy (line 1) | Copy(){let t=new i;t.threadIndex=this.threadIndex;for(let e of this.ca...
    method jsonToken (line 1) | get jsonToken(){let t={},e=[];for(let t of this.callstack){let n={};if...
  class q (line 1) | class q{constructor(t,e){this.variableChangedEventCallbacks=[],this._bat...
    method constructor (line 1) | constructor(t,e){this.variableChangedEventCallbacks=[],this._batchObse...
    method variableChangedEvent (line 1) | variableChangedEvent(t,e){for(let n of this.variableChangedEventCallba...
    method batchObservingVariableChanges (line 1) | get batchObservingVariableChanges(){return this._batchObservingVariabl...
    method batchObservingVariableChanges (line 1) | set batchObservingVariableChanges(t){if(this._batchObservingVariableCh...
    method callStack (line 1) | get callStack(){return this._callStack}
    method callStack (line 1) | set callStack(t){this._callStack=t}
    method $ (line 1) | $(t,e){if(void 0===e){let e=this._globalVariables.get(t);return void 0...
    method CopyFrom (line 1) | CopyFrom(t){if(this._globalVariables=new Map(t._globalVariables),this....
    method jsonToken (line 1) | get jsonToken(){return j.DictionaryRuntimeObjsToJObject(this._globalVa...
    method jsonToken (line 1) | set jsonToken(t){this._globalVariables=j.JObjectToDictionaryRuntimeObj...
    method TryGetDefaultVariableValue (line 1) | TryGetDefaultVariableValue(t){let e=v(this._defaultGlobalVariables,t,n...
    method GlobalVariableExistsWithName (line 1) | GlobalVariableExistsWithName(t){return this._globalVariables.has(t)}
    method GetVariableWithName (line 1) | GetVariableWithName(t,e=-1){let n=this.GetRawVariableWithName(t,e),i=r...
    method GetRawVariableWithName (line 1) | GetRawVariableWithName(t,e){let n=null;if(0==e||-1==e){let e=v(this._g...
    method ValueAtVariablePointer (line 1) | ValueAtVariablePointer(t){return this.GetVariableWithName(t.variableNa...
    method Assign (line 1) | Assign(t,e){let n=t.variableName;if(null===n)return d("name");let i=-1...
    method SnapshotDefaultGlobals (line 1) | SnapshotDefaultGlobals(){this._defaultGlobalVariables=new Map(this._gl...
    method RetainListOriginsForAssignment (line 1) | RetainListOriginsForAssignment(t,e){let n=s(t,O),i=s(e,O);n.value&&i.v...
    method SetGlobal (line 1) | SetGlobal(t,e){let n=v(this._globalVariables,t,null);if(n.exists&&O.Re...
    method ResolveVariablePointer (line 1) | ResolveVariablePointer(t){let e=t.contextIndex;-1==e&&(e=this.GetConte...
    method GetContextIndexOfVariableNamed (line 1) | GetContextIndexOfVariableNamed(t){return this._globalVariables.get(t)?...
    method ObserveVariableChange (line 1) | ObserveVariableChange(t){this.variableChangedEventCallbacks.push(t)}
  class K (line 1) | class K{constructor(t){this.seed=t%2147483647,this.seed<=0&&(this.seed+=...
    method constructor (line 1) | constructor(t){this.seed=t%2147483647,this.seed<=0&&(this.seed+=214748...
    method next (line 1) | next(){return this.seed=16807*this.seed%2147483647}
    method nextFloat (line 1) | nextFloat(){return(this.next()-1)/2147483646}
  class U (line 1) | class U{constructor(t){this.kInkSaveStateVersion=8,this.kMinCompatibleLo...
    method constructor (line 1) | constructor(t){this.kInkSaveStateVersion=8,this.kMinCompatibleLoadVers...
    method ToJson (line 1) | ToJson(t=!1){return JSON.stringify(this.jsonToken,null,t?2:0)}
    method toJson (line 1) | toJson(t=!1){return this.ToJson(t)}
    method LoadJson (line 1) | LoadJson(t){this.jsonToken=JSON.parse(t)}
    method VisitCountAtPathString (line 1) | VisitCountAtPathString(t){let e=v(this.visitCounts,t,null);return e.ex...
    method callstackDepth (line 1) | get callstackDepth(){return this.callStack.depth}
    method outputStream (line 1) | get outputStream(){return this._outputStream}
    method currentChoices (line 1) | get currentChoices(){return this.canContinue?[]:this._currentChoices}
    method generatedChoices (line 1) | get generatedChoices(){return this._currentChoices}
    method currentErrors (line 1) | get currentErrors(){return this._currentErrors}
    method currentWarnings (line 1) | get currentWarnings(){return this._currentWarnings}
    method variablesState (line 1) | get variablesState(){return this._variablesState}
    method evaluationStack (line 1) | get evaluationStack(){return this._evaluationStack}
    method visitCounts (line 1) | get visitCounts(){return this._visitCounts}
    method turnIndices (line 1) | get turnIndices(){return this._turnIndices}
    method currentTurnIndex (line 1) | get currentTurnIndex(){return this._currentTurnIndex}
    method currentPathString (line 1) | get currentPathString(){let t=this.currentPointer;return t.isNull?null...
    method currentPointer (line 1) | get currentPointer(){return this.callStack.currentElement.currentPoint...
    method currentPointer (line 1) | set currentPointer(t){this.callStack.currentElement.currentPointer=t.c...
    method previousPointer (line 1) | get previousPointer(){return this.callStack.currentThread.previousPoin...
    method previousPointer (line 1) | set previousPointer(t){this.callStack.currentThread.previousPointer=t....
    method canContinue (line 1) | get canContinue(){return!this.currentPointer.isNull&&!this.hasError}
    method hasError (line 1) | get hasError(){return null!=this.currentErrors&&this.currentErrors.len...
    method hasWarning (line 1) | get hasWarning(){return null!=this.currentWarnings&&this.currentWarnin...
    method currentText (line 1) | get currentText(){if(this._outputStreamTextDirty){let t=new m;for(let ...
    method CleanOutputWhitespace (line 1) | CleanOutputWhitespace(t){let e=new m,n=-1,i=0;for(let a=0;a<t.length;a...
    method currentTags (line 1) | get currentTags(){if(this._outputStreamTagsDirty){this._currentTags=[]...
    method inExpressionEvaluation (line 1) | get inExpressionEvaluation(){return this.callStack.currentElement.inEx...
    method inExpressionEvaluation (line 1) | set inExpressionEvaluation(t){this.callStack.currentElement.inExpressi...
    method GoToStart (line 1) | GoToStart(){this.callStack.currentElement.currentPointer=k.StartOf(thi...
    method Copy (line 1) | Copy(){let t=new U(this.story);return t.outputStream.push.apply(t.outp...
    method jsonToken (line 1) | get jsonToken(){let t,e={};for(let e of this._currentChoices){if(null=...
    method jsonToken (line 1) | set jsonToken(t){let n=t,i=n.inkSaveVersion;if(null==i)throw new C("in...
    method ResetErrors (line 1) | ResetErrors(){this._currentErrors=null,this._currentWarnings=null}
    method ResetOutput (line 1) | ResetOutput(t=null){this._outputStream.length=0,null!==t&&this._output...
    method PushToOutputStream (line 1) | PushToOutputStream(t){let e=r(t,_);if(null!==e){let t=this.TrySplittin...
    method PopFromOutputStream (line 1) | PopFromOutputStream(t){this.outputStream.splice(this.outputStream.leng...
    method TrySplittingHeadTailWhitespace (line 1) | TrySplittingHeadTailWhitespace(t){let e=t.value;if(null===e)return d("...
    method PushToOutputStreamIndividual (line 1) | PushToOutputStreamIndividual(t){let e=r(t,P),n=r(t,_),i=!0;if(e)this.T...
    method TrimNewlinesFromOutputStream (line 1) | TrimNewlinesFromOutputStream(){let t=-1,e=this._outputStream.length-1;...
    method RemoveExistingGlue (line 1) | RemoveExistingGlue(){for(let t=this._outputStream.length-1;t>=0;t--){l...
    method outputStreamEndsInNewline (line 1) | get outputStreamEndsInNewline(){if(this._outputStream.length>0)for(let...
    method outputStreamContainsContent (line 1) | get outputStreamContainsContent(){for(let t=0;t<this._outputStream.len...
    method inStringEvaluation (line 1) | get inStringEvaluation(){for(let t=this._outputStream.length-1;t>=0;t-...
    method PushEvaluationStack (line 1) | PushEvaluationStack(t){let e=r(t,O);if(e){let t=e.value;if(null===t)re...
    method PopEvaluationStack (line 1) | PopEvaluationStack(t){if(void 0===t){return u(this.evaluationStack.pop...
    method PeekEvaluationStack (line 1) | PeekEvaluationStack(){return this.evaluationStack[this.evaluationStack...
    method ForceEnd (line 1) | ForceEnd(){this.callStack.Reset(),this._currentChoices.length=0,this.c...
    method TrimWhitespaceFromFunctionEnd (line 1) | TrimWhitespaceFromFunctionEnd(){n.Assert(this.callStack.currentElement...
    method PopCallStack (line 1) | PopCallStack(t=null){this.callStack.currentElement.type==a.Function&&t...
    method SetChosenPath (line 1) | SetChosenPath(t,e){this._currentChoices.length=0;let n=this.story.Poin...
    method StartFunctionEvaluationFromGame (line 1) | StartFunctionEvaluationFromGame(t,e){this.callStack.Push(a.FunctionEva...
    method PassArgumentsToEvaluationStack (line 1) | PassArgumentsToEvaluationStack(t){if(null!=t)for(let e=0;e<t.length;e+...
    method TryExitFunctionEvaluationFromGame (line 1) | TryExitFunctionEvaluationFromGame(){return this.callStack.currentEleme...
    method CompleteFunctionEvaluationFromGame (line 1) | CompleteFunctionEvaluationFromGame(){if(this.callStack.currentElement....
    method AddError (line 1) | AddError(t,e){e?(null==this._currentWarnings&&(this._currentWarnings=[...
    method OutputStreamDirty (line 1) | OutputStreamDirty(){this._outputStreamTextDirty=!0,this._outputStreamT...
  class z (line 1) | class z{constructor(){this.startTime=void 0}get ElapsedMilliseconds(){re...
    method constructor (line 1) | constructor(){this.startTime=void 0}
    method ElapsedMilliseconds (line 1) | get ElapsedMilliseconds(){return void 0===this.startTime?0:(new Date)....
    method Start (line 1) | Start(){this.startTime=(new Date).getTime()}
    method Stop (line 1) | Stop(){this.startTime=void 0}
  class H (line 1) | class H extends p{constructor(){let t;super(),this.inkVersionCurrent=19,...
    method constructor (line 1) | constructor(){let t;super(),this.inkVersionCurrent=19,this.inkVersionM...
    method currentChoices (line 1) | get currentChoices(){let t=[];if(null===this._state)return d("this._st...
    method currentText (line 1) | get currentText(){return this.IfAsyncWeCant("call currentText since it...
    method currentTags (line 1) | get currentTags(){return this.IfAsyncWeCant("call currentTags since it...
    method currentErrors (line 1) | get currentErrors(){return this.state.currentErrors}
    method currentWarnings (line 1) | get currentWarnings(){return this.state.currentWarnings}
    method hasError (line 1) | get hasError(){return this.state.hasError}
    method hasWarning (line 1) | get hasWarning(){return this.state.hasWarning}
    method variablesState (line 1) | get variablesState(){return this.state.variablesState}
    method listDefinitions (line 1) | get listDefinitions(){return this._listDefinitions}
    method state (line 1) | get state(){return this._state}
    method StartProfiling (line 1) | StartProfiling(){}
    method EndProfiling (line 1) | EndProfiling(){}
    method ToJsonString (line 1) | ToJsonString(){let t=j.RuntimeObjectToJToken(this._mainContentContaine...
    method ResetState (line 1) | ResetState(){this.IfAsyncWeCant("ResetState"),this._state=new U(this),...
    method ResetErrors (line 1) | ResetErrors(){if(null===this._state)return d("this._state");this._stat...
    method ResetCallstack (line 1) | ResetCallstack(){if(this.IfAsyncWeCant("ResetCallstack"),null===this._...
    method ResetGlobals (line 1) | ResetGlobals(){if(this._mainContentContainer.namedContent.get("global ...
    method Continue (line 1) | Continue(){return this.ContinueAsync(0),this.currentText}
    method canContinue (line 1) | get canContinue(){return this.state.canContinue}
    method asyncContinueComplete (line 1) | get asyncContinueComplete(){return!this._asyncContinueActive}
    method ContinueAsync (line 1) | ContinueAsync(t){this._hasValidatedExternals||this.ValidateExternalBin...
    method ContinueInternal (line 1) | ContinueInternal(t=0){null!=this._profiler&&this._profiler.PreContinue...
    method ContinueSingleStep (line 1) | ContinueSingleStep(){if(null!=this._profiler&&this._profiler.PreStep()...
    method CalculateNewlineOutputStateChange (line 1) | CalculateNewlineOutputStateChange(t,e,n,i){if(null===t)return d("prevT...
    method ContinueMaximally (line 1) | ContinueMaximally(){this.IfAsyncWeCant("ContinueMaximally");let t=new ...
    method ContentAtPath (line 1) | ContentAtPath(t){return this.mainContentContainer.ContentAtPath(t)}
    method KnotContainerWithName (line 1) | KnotContainerWithName(t){let e=this.mainContentContainer.namedContent....
    method PointerAtPath (line 1) | PointerAtPath(t){if(0==t.length)return k.Null;let e=new k,n=t.length,i...
    method StateSnapshot (line 1) | StateSnapshot(){return this.state.Copy()}
    method RestoreStateSnapshot (line 1) | RestoreStateSnapshot(t){this._state=t}
    method Step (line 1) | Step(){let t=!0,e=this.state.currentPointer.copy();if(e.isNull)return;...
    method VisitContainer (line 1) | VisitContainer(t,e){t.countingAtStartOnly&&!e||(t.visitsShouldBeCounte...
    method VisitChangedContainersDueToDivert (line 1) | VisitChangedContainersDueToDivert(){let t=this.state.previousPointer.c...
    method ProcessChoice (line 1) | ProcessChoice(t){let e=!0;if(t.hasCondition){let t=this.state.PopEvalu...
    method IsTruthy (line 1) | IsTruthy(t){if(t instanceof y){let e=t;if(e instanceof w){let t=e;retu...
    method PerformLogicAndFlowControl (line 1) | PerformLogicAndFlowControl(t){if(null==t)return!1;if(t instanceof I){l...
    method ChoosePathString (line 1) | ChoosePathString(t,n=!0,i=[]){if(this.IfAsyncWeCant("call ChoosePathSt...
    method IfAsyncWeCant (line 1) | IfAsyncWeCant(t){if(this._asyncContinueActive)throw new Error("Can't "...
    method ChoosePath (line 1) | ChoosePath(t,e=!0){this.state.SetChosenPath(t,e),this.VisitChangedCont...
    method ChooseChoiceIndex (line 1) | ChooseChoiceIndex(t){t=t;let e=this.currentChoices;this.Assert(t>=0&&t...
    method HasFunction (line 1) | HasFunction(t){try{return null!=this.KnotContainerWithName(t)}catch(t)...
    method EvaluateFunction (line 1) | EvaluateFunction(t,e=[],n=!1){if(this.IfAsyncWeCant("evaluate a functi...
    method EvaluateExpression (line 1) | EvaluateExpression(t){let e=this.state.callStack.elements.length;this....
    method CallExternalFunction (line 1) | CallExternalFunction(t,e){if(null===t)return d("funcName");let n=this....
    method BindExternalFunctionGeneral (line 1) | BindExternalFunctionGeneral(t,e){this.IfAsyncWeCant("bind an external ...
    method TryCoerce (line 1) | TryCoerce(t){return t}
    method BindExternalFunction (line 1) | BindExternalFunction(t,e){this.Assert(null!=e,"Can't bind a null funct...
    method UnbindExternalFunction (line 1) | UnbindExternalFunction(t){this.IfAsyncWeCant("unbind an external a fun...
    method ValidateExternalBindings (line 1) | ValidateExternalBindings(){let t=null,e=null,n=arguments[1]||new Set;i...
    method ObserveVariable (line 1) | ObserveVariable(t,e){if(this.IfAsyncWeCant("observe a new variable"),n...
    method ObserveVariables (line 1) | ObserveVariables(t,e){for(let n=0,i=t.length;n<i;n++)this.ObserveVaria...
    method RemoveVariableObserver (line 1) | RemoveVariableObserver(t,e){if(this.IfAsyncWeCant("remove a variable o...
    method VariableStateDidChangeEvent (line 1) | VariableStateDidChangeEvent(t,e){if(null===this._variableObservers)ret...
    method globalTags (line 1) | get globalTags(){return this.TagsAtStartOfFlowContainerWithPathString(...
    method TagsForContentAtPath (line 1) | TagsForContentAtPath(t){return this.TagsAtStartOfFlowContainerWithPath...
    method TagsAtStartOfFlowContainerWithPathString (line 1) | TagsAtStartOfFlowContainerWithPathString(t){let n=new e(t),i=this.Cont...
    method BuildStringOfHierarchy (line 1) | BuildStringOfHierarchy(){let t=new m;return this.mainContentContainer....
    method BuildStringOfContainer (line 1) | BuildStringOfContainer(t){let e=new m;return t.BuildStringOfHierarchy(...
    method NextContent (line 1) | NextContent(){if(this.state.previousPointer=this.state.currentPointer....
    method IncrementContentPointer (line 1) | IncrementContentPointer(){let t=!0,e=this.state.callStack.currentEleme...
    method TryFollowDefaultInvisibleChoice (line 1) | TryFollowDefaultInvisibleChoice(){let t=this._state.currentChoices,e=t...
    method VisitCountForContainer (line 1) | VisitCountForContainer(t){if(null===t)return d("container");if(!t.visi...
    method IncrementVisitCountForContainer (line 1) | IncrementVisitCountForContainer(t){let e=0,n=t.path.toString();this.st...
    method RecordTurnIndexVisitToContainer (line 1) | RecordTurnIndexVisitToContainer(t){let e=t.path.toString();this.state....
    method TurnsSinceForContainer (line 1) | TurnsSinceForContainer(t){t.turnIndexShouldBeCounted||this.Error("TURN...
    method NextSequenceShuffleIndex (line 1) | NextSequenceShuffleIndex(){let t=r(this.state.PopEvaluationStack(),T);...
    method Error (line 1) | Error(t,e=!1){let n=new C(t);throw n.useEndLineNumber=e,n}
    method Warning (line 1) | Warning(t){this.AddError(t,!0)}
    method AddError (line 1) | AddError(t,e=!1,n=!1){let i=this.currentDebugMetadata,a=e?"WARNING":"E...
    method Assert (line 1) | Assert(t,e=null){if(0==t)throw null==e&&(e="Story assert"),new Error(e...
    method currentDebugMetadata (line 1) | get currentDebugMetadata(){let t,e=this.state.currentPointer;if(!e.isN...
    method mainContentContainer (line 1) | get mainContentContainer(){return this._temporaryEvaluationContainer?t...

FILE: static/mainink.js
  function loadStory (line 5) | function loadStory(storyName) {
  function showAfter (line 16) | function showAfter(delay, el) {
  function scrollToBottom (line 20) | function scrollToBottom() {
  function continueStory (line 38) | function continueStory() {

FILE: static/stopwatch.js
  function defineProperties (line 2) | function defineProperties(target, props) { for (var i = 0; i < props.len...
  function _classCallCheck (line 2) | function _classCallCheck(instance, Constructor) { if (!(instance instanc...
  function Stopwatch (line 3) | function Stopwatch(display, results) {
  function pad0 (line 94) | function pad0(value, count) {
  function clearChildren (line 102) | function clearChildren(node) {

FILE: static/wheel.js
  function custom_colors (line 4) | function custom_colors(n) {
  function spin (line 61) | function spin(d) {
  function rotTween (line 130) | function rotTween(to) {
Condensed preview — 16 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (168K chars).
[
  {
    "path": ".github/FUNDING.yml",
    "chars": 18,
    "preview": "github: dastergon\n"
  },
  {
    "path": ".github/workflows/deploy_to_gh_pages.yml",
    "chars": 695,
    "preview": "name: Deploy Wheel of Misfortune to GitHub Pages\n\non:\n  push:\n    branches:\n      - master\n\njobs:\n  deploy:\n    runs-on:"
  },
  {
    "path": ".gitignore",
    "chars": 37,
    "preview": "incidents/*.json\nincidents/*.jsonnet\n"
  },
  {
    "path": "LICENSE",
    "chars": 1069,
    "preview": "MIT License\n\nCopyright (c) 2018 Pavlos Ratis\n\nPermission is hereby granted, free of charge, to any person obtaining a co"
  },
  {
    "path": "README.md",
    "chars": 5059,
    "preview": "# Wheel of Misfortune\n**Wheel of Misfortune** is a game that aims to build confidence in on-call engineers via simulated"
  },
  {
    "path": "incidents/general_incidents.json.sample",
    "chars": 1276,
    "preview": "[\n    {\n        \"ID\": \"1\",\n        \"title\": \"Incident 1\",\n        \"scenario\": \"You received alerts that your MySQL maste"
  },
  {
    "path": "incidents/general_incidents.jsonnet.sample",
    "chars": 1216,
    "preview": "local incidents = [\n  { scenario: \"You've received an alert that your \" + database + ' server is killed...' }\n  for data"
  },
  {
    "path": "incidents/redis-story.json.sample",
    "chars": 6910,
    "preview": "{\"inkVersion\":19,\"root\":[[[\"^You've received an alert that your Redis server is killed...\",\"\\n\",[\"ev\",{\"^->\":\"0.g-0.2.$"
  },
  {
    "path": "index.html",
    "chars": 10051,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n    <meta charset=\"utf-8\">\n    <meta http-equiv=\"X-UA-Compatible\" content=\"IE=e"
  },
  {
    "path": "instructions.html",
    "chars": 11445,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n    <meta charset=\"utf-8\">\n    <meta http-equiv=\"X-UA-Compatible\" content=\"IE=e"
  },
  {
    "path": "static/ink.js",
    "chars": 100001,
    "preview": "!function(t,e){\"object\"==typeof exports&&\"undefined\"!=typeof module?e(exports):\"function\"==typeof define&&define.amd?def"
  },
  {
    "path": "static/mainink.js",
    "chars": 2941,
    "preview": "var story;\n\nvar storyContainer = document.querySelectorAll('#incident')[0];\n\nfunction loadStory(storyName) {\n    fetch(s"
  },
  {
    "path": "static/site.webmanifest",
    "chars": 263,
    "preview": "{\"name\":\"\",\"short_name\":\"\",\"icons\":[{\"src\":\"/android-chrome-192x192.png\",\"sizes\":\"192x192\",\"type\":\"image/png\"},{\"src\":\"/"
  },
  {
    "path": "static/stopwatch.js",
    "chars": 3951,
    "preview": "/* Kudos to Billy Brown: https://codepen.io/_Billy_Brown/pen/dbJeh */\nvar _createClass = function () { function definePr"
  },
  {
    "path": "static/styles.css",
    "chars": 11973,
    "preview": "html,\nbody {\n  width: 100%;\n  margin: 0;\n  padding: 0;\n\n}\n\nbody {\n    font-family: \"Roboto\", Helvetica, sans-serif;\n    "
  },
  {
    "path": "static/wheel.js",
    "chars": 4920,
    "preview": "/* This is a modified version of Jeremy Rue's Wheel of Fortune\n * http://bl.ocks.org/jrue/a2aaf36b3c096925ccbf */\n\nfunct"
  }
]

About this extraction

This page contains the full source code of the dastergon/wheel-of-misfortune GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 16 files (158.0 KB), approximately 46.6k tokens, and a symbol index with 514 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!