diff --git a/.gitignore b/.gitignore
index 6e422dc..a2ce5d6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,6 @@
.DS_Store
bgdocs.code-workspace
+node_modules
+
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000..52e093f
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,4 @@
+
+[submodule "doc-site/themes/docsy"]
+ path = doc-site/themes/docsy
+ url = https://github.com/google/docsy
diff --git a/doc-site/.github/dependabot.yml b/doc-site/.github/dependabot.yml
new file mode 100644
index 0000000..573e2f0
--- /dev/null
+++ b/doc-site/.github/dependabot.yml
@@ -0,0 +1,14 @@
+version: 2
+updates:
+- package-ecosystem: npm
+ directory: "/"
+ schedule:
+ interval: daily
+ time: '20:00'
+ open-pull-requests-limit: 10
+- package-ecosystem: bundler
+ directory: "/"
+ schedule:
+ interval: daily
+ time: '20:00'
+ open-pull-requests-limit: 10
diff --git a/doc-site/.gitignore b/doc-site/.gitignore
new file mode 100644
index 0000000..8afd9f0
--- /dev/null
+++ b/doc-site/.gitignore
@@ -0,0 +1,6 @@
+/public
+resources/
+node_modules/
+package-lock.json
+.hugo_build.lock
+*.bak
diff --git a/doc-site/.nvmrc b/doc-site/.nvmrc
new file mode 100644
index 0000000..b009dfb
--- /dev/null
+++ b/doc-site/.nvmrc
@@ -0,0 +1 @@
+lts/*
diff --git a/doc-site/Dockerfile b/doc-site/Dockerfile
new file mode 100644
index 0000000..f28f7b6
--- /dev/null
+++ b/doc-site/Dockerfile
@@ -0,0 +1,3 @@
+FROM klakegg/hugo:ext-alpine
+
+RUN apk add git
diff --git a/doc-site/assets/scss/_variables_project.scss b/doc-site/assets/scss/_variables_project.scss
new file mode 100644
index 0000000..2569027
--- /dev/null
+++ b/doc-site/assets/scss/_variables_project.scss
@@ -0,0 +1,6 @@
+/*
+
+Add styles or override variables from the theme here.
+
+*/
+
diff --git a/doc-site/config.toml b/doc-site/config.toml
new file mode 100644
index 0000000..e2a7e69
--- /dev/null
+++ b/doc-site/config.toml
@@ -0,0 +1,233 @@
+baseURL = "https://yahoo.github.io/bgdocs/docs/typescript/"
+title = "Behavior Graph"
+
+ignoreFiles = ["community", "blog", "about", "docs2", "ignore", "\\.bak$"]
+
+enableRobotsTXT = true
+
+# Hugo allows theme composition (and inheritance). The precedence is from left to right.
+theme = ["docsy"]
+
+# Will give values to .Lastmod etc.
+enableGitInfo = true
+
+# Language settings
+contentDir = "content/en"
+defaultContentLanguage = "en"
+defaultContentLanguageInSubdir = false
+# Useful when translating.
+enableMissingTranslationPlaceholders = true
+
+# Comment out to enable taxonomies in Docsy
+# disableKinds = ["taxonomy", "taxonomyTerm"]
+
+# You can add your own taxonomies
+[taxonomies]
+tag = "tags"
+category = "categories"
+
+[params.taxonomy]
+# set taxonomyCloud = [] to hide taxonomy clouds
+taxonomyCloud = ["tags", "categories"]
+
+# If used, must have same lang as taxonomyCloud
+taxonomyCloudTitle = ["Tag Cloud", "Categories"]
+
+# set taxonomyPageHeader = [] to hide taxonomies on the page headers
+taxonomyPageHeader = ["tags", "categories"]
+
+
+# Highlighting config
+pygmentsCodeFences = true
+pygmentsUseClasses = false
+# Use the new Chroma Go highlighter in Hugo.
+pygmentsUseClassic = false
+#pygmentsOptions = "linenos=table"
+# See https://help.farbox.com/pygments.html
+pygmentsStyle = "tango"
+
+# Configure how URLs look like per section.
+[permalinks]
+blog = "/:section/:year/:month/:day/:slug/"
+
+
+## Configuration for BlackFriday markdown parser: https://github.com/russross/blackfriday
+[blackfriday]
+plainIDAnchors = true
+hrefTargetBlank = true
+angledQuotes = false
+latexDashes = true
+
+# Image processing configuration.
+[imaging]
+resampleFilter = "CatmullRom"
+quality = 75
+anchor = "smart"
+
+[services]
+[services.googleAnalytics]
+# Comment out the next line to disable GA tracking. Also disables the feature described in [params.ui.feedback].
+#id = "UA-00000000-0"
+
+# Language configuration
+
+[languages]
+[languages.en]
+title = "Behavior Graph"
+description = "Behaior Graph Documentation"
+languageName ="English"
+# Weight used for sorting.
+weight = 1
+#[languages.no]
+#title = "Goldydocs"
+#description = "Docsy er operativsystem for skyen"
+#languageName ="Norsk"
+#contentDir = "content/no"
+#time_format_default = "02.01.2006"
+#time_format_blog = "02.01.2006"
+
+#[languages.fa]
+#title = "اسناد گلدی"
+#description = "یک نمونه برای پوسته داکسی"
+#languageName ="فارسی"
+#contentDir = "content/fa"
+#time_format_default = "2006.01.02"
+#time_format_blog = "2006.01.02"
+
+[markup]
+ [markup.goldmark]
+ [markup.goldmark.renderer]
+ unsafe = true
+ [markup.highlight]
+ # See a complete list of available styles at https://xyproto.github.io/splash/docs/all.html
+ style = "tango"
+ # Uncomment if you want your chosen highlight style used for code blocks without a specified language
+ guessSyntax = "false"
+
+# Everything below this are Site Params
+
+[security]
+ [security.exec]
+ allow = ['^dart-sass-embedded$', '^go$', '^npx$', '^postcss$', '^asciidoctor$']
+
+# Comment out if you don't want the "print entire section" link enabled.
+[outputs]
+section = ["HTML", "print", "RSS"]
+
+[params]
+copyright = "Yahoo"
+#privacy_policy = "https://policies.google.com/privacy"
+
+# First one is picked as the Twitter card image if not set on page.
+# images = ["images/project-illustration.png"]
+
+# Menu title if your navbar has a versions selector to access old versions of your site.
+# This menu appears only if you have at least one [params.versions] set.
+version_menu = "Releases"
+
+# Flag used in the "version-banner" partial to decide whether to display a
+# banner on every page indicating that this is an archived version of the docs.
+# Set this flag to "true" if you want to display the banner.
+archived_version = false
+
+# The version number for the version of the docs represented in this doc set.
+# Used in the "version-banner" partial to display a version number for the
+# current doc set.
+version = "1.0.0"
+
+# A link to latest version of the docs. Used in the "version-banner" partial to
+# point people to the main doc site.
+url_latest_version = "https://yahoo.github.io/bgdocs"
+
+# Repository configuration (URLs for in-page links to opening issues and suggesting changes)
+#github_repo = "https://github.com/google/docsy-example"
+# An optional link to a related project repo. For example, the sibling repository where your product code lives.
+#github_project_repo = "https://github.com/google/docsy"
+
+# Specify a value here if your content directory is not in your repo's root directory
+# github_subdir = ""
+
+# Uncomment this if you have a newer GitHub repo with "main" as the default branch,
+# or specify a new value if you want to reference another branch in your GitHub links
+# github_branch= "main"
+
+# Google Custom Search Engine ID. Remove or comment out to disable search.
+#gcs_engine_id = "d72aa9b2712488cc3"
+
+# Enable Algolia DocSearch
+algolia_docsearch = false
+
+# Enable Lunr.js offline search
+offlineSearch = false
+
+# Enable syntax highlighting and copy buttons on code blocks with Prism
+prism_syntax_highlighting = false
+
+# User interface configuration
+[params.ui]
+# Set to true to disable breadcrumb navigation.
+breadcrumb_disable = false
+# Set to true to disable the About link in the site footer
+footer_about_disable = false
+# Set to false if you don't want to display a logo (/assets/icons/logo.svg) in the top navbar
+navbar_logo = true
+# Set to true if you don't want the top navbar to be translucent when over a `block/cover`, like on the homepage.
+navbar_translucent_over_cover_disable = false
+# Enable to show the side bar menu in its compact state.
+sidebar_menu_compact = false
+# Set to true to hide the sidebar search box (the top nav search box will still be displayed if search is enabled)
+sidebar_search_disable = false
+
+# Adds a H2 section titled "Feedback" to the bottom of each doc. The responses are sent to Google Analytics as events.
+# This feature depends on [services.googleAnalytics] and will be disabled if "services.googleAnalytics.id" is not set.
+# If you want this feature, but occasionally need to remove the "Feedback" section from a single page,
+# add "hide_feedback: true" to the page's front matter.
+[params.ui.feedback]
+enable = false
+# The responses that the user sees after clicking "yes" (the page was helpful) or "no" (the page was not helpful).
+#yes = 'Glad to hear it! Please tell us how we can improve.'
+#no = 'Sorry to hear that. Please tell us how we can improve.'
+
+# Adds a reading time to the top of each doc.
+# If you want this feature, but occasionally need to remove the Reading time from a single page,
+# add "hide_readingtime: true" to the page's front matter
+[params.ui.readingtime]
+enable = false
+
+[params.links]
+# End user relevant links. These will show up on left side of footer and in the community page if you have one.
+#[[params.links.user]]
+ #name = "User mailing list"
+ #url = "https://example.org/mail"
+ #icon = "fa fa-envelope"
+ #desc = "Discussion and help from your fellow users"
+#[[params.links.user]]
+ #name ="Twitter"
+ #url = "https://example.org/twitter"
+ #icon = "fab fa-twitter"
+ #desc = "Follow us on Twitter to get the latest news!"
+#[[params.links.user]]
+ #name = "Stack Overflow"
+ #url = "https://example.org/stack"
+ #icon = "fab fa-stack-overflow"
+ #desc = "Practical questions and curated answers"
+# Developer relevant links. These will show up on right side of footer and in the community page if you have one.
+#[[params.links.developer]]
+ #name = "GitHub"
+ #url = "https://github.com/google/docsy"
+ #icon = "fab fa-github"
+ #desc = "Development takes place here!"
+#[[params.links.developer]]
+ #name = "Slack"
+ #url = "https://example.org/slack"
+ #icon = "fab fa-slack"
+ #desc = "Chat with other project developers"
+#[[params.links.developer]]
+ #name = "Developer mailing list"
+ #url = "https://example.org/mail"
+ #icon = "fa fa-envelope"
+ #desc = "Discuss development issues around the project"
+
+[params.mermaid]
+ enable = true
+
diff --git a/doc-site/content/en/_index.md b/doc-site/content/en/_index.md
new file mode 100644
index 0000000..5d810ba
--- /dev/null
+++ b/doc-site/content/en/_index.md
@@ -0,0 +1,89 @@
+---
+title: "Behavior Graph"
+linkTitle: "Documentation"
+weight: 20
+type: "docs"
+
+cascade:
+- type: "blog"
+ # set to false to include a blog section in the section nav along with docs
+ toc_root: true
+ _target:
+ path: "/blog/**"
+- type: "docs"
+ _target:
+ path: "/**"
+ kind: "page"
+- type: "docs"
+ _target:
+ path: "/**"
+ kind: "section"
+---
+
+Behavior Graph is a software architecture and state management library. It greatly enhances your ability to write complex user facing software and control systems. Broadly speaking, it belongs to the category of libraries which includes Redux, MobX, Rx (Reactive Extensions), and XState. It works by providing a specialized unit of composition which we call the __behavior__. Behaviors are simple blocks of code together with their dependency relationships.
+
+## Is it any good?
+
+Yes
+
+## Highlights
+
+* Minimal boilerplate
+* Scales from the simple to the very complex
+* Incremental adoption: works alongside existing code and frameworks
+* Handles state, events, and effects all in one
+* Multi-platform (Javascript/Typescript, Kotlin, Objective-C, Swift)
+
+We developed Behavior Graph to address our own complexity challenges while building an iOS video playing library which is used internally throughout the suite of native Yahoo mobile apps. After years of development and production usage, it has proven to be incredibly competent at scale. We have since ported it to multiple languages including Javascript/Typescript. It is less than 1500 lines of code and contains no external dependencies.
+
+Behavior Graph will particularly appeal to anyone with a willingness to rethink how we write software applications.
+
+## What does it look like?
+
+The below block of code implements a simple counter using Behavior Graph.
+It can increment the counter or reset it back to zero.
+
+About 70% of the concepts you need to work with Behavior Graph are contained in this one example.
+
+
+{{< highlight javascript >}}
+this.increment = this.moment();
+this.reset = this.moment();
+this.counter = this.state(0);
+
+this.behavior()
+ .demands(this.increment, this.reset)
+ .supplies(this.counter)
+ .runs(this => {
+ if (this.increment.justUpdated) {
+ this.counter.update(this.counter.value + 1);
+ } else if (this.reset.justUpdated) {
+ this.counter.update(0);
+ }
+ });
+{{< /highlight >}}
+
+A typical Behavior Graph program consists of dozens or hundreds of behaviors like this, each with its own responsibilities.
+The Behavior Graph library then ensures these behaviors are correctly specified and runs them at the correct time and in the correct order.
+At scale this is shockingly effective.
+
+## Is it for me?
+
+Behavior Graph is a general purpose library which you can use to organize the event driven logic in any program.
+It should also be of interest to anyone with an interest in software engineering and architectures.
+
+Specifically if you are working on any of these categories, you should definitely consider it:
+
+* Web apps
+* Mobile apps
+* Desktop Applications
+* User Interfaces
+* Control Systems
+* Robots
+* Games
+
+## Learning Behavior Graph
+
+While there are only a handful of basic concepts in Behavior Graph, it does require a shift in thinking.
+We recommend you start with the [Quick Start]({{< ref quickstart >}}) then work through the [Tutorials]({{< ref "tutorials/tutorial-1" >}}).
+They will help you understand how the pieces fit together.
diff --git a/doc-site/content/en/api.md b/doc-site/content/en/api.md
new file mode 100644
index 0000000..e836bf4
--- /dev/null
+++ b/doc-site/content/en/api.md
@@ -0,0 +1,599 @@
+---
+title: "API"
+weight: 40
+---
+# Types
+
+## Behavior
+
+A behavior is a block of code together with its dependency relationships (links).
+They are one of the two node types in a behavior graph.
+You define behaviors using the `behavior()` factory method of an Extent.
+
+Behaviors have both static and dynamic links.
+You provide static links when you create the behavior.
+Behavior Graph will update dynamic links per special methods on BehaviorBuilder or you can update them directly on a behavior.
+
+### `demands`
+
+* returns: `Set | null`
+* _read only property_
+
+The current set of all Resources which the behavior demands.
+
+### `extent`
+
+* returns: `Extent`
+* _read only property_
+
+A behavior always has an Extent with which it is created.
+
+### `setDynamicDemands()`
+
+* param: `newDemands: (Demandable | undefined)[] | null)`
+
+Provide an array of Demandables.
+`undefined` is also an element type to make for easier use of optional chaining.
+Providing `null` is equivalent to saying there are no dynamic demands.
+
+### `setDynamicSupplies()`
+
+* param: `newSupplies: (Resource | undefined)[] | null)`
+
+Provide an array of Resources to supply.
+`undefined` is also an element type to make for easier use of optional chaining.
+Providing `null` is equivalent to saying there are no dynamic supplies.
+
+### `supplies`
+
+* returns: `Set | null`
+* _read only property_
+
+The current set of all Resources which the behavior supplies.
+
+
+## BehaviorBuilder
+
+BehaviorBuilder provides fluent API for constructing instances of a Behavior.
+You create an instance of a BehaviorBuilder using the `behavior()` method on Extent.
+All methods except `runs()` return the same instance of BehaviorBuilder so you can chain multiple optional clauses.
+
+Generic type T is the Extent subtype BehaviorBuilder is created with.
+
+
+{{< highlight javascript >}}
+// Create a single behavior with one demand and one supply.
+this.moment1 = this.moment();
+this.moment2 = this.moment();
+this.moment3 = this.moment();
+this.behavior()
+ .demands(this.moment1, this.moment2)
+ .supplies(this.moment3)
+ .runs(ext => {
+ if (ext.moment1.justUpdated || ext.moment2.justUpdatedTo(false)) {
+ ext.moment3.update();
+ }
+ });
+{{< /highlight >}}
+
+### `demands()`
+
+* params: `...demands: Demandable[]`
+* returns: `BehaviorBuilder`
+
+Provide a list of static demands this behavior will link to.
+
+### `dynamicDemands()`
+
+* param: `switches: Demandable[]`
+* param: `links: ((ext: T) => (Demandable | undefined)[] | null)`
+* param: `relinkingOrder?: RelinkingOrder`
+* returns: `BehaviorBuilder`
+
+This clause updates the dynamicDemands of this behavior based on the updating of other resources, the switches.
+When the switches update, the links parameter will be called which should return the list of new resources.
+
+We permit `undefined` in the list to make for easier optional chaining.
+Returning `null` is equivalent to setting no dynamicDemands.
+
+`relinkingOrder` parameter can optionally be set to `Extent.relinkingOrderSubsequent` which will update the demands _after_ the runs block is run.
+
+
+{{< highlight javascript >}}
+// This behavior will automatically demand the deleteButton resource of
+// the currentChild extent whenever it changes.
+this.currentChild = this.state(null);
+this.behavior()
+ .dynamicDemands([this.currentChild], ext => {
+ return [ext.currentChild.value?.deleteButton];
+ })
+ .runs(ext => {
+ if (ext.currentChild.value?.deleteButton.justUpdated) {
+ // do something in response
+ }
+ });
+{{< /highlight >}}
+
+### `dynamicSupplies()`
+
+* param: `switches: Demandable[]`
+* param: `links: ((ext: T) => (Resource | undefined)[] | null)`
+* returns: `BehaviorBuilder`
+
+This clause updates the dynamicSupplies similarly to the dynamicDemands clause.
+
+### `runs()`
+
+* param: `block: (ext: T) => void`
+* returns: `Behavior`
+
+This clause sets the block of code which will run when the behavior is activated.
+The parameter `ext` will be the instance of the Extent this behavior was created on.
+
+`runs()` will return the created behavior which will typically be ignored.
+
+### `supplies()`
+
+* params: `...supplies: Resource[]`
+* returns: `BehaviorBuilder`
+
+Provide a list of static supplies this behavior will link to.
+
+## Extent
+
+Extents are collections of resources and behaviors.
+You will create your own Extent subclasses to define your Behavior Graph functionality.
+
+
+{{< highlight javascript >}}
+// Define extent that toggles state on a switch
+class MyExtent extends Extent {
+ constructor(graph) {
+ super(graph);
+
+ this.toggleSwitch = this.moment();
+ this.currentState = this.state(false);
+
+ this.behavior()
+ .demands(this.toggleSwitch)
+ .supplies(this.currentState)
+ .runs(ext => {
+ this.currentState.update(!this.currentState.value);
+ });
+ }
+}
+
+// Create instance of MyExtent and add it to the graph
+let myGraph = new Graph();
+let main = new MyExtent(myGraph);
+main.addToGraphWithAction();
+{{< /highlight >}}
+
+### `action()`
+
+* param `action: (ext: this) => void`
+* param `debugName?: string`
+
+Calls the `action()` method on the underlying Graph object.
+Contains an additional `ext` parameter which will be this Extent instance.
+
+### `async actionAsync()`
+
+* param `action: (ext: this) => void`
+* param `debugName?: string`
+* returns: `Promise`
+
+Calls the `actionAsync()` method on the underlying Graph object.
+Contains an additional `ext` parameter which will be this Extent instance.
+
+### `addChildLifetime()`
+
+* param: `extent: Extent`
+
+Adds the parameter to list of child lifetimes.
+An extent with child lifetimes is guaranteed to be part of the graph while the child is.
+Behaviors in child extents are permitted to have static links to resources in the parent.
+
+### `addedToGraph`
+
+* returns `State`
+* _read only_ property
+
+Every extent comes with this state resource.
+It updates to `true` when the extent is added to the graph.
+
+### `addedToGraphWhen`
+
+* returns `number | null`
+* _read only_ property
+
+The sequence number of the event the Extent was added to the Graph.
+It is null if it has not been added or once it is removed.
+
+### `addToGraph()`
+
+Adds this extent to the graph.
+Behavior Graph will only interact with resources and behaviors after their extent has been added.
+
+### `addToGraphWithAction()`
+
+* param: `debugName?: string`
+
+Syntactic sugar for creating a new action and calling `addToGraph()`.
+`debugName` is passed to the action.
+
+### `behavior()`
+
+Creates a BehaviorBuilder instance.
+
+### `debugName`
+
+* returns `string | null`
+* _read write_ property
+
+You can define a runtime debugName for your instances to aid in debugging.
+It defaults to the name of your Extent subclass.
+
+### `new Extent()`
+
+* param: `graph: Graph`
+* constructor
+
+An Extent must be initialized with a Graph.
+You must call super() with the graph in your overridden constructor.
+
+### `graph`
+
+* returns `Graph`
+* _read only_ property
+
+The graph on which this extent was created.
+
+### `moment()`
+
+* param `debugName?: string`
+
+Factory method to create a moment resource.
+By default the `debugName` will be the name of the property that points to this resource.
+
+### `removeFromGraph()`
+
+* param `strategy?: ExtentRemoveStrategy`
+
+After an extent is removed from the graph its resource and behaviors will no longer interact with other extents in the graph.
+
+Extents must be removed in a manner that is consistent with their lifetimes.
+* All extents with a unified lifetime must be removed during the same event.
+* All extents that have a parent lifetime must not remain in the graph longer than their parent.
+
+Providing `Extent.removeContainedLifetimes` as the strategy parameter will automatically remove all extents with the unified or child lifetimes.
+
+### `removeFromGraphWithAction()`
+
+* param: `strategy?: ExtentRemoveStrategy`
+* param: `debugName?: string`
+
+Syntactic sugar for creating a new action and calling `removeFromGraph()`.
+`debugName` is passed to the action.
+
+### `resource()`
+
+* param `debugName?: string`
+
+Factory method to create a resource.
+By default the `debugName` will be the name of the property that points to this resource.
+
+### `sideEffect()`
+
+* param `block: (ext: this) => void`
+* param `debugName?: string`
+
+Calls the `sideEffect()` method on the underlying Graph object.
+Contains an additional `ext` parameter
+
+### `state()`
+
+* param `initialState: T`
+* param `debugName?: string`
+
+Factory method to create a state resource.
+By default the `debugName` will be the name of the property that points to this resource.
+
+### `unifyLifetime()`
+
+* param: `extent: Extent`
+
+Combines the lifetime of this extent with that of the parameter.
+Extents with unified lifetimes are guaranteed to be part of the graph for the same period of time.
+They are permitted to have static links between them.
+
+## Graph
+
+### `action()`
+
+* param `block: () => void`
+* param `debugName?: string`
+
+Creates a synchronous action on the graph.
+By default the debugName will be derived from the set of resources that are updated inside the action block.
+
+### `async actionAsync()`
+
+* param `block: () => void`
+* param `debugName?: string`
+* returns: `Promise`
+
+Creates an action that will run asynchronously.
+
+### `currentBehavior`
+
+* returns `Behavior | null`
+* _read only_ property
+
+Returns the currently running behavior or null if otherwise.
+
+### `currentEvent`
+
+* returns `GraphEvent | null`
+* _read only_ property
+
+Returns the current GraphEvent if the graph is running an event or null otherwise.
+
+### `dateProvider`
+
+* returns: `GraphDateProvider`
+* _read write_ property
+
+The default dateProvider returns `Date.now()` which is the source of `timestamp` on GraphEvent instances.
+Overriding is primarily useful for controlling values during testing.
+
+### `debugCycleForBehavior()`
+
+* param `behavior: Behavior`
+* returns: `Resource[]`
+
+Used during debugging as an aid when there are dependency cycles.
+The returned array contains the sequence of Resource objects which will result in a dependency cycle including this behavior.
+The array will be empty if there is not a cycle.
+
+### `lastEvent`
+
+* returns `GraphEvent`
+* _read only_ property
+
+Returns the last GraphEvent that completed.
+It starts as `GraphEvent.initialEvent`.
+
+### `sideEffect()`
+
+* param: `block: () => void`
+* param: `debugName?: string`
+
+Creates a block of code that will run during the side effect phase of the event.
+
+## GraphEvent
+
+### `sequence`
+
+* returns: `number`
+* _read only_ property
+
+Each GraphEvent is assigned a monotonically increasing number for each event run on the graph.
+You can use this information to quickly determine the order resources update.
+
+### `timestamp`
+
+* returns: `Date`
+* _read only_ property
+
+Each GraphEvent is given a timestamp according to the registered DateProvider given to a graph instance.
+It defaults to `Date.now()`.
+
+## Moment
+
+_extends Resource_
+
+A Moment is a type of Resource for tracking information that exists at a moment in time.
+Button presses or network call returns are examples of Moments.
+
+Moments optionally have values associated with them.
+The payload of a network call return is a possible value for a moment.
+Those values are reset to `undefined` at the end of the event.
+
+### `event`
+
+* returns `GraphEvent | null`
+* _read only_ property
+
+Returns the GraphEvent of the most recent time the moment was updated.
+It is `null` if it has never been updated.
+
+### `justUpdated`
+
+* returns: `boolean`
+* _read only_ property
+
+Returns true if the moment updated during this event.
+
+### `justUpdatedTo()`
+
+* param `value: T`
+* returns `boolean`
+
+Returns true if the moment was justUpdated and the value `==` the parameter.
+If you wish to use something different than `==` you can implement your own check as this method is syntactic sugar.
+
+### `update()`
+
+* param `value: T | undefined`
+
+Marks the moment as justUpdated.
+If a value is provided, it will be set on the moment for reading.
+
+### `updateWithAction()`
+
+* param `value: T | undefined`
+* param `debugName? : string`
+
+Syntactic sugar for calling `action()` on the underlying graph and calling `update()` on the moment.
+
+### `value`
+
+* returns `T`
+* _read only_ property
+
+Returns the value stored in the moment if it was updated during this event.
+It is `undefined` if it was not updated or outside of an event.
+
+Moments do not necessarily have a value.
+They will not if they were not given one in their `update()` method.
+
+## Resource
+
+The base class for State and Moment.
+Prefer those types in almost all circumstances.
+Wherever you see "resource" in this document, assume we are referring to instances of State and Moment.
+
+Resource has minimal functionality.
+Using instances of this base class directly is useful when forcing a certain ordering relationship between behaviors.
+
+### `debugName`
+
+* returns `string | null`
+* _read write_ property
+
+Assignable name for use during debugging.
+
+### `extent`
+
+* returns `Extent`
+* _read only_ property
+
+All resources belong to an Extent.
+
+### `graph`
+
+* returns `Graph`
+* _read only_ property
+
+All resources belong to a Graph.
+
+### `order`
+
+* returns `Demandable`
+* _read only_ property
+
+A behavior can also demand `resource.order` which tells the behavior not to activate when the resource updates.
+
+### `suppliedBy`
+
+* returns `Behavior | null`
+* _read only_ property
+
+If the resource is supplied by a behavior it will be returned, null otherwise.
+
+## State
+
+_extends Resource_
+
+A State is a type of resource for storing information over a period of time.
+Its value will persist into the future until it is updated.
+
+All States must be given an initial value when created.
+
+### `event`
+
+* returns `GraphEvent`
+* _read only_ property
+
+The last time the State was updated.
+Will return `GraphEvent.initialEvent` for its initial value before it has been updated.
+
+### `justUpdated`
+
+* returns `boolean`
+* _read only_ property
+
+Returns true if the state was updated during this event.
+
+### `justUpdatedTo()`
+
+* param: `toState: T`
+* returns: `boolean`
+
+Returns true if the state was updated during this event and toState parameter `==` value.
+
+### `justUpdatedFrom()`
+
+* param: `fromState: T`
+* returns: `boolean`
+
+Returns true if the state was updated during this event and the fromState parameter `==` the value it had before updating.
+
+### `justUpdatedToFrom()`
+
+* param: `toState: T`
+* param: `fromState: T`
+* returns: `boolean`
+
+A combination of `justUpdatedTo()` and `justUpdatedFrom()`
+
+### `traceEvent`
+
+* returns: `GraphEvent`
+* _read only_ property
+
+What was the value of the `event` property at the beginning of the current event.
+
+### `traceValue`
+
+* returns: `T`
+* _read only_ property
+
+What was the value of the `value` property at the beginning of the current event.
+
+### `updateWithAction()`
+
+* param: `newValue: T`
+* param: `debugName?: string`
+
+Equivalent to calling `action()` on the underlying Graph instance and `update()` on the State object.
+
+### `update()`
+
+* param: `newValue: T`
+
+Checks to see if the newValue parameter `!==` the current value, and if so updates it to that new value.
+
+### `updateForce()`
+
+* param: `newValue: T`
+
+Updates value to the newValue even if they are the same.
+
+### `value`
+
+* returns: `T`
+* _read only_ property
+
+The current underlying value.
+
+# Interfaces
+
+## Demandable
+
+What a behavior can demand. A sealed opaque type which includes:
+* Instances of Resource and its subclasses State and Moment
+* The object returned by `.order` on an instance of Resource
+
+There are no other Demandable types and it is not open for extension.
+
+## DateProvider
+
+Optional override for default `dateProvier` on Graph instance.
+
+Implement a type with a single method:
+
+`now(): Date`
+
+
diff --git a/doc-site/content/en/api.md.bak b/doc-site/content/en/api.md.bak
new file mode 100644
index 0000000..a804a31
--- /dev/null
+++ b/doc-site/content/en/api.md.bak
@@ -0,0 +1,588 @@
+---
+title: "API"
+---
+# Types
+
+## Behavior
+
+A behavior is a block of code together with its dependency relationships (links).
+They are one of the two node types in a behavior graph.
+You define behaviors using the `behavior()` factory method of an Extent.
+
+Behaviors have both static and dynamic links.
+You provide static links when you create the behavior.
+Behavior Graph will update dynamic links per special methods on BehaviorBuilder or you can update them directly on a behavior.
+
+### `demands`
+
+* returns: `Set | null`
+* _read only property_
+
+The current set of all Resoruces which the behavior demands.
+
+### `extent`
+
+* returns: `Extent`
+* _read only property_
+
+A behavior always has an Extent with which it is created.
+
+### `setDynamicDemands()`
+
+* param: `newDemands: (Demandable | undefined)[] | null)`
+
+Provide an array of Demandables.
+`undefined` is also an element type to make for easier use of optional chaining.
+Providing `null` is equivalent to saying there are no dynamic demands.
+
+### `setDynamicSupplies()`
+
+* param: `newSupplies: (Resource | undefined)[] | null)`
+
+Provide an array of Resources to supply.
+`undefined` is also an element type to make for easier use of optional chaining.
+Providing `null` is equivalent to saying there are no dynamic supplies.
+
+### `supplies`
+
+* returns: `Set | null`
+* _read only property_
+
+The current set of all Resources which the behavior supplies.
+
+
+## BehaviorBuilder
+
+BehaviorBuilder provides fluent API for constructing instances of a Behavior.
+You create an instance of a BehaviorBuilder using the `behavior()` method on Extent.
+All methods except `runs()` return the same instance of BehaviorBuilder so you can chain multiple optional clauses.
+
+Generic type T is the Extent subtype BehaviorBuilder is created with.
+
+
+{{< highlight javascript >}}
+// Create a single behavior with one demand and one supply.
+this.moment1 = this.moment();
+this.moment2 = this.moment();
+this.moment3 = this.moment();
+this.behavior()
+ .demands(this.moment1, this.moment2)
+ .supplies(this.moment3)
+ .runs(ext => {
+ if (ext.moment1.justUpdated || ext.moment2.justUpdatedTo(false)) {
+ ext.moment3.update();
+ }
+ });
+{{< /highlight >}}
+
+### `demands()`
+
+* params: `...demands: Demandable[]`
+* returns: `BehaviorBuilder`
+
+Provide a list of static demands this behavior will link to.
+
+### `dynamicDemands()`
+
+* param: `switches: Demandable[]`
+* param: `links: ((ext: T) => (Demandable | undefined)[] | null)`
+* param: `relinkingOrder?: RelinkingOrder`
+* returns: `BehaviorBuilder`
+
+This clause updates the dynamicDemands of this behavior based on the updating of other resources, the switches.
+When the switches update, the links parameter will be called which should return the list of new resources.
+
+We permit `undefined` in the list to make for easier optional chaning.
+Returning `null` is equivalent to setting no dynamicDemands.
+
+`relinkingOrder` parameter can optionally be set to `Extent.relinkingOrderSubsequent` which will update the demands _after_ the runs block is run.
+
+
+{{< highlight javascript >}}
+// This behavior will automatically demand the deleteButton resource of
+// the currentChild extent whenever it changes.
+this.currentChild = this.state(null);
+this.behavior()
+ .dynamicDemands([this.currentChild], ext => {
+ return [ext.currentChild.value?.deleteButton];
+ })
+ .runs(ext => {
+ if (ext.currentChild.value?.deleteButton.justUpdated) {
+ // do something in response
+ }
+ });
+{{< /highlight >}}
+
+### `dynamicSupplies()`
+
+* param: `switches: Demandable[]`
+* param: `links: ((ext: T) => (Resource | undefined)[] | null)`
+* returns: `BehaviorBuilder`
+
+This clause updates the dynamicSupplies similarly to the dynamicDemands clause.
+
+### `runs()`
+
+* param: `block: (ext: T) => void`
+* returns: `Behavior`
+
+This clause sets the block of code which will run when the behavior is activated.
+The parameter `ext` will be the instance of the Extent this behavior was created on.
+
+`runs()` will return the created behavior which will typically be ignored.
+
+### `supplies()`
+
+* params: `...supplies: Resource[]`
+* returns: `BehaviorBuilder`
+
+Provide a list of static supplies this behavior will link to.
+
+## Extent
+
+Extents are collections of resources and behaviors.
+You will create your own Extent subclasses to define your Behavior Graph functionality.
+
+{{< highlight javascript >}}
+// Define extent that toggles state on a switch
+class MyExtent extends Extent {
+ constructor(graph) {
+ super(graph);
+
+ this.toggleSwitch = this.moment();
+ this.onState = this.state(false);
+
+ this.behavior()
+ .demands(this.toggleSwitch)
+ .supplies(this.onState)
+ .runs(ext => {
+ this.onState.update(!this.onState.value);
+ });
+ }
+}
+
+// Create instance of MyExtent and add it to the graph
+let myGraph = new Graph();
+let main = new MyExtent(myGraph);
+main.addToGraphWithAction();
+{{< /highlight >}}
+
+### `action()`
+
+* param `action: (ext: this) => void`
+* param `debugName?: string`
+
+Calls the `action()` method on the underlying Graph object.
+Contains an additional `ext` parameter.
+
+### `async actionAsync()`
+
+* param `action: (ext: this) => void`
+* param `debugName?: string`
+* returns: `Promise`
+
+Calls the `actionAsync()` method on the underlying Graph object.
+Contains an additional `ext` parameter.
+
+### `addChildLifetime()`
+
+* param: `extent: Extent`
+
+Adds the parameter to list of child lifetimes.
+An extent extents with child lifetimes is guarantee to be part of the graph while the child is.
+Behaviors in Child extents are permitted to have static links to resources in the parent.
+
+### `addedToGraph`
+
+* returns `State`
+* _read only_ property
+
+Every extent comes with this state resource.
+It updates to `true` when the extent is added to the graph.
+
+### `addedToGraphWhen`
+
+* returns `number | null`
+* _read only_ proptery
+
+### `addToGraph()`
+
+Adds this extent to the graph.
+Behavior Graph will only interact with resoruces and behaviors after their extent has been added.
+
+### `addToGraphWithAction()`
+
+* param: `debugName?: string`
+
+Syntactic sugar for creating a new action and calling `addToGraph()`.
+`debugName` is passed to the action.
+
+### `behavior()`
+
+Creates a BehaviorBuilder instance.
+
+### `debugName`
+
+* returns `string | null`
+* _read write_ property
+
+You can define a runtime debugName for your instances to aid in debugging.
+It defaults to the name of your Extent subclass.
+
+### `new Extent()`
+
+* param: `graph: Graph`
+* constructor
+
+An Extent must be initialized with a Graph.
+You must call super() with the graph in your overriden constructor.
+
+### `graph`
+
+* returns `Graph`
+* _read only_ property
+
+The graph on which this extent was created.
+
+### `moment()`
+
+* param `debugName?: string`
+
+Factory method to create a moment resource.
+By default the `debugName` will be the name of the property that points to this resource.
+
+### `removeFromGraph()`
+
+* param `strategy?: ExtentRemoveStrategy`
+
+After an extent is removed from the graph its resource and behaviors will no longer interact with other extents in the graph.
+
+Extents must be removed in a manner that is consistent with their lifetimes.
+* All extents with a unified lifetime must be removed during the same event.
+* All extents that have a parent lifetime must not remain in the graph longer than their parent.
+
+Providing `Extent.removeContainedLifetimes` as the strategy parameter will automatically remove all extents with the unified or child lifetimes.
+
+### `removeFromGraphWithAction()`
+
+* param: `strategy?: ExtentRemoveStrategy`
+* param: `debugName?: string`
+
+Syntactic sugar for creating a new action and calling `removeFromGraph()`.
+`debugName` is passed to the action.
+
+### `resource()`
+
+* param `debugName?: string`
+
+Factory method to create a resource.
+By default the `debugName` will be the name of the property that points to this resource.
+
+### `sideEffect()`
+
+* param `block: (ext: this) => void`
+* param `debugName?: string`
+
+Calls the `sideEffect()` method on the underlying Graph object.
+Contains an additional `ext` parameter
+
+### `state()`
+
+* param `initialState: T`
+* param `debugName?: string`
+
+Factory method to create a state resource.
+By default the `debugName` will be the name of the property that points to this resource.
+
+### `unifyLifetime()`
+
+* param: `extent: Extent`
+
+Combines the lifetime of this Extent instance with that of the parameter.
+Extents with unified lifetimes are guaranteed to be part of the graph for the same period of time.
+They are permitted to have static links between them.
+
+## Graph
+
+### `action()`
+
+* param `block: () => void`
+* param `debugName?: string`
+
+Creates a synchronous action on the graph.
+By default the debugName will be derived from the set of resources that are updated inside the action block.
+
+### `async actionAsync()`
+
+* param `block: () => void`
+* param `debugName?: string`
+* returns: `Promise`
+
+Creates an action that will run asynchronously.
+
+### `currentBehavior`
+
+* returns `Behavior | null`
+* _read only_ property
+
+Returns the currently running behavior or null if otherwise.
+
+### `currentEvent`
+
+* returns `GraphEvent | null`
+* _read only_ property
+
+Returns the current GraphEvent if the graph is running an event, null otherwise.
+
+### `dateProvider`
+
+* returns: `GraphDateProvider`
+* _read write_ property
+
+The default dateProvider returns `Date.now()` which is the source of `timestamp` on GraphEvent instances.
+Overriding is primarily useful for controlling values during testing.
+
+### `debugCycleForBehavior()`
+
+* param `behavior: Behavior`
+* returns: `Resource[]`
+
+Used during debugging aid when there are dependency cycles.
+The returned array contains the sequence of Resource objects which will result in a dependency cycle including this behavior.
+The array will be empty if there is not a cycle.
+
+### `lastEvent`
+
+* returns `GraphEvent`
+* _read only_ property
+
+Returns the last GraphEvent that completed.
+It starts as `GraphEvent.initialEvent`.
+
+### `sideEffect()`
+
+* param: `block: () => void`
+* param: `debugName?: string`
+
+Creates a block of code that will run during the side effect phase of the event.
+
+## GraphEvent
+
+### `sequence`
+
+* returns: `number`
+* _read only_ property
+
+Each GraphEvent is assigned a monotonically increasing number for each event run on the graph.
+
+### `timestamp`
+
+* returns: `Date`
+* _read only_ property
+
+Each GraphEvent is given the timestamp according to the registered GraphDateProvider given to a graph instance.
+It defaults to `Date.now()`.
+
+## Moment
+
+_extends Resource_
+
+A Moment is a type of Resource for tracking information that exists at a moment in time.
+Button presses or network call returns are examples of Moments.
+
+Moments optionally have values associated with them.
+The payload of a network call return is a possible value for a moment.
+Those values are reset to `undefined` at the end of the event.
+
+### `event`
+
+* returns `GraphEvent | null`
+* _read only_ property
+* demanding behavior
+
+Returns the GraphEvent of the most recent time the moment was updated.
+It is `null` if it has never been updated.
+
+### `justUpdated`
+
+* returns: `boolean`
+* _read only_ property
+* demanding behavior
+
+Returns true if the moment updated during this event.
+
+### `justUpdatedTo()`
+
+* param `value: T`
+* returns `boolean`
+
+Returns true if the moment was justUpdated and the value `==` the parameter.
+If you wish to use something different than `==` you can implement your own check as this method is syntactic sugar.
+
+### `update()`
+
+* param `value: T | undefined`
+
+Marks the moment as justUpdated.
+If a value is provided, it will be set on the moment for reading.
+
+### `updateWithAction()`
+
+* param `value: T | undefined`
+* param `debugName? : string`
+
+Synctactic sugar for calling `action()` on the underlying graph and calling `update()` on the moment.
+
+### `value`
+
+* returns `T`
+* _read only_ property
+
+Returns the value stored in the moment if it was updated during this event.
+It is `undefined` if it was not updated or outside of an event.
+
+## Resource
+
+The base class for State and Moment.
+Prefer those types for modeling your problems.
+
+### `debugName`
+
+* returns `string | null`
+* _read write_ property
+
+Assignable name for use during debugging.
+
+### `extent`
+
+* returns `Extent`
+* _read only_ property
+
+All resources belong to an Extent.
+
+### `graph`
+
+* returns `Graph`
+* _read only_ property
+
+All resources belong to a Graph.
+
+### `order`
+
+* returns `Demandable`
+* _read only_ property
+
+A behavior can also demand `resource.order` which tells the behavior not to activate when the resource updates.
+
+### `suppliedBy`
+
+* returns `Behavior | null`
+* _read only_ property
+
+If the resource is supplied by a behavior it will be this return, null otherwise.
+
+## State
+
+_extends Resource_
+
+A State is a type of resource for storing information over a period of time.
+Its value will persist into the future until it is updated.
+
+All States must be given an initial value when created.
+
+### `event`
+
+* returns `GraphEvent`
+* _read only_ property
+
+The last time the State was updated.
+Its event property will be `GraphEvent.initialEvent` for its initial value before it is updated.
+
+### `justUpdated`
+
+* returns `boolean`
+* _read only_ property
+
+Returns true if the state was updated during this event.
+
+### `justUpdatedTo()`
+
+* param: `toState: T`
+* returns: `boolean`
+
+Returns true if the state was updated during this event and toState parameter `==` value.
+
+### `justUpdatedFrom()`
+
+* param: `fromState: T`
+* returns: `boolean`
+
+Returns true if the state was updated during this event and the fromState parameter `==` the value it had before updating.
+
+### `justUpdatedToFrom()`
+
+* param: `toState: T`
+* param: `fromState: T`
+* returns: `boolean`
+
+A combination of `justUpdatedTo()` and `justUpdatedFrom()`
+
+### `traceEvent`
+
+* returns: `GraphEvent`
+* _read only_ property
+
+What was the value of the `event` property at the beginning of the current event.
+
+### `traceValue`
+
+* returns: `T`
+* _read only_ property
+
+What was the value of the `value` property at the beginning of the current event.
+
+### `updateWithAction()`
+
+* param: `newValue: T`
+* param: `debugName?: string`
+
+Equivalent to calling `action()` on the underlying Graph instance and `update()` on the State object.
+
+### `update()`
+
+* param: `newValue: T`
+
+Checks to see if the newValue parameter `!=` the current value, and if so updates it to that new value.
+
+### `updateForce()`
+
+* param: `newValue: T`
+
+Updates value to the newValue even if they are the same.
+
+### `value`
+
+* returns: `T`
+* _read only_ property
+
+The current underlying value.
+
+# Interfaces
+
+## Demandable
+
+What a behavior can demand. A sealed opaque type which includes:
+* Instances of Resource and its subclasses State and Moment
+* The object returned by `.order` on an instance of Resource
+
+There are no other Demandable types and it is not open for extension.
+
+## GraphDateProvider
+
+Optional override for default `dateProvier` on Graph instance.
+
+Implement a type with a single method:
+
+`now(): Date`
+
+
diff --git a/doc-site/content/en/blog/_index.md b/doc-site/content/en/blog/_index.md
new file mode 100644
index 0000000..43820eb
--- /dev/null
+++ b/doc-site/content/en/blog/_index.md
@@ -0,0 +1,13 @@
+---
+title: "Docsy Blog"
+linkTitle: "Blog"
+menu:
+ main:
+ weight: 30
+---
+
+
+This is the **blog** section. It has two categories: News and Releases.
+
+Files in these directories will be listed in reverse chronological order.
+
diff --git a/doc-site/content/en/blog/news/_index.md b/doc-site/content/en/blog/news/_index.md
new file mode 100644
index 0000000..13d25ea
--- /dev/null
+++ b/doc-site/content/en/blog/news/_index.md
@@ -0,0 +1,8 @@
+
+---
+title: "News About Docsy"
+linkTitle: "News"
+weight: 20
+---
+
+
diff --git a/doc-site/content/en/blog/news/first-post/featured-sunset-get.png b/doc-site/content/en/blog/news/first-post/featured-sunset-get.png
new file mode 100644
index 0000000..db3373c
Binary files /dev/null and b/doc-site/content/en/blog/news/first-post/featured-sunset-get.png differ
diff --git a/doc-site/content/en/blog/news/first-post/index.md b/doc-site/content/en/blog/news/first-post/index.md
new file mode 100644
index 0000000..b6bfb47
--- /dev/null
+++ b/doc-site/content/en/blog/news/first-post/index.md
@@ -0,0 +1,46 @@
+---
+date: 2018-10-06
+title: "Easy documentation with Docsy"
+linkTitle: "Announcing Docsy"
+description: "The Docsy Hugo theme lets project maintainers and contributors focus on content, not on reinventing a website infrastructure from scratch"
+author: Riona MacNamara ([@rionam](https://twitter.com/bepsays))
+resources:
+- src: "**.{png,jpg}"
+ title: "Image #:counter"
+ params:
+ byline: "Photo: Riona MacNamara / CC-BY-CA"
+---
+
+**This is a typical blog post that includes images.**
+
+The front matter specifies the date of the blog post, its title, a short description that will be displayed on the blog landing page, and its author.
+
+## Including images
+
+Here's an image (`featured-sunset-get.png`) that includes a byline and a caption.
+
+{{< imgproc sunset Fill "600x300" >}}
+Fetch and scale an image in the upcoming Hugo 0.43.
+{{< /imgproc >}}
+
+The front matter of this post specifies properties to be assigned to all image resources:
+
+```
+resources:
+- src: "**.{png,jpg}"
+ title: "Image #:counter"
+ params:
+ byline: "Photo: Riona MacNamara / CC-BY-CA"
+```
+
+To include the image in a page, specify its details like this:
+
+```
+{{< imgproc sunset Fill "600x300" >}}
+Fetch and scale an image in the upcoming Hugo 0.43.
+{{< /imgproc >}}
+```
+
+The image will be rendered at the size and byline specified in the front matter.
+
+
diff --git a/doc-site/content/en/blog/news/second-post.md b/doc-site/content/en/blog/news/second-post.md
new file mode 100755
index 0000000..0ef58d7
--- /dev/null
+++ b/doc-site/content/en/blog/news/second-post.md
@@ -0,0 +1,245 @@
+
+---
+title: "The second blog post"
+linkTitle: "Second blog post"
+date: 2018-10-06
+description: >
+ A short lead description about this content page. Text here can also be **bold** or _italic_ and can even be split over multiple paragraphs.
+---
+
+Text can be **bold**, _italic_, or ~~strikethrough~~. [Links](https://github.com) should be blue with no underlines (unless hovered over).
+
+There should be whitespace between paragraphs. There should be whitespace between paragraphs. There should be whitespace between paragraphs. There should be whitespace between paragraphs.
+
+There should be whitespace between paragraphs. There should be whitespace between paragraphs. There should be whitespace between paragraphs. There should be whitespace between paragraphs.
+
+> There should be no margin above this first sentence.
+>
+> Blockquotes should be a lighter gray with a border along the left side in the secondary color.
+>
+> There should be no margin below this final sentence.
+
+## First Header
+
+This is a normal paragraph following a header. Bacon ipsum dolor sit amet t-bone doner shank drumstick, pork belly porchetta chuck sausage brisket ham hock rump pig. Chuck kielbasa leberkas, pork bresaola ham hock filet mignon cow shoulder short ribs biltong. Bacon ipsum dolor sit amet t-bone doner shank drumstick, pork belly porchetta chuck sausage brisket ham hock rump pig. Chuck kielbasa leberkas, pork bresaola ham hock filet mignon cow shoulder short ribs biltong. Bacon ipsum dolor sit amet t-bone doner shank drumstick, pork belly porchetta chuck sausage brisket ham hock rump pig. Chuck kielbasa leberkas, pork bresaola ham hock filet mignon cow shoulder short ribs biltong.
+
+
+
+Bacon ipsum dolor sit amet t-bone doner shank drumstick, pork belly porchetta chuck sausage brisket ham hock rump pig. Chuck kielbasa leberkas, pork bresaola ham hock filet mignon cow shoulder short ribs biltong.
+
+On big screens, paragraphs and headings should not take up the full container width, but we want tables, code blocks and similar to take the full width.
+
+Lorem markdownum tuta hospes stabat; idem saxum facit quaterque repetito
+occumbere, oves novem gestit haerebat frena; qui. Respicit recurvam erat:
+pignora hinc reppulit nos **aut**, aptos, ipsa.
+
+Meae optatos *passa est* Epiros utiliter *Talibus niveis*, hoc lata, edidit.
+Dixi ad aestum.
+
+## Header 2
+
+> This is a blockquote following a header. Bacon ipsum dolor sit amet t-bone doner shank drumstick, pork belly porchetta chuck sausage brisket ham hock rump pig. Chuck kielbasa leberkas, pork bresaola ham hock filet mignon cow shoulder short ribs biltong.
+
+### Header 3
+
+```
+This is a code block following a header.
+```
+
+#### Header 4
+
+* This is an unordered list following a header.
+* This is an unordered list following a header.
+* This is an unordered list following a header.
+
+##### Header 5
+
+1. This is an ordered list following a header.
+2. This is an ordered list following a header.
+3. This is an ordered list following a header.
+
+###### Header 6
+
+| What | Follows |
+|-----------|-----------------|
+| A table | A header |
+| A table | A header |
+| A table | A header |
+
+----------------
+
+There's a horizontal rule above and below this.
+
+----------------
+
+Here is an unordered list:
+
+* Salt-n-Pepa
+* Bel Biv DeVoe
+* Kid 'N Play
+
+And an ordered list:
+
+1. Michael Jackson
+2. Michael Bolton
+3. Michael Bublé
+
+And an unordered task list:
+
+- [x] Create a sample markdown document
+- [x] Add task lists to it
+- [ ] Take a vacation
+
+And a "mixed" task list:
+
+- [ ] Steal underpants
+- ?
+- [ ] Profit!
+
+And a nested list:
+
+* Jackson 5
+ * Michael
+ * Tito
+ * Jackie
+ * Marlon
+ * Jermaine
+* TMNT
+ * Leonardo
+ * Michelangelo
+ * Donatello
+ * Raphael
+
+Definition lists can be used with Markdown syntax. Definition terms are bold.
+
+Name
+: Godzilla
+
+Born
+: 1952
+
+Birthplace
+: Japan
+
+Color
+: Green
+
+
+----------------
+
+Tables should have bold headings and alternating shaded rows.
+
+| Artist | Album | Year |
+|-------------------|-----------------|------|
+| Michael Jackson | Thriller | 1982 |
+| Prince | Purple Rain | 1984 |
+| Beastie Boys | License to Ill | 1986 |
+
+If a table is too wide, it should scroll horizontally.
+
+| Artist | Album | Year | Label | Awards | Songs |
+|-------------------|-----------------|------|-------------|----------|-----------|
+| Michael Jackson | Thriller | 1982 | Epic Records | Grammy Award for Album of the Year, American Music Award for Favorite Pop/Rock Album, American Music Award for Favorite Soul/R&B Album, Brit Award for Best Selling Album, Grammy Award for Best Engineered Album, Non-Classical | Wanna Be Startin' Somethin', Baby Be Mine, The Girl Is Mine, Thriller, Beat It, Billie Jean, Human Nature, P.Y.T. (Pretty Young Thing), The Lady in My Life |
+| Prince | Purple Rain | 1984 | Warner Brothers Records | Grammy Award for Best Score Soundtrack for Visual Media, American Music Award for Favorite Pop/Rock Album, American Music Award for Favorite Soul/R&B Album, Brit Award for Best Soundtrack/Cast Recording, Grammy Award for Best Rock Performance by a Duo or Group with Vocal | Let's Go Crazy, Take Me With U, The Beautiful Ones, Computer Blue, Darling Nikki, When Doves Cry, I Would Die 4 U, Baby I'm a Star, Purple Rain |
+| Beastie Boys | License to Ill | 1986 | Mercury Records | noawardsbutthistablecelliswide | Rhymin & Stealin, The New Style, She's Crafty, Posse in Effect, Slow Ride, Girls, (You Gotta) Fight for Your Right, No Sleep Till Brooklyn, Paul Revere, Hold It Now, Hit It, Brass Monkey, Slow and Low, Time to Get Ill |
+
+----------------
+
+Code snippets like `var foo = "bar";` can be shown inline.
+
+Also, `this should vertically align` ~~`with this`~~ ~~and this~~.
+
+Code can also be shown in a block element.
+
+```
+foo := "bar";
+bar := "foo";
+```
+
+Code can also use syntax highlighting.
+
+```go
+func main() {
+ input := `var foo = "bar";`
+
+ lexer := lexers.Get("javascript")
+ iterator, _ := lexer.Tokenise(nil, input)
+ style := styles.Get("github")
+ formatter := html.New(html.WithLineNumbers())
+
+ var buff bytes.Buffer
+ formatter.Format(&buff, style, iterator)
+
+ fmt.Println(buff.String())
+}
+```
+
+```
+Long, single-line code blocks should not wrap. They should horizontally scroll if they are too long. This line should be long enough to demonstrate this.
+```
+
+Inline code inside table cells should still be distinguishable.
+
+| Language | Code |
+|-------------|--------------------|
+| Javascript | `var foo = "bar";` |
+| Ruby | `foo = "bar"{` |
+
+----------------
+
+Small images should be shown at their actual size.
+
+
+
+Large images should always scale down and fit in the content container.
+
+
+
+## Components
+
+### Alerts
+
+{{< alert >}}This is an alert.{{< /alert >}}
+{{< alert title="Note:" >}}This is an alert with a title.{{< /alert >}}
+{{< alert type="success" >}}This is a successful alert.{{< /alert >}}
+{{< alert type="warning" >}}This is a warning!{{< /alert >}}
+{{< alert type="warning" title="Warning!" >}}This is a warning with a title!{{< /alert >}}
+
+
+## Sizing
+
+Add some sections here to see how the ToC looks like. Bacon ipsum dolor sit amet t-bone doner shank drumstick, pork belly porchetta chuck sausage brisket ham hock rump pig. Chuck kielbasa leberkas, pork bresaola ham hock filet mignon cow shoulder short ribs biltong.
+
+### Parameters available
+
+Bacon ipsum dolor sit amet t-bone doner shank drumstick, pork belly porchetta chuck sausage brisket ham hock rump pig. Chuck kielbasa leberkas, pork bresaola ham hock filet mignon cow shoulder short ribs biltong.
+
+### Using pixels
+
+Bacon ipsum dolor sit amet t-bone doner shank drumstick, pork belly porchetta chuck sausage brisket ham hock rump pig. Chuck kielbasa leberkas, pork bresaola ham hock filet mignon cow shoulder short ribs biltong.
+
+### Using rem
+
+Bacon ipsum dolor sit amet t-bone doner shank drumstick, pork belly porchetta chuck sausage brisket ham hock rump pig. Chuck kielbasa leberkas, pork bresaola ham hock filet mignon cow shoulder short ribs biltong.
+
+## Memory
+
+Bacon ipsum dolor sit amet t-bone doner shank drumstick, pork belly porchetta chuck sausage brisket ham hock rump pig. Chuck kielbasa leberkas, pork bresaola ham hock filet mignon cow shoulder short ribs biltong.
+
+### RAM to use
+
+Bacon ipsum dolor sit amet t-bone doner shank drumstick, pork belly porchetta chuck sausage brisket ham hock rump pig. Chuck kielbasa leberkas, pork bresaola ham hock filet mignon cow shoulder short ribs biltong.
+
+### More is better
+
+Bacon ipsum dolor sit amet t-bone doner shank drumstick, pork belly porchetta chuck sausage brisket ham hock rump pig. Chuck kielbasa leberkas, pork bresaola ham hock filet mignon cow shoulder short ribs biltong.
+
+### Used RAM
+
+Bacon ipsum dolor sit amet t-bone doner shank drumstick, pork belly porchetta chuck sausage brisket ham hock rump pig. Chuck kielbasa leberkas, pork bresaola ham hock filet mignon cow shoulder short ribs biltong.
+
+
+
+```
+This is the final element on the page and there should be no margin below this.
+```
diff --git a/doc-site/content/en/blog/releases/_index.md b/doc-site/content/en/blog/releases/_index.md
new file mode 100644
index 0000000..b1d9eb4
--- /dev/null
+++ b/doc-site/content/en/blog/releases/_index.md
@@ -0,0 +1,8 @@
+
+---
+title: "New Releases"
+linkTitle: "Releases"
+weight: 20
+---
+
+
diff --git a/doc-site/content/en/blog/releases/in-depth-monoliths-detailed-spec.md b/doc-site/content/en/blog/releases/in-depth-monoliths-detailed-spec.md
new file mode 100755
index 0000000..ba8bd52
--- /dev/null
+++ b/doc-site/content/en/blog/releases/in-depth-monoliths-detailed-spec.md
@@ -0,0 +1,245 @@
+
+---
+title: "Another Great Release"
+linkTitle: "Release New Features"
+date: 2018-01-04
+description: >
+ A short lead description about this content page. Text here can also be **bold** or _italic_ and can even be split over multiple paragraphs.
+---
+
+Text can be **bold**, _italic_, or ~~strikethrough~~. [Links](https://github.com) should be blue with no underlines (unless hovered over).
+
+There should be whitespace between paragraphs. There should be whitespace between paragraphs. There should be whitespace between paragraphs. There should be whitespace between paragraphs.
+
+There should be whitespace between paragraphs. There should be whitespace between paragraphs. There should be whitespace between paragraphs. There should be whitespace between paragraphs.
+
+> There should be no margin above this first sentence.
+>
+> Blockquotes should be a lighter gray with a border along the left side in the secondary color.
+>
+> There should be no margin below this final sentence.
+
+## First Header
+
+This is a normal paragraph following a header. Bacon ipsum dolor sit amet t-bone doner shank drumstick, pork belly porchetta chuck sausage brisket ham hock rump pig. Chuck kielbasa leberkas, pork bresaola ham hock filet mignon cow shoulder short ribs biltong. Bacon ipsum dolor sit amet t-bone doner shank drumstick, pork belly porchetta chuck sausage brisket ham hock rump pig. Chuck kielbasa leberkas, pork bresaola ham hock filet mignon cow shoulder short ribs biltong. Bacon ipsum dolor sit amet t-bone doner shank drumstick, pork belly porchetta chuck sausage brisket ham hock rump pig. Chuck kielbasa leberkas, pork bresaola ham hock filet mignon cow shoulder short ribs biltong.
+
+
+
+Bacon ipsum dolor sit amet t-bone doner shank drumstick, pork belly porchetta chuck sausage brisket ham hock rump pig. Chuck kielbasa leberkas, pork bresaola ham hock filet mignon cow shoulder short ribs biltong.
+
+On big screens, paragraphs and headings should not take up the full container width, but we want tables, code blocks and similar to take the full width.
+
+Lorem markdownum tuta hospes stabat; idem saxum facit quaterque repetito
+occumbere, oves novem gestit haerebat frena; qui. Respicit recurvam erat:
+pignora hinc reppulit nos **aut**, aptos, ipsa.
+
+Meae optatos *passa est* Epiros utiliter *Talibus niveis*, hoc lata, edidit.
+Dixi ad aestum.
+
+## Header 2
+
+> This is a blockquote following a header. Bacon ipsum dolor sit amet t-bone doner shank drumstick, pork belly porchetta chuck sausage brisket ham hock rump pig. Chuck kielbasa leberkas, pork bresaola ham hock filet mignon cow shoulder short ribs biltong.
+
+### Header 3
+
+```
+This is a code block following a header.
+```
+
+#### Header 4
+
+* This is an unordered list following a header.
+* This is an unordered list following a header.
+* This is an unordered list following a header.
+
+##### Header 5
+
+1. This is an ordered list following a header.
+2. This is an ordered list following a header.
+3. This is an ordered list following a header.
+
+###### Header 6
+
+| What | Follows |
+|-----------|-----------------|
+| A table | A header |
+| A table | A header |
+| A table | A header |
+
+----------------
+
+There's a horizontal rule above and below this.
+
+----------------
+
+Here is an unordered list:
+
+* Salt-n-Pepa
+* Bel Biv DeVoe
+* Kid 'N Play
+
+And an ordered list:
+
+1. Michael Jackson
+2. Michael Bolton
+3. Michael Bublé
+
+And an unordered task list:
+
+- [x] Create a sample markdown document
+- [x] Add task lists to it
+- [ ] Take a vacation
+
+And a "mixed" task list:
+
+- [ ] Steal underpants
+- ?
+- [ ] Profit!
+
+And a nested list:
+
+* Jackson 5
+ * Michael
+ * Tito
+ * Jackie
+ * Marlon
+ * Jermaine
+* TMNT
+ * Leonardo
+ * Michelangelo
+ * Donatello
+ * Raphael
+
+Definition lists can be used with Markdown syntax. Definition terms are bold.
+
+Name
+: Godzilla
+
+Born
+: 1952
+
+Birthplace
+: Japan
+
+Color
+: Green
+
+
+----------------
+
+Tables should have bold headings and alternating shaded rows.
+
+| Artist | Album | Year |
+|-------------------|-----------------|------|
+| Michael Jackson | Thriller | 1982 |
+| Prince | Purple Rain | 1984 |
+| Beastie Boys | License to Ill | 1986 |
+
+If a table is too wide, it should scroll horizontally.
+
+| Artist | Album | Year | Label | Awards | Songs |
+|-------------------|-----------------|------|-------------|----------|-----------|
+| Michael Jackson | Thriller | 1982 | Epic Records | Grammy Award for Album of the Year, American Music Award for Favorite Pop/Rock Album, American Music Award for Favorite Soul/R&B Album, Brit Award for Best Selling Album, Grammy Award for Best Engineered Album, Non-Classical | Wanna Be Startin' Somethin', Baby Be Mine, The Girl Is Mine, Thriller, Beat It, Billie Jean, Human Nature, P.Y.T. (Pretty Young Thing), The Lady in My Life |
+| Prince | Purple Rain | 1984 | Warner Brothers Records | Grammy Award for Best Score Soundtrack for Visual Media, American Music Award for Favorite Pop/Rock Album, American Music Award for Favorite Soul/R&B Album, Brit Award for Best Soundtrack/Cast Recording, Grammy Award for Best Rock Performance by a Duo or Group with Vocal | Let's Go Crazy, Take Me With U, The Beautiful Ones, Computer Blue, Darling Nikki, When Doves Cry, I Would Die 4 U, Baby I'm a Star, Purple Rain |
+| Beastie Boys | License to Ill | 1986 | Mercury Records | noawardsbutthistablecelliswide | Rhymin & Stealin, The New Style, She's Crafty, Posse in Effect, Slow Ride, Girls, (You Gotta) Fight for Your Right, No Sleep Till Brooklyn, Paul Revere, Hold It Now, Hit It, Brass Monkey, Slow and Low, Time to Get Ill |
+
+----------------
+
+Code snippets like `var foo = "bar";` can be shown inline.
+
+Also, `this should vertically align` ~~`with this`~~ ~~and this~~.
+
+Code can also be shown in a block element.
+
+```
+foo := "bar";
+bar := "foo";
+```
+
+Code can also use syntax highlighting.
+
+```go
+func main() {
+ input := `var foo = "bar";`
+
+ lexer := lexers.Get("javascript")
+ iterator, _ := lexer.Tokenise(nil, input)
+ style := styles.Get("github")
+ formatter := html.New(html.WithLineNumbers())
+
+ var buff bytes.Buffer
+ formatter.Format(&buff, style, iterator)
+
+ fmt.Println(buff.String())
+}
+```
+
+```
+Long, single-line code blocks should not wrap. They should horizontally scroll if they are too long. This line should be long enough to demonstrate this.
+```
+
+Inline code inside table cells should still be distinguishable.
+
+| Language | Code |
+|-------------|--------------------|
+| Javascript | `var foo = "bar";` |
+| Ruby | `foo = "bar"{` |
+
+----------------
+
+Small images should be shown at their actual size.
+
+
+
+Large images should always scale down and fit in the content container.
+
+
+
+## Components
+
+### Alerts
+
+{{< alert >}}This is an alert.{{< /alert >}}
+{{< alert title="Note:" >}}This is an alert with a title.{{< /alert >}}
+{{< alert type="success" >}}This is a successful alert.{{< /alert >}}
+{{< alert type="warning" >}}This is a warning!{{< /alert >}}
+{{< alert type="warning" title="Warning!" >}}This is a warning with a title!{{< /alert >}}
+
+
+## Sizing
+
+Add some sections here to see how the ToC looks like. Bacon ipsum dolor sit amet t-bone doner shank drumstick, pork belly porchetta chuck sausage brisket ham hock rump pig. Chuck kielbasa leberkas, pork bresaola ham hock filet mignon cow shoulder short ribs biltong.
+
+### Parameters available
+
+Bacon ipsum dolor sit amet t-bone doner shank drumstick, pork belly porchetta chuck sausage brisket ham hock rump pig. Chuck kielbasa leberkas, pork bresaola ham hock filet mignon cow shoulder short ribs biltong.
+
+### Using pixels
+
+Bacon ipsum dolor sit amet t-bone doner shank drumstick, pork belly porchetta chuck sausage brisket ham hock rump pig. Chuck kielbasa leberkas, pork bresaola ham hock filet mignon cow shoulder short ribs biltong.
+
+### Using rem
+
+Bacon ipsum dolor sit amet t-bone doner shank drumstick, pork belly porchetta chuck sausage brisket ham hock rump pig. Chuck kielbasa leberkas, pork bresaola ham hock filet mignon cow shoulder short ribs biltong.
+
+## Memory
+
+Bacon ipsum dolor sit amet t-bone doner shank drumstick, pork belly porchetta chuck sausage brisket ham hock rump pig. Chuck kielbasa leberkas, pork bresaola ham hock filet mignon cow shoulder short ribs biltong.
+
+### RAM to use
+
+Bacon ipsum dolor sit amet t-bone doner shank drumstick, pork belly porchetta chuck sausage brisket ham hock rump pig. Chuck kielbasa leberkas, pork bresaola ham hock filet mignon cow shoulder short ribs biltong.
+
+### More is better
+
+Bacon ipsum dolor sit amet t-bone doner shank drumstick, pork belly porchetta chuck sausage brisket ham hock rump pig. Chuck kielbasa leberkas, pork bresaola ham hock filet mignon cow shoulder short ribs biltong.
+
+### Used RAM
+
+Bacon ipsum dolor sit amet t-bone doner shank drumstick, pork belly porchetta chuck sausage brisket ham hock rump pig. Chuck kielbasa leberkas, pork bresaola ham hock filet mignon cow shoulder short ribs biltong.
+
+
+
+```
+This is the final element on the page and there should be no margin below this.
+```
diff --git a/doc-site/content/en/code-example.md b/doc-site/content/en/code-example.md
new file mode 100644
index 0000000..6a10102
--- /dev/null
+++ b/doc-site/content/en/code-example.md
@@ -0,0 +1,148 @@
+---
+title: "Code Example"
+language: "typescript"
+weight: 30
+---
+
+We can illustrate how Behavior Graph code works in more detail through another example application, a typical login screen.
+_This is just a walkthrough, please use the tutorials for a complete guide to learning Behavior Graph._
+
+
+
+As a first feature, we would like the Login button to remain disabled until the user has entered both a reasonable email and password.
+If the user types in some password but an invalid email address (missing the '@' character, for example) the Login button will remain disabled.
+Once she corrects the email address by adding an '@' character, the Login button should immediately enable.
+
+In Behavior Graph, this unit of functionality constitutes a typical *behavior*.
+It looks like this
+
+{{< highlight javascript "hl_lines=1-9">}}
+this.behavior()
+ .supplies(this.loginEnabled)
+ .demands(this.email, this.password)
+ .runs(() => {
+ const emailValid = this.validEmailAddress(this.email.value);
+ const passwordValid = this.password.value.length > 0;
+ const enabled = emailValid && passwordValid;
+ this.loginEnabled.update(enabled);
+ });
+{{< /highlight >}}
+
+Behaviors have dependencies on units of information called *resources*.
+This behavior depends on two resources, `email` and `password`.
+They appear as parameter of the `demands()` clause of `behavior()`.
+This list is called the behavior's *demands*.
+Our behavior has read only access to these resources.
+
+As stated before, behaviors are never called directly.
+In specifying a behavior's demands, we are saying, _"whenever any of these resources updates (changes), then this behavior needs to run"._
+In our example, when either `email` or `password` (or both) update, this behavior will run in response.
+
+`email` and `password` are a specific type of resource called a *state resource* which is designed for saving and retrieving information.
+The contents of these state resources are available via their `value` property.
+
+The block of code specified in the behavior is the code that will run.
+A typical behavior uses normal code to perform its work.
+Here we check the validity of the email with a normal function.
+We determine if the Login button should be enabled using normal Boolean logic.
+
+This behavior is responsible for the enabled state of the Login button.
+This information is stored in another state resource called `loginEnabled`.
+We specify a behavior's responsibilities in the `supplies()` clause of `behavior()`.
+This list is called the behavior's *supplies*.
+A behavior can read and write the contents of its supplies.
+The contents of a state resource can be written to by calling its `{{< term "update-method" >}}` method.
+
+We can continue to develop our Login page by adding a second feature.
+When the user clicks the Login button and we are not already logging in, then we would like to enter into a logging in state.
+In order to prevent mistakes, when we are in a logging in state, we would also like the Login button to be disabled.
+
+To implement this new feature we introduce a second behavior and make a small change to our existing behavior.
+
+{{< highlight javascript "hl_lines=1-9 12 16">}}
+this.behavior()
+ .supplies(this.loggingIn)
+ .demands(this.loginClick)
+ .runs(() => {
+ if (this.loginClick.justUpdated && !this.loggingIn.value) {
+ this.loggingIn.update(true);
+ }
+ });
+
+this.behavior()
+ .supplies(this.loginEnabled)
+ .demands(this.email, this.password, this.loggingIn)
+ .runs(() => {
+ const emailValid = this.validEmailAddress(this.email.value);
+ const passwordValid = this.password.value.length > 0;
+ const enabled = emailValid && passwordValid & !this.loggingIn.value;
+ this.loginEnabled.update(enabled);
+ })
+{{< /highlight >}}
+
+The new behavior has one demand, `loginClick`.
+This is a second type of resource called a *moment resource*.
+Moments are designed to track momentary happenings such as a button click or network call returning.
+We can check if a moment has just happened by accessing its `{{< term "momentjustupdated-method" >}}` property.
+
+When the user clicks on the button, `loginClick` will update, and this new behavior will run.
+It performs a simple Boolean check to determine if the `loggingIn` state resource needs to update to `{{< term "true-bool" >}}`.
+It is allowed to update this resource because `loggingIn` is part of its supplies.
+
+We also modified our previous behavior to include `loggingIn` as one of its demands.
+This means it will run when the `loggingIn` resource updates as well as have permission to access the Boolean `value` of `loggingIn`.
+Now the state of `loginEnabled` depends on all three pieces of information: `email`, `password`, and `loggingIn`.
+
+## Actions
+
+
+
+Information comes into our system via *actions*.
+A typical UI library will provide some type of callback or event system to capture user inputs.
+In this example we will listen to a click handler to create a new action which updates the `loginClick` moment resource.
+
+{{< highlight javascript >}}
+this.loginButton.onClick = () => {
+ this.action(() => {
+ this.loginClick.update();
+ });
+};
+{{< /highlight >}}
+
+We would similarly connect `email` and `password` to their respective text fields.
+
+Once the user has entered a valid email and password, the Login button will enable.
+When the user subsequently clicks on the Login button, the behavior that supplies `loggingIn` will run.
+It will update the `loggingIn` resource to `{{< term "true-bool" >}}`.
+This in turn will cause the behavior that supplies `loginEnabled` behavior to run.
+It will update the `loginEnabled` resource to `{{< term "false-bool" >}}`.
+
+## Side Effects
+
+In order to perform real output to the UI library, we need to create a *side effect*.
+
+{{< highlight javascript "hl_lines=10-12">}}
+this.behavior()
+ .supplies(this.loginEnabled)
+ .demands(this.email, this.password, this.loggingIn)
+ .runs(() => {
+ const emailValid = this.validEmailAddress(this.email.value);
+ const passwordValid = this.password.value.length > 0;
+ const enabled = emailValid && passwordValid & !this.loggingIn.value;
+ this.loginEnabled.update(enabled);
+
+ this.sideEffect(() => {
+ this.loginButton.enabled = this.loginEnabled.value;
+ });
+ })
+{{< /highlight >}}
+
+Side effects are created directly inside behaviors.
+This side effect updates the `enabled` state of the `loginButton` based on the state of the `loginEnabled` resource.
+It does not run immediately, however.
+Behavior Graph defers the running of side effects until after all behaviors have run.
+Side effects are a practical way for Behavior Graph to create output while ensuring access to consistent state.
+
+This example covers the primary concepts when developing with Behavior Graph.
+There are, however, additional features that make Behavior Graph a practical software library.
+_Please work through the tutorials for a full coverage of the core features._
diff --git a/doc-site/content/en/community/_index.md b/doc-site/content/en/community/_index.md
new file mode 100644
index 0000000..cdade16
--- /dev/null
+++ b/doc-site/content/en/community/_index.md
@@ -0,0 +1,8 @@
+---
+title: Community
+menu:
+ main:
+ weight: 40
+---
+
+
diff --git a/doc-site/content/en/docs2/Concepts/_index.md b/doc-site/content/en/docs2/Concepts/_index.md
new file mode 100644
index 0000000..6cc6420
--- /dev/null
+++ b/doc-site/content/en/docs2/Concepts/_index.md
@@ -0,0 +1,17 @@
+---
+title: "Concepts"
+linkTitle: "Concepts"
+weight: 4
+description: >
+ What does your user need to understand about your project in order to use it - or potentially contribute to it?
+---
+
+{{% pageinfo %}}
+This is a placeholder page that shows you how to use this template site.
+{{% /pageinfo %}}
+
+For many projects, users may not need much information beyond the information in the [Overview](/docs/overview/), so this section is **optional**. However if there are areas where your users will need a more detailed understanding of a given term or feature in order to do anything useful with your project (or to not make mistakes when using it) put that information in this section. For example, you may want to add some conceptual pages if you have a large project with many components and a complex architecture.
+
+Remember to focus on what the user needs to know, not just what you think is interesting about your project! If they don’t need to understand your original design decisions to use or contribute to the project, don’t put them in, or include your design docs in your repo and link to them. Similarly, most users will probably need to know more about how features work when in use rather than how they are implemented. Consider a separate architecture page for more detailed implementation and system design information that potential project contributors can consult.
+
+
diff --git a/doc-site/content/en/docs2/Contribution guidelines/_index.md b/doc-site/content/en/docs2/Contribution guidelines/_index.md
new file mode 100644
index 0000000..bdf7078
--- /dev/null
+++ b/doc-site/content/en/docs2/Contribution guidelines/_index.md
@@ -0,0 +1,81 @@
+---
+title: "Contribution Guidelines"
+linkTitle: "Contribution Guidelines"
+weight: 10
+description: >
+ How to contribute to the docs
+---
+
+{{% pageinfo %}}
+These basic sample guidelines assume that your Docsy site is deployed using Netlify and your files are stored in GitHub. You can use the guidelines "as is" or adapt them with your own instructions: for example, other deployment options, information about your doc project's file structure, project-specific review guidelines, versioning guidelines, or any other information your users might find useful when updating your site. [Kubeflow](https://github.com/kubeflow/website/blob/master/README.md) has a great example.
+
+Don't forget to link to your own doc repo rather than our example site! Also make sure users can find these guidelines from your doc repo README: either add them there and link to them from this page, add them here and link to them from the README, or include them in both locations.
+{{% /pageinfo %}}
+
+We use [Hugo](https://gohugo.io/) to format and generate our website, the
+[Docsy](https://github.com/google/docsy) theme for styling and site structure,
+and [Netlify](https://www.netlify.com/) to manage the deployment of the site.
+Hugo is an open-source static site generator that provides us with templates,
+content organisation in a standard directory structure, and a website generation
+engine. You write the pages in Markdown (or HTML if you want), and Hugo wraps them up into a website.
+
+All submissions, including submissions by project members, require review. We
+use GitHub pull requests for this purpose. Consult
+[GitHub Help](https://help.github.com/articles/about-pull-requests/) for more
+information on using pull requests.
+
+## Quick start with Netlify
+
+Here's a quick guide to updating the docs. It assumes you're familiar with the
+GitHub workflow and you're happy to use the automated preview of your doc
+updates:
+
+1. Fork the [Goldydocs repo](https://github.com/google/docsy-example) on GitHub.
+1. Make your changes and send a pull request (PR).
+1. If you're not yet ready for a review, add "WIP" to the PR name to indicate
+ it's a work in progress. (**Don't** add the Hugo property
+ "draft = true" to the page front matter, because that prevents the
+ auto-deployment of the content preview described in the next point.)
+1. Wait for the automated PR workflow to do some checks. When it's ready,
+ you should see a comment like this: **deploy/netlify — Deploy preview ready!**
+1. Click **Details** to the right of "Deploy preview ready" to see a preview
+ of your updates.
+1. Continue updating your doc and pushing your changes until you're happy with
+ the content.
+1. When you're ready for a review, add a comment to the PR, and remove any
+ "WIP" markers.
+
+## Updating a single page
+
+If you've just spotted something you'd like to change while using the docs, Docsy has a shortcut for you:
+
+1. Click **Edit this page** in the top right hand corner of the page.
+1. If you don't already have an up to date fork of the project repo, you are prompted to get one - click **Fork this repository and propose changes** or **Update your Fork** to get an up to date version of the project to edit. The appropriate page in your fork is displayed in edit mode.
+1. Follow the rest of the [Quick start with Netlify](#quick-start-with-netlify) process above to make, preview, and propose your changes.
+
+## Previewing your changes locally
+
+If you want to run your own local Hugo server to preview your changes as you work:
+
+1. Follow the instructions in [Getting started](/docs/getting-started) to install Hugo and any other tools you need. You'll need at least **Hugo version 0.45** (we recommend using the most recent available version), and it must be the **extended** version, which supports SCSS.
+1. Fork the [Goldydocs repo](https://github.com/google/docsy-example) repo into your own project, then create a local copy using `git clone`. Don’t forget to use `--recurse-submodules` or you won’t pull down some of the code you need to generate a working site.
+
+ ```
+ git clone --recurse-submodules --depth 1 https://github.com/google/docsy-example.git
+ ```
+
+1. Run `hugo server` in the site root directory. By default your site will be available at http://localhost:1313/. Now that you're serving your site locally, Hugo will watch for changes to the content and automatically refresh your site.
+1. Continue with the usual GitHub workflow to edit files, commit them, push the
+ changes up to your fork, and create a pull request.
+
+## Creating an issue
+
+If you've found a problem in the docs, but you're not sure how to fix it yourself, please create an issue in the [Goldydocs repo](https://github.com/google/docsy-example/issues). You can also create an issue about a specific page by clicking the **Create Issue** button in the top right hand corner of the page.
+
+## Useful resources
+
+* [Docsy user guide](https://www.docsy.dev/docs/): All about Docsy, including how it manages navigation, look and feel, and multi-language support.
+* [Hugo documentation](https://gohugo.io/documentation/): Comprehensive reference for Hugo.
+* [Github Hello World!](https://guides.github.com/activities/hello-world/): A basic introduction to GitHub concepts and workflow.
+
+
diff --git a/doc-site/content/en/docs2/Examples/_index.md b/doc-site/content/en/docs2/Examples/_index.md
new file mode 100755
index 0000000..efc8cc8
--- /dev/null
+++ b/doc-site/content/en/docs2/Examples/_index.md
@@ -0,0 +1,17 @@
+
+---
+title: "Examples"
+linkTitle: "Examples"
+weight: 3
+date: 2017-01-05
+description: >
+ See your project in action!
+---
+
+{{% pageinfo %}}
+This is a placeholder page that shows you how to use this template site.
+{{% /pageinfo %}}
+
+Do you have any example **applications** or **code** for your users in your repo or elsewhere? Link to your examples here.
+
+
diff --git a/doc-site/content/en/docs2/Getting started/_index.md b/doc-site/content/en/docs2/Getting started/_index.md
new file mode 100644
index 0000000..5a3bbc7
--- /dev/null
+++ b/doc-site/content/en/docs2/Getting started/_index.md
@@ -0,0 +1,37 @@
+---
+categories: ["Examples", "Placeholders"]
+tags: ["test","docs"]
+title: "Getting Started"
+linkTitle: "Getting Started"
+weight: 2
+description: >
+ What does your user need to know to try your project?
+---
+
+{{% pageinfo %}}
+This is a placeholder page that shows you how to use this template site.
+{{% /pageinfo %}}
+
+Information in this section helps your user try your project themselves.
+
+* What do your users need to do to start using your project? This could include downloading/installation instructions, including any prerequisites or system requirements.
+
+* Introductory “Hello World” example, if appropriate. More complex tutorials should live in the Tutorials section.
+
+Consider using the headings below for your getting started page. You can delete any that are not applicable to your project.
+
+## Prerequisites
+
+Are there any system requirements for using your project? What languages are supported (if any)? Do users need to already have any software or tools installed?
+
+## Installation
+
+Where can your user find your project code? How can they install it (binaries, installable package, build from source)? Are there multiple options/versions they can install and how should they choose the right one for them?
+
+## Setup
+
+Is there any initial setup users need to do after installation to try your project?
+
+## Try it out!
+
+Can your users test their installation, for example by running a command or deploying a Hello World example?
diff --git a/doc-site/content/en/docs2/Getting started/example-page.md b/doc-site/content/en/docs2/Getting started/example-page.md
new file mode 100644
index 0000000..0bdd56c
--- /dev/null
+++ b/doc-site/content/en/docs2/Getting started/example-page.md
@@ -0,0 +1,241 @@
+---
+categories: ["Examples"]
+tags: ["test", "sample", "docs"]
+title: "Example Page"
+linkTitle: "Example Page"
+date: 2017-01-05
+description: >
+ A short lead description about this content page. It can be **bold** or _italic_ and can be split over multiple paragraphs.
+---
+
+{{% pageinfo %}}
+This is a placeholder page. Replace it with your own content.
+{{% /pageinfo %}}
+
+
+Text can be **bold**, _italic_, or ~~strikethrough~~. [Links](https://gohugo.io) should be blue with no underlines (unless hovered over).
+
+There should be whitespace between paragraphs. Vape migas chillwave sriracha poutine try-hard distillery. Tattooed shabby chic small batch, pabst art party heirloom letterpress air plant pop-up. Sustainable chia skateboard art party banjo cardigan normcore affogato vexillologist quinoa meggings man bun master cleanse shoreditch readymade. Yuccie prism four dollar toast tbh cardigan iPhone, tumblr listicle live-edge VHS. Pug lyft normcore hot chicken biodiesel, actually keffiyeh thundercats photo booth pour-over twee fam food truck microdosing banh mi. Vice activated charcoal raclette unicorn live-edge post-ironic. Heirloom vexillologist coloring book, beard deep v letterpress echo park humblebrag tilde.
+
+90's four loko seitan photo booth gochujang freegan tumeric listicle fam ugh humblebrag. Bespoke leggings gastropub, biodiesel brunch pug fashion axe meh swag art party neutra deep v chia. Enamel pin fanny pack knausgaard tofu, artisan cronut hammock meditation occupy master cleanse chartreuse lumbersexual. Kombucha kogi viral truffaut synth distillery single-origin coffee ugh slow-carb marfa selfies. Pitchfork schlitz semiotics fanny pack, ugh artisan vegan vaporware hexagon. Polaroid fixie post-ironic venmo wolf ramps **kale chips**.
+
+> There should be no margin above this first sentence.
+>
+> Blockquotes should be a lighter gray with a border along the left side in the secondary color.
+>
+> There should be no margin below this final sentence.
+
+## First Header 2
+
+This is a normal paragraph following a header. Knausgaard kale chips snackwave microdosing cronut copper mug swag synth bitters letterpress glossier **craft beer**. Mumblecore bushwick authentic gochujang vegan chambray meditation jean shorts irony. Viral farm-to-table kale chips, pork belly palo santo distillery activated charcoal aesthetic jianbing air plant woke lomo VHS organic. Tattooed locavore succulents heirloom, small batch sriracha echo park DIY af. Shaman you probably haven't heard of them copper mug, crucifix green juice vape *single-origin coffee* brunch actually. Mustache etsy vexillologist raclette authentic fam. Tousled beard humblebrag asymmetrical. I love turkey, I love my job, I love my friends, I love Chardonnay!
+
+Deae legum paulatimque terra, non vos mutata tacet: dic. Vocant docuique me plumas fila quin afuerunt copia haec o neque.
+
+On big screens, paragraphs and headings should not take up the full container width, but we want tables, code blocks and similar to take the full width.
+
+Scenester tumeric pickled, authentic crucifix post-ironic fam freegan VHS pork belly 8-bit yuccie PBR&B. **I love this life we live in**.
+
+
+## Second Header 2
+
+> This is a blockquote following a header. Bacon ipsum dolor sit amet t-bone doner shank drumstick, pork belly porchetta chuck sausage brisket ham hock rump pig. Chuck kielbasa leberkas, pork bresaola ham hock filet mignon cow shoulder short ribs biltong.
+
+### Header 3
+
+```
+This is a code block following a header.
+```
+
+Next level leggings before they sold out, PBR&B church-key shaman echo park. Kale chips occupy godard whatever pop-up freegan pork belly selfies. Gastropub Belinda subway tile woke post-ironic seitan. Shabby chic man bun semiotics vape, chia messenger bag plaid cardigan.
+
+#### Header 4
+
+* This is an unordered list following a header.
+* This is an unordered list following a header.
+* This is an unordered list following a header.
+
+##### Header 5
+
+1. This is an ordered list following a header.
+2. This is an ordered list following a header.
+3. This is an ordered list following a header.
+
+###### Header 6
+
+| What | Follows |
+|-----------|-----------------|
+| A table | A header |
+| A table | A header |
+| A table | A header |
+
+----------------
+
+There's a horizontal rule above and below this.
+
+----------------
+
+Here is an unordered list:
+
+* Liverpool F.C.
+* Chelsea F.C.
+* Manchester United F.C.
+
+And an ordered list:
+
+1. Michael Brecker
+2. Seamus Blake
+3. Branford Marsalis
+
+And an unordered task list:
+
+- [x] Create a Hugo theme
+- [x] Add task lists to it
+- [ ] Take a vacation
+
+And a "mixed" task list:
+
+- [ ] Pack bags
+- ?
+- [ ] Travel!
+
+And a nested list:
+
+* Jackson 5
+ * Michael
+ * Tito
+ * Jackie
+ * Marlon
+ * Jermaine
+* TMNT
+ * Leonardo
+ * Michelangelo
+ * Donatello
+ * Raphael
+
+Definition lists can be used with Markdown syntax. Definition headers are bold.
+
+Name
+: Godzilla
+
+Born
+: 1952
+
+Birthplace
+: Japan
+
+Color
+: Green
+
+
+----------------
+
+Tables should have bold headings and alternating shaded rows.
+
+| Artist | Album | Year |
+|-------------------|-----------------|------|
+| Michael Jackson | Thriller | 1982 |
+| Prince | Purple Rain | 1984 |
+| Beastie Boys | License to Ill | 1986 |
+
+If a table is too wide, it should scroll horizontally.
+
+| Artist | Album | Year | Label | Awards | Songs |
+|-------------------|-----------------|------|-------------|----------|-----------|
+| Michael Jackson | Thriller | 1982 | Epic Records | Grammy Award for Album of the Year, American Music Award for Favorite Pop/Rock Album, American Music Award for Favorite Soul/R&B Album, Brit Award for Best Selling Album, Grammy Award for Best Engineered Album, Non-Classical | Wanna Be Startin' Somethin', Baby Be Mine, The Girl Is Mine, Thriller, Beat It, Billie Jean, Human Nature, P.Y.T. (Pretty Young Thing), The Lady in My Life |
+| Prince | Purple Rain | 1984 | Warner Brothers Records | Grammy Award for Best Score Soundtrack for Visual Media, American Music Award for Favorite Pop/Rock Album, American Music Award for Favorite Soul/R&B Album, Brit Award for Best Soundtrack/Cast Recording, Grammy Award for Best Rock Performance by a Duo or Group with Vocal | Let's Go Crazy, Take Me With U, The Beautiful Ones, Computer Blue, Darling Nikki, When Doves Cry, I Would Die 4 U, Baby I'm a Star, Purple Rain |
+| Beastie Boys | License to Ill | 1986 | Mercury Records | noawardsbutthistablecelliswide | Rhymin & Stealin, The New Style, She's Crafty, Posse in Effect, Slow Ride, Girls, (You Gotta) Fight for Your Right, No Sleep Till Brooklyn, Paul Revere, Hold It Now, Hit It, Brass Monkey, Slow and Low, Time to Get Ill |
+
+----------------
+
+Code snippets like `var foo = "bar";` can be shown inline.
+
+Also, `this should vertically align` ~~`with this`~~ ~~and this~~.
+
+Code can also be shown in a block element.
+
+```
+foo := "bar";
+bar := "foo";
+```
+
+Code can also use syntax highlighting.
+
+```go
+func main() {
+ input := `var foo = "bar";`
+
+ lexer := lexers.Get("javascript")
+ iterator, _ := lexer.Tokenise(nil, input)
+ style := styles.Get("github")
+ formatter := html.New(html.WithLineNumbers())
+
+ var buff bytes.Buffer
+ formatter.Format(&buff, style, iterator)
+
+ fmt.Println(buff.String())
+}
+```
+
+```
+Long, single-line code blocks should not wrap. They should horizontally scroll if they are too long. This line should be long enough to demonstrate this.
+```
+
+Inline code inside table cells should still be distinguishable.
+
+| Language | Code |
+|-------------|--------------------|
+| Javascript | `var foo = "bar";` |
+| Ruby | `foo = "bar"{` |
+
+----------------
+
+Small images should be shown at their actual size.
+
+
+
+Large images should always scale down and fit in the content container.
+
+
+
+_The photo above of the Spruce Picea abies shoot with foliage buds: Bjørn Erik Pedersen, CC-BY-SA._
+
+
+## Components
+
+### Alerts
+
+{{< alert >}}This is an alert.{{< /alert >}}
+{{< alert title="Note" >}}This is an alert with a title.{{< /alert >}}
+{{% alert title="Note" %}}This is an alert with a title and **Markdown**.{{% /alert %}}
+{{< alert color="success" >}}This is a successful alert.{{< /alert >}}
+{{< alert color="warning" >}}This is a warning.{{< /alert >}}
+{{< alert color="warning" title="Warning" >}}This is a warning with a title.{{< /alert >}}
+
+
+## Another Heading
+
+Add some sections here to see how the ToC looks like. Bacon ipsum dolor sit amet t-bone doner shank drumstick, pork belly porchetta chuck sausage brisket ham hock rump pig. Chuck kielbasa leberkas, pork bresaola ham hock filet mignon cow shoulder short ribs biltong.
+
+### This Document
+
+Inguina genus: Anaphen post: lingua violente voce suae meus aetate diversi. Orbis unam nec flammaeque status deam Silenum erat et a ferrea. Excitus rigidum ait: vestro et Herculis convicia: nitidae deseruit coniuge Proteaque adiciam *eripitur*? Sitim noceat signa *probat quidem*. Sua longis *fugatis* quidem genae.
+
+
+### Pixel Count
+
+Tilde photo booth wayfarers cliche lomo intelligentsia man braid kombucha vaporware farm-to-table mixtape portland. PBR&B pickled cornhole ugh try-hard ethical subway tile. Fixie paleo intelligentsia pabst. Ennui waistcoat vinyl gochujang. Poutine salvia authentic affogato, chambray lumbersexual shabby chic.
+
+### Contact Info
+
+Plaid hell of cred microdosing, succulents tilde pour-over. Offal shabby chic 3 wolf moon blue bottle raw denim normcore poutine pork belly.
+
+
+### External Links
+
+Stumptown PBR&B keytar plaid street art, forage XOXO pitchfork selvage affogato green juice listicle pickled everyday carry hashtag. Organic sustainable letterpress sartorial scenester intelligentsia swag bushwick. Put a bird on it stumptown neutra locavore. IPhone typewriter messenger bag narwhal. Ennui cold-pressed seitan flannel keytar, single-origin coffee adaptogen occupy yuccie williamsburg chillwave shoreditch forage waistcoat.
+
+
+
+```
+This is the final element on the page and there should be no margin below this.
+```
diff --git a/doc-site/content/en/docs2/Overview/_index.md b/doc-site/content/en/docs2/Overview/_index.md
new file mode 100644
index 0000000..6a03756
--- /dev/null
+++ b/doc-site/content/en/docs2/Overview/_index.md
@@ -0,0 +1,38 @@
+---
+title: "Overview"
+linkTitle: "Overview"
+weight: 1
+description: >
+ Here's where your user finds out if your project is for them.
+---
+
+{{% pageinfo %}}
+This is a placeholder page that shows you how to use this template site.
+{{% /pageinfo %}}
+
+
+The Overview is where your users find out about your project. Depending on the size of your docset, you can have a separate overview page (like this one) or put your overview contents in the Documentation landing page (like in the Docsy User Guide).
+
+Try answering these questions for your user in this page:
+
+## What is it?
+
+Introduce your project, including what it does or lets you do, why you would use it, and its primary goal (and how it achieves it). This should be similar to your README description, though you can go into a little more detail here if you want.
+
+## Why do I want it?
+
+Help your user know if your project will help them. Useful information can include:
+
+* **What is it good for?**: What types of problems does your project solve? What are the benefits of using it?
+
+* **What is it not good for?**: For example, point out situations that might intuitively seem suited for your project, but aren't for some reason. Also mention known limitations, scaling issues, or anything else that might let your users know if the project is not for them.
+
+* **What is it *not yet* good for?**: Highlight any useful features that are coming soon.
+
+## Where should I go next?
+
+Give your users next steps from the Overview. For example:
+
+* [Getting Started](/docs/getting-started/): Get started with $project
+* [Examples](/docs/examples/): Check out some example code!
+
diff --git a/doc-site/content/en/docs2/Reference/_index.md b/doc-site/content/en/docs2/Reference/_index.md
new file mode 100644
index 0000000..f174fc0
--- /dev/null
+++ b/doc-site/content/en/docs2/Reference/_index.md
@@ -0,0 +1,14 @@
+---
+title: "Reference"
+linkTitle: "Reference"
+weight: 9
+description: >
+ Low level reference docs for your project.
+---
+
+{{% pageinfo %}}
+This is a placeholder page that shows you how to use this template site.
+{{% /pageinfo %}}
+
+If your project has an API, configuration, or other reference - anything that users need to look up that’s at an even lower level than a single task - put (or link to it) here. You can serve and link to generated reference docs created using Doxygen,
+Javadoc, or other doc generation tools by putting them in your `static/` directory. Find out more in [Adding static content](https://docsy.dev/docs/adding-content/content/#adding-static-content). For OpenAPI reference, Docsy also provides a [Swagger UI layout and shortcode](https://www.docsy.dev/docs/adding-content/shortcodes/#swaggerui) that renders [Swagger UI](https://swagger.io/tools/swagger-ui/) using any OpenAPI YAML or JSON file as source.
diff --git a/doc-site/content/en/docs2/Reference/parameter-reference.md b/doc-site/content/en/docs2/Reference/parameter-reference.md
new file mode 100644
index 0000000..0012c85
--- /dev/null
+++ b/doc-site/content/en/docs2/Reference/parameter-reference.md
@@ -0,0 +1,212 @@
+---
+title: "Parameter Reference"
+linkTitle: "Parameter Reference"
+date: 2017-01-05
+description: >
+ A short lead description about this content page. It can be **bold** or _italic_ and can be split over multiple paragraphs.
+---
+
+{{% pageinfo %}}
+This is a placeholder page. Replace it with your own content.
+{{% /pageinfo %}}
+
+Text can be **bold**, _italic_, or ~~strikethrough~~. [Links](https://gohugo.io) should be blue with no underlines (unless hovered over).
+
+There should be whitespace between paragraphs. Vape migas chillwave sriracha poutine try-hard distillery. Tattooed shabby chic small batch, pabst art party heirloom letterpress air plant pop-up. Sustainable chia skateboard art party banjo cardigan normcore affogato vexillologist quinoa meggings man bun master cleanse shoreditch readymade. Yuccie prism four dollar toast tbh cardigan iPhone, tumblr listicle live-edge VHS. Pug lyft normcore hot chicken biodiesel, actually keffiyeh thundercats photo booth pour-over twee fam food truck microdosing banh mi. Vice activated charcoal raclette unicorn live-edge post-ironic. Heirloom vexillologist coloring book, beard deep v letterpress echo park humblebrag tilde.
+
+90's four loko seitan photo booth gochujang freegan tumeric listicle fam ugh humblebrag. Bespoke leggings gastropub, biodiesel brunch pug fashion axe meh swag art party neutra deep v chia. Enamel pin fanny pack knausgaard tofu, artisan cronut hammock meditation occupy master cleanse chartreuse lumbersexual. Kombucha kogi viral truffaut synth distillery single-origin coffee ugh slow-carb marfa selfies. Pitchfork schlitz semiotics fanny pack, ugh artisan vegan vaporware hexagon. Polaroid fixie post-ironic venmo wolf ramps **kale chips**.
+
+> There should be no margin above this first sentence.
+>
+> Blockquotes should be a lighter gray with a border along the left side in the secondary color.
+>
+> There should be no margin below this final sentence.
+
+## First Header 2
+
+This is a normal paragraph following a header. Knausgaard kale chips snackwave microdosing cronut copper mug swag synth bitters letterpress glossier **craft beer**. Mumblecore bushwick authentic gochujang vegan chambray meditation jean shorts irony. Viral farm-to-table kale chips, pork belly palo santo distillery activated charcoal aesthetic jianbing air plant woke lomo VHS organic. Tattooed locavore succulents heirloom, small batch sriracha echo park DIY af. Shaman you probably haven't heard of them copper mug, crucifix green juice vape *single-origin coffee* brunch actually. Mustache etsy vexillologist raclette authentic fam. Tousled beard humblebrag asymmetrical. I love turkey, I love my job, I love my friends, I love Chardonnay!
+
+Deae legum paulatimque terra, non vos mutata tacet: dic. Vocant docuique me plumas fila quin afuerunt copia haec o neque.
+
+On big screens, paragraphs and headings should not take up the full container width, but we want tables, code blocks and similar to take the full width.
+
+Scenester tumeric pickled, authentic crucifix post-ironic fam freegan VHS pork belly 8-bit yuccie PBR&B. **I love this life we live in**.
+
+
+## Second Header 2
+
+> This is a blockquote following a header. Bacon ipsum dolor sit amet t-bone doner shank drumstick, pork belly porchetta chuck sausage brisket ham hock rump pig. Chuck kielbasa leberkas, pork bresaola ham hock filet mignon cow shoulder short ribs biltong.
+
+### Header 3
+
+```
+This is a code block following a header.
+```
+
+Next level leggings before they sold out, PBR&B church-key shaman echo park. Kale chips occupy godard whatever pop-up freegan pork belly selfies. Gastropub Belinda subway tile woke post-ironic seitan. Shabby chic man bun semiotics vape, chia messenger bag plaid cardigan.
+
+#### Header 4
+
+* This is an unordered list following a header.
+* This is an unordered list following a header.
+* This is an unordered list following a header.
+
+##### Header 5
+
+1. This is an ordered list following a header.
+2. This is an ordered list following a header.
+3. This is an ordered list following a header.
+
+###### Header 6
+
+| What | Follows |
+|-----------|-----------------|
+| A table | A header |
+| A table | A header |
+| A table | A header |
+
+----------------
+
+There's a horizontal rule above and below this.
+
+----------------
+
+Here is an unordered list:
+
+* Liverpool F.C.
+* Chelsea F.C.
+* Manchester United F.C.
+
+And an ordered list:
+
+1. Michael Brecker
+2. Seamus Blake
+3. Branford Marsalis
+
+And an unordered task list:
+
+- [x] Create a Hugo theme
+- [x] Add task lists to it
+- [ ] Take a vacation
+
+And a "mixed" task list:
+
+- [ ] Pack bags
+- ?
+- [ ] Travel!
+
+And a nested list:
+
+* Jackson 5
+ * Michael
+ * Tito
+ * Jackie
+ * Marlon
+ * Jermaine
+* TMNT
+ * Leonardo
+ * Michelangelo
+ * Donatello
+ * Raphael
+
+Definition lists can be used with Markdown syntax. Definition headers are bold.
+
+Name
+: Godzilla
+
+Born
+: 1952
+
+Birthplace
+: Japan
+
+Color
+: Green
+
+
+----------------
+
+Tables should have bold headings and alternating shaded rows.
+
+| Artist | Album | Year |
+|-------------------|-----------------|------|
+| Michael Jackson | Thriller | 1982 |
+| Prince | Purple Rain | 1984 |
+| Beastie Boys | License to Ill | 1986 |
+
+If a table is too wide, it should scroll horizontally.
+
+| Artist | Album | Year | Label | Awards | Songs |
+|-------------------|-----------------|------|-------------|----------|-----------|
+| Michael Jackson | Thriller | 1982 | Epic Records | Grammy Award for Album of the Year, American Music Award for Favorite Pop/Rock Album, American Music Award for Favorite Soul/R&B Album, Brit Award for Best Selling Album, Grammy Award for Best Engineered Album, Non-Classical | Wanna Be Startin' Somethin', Baby Be Mine, The Girl Is Mine, Thriller, Beat It, Billie Jean, Human Nature, P.Y.T. (Pretty Young Thing), The Lady in My Life |
+| Prince | Purple Rain | 1984 | Warner Brothers Records | Grammy Award for Best Score Soundtrack for Visual Media, American Music Award for Favorite Pop/Rock Album, American Music Award for Favorite Soul/R&B Album, Brit Award for Best Soundtrack/Cast Recording, Grammy Award for Best Rock Performance by a Duo or Group with Vocal | Let's Go Crazy, Take Me With U, The Beautiful Ones, Computer Blue, Darling Nikki, When Doves Cry, I Would Die 4 U, Baby I'm a Star, Purple Rain |
+| Beastie Boys | License to Ill | 1986 | Mercury Records | noawardsbutthistablecelliswide | Rhymin & Stealin, The New Style, She's Crafty, Posse in Effect, Slow Ride, Girls, (You Gotta) Fight for Your Right, No Sleep Till Brooklyn, Paul Revere, Hold It Now, Hit It, Brass Monkey, Slow and Low, Time to Get Ill |
+
+----------------
+
+Code snippets like `var foo = "bar";` can be shown inline.
+
+Also, `this should vertically align` ~~`with this`~~ ~~and this~~.
+
+Code can also be shown in a block element.
+
+```
+foo := "bar";
+bar := "foo";
+```
+
+Code can also use syntax highlighting.
+
+```go
+func main() {
+ input := `var foo = "bar";`
+
+ lexer := lexers.Get("javascript")
+ iterator, _ := lexer.Tokenise(nil, input)
+ style := styles.Get("github")
+ formatter := html.New(html.WithLineNumbers())
+
+ var buff bytes.Buffer
+ formatter.Format(&buff, style, iterator)
+
+ fmt.Println(buff.String())
+}
+```
+
+```
+Long, single-line code blocks should not wrap. They should horizontally scroll if they are too long. This line should be long enough to demonstrate this.
+```
+
+Inline code inside table cells should still be distinguishable.
+
+| Language | Code |
+|-------------|--------------------|
+| Javascript | `var foo = "bar";` |
+| Ruby | `foo = "bar"{` |
+
+----------------
+
+Small images should be shown at their actual size.
+
+
+
+Large images should always scale down and fit in the content container.
+
+
+
+_The photo above of the Spruce Picea abies shoot with foliage buds: Bjørn Erik Pedersen, CC-BY-SA._
+
+
+## Components
+
+### Alerts
+
+{{< alert >}}This is an alert.{{< /alert >}}
+{{< alert title="Note" >}}This is an alert with a title.{{< /alert >}}
+{{% alert title="Note" %}}This is an alert with a title and **Markdown**.{{% /alert %}}
+{{< alert color="success" >}}This is a successful alert.{{< /alert >}}
+{{< alert color="warning" >}}This is a warning.{{< /alert >}}
+{{< alert color="warning" title="Warning" >}}This is a warning with a title.{{< /alert >}}
+
+
+## Another Heading
diff --git a/doc-site/content/en/docs2/Tasks/Ponycopters/_index.md b/doc-site/content/en/docs2/Tasks/Ponycopters/_index.md
new file mode 100755
index 0000000..a1bd522
--- /dev/null
+++ b/doc-site/content/en/docs2/Tasks/Ponycopters/_index.md
@@ -0,0 +1,16 @@
+
+---
+title: "Working with Ponycopters"
+linkTitle: "Working with Ponycopters"
+date: 2017-01-05
+description: >
+ A short lead description about this section page. Text here can also be **bold** or _italic_ and can even be split over multiple paragraphs.
+---
+
+{{% pageinfo %}}
+This is a placeholder page. Replace it with your own content.
+{{% /pageinfo %}}
+
+
+This is the section landing page.
+
diff --git a/doc-site/content/en/docs2/Tasks/Ponycopters/configuring-ponycopters.md b/doc-site/content/en/docs2/Tasks/Ponycopters/configuring-ponycopters.md
new file mode 100644
index 0000000..6f29172
--- /dev/null
+++ b/doc-site/content/en/docs2/Tasks/Ponycopters/configuring-ponycopters.md
@@ -0,0 +1,239 @@
+---
+title: "Configuring Ponycopters"
+linkTitle: "Configuring Ponycopters"
+date: 2017-01-05
+weight: 2
+description: >
+ A short lead description about this content page. It can be **bold** or _italic_ and can be split over multiple paragraphs.
+---
+
+{{% pageinfo %}}
+This is a placeholder page. Replace it with your own content.
+{{% /pageinfo %}}
+
+Text can be **bold**, _italic_, or ~~strikethrough~~. [Links](https://gohugo.io) should be blue with no underlines (unless hovered over).
+
+There should be whitespace between paragraphs. Vape migas chillwave sriracha poutine try-hard distillery. Tattooed shabby chic small batch, pabst art party heirloom letterpress air plant pop-up. Sustainable chia skateboard art party banjo cardigan normcore affogato vexillologist quinoa meggings man bun master cleanse shoreditch readymade. Yuccie prism four dollar toast tbh cardigan iPhone, tumblr listicle live-edge VHS. Pug lyft normcore hot chicken biodiesel, actually keffiyeh thundercats photo booth pour-over twee fam food truck microdosing banh mi. Vice activated charcoal raclette unicorn live-edge post-ironic. Heirloom vexillologist coloring book, beard deep v letterpress echo park humblebrag tilde.
+
+90's four loko seitan photo booth gochujang freegan tumeric listicle fam ugh humblebrag. Bespoke leggings gastropub, biodiesel brunch pug fashion axe meh swag art party neutra deep v chia. Enamel pin fanny pack knausgaard tofu, artisan cronut hammock meditation occupy master cleanse chartreuse lumbersexual. Kombucha kogi viral truffaut synth distillery single-origin coffee ugh slow-carb marfa selfies. Pitchfork schlitz semiotics fanny pack, ugh artisan vegan vaporware hexagon. Polaroid fixie post-ironic venmo wolf ramps **kale chips**.
+
+> There should be no margin above this first sentence.
+>
+> Blockquotes should be a lighter gray with a border along the left side in the secondary color.
+>
+> There should be no margin below this final sentence.
+
+## First Header 2
+
+This is a normal paragraph following a header. Knausgaard kale chips snackwave microdosing cronut copper mug swag synth bitters letterpress glossier **craft beer**. Mumblecore bushwick authentic gochujang vegan chambray meditation jean shorts irony. Viral farm-to-table kale chips, pork belly palo santo distillery activated charcoal aesthetic jianbing air plant woke lomo VHS organic. Tattooed locavore succulents heirloom, small batch sriracha echo park DIY af. Shaman you probably haven't heard of them copper mug, crucifix green juice vape *single-origin coffee* brunch actually. Mustache etsy vexillologist raclette authentic fam. Tousled beard humblebrag asymmetrical. I love turkey, I love my job, I love my friends, I love Chardonnay!
+
+Deae legum paulatimque terra, non vos mutata tacet: dic. Vocant docuique me plumas fila quin afuerunt copia haec o neque.
+
+On big screens, paragraphs and headings should not take up the full container width, but we want tables, code blocks and similar to take the full width.
+
+Scenester tumeric pickled, authentic crucifix post-ironic fam freegan VHS pork belly 8-bit yuccie PBR&B. **I love this life we live in**.
+
+
+## Second Header 2
+
+> This is a blockquote following a header. Bacon ipsum dolor sit amet t-bone doner shank drumstick, pork belly porchetta chuck sausage brisket ham hock rump pig. Chuck kielbasa leberkas, pork bresaola ham hock filet mignon cow shoulder short ribs biltong.
+
+### Header 3
+
+```
+This is a code block following a header.
+```
+
+Next level leggings before they sold out, PBR&B church-key shaman echo park. Kale chips occupy godard whatever pop-up freegan pork belly selfies. Gastropub Belinda subway tile woke post-ironic seitan. Shabby chic man bun semiotics vape, chia messenger bag plaid cardigan.
+
+#### Header 4
+
+* This is an unordered list following a header.
+* This is an unordered list following a header.
+* This is an unordered list following a header.
+
+##### Header 5
+
+1. This is an ordered list following a header.
+2. This is an ordered list following a header.
+3. This is an ordered list following a header.
+
+###### Header 6
+
+| What | Follows |
+|-----------|-----------------|
+| A table | A header |
+| A table | A header |
+| A table | A header |
+
+----------------
+
+There's a horizontal rule above and below this.
+
+----------------
+
+Here is an unordered list:
+
+* Liverpool F.C.
+* Chelsea F.C.
+* Manchester United F.C.
+
+And an ordered list:
+
+1. Michael Brecker
+2. Seamus Blake
+3. Branford Marsalis
+
+And an unordered task list:
+
+- [x] Create a Hugo theme
+- [x] Add task lists to it
+- [ ] Take a vacation
+
+And a "mixed" task list:
+
+- [ ] Pack bags
+- ?
+- [ ] Travel!
+
+And a nested list:
+
+* Jackson 5
+ * Michael
+ * Tito
+ * Jackie
+ * Marlon
+ * Jermaine
+* TMNT
+ * Leonardo
+ * Michelangelo
+ * Donatello
+ * Raphael
+
+Definition lists can be used with Markdown syntax. Definition headers are bold.
+
+Name
+: Godzilla
+
+Born
+: 1952
+
+Birthplace
+: Japan
+
+Color
+: Green
+
+
+----------------
+
+Tables should have bold headings and alternating shaded rows.
+
+| Artist | Album | Year |
+|-------------------|-----------------|------|
+| Michael Jackson | Thriller | 1982 |
+| Prince | Purple Rain | 1984 |
+| Beastie Boys | License to Ill | 1986 |
+
+If a table is too wide, it should scroll horizontally.
+
+| Artist | Album | Year | Label | Awards | Songs |
+|-------------------|-----------------|------|-------------|----------|-----------|
+| Michael Jackson | Thriller | 1982 | Epic Records | Grammy Award for Album of the Year, American Music Award for Favorite Pop/Rock Album, American Music Award for Favorite Soul/R&B Album, Brit Award for Best Selling Album, Grammy Award for Best Engineered Album, Non-Classical | Wanna Be Startin' Somethin', Baby Be Mine, The Girl Is Mine, Thriller, Beat It, Billie Jean, Human Nature, P.Y.T. (Pretty Young Thing), The Lady in My Life |
+| Prince | Purple Rain | 1984 | Warner Brothers Records | Grammy Award for Best Score Soundtrack for Visual Media, American Music Award for Favorite Pop/Rock Album, American Music Award for Favorite Soul/R&B Album, Brit Award for Best Soundtrack/Cast Recording, Grammy Award for Best Rock Performance by a Duo or Group with Vocal | Let's Go Crazy, Take Me With U, The Beautiful Ones, Computer Blue, Darling Nikki, When Doves Cry, I Would Die 4 U, Baby I'm a Star, Purple Rain |
+| Beastie Boys | License to Ill | 1986 | Mercury Records | noawardsbutthistablecelliswide | Rhymin & Stealin, The New Style, She's Crafty, Posse in Effect, Slow Ride, Girls, (You Gotta) Fight for Your Right, No Sleep Till Brooklyn, Paul Revere, Hold It Now, Hit It, Brass Monkey, Slow and Low, Time to Get Ill |
+
+----------------
+
+Code snippets like `var foo = "bar";` can be shown inline.
+
+Also, `this should vertically align` ~~`with this`~~ ~~and this~~.
+
+Code can also be shown in a block element.
+
+```
+foo := "bar";
+bar := "foo";
+```
+
+Code can also use syntax highlighting.
+
+```go
+func main() {
+ input := `var foo = "bar";`
+
+ lexer := lexers.Get("javascript")
+ iterator, _ := lexer.Tokenise(nil, input)
+ style := styles.Get("github")
+ formatter := html.New(html.WithLineNumbers())
+
+ var buff bytes.Buffer
+ formatter.Format(&buff, style, iterator)
+
+ fmt.Println(buff.String())
+}
+```
+
+```
+Long, single-line code blocks should not wrap. They should horizontally scroll if they are too long. This line should be long enough to demonstrate this.
+```
+
+Inline code inside table cells should still be distinguishable.
+
+| Language | Code |
+|-------------|--------------------|
+| Javascript | `var foo = "bar";` |
+| Ruby | `foo = "bar"{` |
+
+----------------
+
+Small images should be shown at their actual size.
+
+
+
+Large images should always scale down and fit in the content container.
+
+
+
+_The photo above of the Spruce Picea abies shoot with foliage buds: Bjørn Erik Pedersen, CC-BY-SA._
+
+
+## Components
+
+### Alerts
+
+{{< alert >}}This is an alert.{{< /alert >}}
+{{< alert title="Note" >}}This is an alert with a title.{{< /alert >}}
+{{% alert title="Note" %}}This is an alert with a title and **Markdown**.{{% /alert %}}
+{{< alert color="success" >}}This is a successful alert.{{< /alert >}}
+{{< alert color="warning" >}}This is a warning.{{< /alert >}}
+{{< alert color="warning" title="Warning" >}}This is a warning with a title.{{< /alert >}}
+
+
+## Another Heading
+
+Add some sections here to see how the ToC looks like. Bacon ipsum dolor sit amet t-bone doner shank drumstick, pork belly porchetta chuck sausage brisket ham hock rump pig. Chuck kielbasa leberkas, pork bresaola ham hock filet mignon cow shoulder short ribs biltong.
+
+### This Document
+
+Inguina genus: Anaphen post: lingua violente voce suae meus aetate diversi. Orbis unam nec flammaeque status deam Silenum erat et a ferrea. Excitus rigidum ait: vestro et Herculis convicia: nitidae deseruit coniuge Proteaque adiciam *eripitur*? Sitim noceat signa *probat quidem*. Sua longis *fugatis* quidem genae.
+
+
+### Pixel Count
+
+Tilde photo booth wayfarers cliche lomo intelligentsia man braid kombucha vaporware farm-to-table mixtape portland. PBR&B pickled cornhole ugh try-hard ethical subway tile. Fixie paleo intelligentsia pabst. Ennui waistcoat vinyl gochujang. Poutine salvia authentic affogato, chambray lumbersexual shabby chic.
+
+### Contact Info
+
+Plaid hell of cred microdosing, succulents tilde pour-over. Offal shabby chic 3 wolf moon blue bottle raw denim normcore poutine pork belly.
+
+
+### External Links
+
+Stumptown PBR&B keytar plaid street art, forage XOXO pitchfork selvage affogato green juice listicle pickled everyday carry hashtag. Organic sustainable letterpress sartorial scenester intelligentsia swag bushwick. Put a bird on it stumptown neutra locavore. IPhone typewriter messenger bag narwhal. Ennui cold-pressed seitan flannel keytar, single-origin coffee adaptogen occupy yuccie williamsburg chillwave shoreditch forage waistcoat.
+
+
+
+```
+This is the final element on the page and there should be no margin below this.
+```
diff --git a/doc-site/content/en/docs2/Tasks/Ponycopters/launching-ponycopters.md b/doc-site/content/en/docs2/Tasks/Ponycopters/launching-ponycopters.md
new file mode 100644
index 0000000..54a857a
--- /dev/null
+++ b/doc-site/content/en/docs2/Tasks/Ponycopters/launching-ponycopters.md
@@ -0,0 +1,239 @@
+---
+title: "Launching Ponycopters"
+linkTitle: "Launching Ponycopters"
+date: 2017-01-05
+weight: 3
+description: >
+ A short lead description about this content page. It can be **bold** or _italic_ and can be split over multiple paragraphs.
+---
+
+{{% pageinfo %}}
+This is a placeholder page. Replace it with your own content.
+{{% /pageinfo %}}
+
+Text can be **bold**, _italic_, or ~~strikethrough~~. [Links](https://gohugo.io) should be blue with no underlines (unless hovered over).
+
+There should be whitespace between paragraphs. Vape migas chillwave sriracha poutine try-hard distillery. Tattooed shabby chic small batch, pabst art party heirloom letterpress air plant pop-up. Sustainable chia skateboard art party banjo cardigan normcore affogato vexillologist quinoa meggings man bun master cleanse shoreditch readymade. Yuccie prism four dollar toast tbh cardigan iPhone, tumblr listicle live-edge VHS. Pug lyft normcore hot chicken biodiesel, actually keffiyeh thundercats photo booth pour-over twee fam food truck microdosing banh mi. Vice activated charcoal raclette unicorn live-edge post-ironic. Heirloom vexillologist coloring book, beard deep v letterpress echo park humblebrag tilde.
+
+90's four loko seitan photo booth gochujang freegan tumeric listicle fam ugh humblebrag. Bespoke leggings gastropub, biodiesel brunch pug fashion axe meh swag art party neutra deep v chia. Enamel pin fanny pack knausgaard tofu, artisan cronut hammock meditation occupy master cleanse chartreuse lumbersexual. Kombucha kogi viral truffaut synth distillery single-origin coffee ugh slow-carb marfa selfies. Pitchfork schlitz semiotics fanny pack, ugh artisan vegan vaporware hexagon. Polaroid fixie post-ironic venmo wolf ramps **kale chips**.
+
+> There should be no margin above this first sentence.
+>
+> Blockquotes should be a lighter gray with a border along the left side in the secondary color.
+>
+> There should be no margin below this final sentence.
+
+## First Header 2
+
+This is a normal paragraph following a header. Knausgaard kale chips snackwave microdosing cronut copper mug swag synth bitters letterpress glossier **craft beer**. Mumblecore bushwick authentic gochujang vegan chambray meditation jean shorts irony. Viral farm-to-table kale chips, pork belly palo santo distillery activated charcoal aesthetic jianbing air plant woke lomo VHS organic. Tattooed locavore succulents heirloom, small batch sriracha echo park DIY af. Shaman you probably haven't heard of them copper mug, crucifix green juice vape *single-origin coffee* brunch actually. Mustache etsy vexillologist raclette authentic fam. Tousled beard humblebrag asymmetrical. I love turkey, I love my job, I love my friends, I love Chardonnay!
+
+Deae legum paulatimque terra, non vos mutata tacet: dic. Vocant docuique me plumas fila quin afuerunt copia haec o neque.
+
+On big screens, paragraphs and headings should not take up the full container width, but we want tables, code blocks and similar to take the full width.
+
+Scenester tumeric pickled, authentic crucifix post-ironic fam freegan VHS pork belly 8-bit yuccie PBR&B. **I love this life we live in**.
+
+
+## Second Header 2
+
+> This is a blockquote following a header. Bacon ipsum dolor sit amet t-bone doner shank drumstick, pork belly porchetta chuck sausage brisket ham hock rump pig. Chuck kielbasa leberkas, pork bresaola ham hock filet mignon cow shoulder short ribs biltong.
+
+### Header 3
+
+```
+This is a code block following a header.
+```
+
+Next level leggings before they sold out, PBR&B church-key shaman echo park. Kale chips occupy godard whatever pop-up freegan pork belly selfies. Gastropub Belinda subway tile woke post-ironic seitan. Shabby chic man bun semiotics vape, chia messenger bag plaid cardigan.
+
+#### Header 4
+
+* This is an unordered list following a header.
+* This is an unordered list following a header.
+* This is an unordered list following a header.
+
+##### Header 5
+
+1. This is an ordered list following a header.
+2. This is an ordered list following a header.
+3. This is an ordered list following a header.
+
+###### Header 6
+
+| What | Follows |
+|-----------|-----------------|
+| A table | A header |
+| A table | A header |
+| A table | A header |
+
+----------------
+
+There's a horizontal rule above and below this.
+
+----------------
+
+Here is an unordered list:
+
+* Liverpool F.C.
+* Chelsea F.C.
+* Manchester United F.C.
+
+And an ordered list:
+
+1. Michael Brecker
+2. Seamus Blake
+3. Branford Marsalis
+
+And an unordered task list:
+
+- [x] Create a Hugo theme
+- [x] Add task lists to it
+- [ ] Take a vacation
+
+And a "mixed" task list:
+
+- [ ] Pack bags
+- ?
+- [ ] Travel!
+
+And a nested list:
+
+* Jackson 5
+ * Michael
+ * Tito
+ * Jackie
+ * Marlon
+ * Jermaine
+* TMNT
+ * Leonardo
+ * Michelangelo
+ * Donatello
+ * Raphael
+
+Definition lists can be used with Markdown syntax. Definition headers are bold.
+
+Name
+: Godzilla
+
+Born
+: 1952
+
+Birthplace
+: Japan
+
+Color
+: Green
+
+
+----------------
+
+Tables should have bold headings and alternating shaded rows.
+
+| Artist | Album | Year |
+|-------------------|-----------------|------|
+| Michael Jackson | Thriller | 1982 |
+| Prince | Purple Rain | 1984 |
+| Beastie Boys | License to Ill | 1986 |
+
+If a table is too wide, it should scroll horizontally.
+
+| Artist | Album | Year | Label | Awards | Songs |
+|-------------------|-----------------|------|-------------|----------|-----------|
+| Michael Jackson | Thriller | 1982 | Epic Records | Grammy Award for Album of the Year, American Music Award for Favorite Pop/Rock Album, American Music Award for Favorite Soul/R&B Album, Brit Award for Best Selling Album, Grammy Award for Best Engineered Album, Non-Classical | Wanna Be Startin' Somethin', Baby Be Mine, The Girl Is Mine, Thriller, Beat It, Billie Jean, Human Nature, P.Y.T. (Pretty Young Thing), The Lady in My Life |
+| Prince | Purple Rain | 1984 | Warner Brothers Records | Grammy Award for Best Score Soundtrack for Visual Media, American Music Award for Favorite Pop/Rock Album, American Music Award for Favorite Soul/R&B Album, Brit Award for Best Soundtrack/Cast Recording, Grammy Award for Best Rock Performance by a Duo or Group with Vocal | Let's Go Crazy, Take Me With U, The Beautiful Ones, Computer Blue, Darling Nikki, When Doves Cry, I Would Die 4 U, Baby I'm a Star, Purple Rain |
+| Beastie Boys | License to Ill | 1986 | Mercury Records | noawardsbutthistablecelliswide | Rhymin & Stealin, The New Style, She's Crafty, Posse in Effect, Slow Ride, Girls, (You Gotta) Fight for Your Right, No Sleep Till Brooklyn, Paul Revere, Hold It Now, Hit It, Brass Monkey, Slow and Low, Time to Get Ill |
+
+----------------
+
+Code snippets like `var foo = "bar";` can be shown inline.
+
+Also, `this should vertically align` ~~`with this`~~ ~~and this~~.
+
+Code can also be shown in a block element.
+
+```
+foo := "bar";
+bar := "foo";
+```
+
+Code can also use syntax highlighting.
+
+```go
+func main() {
+ input := `var foo = "bar";`
+
+ lexer := lexers.Get("javascript")
+ iterator, _ := lexer.Tokenise(nil, input)
+ style := styles.Get("github")
+ formatter := html.New(html.WithLineNumbers())
+
+ var buff bytes.Buffer
+ formatter.Format(&buff, style, iterator)
+
+ fmt.Println(buff.String())
+}
+```
+
+```
+Long, single-line code blocks should not wrap. They should horizontally scroll if they are too long. This line should be long enough to demonstrate this.
+```
+
+Inline code inside table cells should still be distinguishable.
+
+| Language | Code |
+|-------------|--------------------|
+| Javascript | `var foo = "bar";` |
+| Ruby | `foo = "bar"{` |
+
+----------------
+
+Small images should be shown at their actual size.
+
+
+
+Large images should always scale down and fit in the content container.
+
+
+
+_The photo above of the Spruce Picea abies shoot with foliage buds: Bjørn Erik Pedersen, CC-BY-SA._
+
+
+## Components
+
+### Alerts
+
+{{< alert >}}This is an alert.{{< /alert >}}
+{{< alert title="Note" >}}This is an alert with a title.{{< /alert >}}
+{{% alert title="Note" %}}This is an alert with a title and **Markdown**.{{% /alert %}}
+{{< alert color="success" >}}This is a successful alert.{{< /alert >}}
+{{< alert color="warning" >}}This is a warning.{{< /alert >}}
+{{< alert color="warning" title="Warning" >}}This is a warning with a title.{{< /alert >}}
+
+
+## Another Heading
+
+Add some sections here to see how the ToC looks like. Bacon ipsum dolor sit amet t-bone doner shank drumstick, pork belly porchetta chuck sausage brisket ham hock rump pig. Chuck kielbasa leberkas, pork bresaola ham hock filet mignon cow shoulder short ribs biltong.
+
+### This Document
+
+Inguina genus: Anaphen post: lingua violente voce suae meus aetate diversi. Orbis unam nec flammaeque status deam Silenum erat et a ferrea. Excitus rigidum ait: vestro et Herculis convicia: nitidae deseruit coniuge Proteaque adiciam *eripitur*? Sitim noceat signa *probat quidem*. Sua longis *fugatis* quidem genae.
+
+
+### Pixel Count
+
+Tilde photo booth wayfarers cliche lomo intelligentsia man braid kombucha vaporware farm-to-table mixtape portland. PBR&B pickled cornhole ugh try-hard ethical subway tile. Fixie paleo intelligentsia pabst. Ennui waistcoat vinyl gochujang. Poutine salvia authentic affogato, chambray lumbersexual shabby chic.
+
+### Contact Info
+
+Plaid hell of cred microdosing, succulents tilde pour-over. Offal shabby chic 3 wolf moon blue bottle raw denim normcore poutine pork belly.
+
+
+### External Links
+
+Stumptown PBR&B keytar plaid street art, forage XOXO pitchfork selvage affogato green juice listicle pickled everyday carry hashtag. Organic sustainable letterpress sartorial scenester intelligentsia swag bushwick. Put a bird on it stumptown neutra locavore. IPhone typewriter messenger bag narwhal. Ennui cold-pressed seitan flannel keytar, single-origin coffee adaptogen occupy yuccie williamsburg chillwave shoreditch forage waistcoat.
+
+
+
+```
+This is the final element on the page and there should be no margin below this.
+```
diff --git a/doc-site/content/en/docs2/Tasks/_index.md b/doc-site/content/en/docs2/Tasks/_index.md
new file mode 100755
index 0000000..e43ab7c
--- /dev/null
+++ b/doc-site/content/en/docs2/Tasks/_index.md
@@ -0,0 +1,25 @@
+
+---
+title: "Core Tasks"
+linkTitle: "Core Tasks"
+weight: 6
+date: 2017-01-05
+description: >
+ What can your user do with your project?
+---
+
+{{% pageinfo %}}
+This is a placeholder page that shows you how to use this template site.
+{{% /pageinfo %}}
+
+Think about your project’s features and use cases. Use these to choose your core tasks. Each granular use case (enable x, configure y) should have a corresponding tasks page or tasks page section. Users should be able to quickly refer to your core tasks when they need to find out how to do one specific thing, rather than having to look for the instructions in a bigger tutorial or example. Think of your tasks pages as a cookbook with different procedures your users can combine to create something more substantial.
+
+You can give each task a page, or you can group related tasks together in a page, such as tasks related to a particular feature. As well as grouping related tasks in single pages, you can also group task pages in nested folders with an index page as an overview, as seen in this example site. Or if you have a small docset like the [Docsy User Guide](https://docsy.dev/docs/) with no Tutorials or Concepts pages, consider adding your feature-specific pages at the top level of your docs rather than in a Tasks section.
+
+Each task should give the user
+
+* The prerequisites for this task, if any (this can be specified at the top of a multi-task page if they're the same for all the page's tasks. "All these tasks assume that you understand....and that you have already....").
+* What this task accomplishes.
+* Instructions for the task. If it involves editing a file, running a command, or writing code, provide code-formatted example snippets to show the user what to do! If there are multiple steps, provide them as a numbered list.
+* If appropriate, links to related concept, tutorial, or example pages.
+
diff --git a/doc-site/content/en/docs2/Tasks/beds.md b/doc-site/content/en/docs2/Tasks/beds.md
new file mode 100644
index 0000000..4c5803d
--- /dev/null
+++ b/doc-site/content/en/docs2/Tasks/beds.md
@@ -0,0 +1,239 @@
+---
+title: "Bed and Chair Metrics"
+date: 2017-01-05
+weight: 2
+description: >
+ A short lead description about this content page. It can be **bold** or _italic_ and can be split over multiple paragraphs.
+---
+
+{{% pageinfo %}}
+This is a placeholder page. Replace it with your own content.
+{{% /pageinfo %}}
+
+
+Text can be **bold**, _italic_, or ~~strikethrough~~. [Links](https://gohugo.io) should be blue with no underlines (unless hovered over).
+
+There should be whitespace between paragraphs. Vape migas chillwave sriracha poutine try-hard distillery. Tattooed shabby chic small batch, pabst art party heirloom letterpress air plant pop-up. Sustainable chia skateboard art party banjo cardigan normcore affogato vexillologist quinoa meggings man bun master cleanse shoreditch readymade. Yuccie prism four dollar toast tbh cardigan iPhone, tumblr listicle live-edge VHS. Pug lyft normcore hot chicken biodiesel, actually keffiyeh thundercats photo booth pour-over twee fam food truck microdosing banh mi. Vice activated charcoal raclette unicorn live-edge post-ironic. Heirloom vexillologist coloring book, beard deep v letterpress echo park humblebrag tilde.
+
+90's four loko seitan photo booth gochujang freegan tumeric listicle fam ugh humblebrag. Bespoke leggings gastropub, biodiesel brunch pug fashion axe meh swag art party neutra deep v chia. Enamel pin fanny pack knausgaard tofu, artisan cronut hammock meditation occupy master cleanse chartreuse lumbersexual. Kombucha kogi viral truffaut synth distillery single-origin coffee ugh slow-carb marfa selfies. Pitchfork schlitz semiotics fanny pack, ugh artisan vegan vaporware hexagon. Polaroid fixie post-ironic venmo wolf ramps **kale chips**.
+
+> There should be no margin above this first sentence.
+>
+> Blockquotes should be a lighter gray with a border along the left side in the secondary color.
+>
+> There should be no margin below this final sentence.
+
+## First Header 2
+
+This is a normal paragraph following a header. Knausgaard kale chips snackwave microdosing cronut copper mug swag synth bitters letterpress glossier **craft beer**. Mumblecore bushwick authentic gochujang vegan chambray meditation jean shorts irony. Viral farm-to-table kale chips, pork belly palo santo distillery activated charcoal aesthetic jianbing air plant woke lomo VHS organic. Tattooed locavore succulents heirloom, small batch sriracha echo park DIY af. Shaman you probably haven't heard of them copper mug, crucifix green juice vape *single-origin coffee* brunch actually. Mustache etsy vexillologist raclette authentic fam. Tousled beard humblebrag asymmetrical. I love turkey, I love my job, I love my friends, I love Chardonnay!
+
+Deae legum paulatimque terra, non vos mutata tacet: dic. Vocant docuique me plumas fila quin afuerunt copia haec o neque.
+
+On big screens, paragraphs and headings should not take up the full container width, but we want tables, code blocks and similar to take the full width.
+
+Scenester tumeric pickled, authentic crucifix post-ironic fam freegan VHS pork belly 8-bit yuccie PBR&B. **I love this life we live in**.
+
+
+## Second Header 2
+
+> This is a blockquote following a header. Bacon ipsum dolor sit amet t-bone doner shank drumstick, pork belly porchetta chuck sausage brisket ham hock rump pig. Chuck kielbasa leberkas, pork bresaola ham hock filet mignon cow shoulder short ribs biltong.
+
+### Header 3
+
+```
+This is a code block following a header.
+```
+
+Next level leggings before they sold out, PBR&B church-key shaman echo park. Kale chips occupy godard whatever pop-up freegan pork belly selfies. Gastropub Belinda subway tile woke post-ironic seitan. Shabby chic man bun semiotics vape, chia messenger bag plaid cardigan.
+
+#### Header 4
+
+* This is an unordered list following a header.
+* This is an unordered list following a header.
+* This is an unordered list following a header.
+
+##### Header 5
+
+1. This is an ordered list following a header.
+2. This is an ordered list following a header.
+3. This is an ordered list following a header.
+
+###### Header 6
+
+| What | Follows |
+|-----------|-----------------|
+| A table | A header |
+| A table | A header |
+| A table | A header |
+
+----------------
+
+There's a horizontal rule above and below this.
+
+----------------
+
+Here is an unordered list:
+
+* Liverpool F.C.
+* Chelsea F.C.
+* Manchester United F.C.
+
+And an ordered list:
+
+1. Michael Brecker
+2. Seamus Blake
+3. Branford Marsalis
+
+And an unordered task list:
+
+- [x] Create a Hugo theme
+- [x] Add task lists to it
+- [ ] Take a vacation
+
+And a "mixed" task list:
+
+- [ ] Pack bags
+- ?
+- [ ] Travel!
+
+And a nested list:
+
+* Jackson 5
+ * Michael
+ * Tito
+ * Jackie
+ * Marlon
+ * Jermaine
+* TMNT
+ * Leonardo
+ * Michelangelo
+ * Donatello
+ * Raphael
+
+Definition lists can be used with Markdown syntax. Definition headers are bold.
+
+Name
+: Godzilla
+
+Born
+: 1952
+
+Birthplace
+: Japan
+
+Color
+: Green
+
+
+----------------
+
+Tables should have bold headings and alternating shaded rows.
+
+| Artist | Album | Year |
+|-------------------|-----------------|------|
+| Michael Jackson | Thriller | 1982 |
+| Prince | Purple Rain | 1984 |
+| Beastie Boys | License to Ill | 1986 |
+
+If a table is too wide, it should scroll horizontally.
+
+| Artist | Album | Year | Label | Awards | Songs |
+|-------------------|-----------------|------|-------------|----------|-----------|
+| Michael Jackson | Thriller | 1982 | Epic Records | Grammy Award for Album of the Year, American Music Award for Favorite Pop/Rock Album, American Music Award for Favorite Soul/R&B Album, Brit Award for Best Selling Album, Grammy Award for Best Engineered Album, Non-Classical | Wanna Be Startin' Somethin', Baby Be Mine, The Girl Is Mine, Thriller, Beat It, Billie Jean, Human Nature, P.Y.T. (Pretty Young Thing), The Lady in My Life |
+| Prince | Purple Rain | 1984 | Warner Brothers Records | Grammy Award for Best Score Soundtrack for Visual Media, American Music Award for Favorite Pop/Rock Album, American Music Award for Favorite Soul/R&B Album, Brit Award for Best Soundtrack/Cast Recording, Grammy Award for Best Rock Performance by a Duo or Group with Vocal | Let's Go Crazy, Take Me With U, The Beautiful Ones, Computer Blue, Darling Nikki, When Doves Cry, I Would Die 4 U, Baby I'm a Star, Purple Rain |
+| Beastie Boys | License to Ill | 1986 | Mercury Records | noawardsbutthistablecelliswide | Rhymin & Stealin, The New Style, She's Crafty, Posse in Effect, Slow Ride, Girls, (You Gotta) Fight for Your Right, No Sleep Till Brooklyn, Paul Revere, Hold It Now, Hit It, Brass Monkey, Slow and Low, Time to Get Ill |
+
+----------------
+
+Code snippets like `var foo = "bar";` can be shown inline.
+
+Also, `this should vertically align` ~~`with this`~~ ~~and this~~.
+
+Code can also be shown in a block element.
+
+```
+foo := "bar";
+bar := "foo";
+```
+
+Code can also use syntax highlighting.
+
+```go
+func main() {
+ input := `var foo = "bar";`
+
+ lexer := lexers.Get("javascript")
+ iterator, _ := lexer.Tokenise(nil, input)
+ style := styles.Get("github")
+ formatter := html.New(html.WithLineNumbers())
+
+ var buff bytes.Buffer
+ formatter.Format(&buff, style, iterator)
+
+ fmt.Println(buff.String())
+}
+```
+
+```
+Long, single-line code blocks should not wrap. They should horizontally scroll if they are too long. This line should be long enough to demonstrate this.
+```
+
+Inline code inside table cells should still be distinguishable.
+
+| Language | Code |
+|-------------|--------------------|
+| Javascript | `var foo = "bar";` |
+| Ruby | `foo = "bar"{` |
+
+----------------
+
+Small images should be shown at their actual size.
+
+
+
+Large images should always scale down and fit in the content container.
+
+
+
+_The photo above of the Spruce Picea abies shoot with foliage buds: Bjørn Erik Pedersen, CC-BY-SA._
+
+
+## Components
+
+### Alerts
+
+{{< alert >}}This is an alert.{{< /alert >}}
+{{< alert title="Note" >}}This is an alert with a title.{{< /alert >}}
+{{% alert title="Note" %}}This is an alert with a title and **Markdown**.{{% /alert %}}
+{{< alert color="success" >}}This is a successful alert.{{< /alert >}}
+{{< alert color="warning" >}}This is a warning.{{< /alert >}}
+{{< alert color="warning" title="Warning" >}}This is a warning with a title.{{< /alert >}}
+
+
+## Another Heading
+
+Add some sections here to see how the ToC looks like. Bacon ipsum dolor sit amet t-bone doner shank drumstick, pork belly porchetta chuck sausage brisket ham hock rump pig. Chuck kielbasa leberkas, pork bresaola ham hock filet mignon cow shoulder short ribs biltong.
+
+### This Document
+
+Inguina genus: Anaphen post: lingua violente voce suae meus aetate diversi. Orbis unam nec flammaeque status deam Silenum erat et a ferrea. Excitus rigidum ait: vestro et Herculis convicia: nitidae deseruit coniuge Proteaque adiciam *eripitur*? Sitim noceat signa *probat quidem*. Sua longis *fugatis* quidem genae.
+
+
+### Pixel Count
+
+Tilde photo booth wayfarers cliche lomo intelligentsia man braid kombucha vaporware farm-to-table mixtape portland. PBR&B pickled cornhole ugh try-hard ethical subway tile. Fixie paleo intelligentsia pabst. Ennui waistcoat vinyl gochujang. Poutine salvia authentic affogato, chambray lumbersexual shabby chic.
+
+### Contact Info
+
+Plaid hell of cred microdosing, succulents tilde pour-over. Offal shabby chic 3 wolf moon blue bottle raw denim normcore poutine pork belly.
+
+
+### External Links
+
+Stumptown PBR&B keytar plaid street art, forage XOXO pitchfork selvage affogato green juice listicle pickled everyday carry hashtag. Organic sustainable letterpress sartorial scenester intelligentsia swag bushwick. Put a bird on it stumptown neutra locavore. IPhone typewriter messenger bag narwhal. Ennui cold-pressed seitan flannel keytar, single-origin coffee adaptogen occupy yuccie williamsburg chillwave shoreditch forage waistcoat.
+
+
+
+```
+This is the final element on the page and there should be no margin below this.
+```
diff --git a/doc-site/content/en/docs2/Tasks/porridge.md b/doc-site/content/en/docs2/Tasks/porridge.md
new file mode 100644
index 0000000..71ef273
--- /dev/null
+++ b/doc-site/content/en/docs2/Tasks/porridge.md
@@ -0,0 +1,239 @@
+---
+title: "Porridge Assessment"
+date: 2017-01-05
+weight: 4
+description: >
+ A short lead description about this content page. It can be **bold** or _italic_ and can be split over multiple paragraphs.
+---
+
+{{% pageinfo %}}
+This is a placeholder page. Replace it with your own content.
+{{% /pageinfo %}}
+
+
+Text can be **bold**, _italic_, or ~~strikethrough~~. [Links](https://gohugo.io) should be blue with no underlines (unless hovered over).
+
+There should be whitespace between paragraphs. Vape migas chillwave sriracha poutine try-hard distillery. Tattooed shabby chic small batch, pabst art party heirloom letterpress air plant pop-up. Sustainable chia skateboard art party banjo cardigan normcore affogato vexillologist quinoa meggings man bun master cleanse shoreditch readymade. Yuccie prism four dollar toast tbh cardigan iPhone, tumblr listicle live-edge VHS. Pug lyft normcore hot chicken biodiesel, actually keffiyeh thundercats photo booth pour-over twee fam food truck microdosing banh mi. Vice activated charcoal raclette unicorn live-edge post-ironic. Heirloom vexillologist coloring book, beard deep v letterpress echo park humblebrag tilde.
+
+90's four loko seitan photo booth gochujang freegan tumeric listicle fam ugh humblebrag. Bespoke leggings gastropub, biodiesel brunch pug fashion axe meh swag art party neutra deep v chia. Enamel pin fanny pack knausgaard tofu, artisan cronut hammock meditation occupy master cleanse chartreuse lumbersexual. Kombucha kogi viral truffaut synth distillery single-origin coffee ugh slow-carb marfa selfies. Pitchfork schlitz semiotics fanny pack, ugh artisan vegan vaporware hexagon. Polaroid fixie post-ironic venmo wolf ramps **kale chips**.
+
+> There should be no margin above this first sentence.
+>
+> Blockquotes should be a lighter gray with a border along the left side in the secondary color.
+>
+> There should be no margin below this final sentence.
+
+## First Header 2
+
+This is a normal paragraph following a header. Knausgaard kale chips snackwave microdosing cronut copper mug swag synth bitters letterpress glossier **craft beer**. Mumblecore bushwick authentic gochujang vegan chambray meditation jean shorts irony. Viral farm-to-table kale chips, pork belly palo santo distillery activated charcoal aesthetic jianbing air plant woke lomo VHS organic. Tattooed locavore succulents heirloom, small batch sriracha echo park DIY af. Shaman you probably haven't heard of them copper mug, crucifix green juice vape *single-origin coffee* brunch actually. Mustache etsy vexillologist raclette authentic fam. Tousled beard humblebrag asymmetrical. I love turkey, I love my job, I love my friends, I love Chardonnay!
+
+Deae legum paulatimque terra, non vos mutata tacet: dic. Vocant docuique me plumas fila quin afuerunt copia haec o neque.
+
+On big screens, paragraphs and headings should not take up the full container width, but we want tables, code blocks and similar to take the full width.
+
+Scenester tumeric pickled, authentic crucifix post-ironic fam freegan VHS pork belly 8-bit yuccie PBR&B. **I love this life we live in**.
+
+
+## Second Header 2
+
+> This is a blockquote following a header. Bacon ipsum dolor sit amet t-bone doner shank drumstick, pork belly porchetta chuck sausage brisket ham hock rump pig. Chuck kielbasa leberkas, pork bresaola ham hock filet mignon cow shoulder short ribs biltong.
+
+### Header 3
+
+```
+This is a code block following a header.
+```
+
+Next level leggings before they sold out, PBR&B church-key shaman echo park. Kale chips occupy godard whatever pop-up freegan pork belly selfies. Gastropub Belinda subway tile woke post-ironic seitan. Shabby chic man bun semiotics vape, chia messenger bag plaid cardigan.
+
+#### Header 4
+
+* This is an unordered list following a header.
+* This is an unordered list following a header.
+* This is an unordered list following a header.
+
+##### Header 5
+
+1. This is an ordered list following a header.
+2. This is an ordered list following a header.
+3. This is an ordered list following a header.
+
+###### Header 6
+
+| What | Follows |
+|-----------|-----------------|
+| A table | A header |
+| A table | A header |
+| A table | A header |
+
+----------------
+
+There's a horizontal rule above and below this.
+
+----------------
+
+Here is an unordered list:
+
+* Liverpool F.C.
+* Chelsea F.C.
+* Manchester United F.C.
+
+And an ordered list:
+
+1. Michael Brecker
+2. Seamus Blake
+3. Branford Marsalis
+
+And an unordered task list:
+
+- [x] Create a Hugo theme
+- [x] Add task lists to it
+- [ ] Take a vacation
+
+And a "mixed" task list:
+
+- [ ] Pack bags
+- ?
+- [ ] Travel!
+
+And a nested list:
+
+* Jackson 5
+ * Michael
+ * Tito
+ * Jackie
+ * Marlon
+ * Jermaine
+* TMNT
+ * Leonardo
+ * Michelangelo
+ * Donatello
+ * Raphael
+
+Definition lists can be used with Markdown syntax. Definition headers are bold.
+
+Name
+: Godzilla
+
+Born
+: 1952
+
+Birthplace
+: Japan
+
+Color
+: Green
+
+
+----------------
+
+Tables should have bold headings and alternating shaded rows.
+
+| Artist | Album | Year |
+|-------------------|-----------------|------|
+| Michael Jackson | Thriller | 1982 |
+| Prince | Purple Rain | 1984 |
+| Beastie Boys | License to Ill | 1986 |
+
+If a table is too wide, it should scroll horizontally.
+
+| Artist | Album | Year | Label | Awards | Songs |
+|-------------------|-----------------|------|-------------|----------|-----------|
+| Michael Jackson | Thriller | 1982 | Epic Records | Grammy Award for Album of the Year, American Music Award for Favorite Pop/Rock Album, American Music Award for Favorite Soul/R&B Album, Brit Award for Best Selling Album, Grammy Award for Best Engineered Album, Non-Classical | Wanna Be Startin' Somethin', Baby Be Mine, The Girl Is Mine, Thriller, Beat It, Billie Jean, Human Nature, P.Y.T. (Pretty Young Thing), The Lady in My Life |
+| Prince | Purple Rain | 1984 | Warner Brothers Records | Grammy Award for Best Score Soundtrack for Visual Media, American Music Award for Favorite Pop/Rock Album, American Music Award for Favorite Soul/R&B Album, Brit Award for Best Soundtrack/Cast Recording, Grammy Award for Best Rock Performance by a Duo or Group with Vocal | Let's Go Crazy, Take Me With U, The Beautiful Ones, Computer Blue, Darling Nikki, When Doves Cry, I Would Die 4 U, Baby I'm a Star, Purple Rain |
+| Beastie Boys | License to Ill | 1986 | Mercury Records | noawardsbutthistablecelliswide | Rhymin & Stealin, The New Style, She's Crafty, Posse in Effect, Slow Ride, Girls, (You Gotta) Fight for Your Right, No Sleep Till Brooklyn, Paul Revere, Hold It Now, Hit It, Brass Monkey, Slow and Low, Time to Get Ill |
+
+----------------
+
+Code snippets like `var foo = "bar";` can be shown inline.
+
+Also, `this should vertically align` ~~`with this`~~ ~~and this~~.
+
+Code can also be shown in a block element.
+
+```
+foo := "bar";
+bar := "foo";
+```
+
+Code can also use syntax highlighting.
+
+```go
+func main() {
+ input := `var foo = "bar";`
+
+ lexer := lexers.Get("javascript")
+ iterator, _ := lexer.Tokenise(nil, input)
+ style := styles.Get("github")
+ formatter := html.New(html.WithLineNumbers())
+
+ var buff bytes.Buffer
+ formatter.Format(&buff, style, iterator)
+
+ fmt.Println(buff.String())
+}
+```
+
+```
+Long, single-line code blocks should not wrap. They should horizontally scroll if they are too long. This line should be long enough to demonstrate this.
+```
+
+Inline code inside table cells should still be distinguishable.
+
+| Language | Code |
+|-------------|--------------------|
+| Javascript | `var foo = "bar";` |
+| Ruby | `foo = "bar"{` |
+
+----------------
+
+Small images should be shown at their actual size.
+
+
+
+Large images should always scale down and fit in the content container.
+
+
+
+_The photo above of the Spruce Picea abies shoot with foliage buds: Bjørn Erik Pedersen, CC-BY-SA._
+
+
+## Components
+
+### Alerts
+
+{{< alert >}}This is an alert.{{< /alert >}}
+{{< alert title="Note" >}}This is an alert with a title.{{< /alert >}}
+{{% alert title="Note" %}}This is an alert with a title and **Markdown**.{{% /alert %}}
+{{< alert color="success" >}}This is a successful alert.{{< /alert >}}
+{{< alert color="warning" >}}This is a warning.{{< /alert >}}
+{{< alert color="warning" title="Warning" >}}This is a warning with a title.{{< /alert >}}
+
+
+## Another Heading
+
+Add some sections here to see how the ToC looks like. Bacon ipsum dolor sit amet t-bone doner shank drumstick, pork belly porchetta chuck sausage brisket ham hock rump pig. Chuck kielbasa leberkas, pork bresaola ham hock filet mignon cow shoulder short ribs biltong.
+
+### This Document
+
+Inguina genus: Anaphen post: lingua violente voce suae meus aetate diversi. Orbis unam nec flammaeque status deam Silenum erat et a ferrea. Excitus rigidum ait: vestro et Herculis convicia: nitidae deseruit coniuge Proteaque adiciam *eripitur*? Sitim noceat signa *probat quidem*. Sua longis *fugatis* quidem genae.
+
+
+### Pixel Count
+
+Tilde photo booth wayfarers cliche lomo intelligentsia man braid kombucha vaporware farm-to-table mixtape portland. PBR&B pickled cornhole ugh try-hard ethical subway tile. Fixie paleo intelligentsia pabst. Ennui waistcoat vinyl gochujang. Poutine salvia authentic affogato, chambray lumbersexual shabby chic.
+
+### Contact Info
+
+Plaid hell of cred microdosing, succulents tilde pour-over. Offal shabby chic 3 wolf moon blue bottle raw denim normcore poutine pork belly.
+
+
+### External Links
+
+Stumptown PBR&B keytar plaid street art, forage XOXO pitchfork selvage affogato green juice listicle pickled everyday carry hashtag. Organic sustainable letterpress sartorial scenester intelligentsia swag bushwick. Put a bird on it stumptown neutra locavore. IPhone typewriter messenger bag narwhal. Ennui cold-pressed seitan flannel keytar, single-origin coffee adaptogen occupy yuccie williamsburg chillwave shoreditch forage waistcoat.
+
+
+
+```
+This is the final element on the page and there should be no margin below this.
+```
diff --git a/doc-site/content/en/docs2/Tasks/task.md b/doc-site/content/en/docs2/Tasks/task.md
new file mode 100644
index 0000000..65b34cd
--- /dev/null
+++ b/doc-site/content/en/docs2/Tasks/task.md
@@ -0,0 +1,239 @@
+---
+title: "Another Task"
+date: 2017-01-05
+weight: 5
+description: >
+ A short lead description about this content page. It can be **bold** or _italic_ and can be split over multiple paragraphs.
+---
+
+{{% pageinfo %}}
+This is a placeholder page. Replace it with your own content.
+{{% /pageinfo %}}
+
+
+Text can be **bold**, _italic_, or ~~strikethrough~~. [Links](https://gohugo.io) should be blue with no underlines (unless hovered over).
+
+There should be whitespace between paragraphs. Vape migas chillwave sriracha poutine try-hard distillery. Tattooed shabby chic small batch, pabst art party heirloom letterpress air plant pop-up. Sustainable chia skateboard art party banjo cardigan normcore affogato vexillologist quinoa meggings man bun master cleanse shoreditch readymade. Yuccie prism four dollar toast tbh cardigan iPhone, tumblr listicle live-edge VHS. Pug lyft normcore hot chicken biodiesel, actually keffiyeh thundercats photo booth pour-over twee fam food truck microdosing banh mi. Vice activated charcoal raclette unicorn live-edge post-ironic. Heirloom vexillologist coloring book, beard deep v letterpress echo park humblebrag tilde.
+
+90's four loko seitan photo booth gochujang freegan tumeric listicle fam ugh humblebrag. Bespoke leggings gastropub, biodiesel brunch pug fashion axe meh swag art party neutra deep v chia. Enamel pin fanny pack knausgaard tofu, artisan cronut hammock meditation occupy master cleanse chartreuse lumbersexual. Kombucha kogi viral truffaut synth distillery single-origin coffee ugh slow-carb marfa selfies. Pitchfork schlitz semiotics fanny pack, ugh artisan vegan vaporware hexagon. Polaroid fixie post-ironic venmo wolf ramps **kale chips**.
+
+> There should be no margin above this first sentence.
+>
+> Blockquotes should be a lighter gray with a border along the left side in the secondary color.
+>
+> There should be no margin below this final sentence.
+
+## First Header 2
+
+This is a normal paragraph following a header. Knausgaard kale chips snackwave microdosing cronut copper mug swag synth bitters letterpress glossier **craft beer**. Mumblecore bushwick authentic gochujang vegan chambray meditation jean shorts irony. Viral farm-to-table kale chips, pork belly palo santo distillery activated charcoal aesthetic jianbing air plant woke lomo VHS organic. Tattooed locavore succulents heirloom, small batch sriracha echo park DIY af. Shaman you probably haven't heard of them copper mug, crucifix green juice vape *single-origin coffee* brunch actually. Mustache etsy vexillologist raclette authentic fam. Tousled beard humblebrag asymmetrical. I love turkey, I love my job, I love my friends, I love Chardonnay!
+
+Deae legum paulatimque terra, non vos mutata tacet: dic. Vocant docuique me plumas fila quin afuerunt copia haec o neque.
+
+On big screens, paragraphs and headings should not take up the full container width, but we want tables, code blocks and similar to take the full width.
+
+Scenester tumeric pickled, authentic crucifix post-ironic fam freegan VHS pork belly 8-bit yuccie PBR&B. **I love this life we live in**.
+
+
+## Second Header 2
+
+> This is a blockquote following a header. Bacon ipsum dolor sit amet t-bone doner shank drumstick, pork belly porchetta chuck sausage brisket ham hock rump pig. Chuck kielbasa leberkas, pork bresaola ham hock filet mignon cow shoulder short ribs biltong.
+
+### Header 3
+
+```
+This is a code block following a header.
+```
+
+Next level leggings before they sold out, PBR&B church-key shaman echo park. Kale chips occupy godard whatever pop-up freegan pork belly selfies. Gastropub Belinda subway tile woke post-ironic seitan. Shabby chic man bun semiotics vape, chia messenger bag plaid cardigan.
+
+#### Header 4
+
+* This is an unordered list following a header.
+* This is an unordered list following a header.
+* This is an unordered list following a header.
+
+##### Header 5
+
+1. This is an ordered list following a header.
+2. This is an ordered list following a header.
+3. This is an ordered list following a header.
+
+###### Header 6
+
+| What | Follows |
+|-----------|-----------------|
+| A table | A header |
+| A table | A header |
+| A table | A header |
+
+----------------
+
+There's a horizontal rule above and below this.
+
+----------------
+
+Here is an unordered list:
+
+* Liverpool F.C.
+* Chelsea F.C.
+* Manchester United F.C.
+
+And an ordered list:
+
+1. Michael Brecker
+2. Seamus Blake
+3. Branford Marsalis
+
+And an unordered task list:
+
+- [x] Create a Hugo theme
+- [x] Add task lists to it
+- [ ] Take a vacation
+
+And a "mixed" task list:
+
+- [ ] Pack bags
+- ?
+- [ ] Travel!
+
+And a nested list:
+
+* Jackson 5
+ * Michael
+ * Tito
+ * Jackie
+ * Marlon
+ * Jermaine
+* TMNT
+ * Leonardo
+ * Michelangelo
+ * Donatello
+ * Raphael
+
+Definition lists can be used with Markdown syntax. Definition headers are bold.
+
+Name
+: Godzilla
+
+Born
+: 1952
+
+Birthplace
+: Japan
+
+Color
+: Green
+
+
+----------------
+
+Tables should have bold headings and alternating shaded rows.
+
+| Artist | Album | Year |
+|-------------------|-----------------|------|
+| Michael Jackson | Thriller | 1982 |
+| Prince | Purple Rain | 1984 |
+| Beastie Boys | License to Ill | 1986 |
+
+If a table is too wide, it should scroll horizontally.
+
+| Artist | Album | Year | Label | Awards | Songs |
+|-------------------|-----------------|------|-------------|----------|-----------|
+| Michael Jackson | Thriller | 1982 | Epic Records | Grammy Award for Album of the Year, American Music Award for Favorite Pop/Rock Album, American Music Award for Favorite Soul/R&B Album, Brit Award for Best Selling Album, Grammy Award for Best Engineered Album, Non-Classical | Wanna Be Startin' Somethin', Baby Be Mine, The Girl Is Mine, Thriller, Beat It, Billie Jean, Human Nature, P.Y.T. (Pretty Young Thing), The Lady in My Life |
+| Prince | Purple Rain | 1984 | Warner Brothers Records | Grammy Award for Best Score Soundtrack for Visual Media, American Music Award for Favorite Pop/Rock Album, American Music Award for Favorite Soul/R&B Album, Brit Award for Best Soundtrack/Cast Recording, Grammy Award for Best Rock Performance by a Duo or Group with Vocal | Let's Go Crazy, Take Me With U, The Beautiful Ones, Computer Blue, Darling Nikki, When Doves Cry, I Would Die 4 U, Baby I'm a Star, Purple Rain |
+| Beastie Boys | License to Ill | 1986 | Mercury Records | noawardsbutthistablecelliswide | Rhymin & Stealin, The New Style, She's Crafty, Posse in Effect, Slow Ride, Girls, (You Gotta) Fight for Your Right, No Sleep Till Brooklyn, Paul Revere, Hold It Now, Hit It, Brass Monkey, Slow and Low, Time to Get Ill |
+
+----------------
+
+Code snippets like `var foo = "bar";` can be shown inline.
+
+Also, `this should vertically align` ~~`with this`~~ ~~and this~~.
+
+Code can also be shown in a block element.
+
+```
+foo := "bar";
+bar := "foo";
+```
+
+Code can also use syntax highlighting.
+
+```go
+func main() {
+ input := `var foo = "bar";`
+
+ lexer := lexers.Get("javascript")
+ iterator, _ := lexer.Tokenise(nil, input)
+ style := styles.Get("github")
+ formatter := html.New(html.WithLineNumbers())
+
+ var buff bytes.Buffer
+ formatter.Format(&buff, style, iterator)
+
+ fmt.Println(buff.String())
+}
+```
+
+```
+Long, single-line code blocks should not wrap. They should horizontally scroll if they are too long. This line should be long enough to demonstrate this.
+```
+
+Inline code inside table cells should still be distinguishable.
+
+| Language | Code |
+|-------------|--------------------|
+| Javascript | `var foo = "bar";` |
+| Ruby | `foo = "bar"{` |
+
+----------------
+
+Small images should be shown at their actual size.
+
+
+
+Large images should always scale down and fit in the content container.
+
+
+
+_The photo above of the Spruce Picea abies shoot with foliage buds: Bjørn Erik Pedersen, CC-BY-SA._
+
+
+## Components
+
+### Alerts
+
+{{< alert >}}This is an alert.{{< /alert >}}
+{{< alert title="Note" >}}This is an alert with a title.{{< /alert >}}
+{{% alert title="Note" %}}This is an alert with a title and **Markdown**.{{% /alert %}}
+{{< alert color="success" >}}This is a successful alert.{{< /alert >}}
+{{< alert color="warning" >}}This is a warning.{{< /alert >}}
+{{< alert color="warning" title="Warning" >}}This is a warning with a title.{{< /alert >}}
+
+
+## Another Heading
+
+Add some sections here to see how the ToC looks like. Bacon ipsum dolor sit amet t-bone doner shank drumstick, pork belly porchetta chuck sausage brisket ham hock rump pig. Chuck kielbasa leberkas, pork bresaola ham hock filet mignon cow shoulder short ribs biltong.
+
+### This Document
+
+Inguina genus: Anaphen post: lingua violente voce suae meus aetate diversi. Orbis unam nec flammaeque status deam Silenum erat et a ferrea. Excitus rigidum ait: vestro et Herculis convicia: nitidae deseruit coniuge Proteaque adiciam *eripitur*? Sitim noceat signa *probat quidem*. Sua longis *fugatis* quidem genae.
+
+
+### Pixel Count
+
+Tilde photo booth wayfarers cliche lomo intelligentsia man braid kombucha vaporware farm-to-table mixtape portland. PBR&B pickled cornhole ugh try-hard ethical subway tile. Fixie paleo intelligentsia pabst. Ennui waistcoat vinyl gochujang. Poutine salvia authentic affogato, chambray lumbersexual shabby chic.
+
+### Contact Info
+
+Plaid hell of cred microdosing, succulents tilde pour-over. Offal shabby chic 3 wolf moon blue bottle raw denim normcore poutine pork belly.
+
+
+### External Links
+
+Stumptown PBR&B keytar plaid street art, forage XOXO pitchfork selvage affogato green juice listicle pickled everyday carry hashtag. Organic sustainable letterpress sartorial scenester intelligentsia swag bushwick. Put a bird on it stumptown neutra locavore. IPhone typewriter messenger bag narwhal. Ennui cold-pressed seitan flannel keytar, single-origin coffee adaptogen occupy yuccie williamsburg chillwave shoreditch forage waistcoat.
+
+
+
+```
+This is the final element on the page and there should be no margin below this.
+```
diff --git a/doc-site/content/en/docs2/Tutorials/_index.md b/doc-site/content/en/docs2/Tutorials/_index.md
new file mode 100755
index 0000000..df2584d
--- /dev/null
+++ b/doc-site/content/en/docs2/Tutorials/_index.md
@@ -0,0 +1,16 @@
+
+---
+title: "Tutorials"
+linkTitle: "Tutorials"
+weight: 8
+date: 2017-01-04
+description: >
+ Show your user how to work through some end to end examples.
+---
+
+{{% pageinfo %}}
+This is a placeholder page that shows you how to use this template site.
+{{% /pageinfo %}}
+
+Tutorials are **complete worked examples** made up of **multiple tasks** that guide the user through a relatively simple but realistic scenario: building an application that uses some of your project’s features, for example. If you have already created some Examples for your project you can base Tutorials on them. This section is **optional**. However, remember that although you may not need this section at first, having tutorials can be useful to help your users engage with your example code, especially if there are aspects that need more explanation than you can easily provide in code comments.
+
diff --git a/doc-site/content/en/docs2/Tutorials/multi-bear.md b/doc-site/content/en/docs2/Tutorials/multi-bear.md
new file mode 100644
index 0000000..0c07e1f
--- /dev/null
+++ b/doc-site/content/en/docs2/Tutorials/multi-bear.md
@@ -0,0 +1,238 @@
+---
+title: "Multi-Bear Domicile Setup"
+date: 2017-01-05
+weight: 4
+description: >
+ A short lead description about this content page. It can be **bold** or _italic_ and can be split over multiple paragraphs.
+---
+
+{{% pageinfo %}}
+This is a placeholder page. Replace it with your own content.
+{{% /pageinfo %}}
+
+Text can be **bold**, _italic_, or ~~strikethrough~~. [Links](https://gohugo.io) should be blue with no underlines (unless hovered over).
+
+There should be whitespace between paragraphs. Vape migas chillwave sriracha poutine try-hard distillery. Tattooed shabby chic small batch, pabst art party heirloom letterpress air plant pop-up. Sustainable chia skateboard art party banjo cardigan normcore affogato vexillologist quinoa meggings man bun master cleanse shoreditch readymade. Yuccie prism four dollar toast tbh cardigan iPhone, tumblr listicle live-edge VHS. Pug lyft normcore hot chicken biodiesel, actually keffiyeh thundercats photo booth pour-over twee fam food truck microdosing banh mi. Vice activated charcoal raclette unicorn live-edge post-ironic. Heirloom vexillologist coloring book, beard deep v letterpress echo park humblebrag tilde.
+
+90's four loko seitan photo booth gochujang freegan tumeric listicle fam ugh humblebrag. Bespoke leggings gastropub, biodiesel brunch pug fashion axe meh swag art party neutra deep v chia. Enamel pin fanny pack knausgaard tofu, artisan cronut hammock meditation occupy master cleanse chartreuse lumbersexual. Kombucha kogi viral truffaut synth distillery single-origin coffee ugh slow-carb marfa selfies. Pitchfork schlitz semiotics fanny pack, ugh artisan vegan vaporware hexagon. Polaroid fixie post-ironic venmo wolf ramps **kale chips**.
+
+> There should be no margin above this first sentence.
+>
+> Blockquotes should be a lighter gray with a border along the left side in the secondary color.
+>
+> There should be no margin below this final sentence.
+
+## First Header 2
+
+This is a normal paragraph following a header. Knausgaard kale chips snackwave microdosing cronut copper mug swag synth bitters letterpress glossier **craft beer**. Mumblecore bushwick authentic gochujang vegan chambray meditation jean shorts irony. Viral farm-to-table kale chips, pork belly palo santo distillery activated charcoal aesthetic jianbing air plant woke lomo VHS organic. Tattooed locavore succulents heirloom, small batch sriracha echo park DIY af. Shaman you probably haven't heard of them copper mug, crucifix green juice vape *single-origin coffee* brunch actually. Mustache etsy vexillologist raclette authentic fam. Tousled beard humblebrag asymmetrical. I love turkey, I love my job, I love my friends, I love Chardonnay!
+
+Deae legum paulatimque terra, non vos mutata tacet: dic. Vocant docuique me plumas fila quin afuerunt copia haec o neque.
+
+On big screens, paragraphs and headings should not take up the full container width, but we want tables, code blocks and similar to take the full width.
+
+Scenester tumeric pickled, authentic crucifix post-ironic fam freegan VHS pork belly 8-bit yuccie PBR&B. **I love this life we live in**.
+
+
+## Second Header 2
+
+> This is a blockquote following a header. Bacon ipsum dolor sit amet t-bone doner shank drumstick, pork belly porchetta chuck sausage brisket ham hock rump pig. Chuck kielbasa leberkas, pork bresaola ham hock filet mignon cow shoulder short ribs biltong.
+
+### Header 3
+
+```
+This is a code block following a header.
+```
+
+Next level leggings before they sold out, PBR&B church-key shaman echo park. Kale chips occupy godard whatever pop-up freegan pork belly selfies. Gastropub Belinda subway tile woke post-ironic seitan. Shabby chic man bun semiotics vape, chia messenger bag plaid cardigan.
+
+#### Header 4
+
+* This is an unordered list following a header.
+* This is an unordered list following a header.
+* This is an unordered list following a header.
+
+##### Header 5
+
+1. This is an ordered list following a header.
+2. This is an ordered list following a header.
+3. This is an ordered list following a header.
+
+###### Header 6
+
+| What | Follows |
+|-----------|-----------------|
+| A table | A header |
+| A table | A header |
+| A table | A header |
+
+----------------
+
+There's a horizontal rule above and below this.
+
+----------------
+
+Here is an unordered list:
+
+* Liverpool F.C.
+* Chelsea F.C.
+* Manchester United F.C.
+
+And an ordered list:
+
+1. Michael Brecker
+2. Seamus Blake
+3. Branford Marsalis
+
+And an unordered task list:
+
+- [x] Create a Hugo theme
+- [x] Add task lists to it
+- [ ] Take a vacation
+
+And a "mixed" task list:
+
+- [ ] Pack bags
+- ?
+- [ ] Travel!
+
+And a nested list:
+
+* Jackson 5
+ * Michael
+ * Tito
+ * Jackie
+ * Marlon
+ * Jermaine
+* TMNT
+ * Leonardo
+ * Michelangelo
+ * Donatello
+ * Raphael
+
+Definition lists can be used with Markdown syntax. Definition headers are bold.
+
+Name
+: Godzilla
+
+Born
+: 1952
+
+Birthplace
+: Japan
+
+Color
+: Green
+
+
+----------------
+
+Tables should have bold headings and alternating shaded rows.
+
+| Artist | Album | Year |
+|-------------------|-----------------|------|
+| Michael Jackson | Thriller | 1982 |
+| Prince | Purple Rain | 1984 |
+| Beastie Boys | License to Ill | 1986 |
+
+If a table is too wide, it should scroll horizontally.
+
+| Artist | Album | Year | Label | Awards | Songs |
+|-------------------|-----------------|------|-------------|----------|-----------|
+| Michael Jackson | Thriller | 1982 | Epic Records | Grammy Award for Album of the Year, American Music Award for Favorite Pop/Rock Album, American Music Award for Favorite Soul/R&B Album, Brit Award for Best Selling Album, Grammy Award for Best Engineered Album, Non-Classical | Wanna Be Startin' Somethin', Baby Be Mine, The Girl Is Mine, Thriller, Beat It, Billie Jean, Human Nature, P.Y.T. (Pretty Young Thing), The Lady in My Life |
+| Prince | Purple Rain | 1984 | Warner Brothers Records | Grammy Award for Best Score Soundtrack for Visual Media, American Music Award for Favorite Pop/Rock Album, American Music Award for Favorite Soul/R&B Album, Brit Award for Best Soundtrack/Cast Recording, Grammy Award for Best Rock Performance by a Duo or Group with Vocal | Let's Go Crazy, Take Me With U, The Beautiful Ones, Computer Blue, Darling Nikki, When Doves Cry, I Would Die 4 U, Baby I'm a Star, Purple Rain |
+| Beastie Boys | License to Ill | 1986 | Mercury Records | noawardsbutthistablecelliswide | Rhymin & Stealin, The New Style, She's Crafty, Posse in Effect, Slow Ride, Girls, (You Gotta) Fight for Your Right, No Sleep Till Brooklyn, Paul Revere, Hold It Now, Hit It, Brass Monkey, Slow and Low, Time to Get Ill |
+
+----------------
+
+Code snippets like `var foo = "bar";` can be shown inline.
+
+Also, `this should vertically align` ~~`with this`~~ ~~and this~~.
+
+Code can also be shown in a block element.
+
+```
+foo := "bar";
+bar := "foo";
+```
+
+Code can also use syntax highlighting.
+
+```go
+func main() {
+ input := `var foo = "bar";`
+
+ lexer := lexers.Get("javascript")
+ iterator, _ := lexer.Tokenise(nil, input)
+ style := styles.Get("github")
+ formatter := html.New(html.WithLineNumbers())
+
+ var buff bytes.Buffer
+ formatter.Format(&buff, style, iterator)
+
+ fmt.Println(buff.String())
+}
+```
+
+```
+Long, single-line code blocks should not wrap. They should horizontally scroll if they are too long. This line should be long enough to demonstrate this.
+```
+
+Inline code inside table cells should still be distinguishable.
+
+| Language | Code |
+|-------------|--------------------|
+| Javascript | `var foo = "bar";` |
+| Ruby | `foo = "bar"{` |
+
+----------------
+
+Small images should be shown at their actual size.
+
+
+
+Large images should always scale down and fit in the content container.
+
+
+
+_The photo above of the Spruce Picea abies shoot with foliage buds: Bjørn Erik Pedersen, CC-BY-SA._
+
+
+## Components
+
+### Alerts
+
+{{< alert >}}This is an alert.{{< /alert >}}
+{{< alert title="Note" >}}This is an alert with a title.{{< /alert >}}
+{{% alert title="Note" %}}This is an alert with a title and **Markdown**.{{% /alert %}}
+{{< alert color="success" >}}This is a successful alert.{{< /alert >}}
+{{< alert color="warning" >}}This is a warning.{{< /alert >}}
+{{< alert color="warning" title="Warning" >}}This is a warning with a title.{{< /alert >}}
+
+
+## Another Heading
+
+Add some sections here to see how the ToC looks like. Bacon ipsum dolor sit amet t-bone doner shank drumstick, pork belly porchetta chuck sausage brisket ham hock rump pig. Chuck kielbasa leberkas, pork bresaola ham hock filet mignon cow shoulder short ribs biltong.
+
+### This Document
+
+Inguina genus: Anaphen post: lingua violente voce suae meus aetate diversi. Orbis unam nec flammaeque status deam Silenum erat et a ferrea. Excitus rigidum ait: vestro et Herculis convicia: nitidae deseruit coniuge Proteaque adiciam *eripitur*? Sitim noceat signa *probat quidem*. Sua longis *fugatis* quidem genae.
+
+
+### Pixel Count
+
+Tilde photo booth wayfarers cliche lomo intelligentsia man braid kombucha vaporware farm-to-table mixtape portland. PBR&B pickled cornhole ugh try-hard ethical subway tile. Fixie paleo intelligentsia pabst. Ennui waistcoat vinyl gochujang. Poutine salvia authentic affogato, chambray lumbersexual shabby chic.
+
+### Contact Info
+
+Plaid hell of cred microdosing, succulents tilde pour-over. Offal shabby chic 3 wolf moon blue bottle raw denim normcore poutine pork belly.
+
+
+### External Links
+
+Stumptown PBR&B keytar plaid street art, forage XOXO pitchfork selvage affogato green juice listicle pickled everyday carry hashtag. Organic sustainable letterpress sartorial scenester intelligentsia swag bushwick. Put a bird on it stumptown neutra locavore. IPhone typewriter messenger bag narwhal. Ennui cold-pressed seitan flannel keytar, single-origin coffee adaptogen occupy yuccie williamsburg chillwave shoreditch forage waistcoat.
+
+
+
+```
+This is the final element on the page and there should be no margin below this.
+```
diff --git a/doc-site/content/en/docs2/Tutorials/tutorial2.md b/doc-site/content/en/docs2/Tutorials/tutorial2.md
new file mode 100644
index 0000000..0cad5b5
--- /dev/null
+++ b/doc-site/content/en/docs2/Tutorials/tutorial2.md
@@ -0,0 +1,238 @@
+---
+title: "Another Tutorial"
+date: 2017-01-05
+weight: 5
+description: >
+ A short lead description about this content page. It can be **bold** or _italic_ and can be split over multiple paragraphs.
+---
+
+{{% pageinfo %}}
+This is a placeholder page. Replace it with your own content.
+{{% /pageinfo %}}
+
+Text can be **bold**, _italic_, or ~~strikethrough~~. [Links](https://gohugo.io) should be blue with no underlines (unless hovered over).
+
+There should be whitespace between paragraphs. Vape migas chillwave sriracha poutine try-hard distillery. Tattooed shabby chic small batch, pabst art party heirloom letterpress air plant pop-up. Sustainable chia skateboard art party banjo cardigan normcore affogato vexillologist quinoa meggings man bun master cleanse shoreditch readymade. Yuccie prism four dollar toast tbh cardigan iPhone, tumblr listicle live-edge VHS. Pug lyft normcore hot chicken biodiesel, actually keffiyeh thundercats photo booth pour-over twee fam food truck microdosing banh mi. Vice activated charcoal raclette unicorn live-edge post-ironic. Heirloom vexillologist coloring book, beard deep v letterpress echo park humblebrag tilde.
+
+90's four loko seitan photo booth gochujang freegan tumeric listicle fam ugh humblebrag. Bespoke leggings gastropub, biodiesel brunch pug fashion axe meh swag art party neutra deep v chia. Enamel pin fanny pack knausgaard tofu, artisan cronut hammock meditation occupy master cleanse chartreuse lumbersexual. Kombucha kogi viral truffaut synth distillery single-origin coffee ugh slow-carb marfa selfies. Pitchfork schlitz semiotics fanny pack, ugh artisan vegan vaporware hexagon. Polaroid fixie post-ironic venmo wolf ramps **kale chips**.
+
+> There should be no margin above this first sentence.
+>
+> Blockquotes should be a lighter gray with a border along the left side in the secondary color.
+>
+> There should be no margin below this final sentence.
+
+## First Header 2
+
+This is a normal paragraph following a header. Knausgaard kale chips snackwave microdosing cronut copper mug swag synth bitters letterpress glossier **craft beer**. Mumblecore bushwick authentic gochujang vegan chambray meditation jean shorts irony. Viral farm-to-table kale chips, pork belly palo santo distillery activated charcoal aesthetic jianbing air plant woke lomo VHS organic. Tattooed locavore succulents heirloom, small batch sriracha echo park DIY af. Shaman you probably haven't heard of them copper mug, crucifix green juice vape *single-origin coffee* brunch actually. Mustache etsy vexillologist raclette authentic fam. Tousled beard humblebrag asymmetrical. I love turkey, I love my job, I love my friends, I love Chardonnay!
+
+Deae legum paulatimque terra, non vos mutata tacet: dic. Vocant docuique me plumas fila quin afuerunt copia haec o neque.
+
+On big screens, paragraphs and headings should not take up the full container width, but we want tables, code blocks and similar to take the full width.
+
+Scenester tumeric pickled, authentic crucifix post-ironic fam freegan VHS pork belly 8-bit yuccie PBR&B. **I love this life we live in**.
+
+
+## Second Header 2
+
+> This is a blockquote following a header. Bacon ipsum dolor sit amet t-bone doner shank drumstick, pork belly porchetta chuck sausage brisket ham hock rump pig. Chuck kielbasa leberkas, pork bresaola ham hock filet mignon cow shoulder short ribs biltong.
+
+### Header 3
+
+```
+This is a code block following a header.
+```
+
+Next level leggings before they sold out, PBR&B church-key shaman echo park. Kale chips occupy godard whatever pop-up freegan pork belly selfies. Gastropub Belinda subway tile woke post-ironic seitan. Shabby chic man bun semiotics vape, chia messenger bag plaid cardigan.
+
+#### Header 4
+
+* This is an unordered list following a header.
+* This is an unordered list following a header.
+* This is an unordered list following a header.
+
+##### Header 5
+
+1. This is an ordered list following a header.
+2. This is an ordered list following a header.
+3. This is an ordered list following a header.
+
+###### Header 6
+
+| What | Follows |
+|-----------|-----------------|
+| A table | A header |
+| A table | A header |
+| A table | A header |
+
+----------------
+
+There's a horizontal rule above and below this.
+
+----------------
+
+Here is an unordered list:
+
+* Liverpool F.C.
+* Chelsea F.C.
+* Manchester United F.C.
+
+And an ordered list:
+
+1. Michael Brecker
+2. Seamus Blake
+3. Branford Marsalis
+
+And an unordered task list:
+
+- [x] Create a Hugo theme
+- [x] Add task lists to it
+- [ ] Take a vacation
+
+And a "mixed" task list:
+
+- [ ] Pack bags
+- ?
+- [ ] Travel!
+
+And a nested list:
+
+* Jackson 5
+ * Michael
+ * Tito
+ * Jackie
+ * Marlon
+ * Jermaine
+* TMNT
+ * Leonardo
+ * Michelangelo
+ * Donatello
+ * Raphael
+
+Definition lists can be used with Markdown syntax. Definition headers are bold.
+
+Name
+: Godzilla
+
+Born
+: 1952
+
+Birthplace
+: Japan
+
+Color
+: Green
+
+
+----------------
+
+Tables should have bold headings and alternating shaded rows.
+
+| Artist | Album | Year |
+|-------------------|-----------------|------|
+| Michael Jackson | Thriller | 1982 |
+| Prince | Purple Rain | 1984 |
+| Beastie Boys | License to Ill | 1986 |
+
+If a table is too wide, it should scroll horizontally.
+
+| Artist | Album | Year | Label | Awards | Songs |
+|-------------------|-----------------|------|-------------|----------|-----------|
+| Michael Jackson | Thriller | 1982 | Epic Records | Grammy Award for Album of the Year, American Music Award for Favorite Pop/Rock Album, American Music Award for Favorite Soul/R&B Album, Brit Award for Best Selling Album, Grammy Award for Best Engineered Album, Non-Classical | Wanna Be Startin' Somethin', Baby Be Mine, The Girl Is Mine, Thriller, Beat It, Billie Jean, Human Nature, P.Y.T. (Pretty Young Thing), The Lady in My Life |
+| Prince | Purple Rain | 1984 | Warner Brothers Records | Grammy Award for Best Score Soundtrack for Visual Media, American Music Award for Favorite Pop/Rock Album, American Music Award for Favorite Soul/R&B Album, Brit Award for Best Soundtrack/Cast Recording, Grammy Award for Best Rock Performance by a Duo or Group with Vocal | Let's Go Crazy, Take Me With U, The Beautiful Ones, Computer Blue, Darling Nikki, When Doves Cry, I Would Die 4 U, Baby I'm a Star, Purple Rain |
+| Beastie Boys | License to Ill | 1986 | Mercury Records | noawardsbutthistablecelliswide | Rhymin & Stealin, The New Style, She's Crafty, Posse in Effect, Slow Ride, Girls, (You Gotta) Fight for Your Right, No Sleep Till Brooklyn, Paul Revere, Hold It Now, Hit It, Brass Monkey, Slow and Low, Time to Get Ill |
+
+----------------
+
+Code snippets like `var foo = "bar";` can be shown inline.
+
+Also, `this should vertically align` ~~`with this`~~ ~~and this~~.
+
+Code can also be shown in a block element.
+
+```
+foo := "bar";
+bar := "foo";
+```
+
+Code can also use syntax highlighting.
+
+```go
+func main() {
+ input := `var foo = "bar";`
+
+ lexer := lexers.Get("javascript")
+ iterator, _ := lexer.Tokenise(nil, input)
+ style := styles.Get("github")
+ formatter := html.New(html.WithLineNumbers())
+
+ var buff bytes.Buffer
+ formatter.Format(&buff, style, iterator)
+
+ fmt.Println(buff.String())
+}
+```
+
+```
+Long, single-line code blocks should not wrap. They should horizontally scroll if they are too long. This line should be long enough to demonstrate this.
+```
+
+Inline code inside table cells should still be distinguishable.
+
+| Language | Code |
+|-------------|--------------------|
+| Javascript | `var foo = "bar";` |
+| Ruby | `foo = "bar"{` |
+
+----------------
+
+Small images should be shown at their actual size.
+
+
+
+Large images should always scale down and fit in the content container.
+
+
+
+_The photo above of the Spruce Picea abies shoot with foliage buds: Bjørn Erik Pedersen, CC-BY-SA._
+
+
+## Components
+
+### Alerts
+
+{{< alert >}}This is an alert.{{< /alert >}}
+{{< alert title="Note" >}}This is an alert with a title.{{< /alert >}}
+{{% alert title="Note" %}}This is an alert with a title and **Markdown**.{{% /alert %}}
+{{< alert color="success" >}}This is a successful alert.{{< /alert >}}
+{{< alert color="warning" >}}This is a warning.{{< /alert >}}
+{{< alert color="warning" title="Warning" >}}This is a warning with a title.{{< /alert >}}
+
+
+## Another Heading
+
+Add some sections here to see how the ToC looks like. Bacon ipsum dolor sit amet t-bone doner shank drumstick, pork belly porchetta chuck sausage brisket ham hock rump pig. Chuck kielbasa leberkas, pork bresaola ham hock filet mignon cow shoulder short ribs biltong.
+
+### This Document
+
+Inguina genus: Anaphen post: lingua violente voce suae meus aetate diversi. Orbis unam nec flammaeque status deam Silenum erat et a ferrea. Excitus rigidum ait: vestro et Herculis convicia: nitidae deseruit coniuge Proteaque adiciam *eripitur*? Sitim noceat signa *probat quidem*. Sua longis *fugatis* quidem genae.
+
+
+### Pixel Count
+
+Tilde photo booth wayfarers cliche lomo intelligentsia man braid kombucha vaporware farm-to-table mixtape portland. PBR&B pickled cornhole ugh try-hard ethical subway tile. Fixie paleo intelligentsia pabst. Ennui waistcoat vinyl gochujang. Poutine salvia authentic affogato, chambray lumbersexual shabby chic.
+
+### Contact Info
+
+Plaid hell of cred microdosing, succulents tilde pour-over. Offal shabby chic 3 wolf moon blue bottle raw denim normcore poutine pork belly.
+
+
+### External Links
+
+Stumptown PBR&B keytar plaid street art, forage XOXO pitchfork selvage affogato green juice listicle pickled everyday carry hashtag. Organic sustainable letterpress sartorial scenester intelligentsia swag bushwick. Put a bird on it stumptown neutra locavore. IPhone typewriter messenger bag narwhal. Ennui cold-pressed seitan flannel keytar, single-origin coffee adaptogen occupy yuccie williamsburg chillwave shoreditch forage waistcoat.
+
+
+
+```
+This is the final element on the page and there should be no margin below this.
+```
diff --git a/doc-site/content/en/docs2/_index.md b/doc-site/content/en/docs2/_index.md
new file mode 100755
index 0000000..d5ec96a
--- /dev/null
+++ b/doc-site/content/en/docs2/_index.md
@@ -0,0 +1,24 @@
+
+---
+title: "Documentation"
+linkTitle: "Documentation"
+weight: 20
+menu:
+ main:
+ weight: 20
+---
+
+{{% pageinfo %}}
+This is a placeholder page that shows you how to use this template site.
+{{% /pageinfo %}}
+
+
+This section is where the user documentation for your project lives - all the information your users need to understand and successfully use your project.
+
+For large documentation sets we recommend adding content under the headings in this section, though if some or all of them don’t apply to your project feel free to remove them or add your own. You can see an example of a smaller Docsy documentation site in the [Docsy User Guide](https://docsy.dev/docs/), which lives in the [Docsy theme repo](https://github.com/google/docsy/tree/master/userguide) if you'd like to copy its docs section.
+
+Other content such as marketing material, case studies, and community updates should live in the [About](/about/) and [Community](/community/) pages.
+
+Find out how to use the Docsy theme in the [Docsy User Guide](https://docsy.dev/docs/). You can learn more about how to organize your documentation (and how we organized this site) in [Organizing Your Content](https://docsy.dev/docs/best-practices/organizing-content/).
+
+
diff --git a/doc-site/content/en/ignore/guide.adoc b/doc-site/content/en/ignore/guide.adoc
new file mode 100644
index 0000000..88ccdde
--- /dev/null
+++ b/doc-site/content/en/ignore/guide.adoc
@@ -0,0 +1,1086 @@
+---
+title: "Programming Guide"
+---
+
+= Behavior Graph
+:icons: font
+:imagesdir: ../images
+:sectlinks:
+:source-highlighter: rouge
+:rouge-theme: molokai
+:toc: left
+//:bg-doc-version: objc
+:bg-doc-version: typescript
+
+
+include::content/en/includes.adoc[tag={bg-doc-version}]
+
+== The Basics
+
+=== Login button
+
+[[login_example1, Login page example]]
+.Login page example
+====
+
+Our first example implements a standard login page.
+
+image::login_ui.png[Login Page]
+
+* A login button should be enabled if a valid email and password have been entered.
+* A valid email has standard email formatting.
+* A valid password is non-empty.
+* As the user types into those fields, the enabled state of the login button will update automatically.
+====
+
+=== A behavior is a unit of functionality
+
+*Behaviors* are the fundamental unit of composition when using Behavior Graph.
+They are blocks of code along with the dependency relationships they are involved in.
+
+====
+Here is the behavior that implements our <>.
+It gets run whenever the email and password fields change.
+It validates those fields and updates the enabled state of the login button accordingly.
+
+[source, {source-language}, numbered, indent=0]
+[[login_behavior]]
+.Login behavior
+----
+include::{sourcedir}/{example-loginextent}[tags=login_enable_behavior]
+----
+====
+
+The bulk of your code will comprise of many behaviors similar to this one.
+Without worrying too much about some unfamiliar details, keep in mind that code inside a behavior is normal code:
+
+* Conventional programming constructs: property access, method calls, and boolean expressions.
+* Platform standard API calls for interacting with the environment
+
+=== A resource is a unit of state
+
+*Resources* carry state in a controlled manner.
+Resources store information about the system you are modeling.
+
+====
+[source, {source-language}, numbered, indent=0]
+.Login behavior
+----
+include::{sourcedir}/{example-loginextent}[tags=login_enable_behavior]
+----
+* Line 1 of references the `email` and `password` properties.
+These are both resources.
+* {source-resource-1} access the same properties via the `extent` object passed into the block.
+
+`email` contains the current textual content of the email UI element.
+`password` plays a similar role.
+====
+
+=== Extents are collections of related behaviors and resources
+
+*Extents* are specialized containers for behaviors and resources.
+You must subclass `{extent-class}`.
+On your subclass:
+
+1. Define resources as properties.
+2. Initialize resources in the {constructor}.
+3. Create behaviors in the {constructor}.
+
+====
+<> needs only a single extent, `LoginExtent`.
+`email` and `password` are defined as properties.
+
+[source, {source-language}, numbered, indent=0]
+----
+include::{sourcedir}/{example-loginextent-header}[tags=login_enable_extent]
+----
+
+`{state-class}` is a resource subtype for retaining state.
+Here they are specialized on the type `{string-class}` for holding the contents of their respective text fields.
+Inside the {constructor} we create the graph elements
+
+[source, {source-language}, numbered, indent=0]
+----
+include::{sourcedir}/{example-loginextent}[tags=login_enable_init]
+----
+====
+
+ifdef::uses-crtp[]
+IMPORTANT: Remember to specify your `{extent-class}` subclass with the generic parameter as the subclass itself.
+This unusual syntax is an instance of the link:https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern[curiously recurring template pattern].
+It is a known technique which lets us be more precise with our types without having to override some common methods, specifically `{behavior-method}` and `{sideeffect-method}`.
+endif::[]
+
+=== {state-class} resources retain information
+
+`{state-class}` is a type of resource.
+It is generic on the type of its contents.
+
+Initial contents are supplied in the {constructor}.
+
+Use the `value` property to access its contents.
+
+Calling `{update-method}` on it will update those contents.
+The contents will remain the same until `{update-method}` is called again with different information.
+
+ifeval::["{bg-doc-version}" == "objc"]
+NOTE: Objective-C "implements" generics with pointer types.
+Therefore storing native types such as booleans and numbers in instances of `{state-class}` requires the use of Objective-C's boxed types such as `NSNumber`.
+endif::[]
+
+=== Demands are the resources a behavior depends on
+
+A behavior has read-only access to a set of resources it depends on called *demands*.
+Behavior Graph uses this dependency information to run the behavior at the correct time.
+
+You must explicitly set the demands on a behavior either when creating a behavior or via the `setDemands` method.
+
+Accessing the `value` property from inside a behavior without specifying it as a demand will raise an error.
+
+Calling `{update-method}` on a demanded resource will also raise an error.
+It is read-only.
+
+====
+In order to determine if the login button should be enabled, our login behavior needs access the information stored in the `email` and `password` resources.
+
+[source, {source-language}, numbered, indent=0]
+.Login behavior
+----
+include::{sourcedir}/{example-loginextent}[tags=login_enable_behavior]
+----
+* Line 1 initializes the behavior with an array containing these resources as demands.
+* {source-resource-1} are able to access the stored contents of `email` and `password` via the `value` property.
+This is because they are listed as demands in line 1.
+
+====
+
+=== Input happens via actions
+
+The *environment* is everything that is not part of your Behavior Graph subsystem.
+In order to track changes to that environment, you program must create new *actions* via the `{action-method}` method on the graph object.
+Inside that you are able to update the contents of resources.
+
+====
+As the user types into the email and password fields, we want our program to react by updating the enabled state of the login button.
+Those UI fields are part of the environment.
+So we write handlers for those fields which will create new actions as those fields change.
+
+[source, {source-language}, numbered, indent=0]
+[[login_actions]]
+.Login actions
+----
+include::{sourcedir}/{example-loginpage}[tags=login_enable_actions]
+----
+
+* Line 1 is a typical callback method that is called from the UI framework when the email text field is updated.
+* Line 2 creates a new action block.
+* Line 3 updates the contents of the `email` resource using the `{update-method}` method.
+This is the same resource instance that is accessed in <>.
+====
+
+TIP: Action blocks are initialized with an optional string called the *impulse*.
+The impulse is a debugging aid to help answer the question, "why is this happening?".
+
+=== Updating a resource causes its demanding behaviors to be activated
+
+Calling `{update-method}` on a resource will change the information stored in that resource.
+When the value changes, any behavior that demands that resource will be *activated*.
+Activated behaviors are put into an internal queue to run in the correct order.
+
+====
+* Each time the user types characters into the email field, the `didUpdateEmailField` will run, creating a new action block.
+* When that action block runs, the `email` resource will update to contain the current contents of the email field.
+* The <> demands the `email` resource, so it activates.
+====
+
+=== The next activated behavior will run after the current action or behavior completes
+
+After the current action or behavior block completes, the Behavior Graph will check if any there are any activated behaviors on its internal queue.
+If so it will remove the top one from the queue and run it.
+This will continue until there are no more activated behaviors on the queue.
+
+====
+* After the action block completes in <>, the activated behavior <> will run.
+====
+
+=== Side effects create output in the environment
+
+While a behavior is running, it may determine that changing circumstances warrant a reaction in the environment.
+Create this mechanism for output by calling `{sideeffect-method}` on the current extent.
+
+All interactions with the environment that do something should happen through *side effects*.
+This lets the Behavior Graph runtime postpone changes to the environment until after any state changes.
+
+====
+[source, {source-language}, numbered, indent=0]
+.Login behavior
+----
+include::{sourcedir}/{example-loginextent}[tags=login_enable_behavior]
+----
+* {source-sideeffect-1} creates a side effect.
+* The next line updates the `enabled` state of the UI element by making a call directly to the platform user interface API.
+====
+
+TIP: The string parameter to `{sideeffect-method}` is an aid for debugging.
+
+=== Behavior Graph is a graph
+
+*Behavior Graph diagrams* provide a compact visual overview of the elements involved in your system.
+
+====
+With our actions, resources, behavior, and side effect, <> takes on the distinctive characteristics of a graph.
+
+image::login_simple_diagram.png[Login Diagram]
+
+* `email` and `password` rectangles are resources.
+* The dashed boxes around them are actions.
+* The dashed box around `enable login button` is a side effect.
+* The solid box around it is the behavior.
+That behavior demands those resources as indicated by the connecting lines.
+====
+
+=== There is a graph instance
+
+A program will need at least one instance of `{graph-class}`.
+This object connects all behaviors, resources, and extents involved in your system.
+It is responsible for running behavior code blocks in the correct order and ensuring relationships are valid.
+
+Create a single instance of `{graph-class}` and make it available to any object which will work with Behavior Graph code.
+Then add your extent instances inside an action and the `{addtograph-method}` method.
+
+====
+
+In the root application code, we create an instance of the `LoginExtent` and add it to our graph.
+
+[source, {source-language}, numbered, indent=0]
+----
+include::{sourcedir}/{example-loginpage}[tags=login_enable_setup]
+----
+
+Line 2 creates a new action on this graph object inside which we add our `LoginExtent` instance.
+
+====
+
+=== An Event is a single run through the graph
+
+An *event* begins with the running of an action block.
+The behavior graph will continue to run activated behaviors until there are none left.
+It will then run side effects in the order they were created.
+When there are no remaining side effects, the event will complete.
+
+====
+Assume the user has just typed a character into the email text field which now contains the letters "sal":
+
+1. The `didUpdateEmailField` method is called, which creates a new action.
+2. The graph object will run the action block.
+3. Inside the action block the `email` resource is updated to contain the string "sal"/
+4. The updated resource activates the login behavior.
+5. The action block completes.
+6. The graph object sees only one behavior has been activated and runs it.
+7. The behavior code block runs, determines email is invalid and creates a side effect to disable the login button.
+8. The behavior code block completes.
+9. With no more activated behaviors, the graph object runs the only side effect.
+10. The side effect block runs the code to disable the login button.
+
+This is all part of a single event.
+When the user types an additional character a new event will run.
+====
+
+== Getting Deeper
+
+=== Complete login page example
+
+[[login_example2, Complete login page]]
+.Complete login page
+====
+
+We can extend our prior example to include the asynchronous interactions for logging in.
+
+image::login_ui_complete.png[Complete login page]
+
+This Behavior Graph diagram includes the additional elements and relationships to support the following additional features:
+
+* Clicking on the login button, when enabled, will make a remote API call with the user's credentials.
+* While the login API call is being made the button will disable.
+* Tapping the return key will also initiate a login.
+* Tapping the return key while the login button is disabled will not login.
+* If the API call returns an error, the login button will reenable.
+====
+
+=== Behaviors are units of state management
+
+The most common role of a behavior is to synthesize new information based on its demands.
+
+It stores this information in resources called it's *supplies*.
+These are resources to which it has read-write access.
+
+A behavior can call `{update-method}` and access the `value` property on any resource that it supplies.
+
+A behavior can supply any number of resources.
+
+====
+This behavior reacts to changes to the email text field by updating its `emailValid` resource based on the contents of the `email` resource.
+
+[source, {source-language}, numbered, indent=0]
+[[login_email_valid]]
+.Login email valid
+----
+include::{sourcedir}/{example-loginextent}[tags=login_complete_email]
+----
+
+* Line 1 initializes the `emailValid` resource, a `{state-class}` resource on the `LoginExtent` object.
+Its initial value is `{false-bool}`.
+* The {behavior-method} method includes `emailValid` in its list of supplied resources in its second parameter.
+* {source-statemanagement-1} calls `{update-method}` to update the contents of `emailValid`.
+ifeval::["{bg-doc-version}" == "objc"]
+Note that we use `@(emailValid)` to provide the boxed `NSNumber*` form of the boolean value.
+endif::[]
+
+
+`passwordValid` receives a similar treatment.
+====
+
+=== Resource updates activate behaviors
+
+The primary reason a behavior runs during any given event is that one or more of its demanded resources has updated.
+
+====
+As the user types into the email text field, the `email` resource updates.
+When it updates, the behavior that supplies `emailValid` activates.
+It will be run during the same event.
+
+The similar behavior that supplies `passwordValid` will not activate during this event.
+Therefore it will not run.
+This is because the `password` resource did not update.
+====
+
+=== Updates filter for equality
+
+ifeval::["{bg-doc-version}" == "objc"]
+`{update-method}` changes the contents of a resource if the new value does not equal the current value.
+endif::[]
+ifeval::["{bg-doc-version}" != "objc"]
+`{update-method}` takes an additional parameter indicating whether the update should filter out new values that are the same as the current value.
+Passing `{true-bool}` in the second parameter will enable this filter.
+endif::[]
+If the contents are the same, say updating from `{false-bool}` to `{false-bool}`, then this version of `{update-method}` does nothing.
+Therefore no demanding behavior will activate.
+Behavior Graph uses the standard platform equality check inside `{update-method}`.
+
+ifeval::["{bg-doc-version}" == "objc"]
+There is also a `updateValueForce` method which will always register as an update without doing an internal equality check.
+endif::[]
+ifeval::["{bg-doc-version}" != "objc"]
+Passing `{false-bool}` as the second parameter will perform no additional check.
+endif::[]
+Using this, any demanding behaviors will always be activated.
+
+====
+[source, {source-language}, numbered, indent=0]
+.Login email valid
+----
+include::{sourcedir}/{example-loginextent}[tags=login_complete_email]
+----
+* On line 1, the `emailValid` resource is initialized with `{false-bool}`.
+As the user begins to type into the email text field it will not be a valid email.
+* {source-statemanagement-1} calls `{update-method}` on `emailValid`. Setting it to `{false-bool}` again will do nothing because we filter out the equality.
+The login enabling behavior which demands this resource will not activate.
+* When the user finally types in a valid email, `emailValid` will change to `{true-bool}`, which will activate and subsequently run the login enabling behavior.
+====
+
+TIP: Equality in programming is a nuanced topic, to say the least.
+If the default method does not work for you, skip the equality check and perform your own.
+
+=== Moment resources capture transient knowledge
+
+In contrast to State resources, Moment resources capture information about what is happening only during the current event.
+A button click or a key press are moments.
+
+Moments are instances of `{moment-class}`.
+
+Calling `{momentupdate-method}` on a moment instance inside an action or supplying behavior will capture the idea that it happened.
+
+Querying a moment's `{momentjustupdated-method}` property will determine if the moment happened during the current event.
+
+Some moments may contain no information beyond merely happening, such as a button click.
+Other moments, such as a successful network response, may also have additional information that you will wish to capture.
+A moment instance will be generic on the type of its contents.
+Pass in this additional information as a parameter to the `{momentupdate-method}` method.
+Query this information using the `value` property.
+
+Moments are transient.
+The contents are only available during the event that they happen.
+They will be forgotten at the end of the event.
+
+====
+`loginClick` and `returnKey` are both moments that update when the user clicks on the button or taps the return key.
+Neither has any additional contents.
+
+[source, {source-language}, numbered, indent=0]
+[[login_complete_click]]
+.Login click
+----
+include::{sourcedir}/{example-loginpage}[tags=login_complete_click]
+----
+====
+
+NOTE: Behavior Graph diagrams show moments as resources with a cutout in the top right corner.
+
+=== Moments activate behaviors when they happen
+
+Similar to a state change in state resources, when a moment happens it is considered updated.
+Any behaviors that demand it will activate.
+
+Behaviors that demand moments will frequently check `{momentjustupdated-method}` to determine how to react during the current event.
+
+====
+This behavior decides when to log in and updates the logging in state.
+`loggingIn` is a state resource, while `loginComplete` captures the moment know our login API call has succeeded.
+
+[source, {source-language}, numbered, indent=0]
+[[login_login]]
+.Logging in behavior
+----
+include::{sourcedir}/{example-loginextent}[tags=login_complete_login]
+----
+
+* The first clause of the `if` statement responds to clicking the login button or pressing the return key to start the login API call.
+* The second clause responds to the completed network call, putting us in a logged in state.
+* Lastly if we have just entered the logging in state, we will initiate that API call.
+
+====
+
+=== State changes can be queried
+
+State resources have a `{statejustupdated-method}` property.
+It is only true during the event in which a successful `{update-method}` occurs.
+
+For more precision, there are also `{statejustupdatedto-method}` and `{statejustupdatedtofrom-method}` methods.
+This check can be done in the same behavior that supplies the resource or any behavior that demands it.
+
+====
+* {source-justupdated-1} of <> updates the value of `loggingIn` to {true-bool} when the user clicks the button or presses the return key.
+* {source-justupdated-2} then checks that same resource via `{statejustupdatedto-method}` in order to determine if the system should do the actual side effect of calling the login API method.
+====
+
+=== Updates activate behaviors
+
+A typical behavior graph program will consist of many state and moment resources.
+It is useful to be able to talk about the general idea behind "happening" or "changing".
+This term is called *updating*.
+
+When a resource updates, any behaviors that demand it will be activated.
+
+Updates can only be checked for inside a behavior if that behavior demands or supplies that resource.
+Doing otherwise is an error
+
+=== Updates can only come from actions or behaviors
+
+If the update comes from a behavior, that resource must be supplied by that behavior.
+A resource may be supplied by _only one behavior_.
+
+If a resource is not supplied by a behavior may it be updated inside an action.
+
+These rules guarantee that a resource will be updated only once per event.
+
+== How it Works
+
+=== Behavior Graph is a bipartite directed acyclic graph
+
+The graph of the Behavior Graph contains two node types, resources and behaviors, making it a bipartite graph.
+
+Edges are the links between resources and behaviors.
+
+.Graph example
+[[graph_example, Graph example]]
+====
+image::graph_example.png[Graph example]
+====
+
+Specifically this is a dataflow dependency graph which has a few simple rules:
+
+* Resources can only point to (demanded by) behaviors
+* Behaviors can only point to (supply) resources
+* A resource can be demanded by any number of behaviors (B1 demands R2 and B2 demands R2)
+* A behavior can supply to 0 or more resources (B1 supplies R5 and R4)
+* A resource can be supplied by either:
+ ** 0 behaviors (R1 has no supplier, therefore it is updated in an action)
+ ** 1 behavior (R5 is supplied only by B1)
+* No cycles are permitted
+
+These rules combine to guarantee that during any event, each behavior will be run no more than once, and each resource will update in at most one place.
+
+=== Behavior Graph runs code in the correct order
+
+The value proposition for the Behavior Graph comes down to letting the computer manage control flow.
+Control flow means deciding what code should run next.
+
+After completing an action or behavior, the Behavior Graph will select the next behavior off the queue to run.
+However there may be multiple behaviors on that queue waiting to run.
+Behavior Graph uses a priority queue based on the topological ordering of all the behaviors in the graph.
+
+====
+We can see how this matters looking at <>.
+
+1. When *R1* is updated, the graph knows that *B1* should be run next.
+2. When *R2* is updated however, both *B1* and *B2* are activated.
+3. The graph must run *B1* first because *B1* may update *R5* which is another demand of *B2*.
+====
+
+=== Graph cycles are not permitted
+
+Behavior Graph will throw an error when a graph cycle is detected as extents are added to the graph.
+However, cycles can be easy to create.
+
+====
+Referring to <>, a potential cyclical error occurs with `returnKey`:
+
+1. Pressing the return key, when login is enabled, should put the system in a logging in state.
+2. When the system is in a logging in state, the login button should be disabled.
+3. When login is disabled, the return key should no longer work.
+
+The dependency chain for this looks like:
+`returnKey -> loggingIn behavior -> loggingIn -> loginEnabled behavior -> loginEnabled -> loggingIn behavior (cycle)`
+
+Our solution is to recognize that when pressing the return key, the system is not interested in what state `loginEnabled` will enter during that event, but what it was as the event begins.
+
+* Pressing the return key should not try to log the user in again if loginEnabled is {false-bool}.
+* If it is {true-bool} then the user can log in at which point it will become {false-bool}.
+====
+
+== Fixing Problems
+
+=== State resources have trace values
+
+Use trace values to break cycles.
+
+Calling `traceValue` on a state resource inside a behavior will return its contents as they are at the beginning of the event.
+If at some point during the same event the contents change from an update, the `traceValue` will continue to return the value that it was before that update.
+
+Accessing the `traceValue` of a resource does not require it to be listed in the demands list of the behavior.
+
+It is important to remember that `traceValue` is different than the prior value.
+It is only the prior value if it updated during this event.
+After the event ends, `traceValue` and `value` will return the same contents.
+
+====
+
+[source, {source-language}, numbered, indent=0]
+.Logging in behavior
+----
+include::{sourcedir}/{example-loginextent}[tags=login_complete_login]
+----
+
+{source-tracevalue-1} accesses `loginEnabled.traceValue` to prevent a potential cycle.
+It is not listed in the demands and Behavior Graph will not throw an error.
+
+The behavior graph diagram in <> renders the dependency between `loginEnabled` and the behavior that supplies `loggingIn` with a dotted line to show the connection.
+====
+
+In Behavior Graph diagrams, trace value dependencies are drawn as a dotted line and point to the lower portion of the behavior to visually separate them from other dependencies.
+
+=== Use the debug console to track down cycles
+
+Cycles that span many behaviors and extents are not always obvious.
+The console will print the sequence of behaviors and resources that create the cycle.
+Use this information to help determine where the chain can be broken.
+
+You can call `cycleStringForBehavior` on the instance of the {graph-class} to print or step through these objects.
+
+=== Complex behaviors can make it easier to create cycles
+
+TIP: Combining many different demands and supplies in the same behavior can be practical and sometimes necessary.
+However, these behaviors also make it easier to create graph cycles during the development phase.
+Separating out independent functionality into different behaviors can often free up cycles.
+
+=== Side effect blocks run after all behaviors
+
+Behaviors may create side effects.
+By design, those side effects do not run until there are no more activated behaviors in the queue, ie the end of the event.
+
+====
+[source, {source-language}, numbered, indent=0]
+[[login_complete_enable]]
+.Login Enable
+----
+include::{sourcedir}/{example-loginextent}[tags=login_complete_enable]
+----
+
+As can be seen from the <> diagram, this behavior has no downstream dependencies and will be the last to be run.
+{source-sideeffect-1} creates a side effect which will not be run until after this behavior completes.
+
+Likewise when the user clicks the login button the first time, the side effect on {source-sideeffect-2} of <> will also not be run until this behavior completes.
+
+====
+
+The reason for postponing the running of side effects is to ensure that unstable information doesn't leak out into the environment.
+As a side effect block runs, Behavior Graph must relinquish control flow.
+That code may call out to some external library which subsequently calls back in to read the current value of a resource.
+If we were in the behavior phase of an event, any number of resources may still need updating to new values.
+The resources in the system are only in a stable state after all activated behaviors have been run.
+
+=== Side effect blocks are run in the order they are created
+
+Most systems will need some ability to maintain sequential dependencies between the code run in side effects.
+For example one behavior may create the side effect for initializing a video player.
+A separate behavior may create a side effect which tells the video player to play.
+It is necessary to ensure the video player is created before it is told to play.
+
+====
+In <>, the login call will be made before the button is disabled when the user clicks the login button.
+This is because the two behaviors that create those side effects will be run in that order.
+====
+
+If your code has implicit dependencies between side effect blocks, you can make them explicit by creating a dependency between their creating behaviors.
+Do this by creating a resource which is supplied by the behavior that creates the first side effect and demanded by the behavior that creates the second side effect.
+
+=== Events are serialized
+
+It is possible for an action to be created while another event is currently executing when a side effect block leads to a new action and there are still side effects remaining.
+However, interrupting the current event to run another one could lead to inconsistent results.
+It is necessary for the entire event to work as a single transaction.
+
+When this happens:
+
+1. The new action will be placed in a queue and the current event will continue to run any remaining side effects of current event at the action creation point.
+2. When the current event completes, the newly queued up action will create an event and run until completion.
+3. This will continue until there are no more queued actions, at which point the code will continue.
+
+====
+[source, {source-language}, numbered, indent=0]
+.Logging in behavior
+----
+include::{sourcedir}/{example-loginextent}[tags=login_complete_login]
+----
+1. When the user clicks the login button, this behavior that logs the user in will activate and run.
+2. {source-serialize-1} will be run during the side effect phase of the event.
+3. It is possible this external `login` method may immediately call the completion block which will then create a new action on {source-serialize-2}.
+4. The first event created by clicking the login button is still running. It needs to complete before running this new login complete action. So it will place the login complete action in the queue and continue to its next side effect which will disable the login button.
+5. After running the remaining side effect, the first event will complete, and the code inside the action block created on {source-serialize-2} will run.
+6. This new event will run to completion.
+7. Finally the stack from the initial side effect created on {source-serialize-3} will complete and unwind.
+====
+
+=== Events are synchronized onto the same thread
+
+Behavior Graph does not handle concurrency.
+All events are serialized onto the same thread which can be specified when initializing the `{graph-class}` object.
+
+=== Actions blocks are run synchronously
+
+When the environment creates a new action, that action block and the entire event will be run synchronously.
+The line of code that follows the action will not be run until the event created by that action has run to completion.
+
+This default matches imperative programming expectations.
+If a line of code following the action block attempts to read the value of a resource, you can expect it to have been updated accordingly.
+And all side effect blocks created as a result of that action will be run before that next line of code.
+
+As mentioned previously, however, there may already be a running event or queued actions when a new action is created.
+Many events may get run between the time an action is created and when its own corresponding event is run.
+These events will all run to completion before the code that creates the action returns.
+
+====
+[source, {source-language}, numbered, indent=0]
+.Login click
+----
+include::{sourcedir}/{example-loginpage}[tags=login_complete_click]
+----
+This method creates a new action on line 2; however, we cannot guarantee that the code inside the action is run immediately.
+The Behavior Graph does guarantee that all events will have completed before proceeding past line 4 and exiting this method.
+====
+
+=== Side effects can't be strictly enforced
+
+You should always create a side effect when you wish to output information to the environment.
+However, strictly enforcing this policy would require wrapping every possible type of output.
+
+Instead, the code in behavior blocks is normal imperative code.
+Technically anything is possible.
+In order to manage control flow, the Behavior Graph cannot handle a new synchronous action while still running behaviors during an event.
+
+If, from inside a behavior (not side effect), you create a synchronous action or call some code that eventually leads to a synchronous action, Behavior Graph will raise an error.
+
+Also, if, from inside a behavior (not side effect), you call some code that eventually leads to accessing a non-demanded resource, Behavior Graph will raise an error.
+
+Instances of this error can be subtle.
+Be careful when working with anything that may affect the environment directly from a behavior.
+This includes memory allocation, destructors, exceptions, or any other impure code.
+
+=== Action blocks can have optional synchrony (but not the default)
+
+The safest way to create a new action is to opt out of synchrony.
+To do this call `{asyncaction-method}`.
+
+This method of creating a new action will run the action and associated event immediately if there are no events running currently.
+Otherwise, it will put them on a queue to be run after the current event (and any additional queued actions) are run, returning up the call stack.
+
+Prefer this variation whenever possible.
+
+====
+A better implementation of the login side effect in <> would opt out of synchrony.
+
+[source, {source-language}, numbered, indent=0]
+----
+include::{sourcedir}/{example-loginextent}[tags=login_complete_loginalt]
+----
+
+{source-async} uses the optional asynchronous form when creating a new action.
+Opting out of synchrony here means the side effect created on line 1 will exit its call stack before starting the event associated with this new action.
+====
+
+=== Events have sequence numbers
+
+When a resource is updated it retains a reference to the instance of `{event-class}` in which it was last updated.
+These event instances have a monotonically increasing sequence number (every event run is +1 the prior event).
+You can use these sequence numbers to compare multiple resources to determine the order they were updated.
+
+If this `event` property is accessed inside a behavior its resource must be declared as a demand.
+There is a corresponding `traceEvent` available if such a dependency creates a cycle.
+====
+
+We can compare the timestamps of the `password` and `email` resources to see if one were updated more recently.
+
+[source, {source-language}, numbered, indent=0]
+----
+include::{sourcedir}/{example-loginextent}[tags=login_sequence_compare]
+----
+
+====
+
+=== Events have timestamps
+
+Each event also gets a timestamp when the event is run.
+
+It is a common programing idiom to interweave code with timestamp parameters in order to facilitate testing and instrumentation.
+By providing this information as part of each event, behaviors and side effects have easy access without the typical crosscutting burden.
+
+====
+We can reference when the login happened in some security auditing code.
+
+[source, {source-language}, numbered, indent=0]
+----
+include::{sourcedir}/{example-loginextent}[tags=login_timestamp]
+----
+====
+
+For testing purposes you can mock out the date provider of the graph object in one place.
+Override the `{dateprovider-property}` property with your own implementation of {dateprovider-class}.
+
+== Architecting Systems
+
+=== Video Chat Example
+
+We will now change our example app to a hypothetical but familiar video chat experience.
+
+[[video_chat, Video chat]]
+.Video chat
+====
+image::video_chat_ui.png[Video chat]
+
+* There will be a grid of squares showing the live video feed for a number of participants.
+* Each participant will have a small button overlay for toggling their mute.
+* Each participant will have a small button overlay to pin that participant.
+* When a participant is pinned, that ui will appear larger than the others.
+* Only one participant can be pinned at the same time.
+If there is already a pinned participant, tapping on the pin button of another will unpin the first and pin the new one.
+
+The Behavior Graph diagram for this functionality looks like this
+
+image::video_chat_diagram.png[Video Chat Diagram]
+
+====
+
+=== Extents capture lifetimes
+
+Extents can come and go, reflecting the range of time they play a useful role in your system.
+
+====
+In the <> example, we will have two types of extents.
+
+A single `ChatExtent` will be created and added to the graph every time we initiate a new chat.
+It reflects the lifetime of the video chat.
+Once the chat is over, it no longer plays a functioning roll and can be removed from the graph.
+
+[source, {source-language}, numbered, indent=0]
+----
+include::{sourcedir}/{example-chat-chat-header}[tags=chat_extent]
+----
+
+A `ParticipantExtent` will be created each time a new participant joins the video chat.
+Each chat may have multiple participants.
+They may come and go at any time during the video chat.
+
+[source, {source-language}, numbered, indent=0]
+----
+include::{sourcedir}/{example-chat-participant-header}[tags=participant_extent]
+----
+
+====
+
+=== Extents exclusively own behaviors and resources
+
+Behaviors and resources always belong to one and only one extent.
+
+They do not exist independently from their owning extent.
+
+You cannot create a resource on one extent and assign it to a member variable on another.
+
+=== Resources are named and assigned to extent member variables
+
+You will regularly refer to resources by name via their extent.
+
+====
+Our mute functionality requires some resources which we declare as properties on the ParticipantExtent.
+
+[source, {source-language}, numbered, indent=0]
+----
+include::{sourcedir}/{example-chat-participant-header}[tags=participant_mute_resources]
+----
+
+* `muteTap` is a moment resource that tracks the user tapping on the mute button.
+* `muted` is a state resource that captures the local muted status of that participant.
+ifeval::["{bg-doc-version}" == "objc"]
+It is an `NSNumber` to store our Objective-C boxed boolean.
+endif::[]
+
+We will initialize and assign them in the constructor along with the behavior that links to them.
+
+[source, {source-language}, numbered, indent=0]
+[[participant_mute]]
+.Mute behavior
+----
+include::{sourcedir}/{example-chat-participant}[tags=participant_mute]
+----
+
+On line 3 we refer to their member variables directly when specifying the linked resources for this behavior.
+On almost every line thereafter we refer to them by name via their extent.
+
+====
+
+=== Behaviors are typically unnamed
+
+Initialize behaviors via the `{extent-class}` factory method `{behavior-method}`.
+
+Behaviors can have their linked resources redefined dynamically.
+You can optionally save the results of that method to a member variable if you wish to do this.
+
+====
+[source, {source-language}, numbered, indent=0]
+----
+include::{sourcedir}/{example-chat-participant}[tags=participant_mute_alt]
+----
+====
+
+=== Behaviors and resources are added to and removed from a graph via extents
+
+Add behaviors and resources to the graph by adding their owning extent.
+Use the `{addtograph-method}` method of the extent object.
+
+Adding and removing extents must happen during an event– either inside an action or behavior.
+Doing otherwise will raise an error.
+
+Behaviors which have not yet been added to the graph will not run.
+Updating a resource which has not yet been added to the graph will raise an error.
+
+====
+
+During a video chat, our system will need to handle participants joining and leaving.
+We will model this by adding and removing instances of `ParticipantExtent` to the graph.
+
+To capture this functionality, we will have a behavior that demands resources indicating that a participant has joined or left.
+When these moments happen our behavior can either create and add the new extent or remove the old one.
+
+[source, {source-language}, numbered, indent=0]
+[[participants_add]]
+.Add participants
+----
+include::{sourcedir}/{example-chat-chat}[tags=participants]
+----
+
+Each time a new participant joins our video chat, the moment resource `participantJoined` will update, activating this behavior.
+In response we will create a new instance of `ParticipantExtent` on {source-addtograph-1}.
+We add it to the graph on the next line.
+
+If the participant later disconnects we can remove it's functionality from the graph.
+{source-addtograph-2} removes it from the graph.
+
+====
+
+Once an extent has been removed from the graph its elements will be removed and unlinked from any other elements still in the graph.
+A removed behavior will no longer activate in response to resources it demanded.
+A removed resource will no longer activate behaviors and updating it will result in an error.
+
+=== Behaviors always activate in the same event that their extent is added to a graph
+
+When programming a behavior it is important to remember that it will get run at this time.
+Use this to set initial values on state resources when information is not available at initialization time.
+
+ifeval::["{bg-doc-version}" == "objc"]
+endif::[]
+
+=== Extents are the execution context for behaviors and sideEffects
+
+Extents are reference objects.
+They enable code inside behaviors and side effects to interact with other graph elements or the environment.
+The extent parameter passed into behaviors and side effects is this point of reference.
+
+NOTE: Most object oriented languages predefine `self` or `this` to serve a similar role inside methods.
+
+====
+[source, {source-language}, numbered, indent=0]
+.Mute behavior
+----
+include::{sourcedir}/{example-chat-participant}[tags=participant_mute]
+----
+
+Here we can see nearly every single line in both the behavior and the side effect utilize their `extent` parameter to refer to resources and call methods.
+====
+
+=== Resources and behaviors can link across extents
+
+Frequently information stored in a resource will outlive the behaviors that react to changes in that information.
+To enable this, a behavior can demand or supply resources from extents different from their owning extent.
+
+====
+In our <>, pinning one participant means unpinning the previous one if available.
+The resource which can track which participant is currently pinned must outlive any particular participant.
+
+The central `ChatExtent` has a resource `pinnedParticipant` that stores a reference to the currently pinned `ParticipantExtent`.
+Each `ParticipantExtent` instance has a behavior that demands that resource.
+
+[source, {source-language}, numbered, indent=0]
+----
+include::{sourcedir}/{example-chat-participant}[tags=participant_pinned]
+----
+* Line 1 demands the common `pinnedParticipant` on the `ChatExtent` instance stored in the member variable `chatExtent`.
+* We wish for the UI to draw the pinned participant larger than the others.
+The first clause of the if statement, checks to see if the `pinnedParticipant` became this extent and creates a side effect to enact that change.
+* The second clause checks if the pinned participant is no longer this extent, returning it to normal size.
+
+Because each `ParticipantExtent` instance demands this central resource, updating that one resource will automatically cause this behavior in each participant to update its respective UI during the same event.
+====
+
+=== Store related extents in a collection resource
+
+Many applications naturally organize into a hierarchy of lifetimes.
+These different lifetimes should be modeled with extents.
+
+The parent extent should own a state resource containing a collection of child extents.
+
+====
+[source, {source-language}, numbered, indent=0]
+.Add participants
+----
+include::{sourcedir}/{example-chat-chat}[tags=participants]
+----
+
+In our <>, the lifetime of each participant is necessarily bounded by the lifetime of the entire chat.
+
+On our `ChatExtent` we create a `participants` resource to hold on to them.
+This state resource holds a dictionary which maps a participant id string to each participant extent.
+
+[source, {source-language}, numbered, indent=0]
+----
+include::{sourcedir}/{example-chat-chat-header}[tags=chat_participants_resources]
+----
+
+In the behavior that supplies this resource, we update it any time a participant joins or disconnects.
+
+It is worth noting how we update the `participants` resource in this behavior.
+
+1. On {source-extentcollections-1} we directly manipulate the stored dictionary.
+2. On the following lines {source-extentcollections-2}, we update using the same dictionary instance and skipping the equality check.
+By skipping this equality check, we activate any behaviors which demand the resource while also keeping the same collection.
+If we did not skip the equality check, the state resource would recognize that the collection was the same instance and make no update.
+
+====
+
+=== Relink behaviors dynamically
+
+Behaviors may link to resources in other extents.
+Those extents may come and go.
+Therefore it is necessary to change their links separately from when those behaviors were initialized and added to the graph.
+
+You may call `setDemands` and `setSupplies` on a behavior to change those links.
+Once that behavior has been added to the graph, you can only call those methods from inside an action or behavior block.
+
+It is an error to modify a behavior's links during an event if that behavior has already run during that event.
+To prevent this, the behavior that modifies the links should supply a special resource which the Behavior Graph will use for proper ordering.
+The behavior which has its links modified should demand this ordering resource.
+
+====
+To fully implement our pinning functionality, there will be a `pinTap` moment resource on each `ParticipantExtent`.
+
+[source, {source-language}, numbered, indent=0]
+----
+include::{sourcedir}/{example-chat-participant-header}[tags=participant_pin_resources]
+----
+
+There will also be a behavior in the `ChatExtent` to decide which participant should be pinned.
+This behavior demands the `pinTap` in each `ParticipantExtent` in order to be activated whenever any of those resources is updated.
+However, because participants can come and go, we will not have access to all of these resources at initialization time.
+So we will leave them out initially in the demands list when we define our pinned behavior.
+
+[source, {source-language}, numbered, indent=0]
+[[chat_pinned]]
+.Pinned Behavior
+----
+include::{sourcedir}/{example-chat-chat}[tags=chat_pinned]
+----
+
+* On {source-dynamic-1}, we iterate through each `ParticipantExtent` instance in the `participants` resource.
+* The next lines inspect the `pinTap` resource on each extent to determine if the user tapped on one or if it should maintain the current pinned participant.
+
+At this point however, the behavior will not get run when a pinTap resource is updated.
+Additionally it will be an error for it to access the `pinTap` resources because it does not demand them.
+We will introduce an additional behavior which updates these demands as the participants change.
+
+[source, {source-language}, numbered, indent=0]
+----
+include::{sourcedir}/{example-chat-chat}[tags=chat_relink_pinned]
+----
+
+Each time a new participant is added or removed (updating the `participants` resource), this behavior will activate and update the demands of <>.
+
+* On {source-dynamic-2}, `participants` and `participantsRelink` are added to the array.
+They are added because they are part of the default list of demands when <> was initialized.
+* Next we iterate through each `ParticipantExtent` instance and add its `pinTap` resource to the array.
+* Lastly {source-dynamic-3} updates the demands of that behavior.
+
+Note the use of `participantsRelink` resource.
+It is of the base type `{resource-class}` which has limited functionality.
+This is our ordering resource.
+It has no value and is not updated.
+It only exists to ensure that the demands of the `pinnedBehavior` are updated before the behavior itself is run.
+====
+
+=== Supplies can also be relinked dynamically
+
+It is less useful in practice.
+Occasionally you may wish to have the behavior on one of many child extents update a resource on the parent level.
+As these extents come and go, you may need to designate a new supplier.
+
+=== Updating links will cause dependent behaviors to activate
+
+When the demands of a behavior change, that behavior is activated and run to ensure it is fully taking into account its current set of demands.
+
+When a resource is supplied by a new behavior any behaviors that demand that resource will activate.
+
+=== Removed resources and behaviors are automatically removed from demands and supplies
+
+Whenever an extent is removed from the graph all its resources and behaviors will also be removed.
+Any remaining resources and behaviors will have their links to the removed elements severed.
+
+=== Removed behaviors will not run in the event they are removed
+
+If an activated behavior is removed before it is run, it will not be run.
+
+=== A program may have multiple instances of a graph
+
+A program or library can have any number of independent instances of a {graph-class}.
+Behaviors and resources cannot link across graph instances.
+Extents cannot migrate between graph instances.
+
+=== Independent graphs may communicate through message passing
+
+One graph may pass information to another by creating a side effect on one which creates a new action on the other.
diff --git a/doc-site/content/en/ignore/includes.adoc b/doc-site/content/en/ignore/includes.adoc
new file mode 100644
index 0000000..d36deef
--- /dev/null
+++ b/doc-site/content/en/ignore/includes.adoc
@@ -0,0 +1,172 @@
+tag::objc[]
+:sourcedir: ./examples/objc/BGExamples
+:source-language: objectivec
+:false-bool: @NO
+:true-bool: @YES
+:resource-class: BGResource
+:state-class: BGState
+:moment-class: BGMoment
+:extent-class: BGExtent
+:graph-class: BGGraph
+:event-class: BGEvent
+:action-method: action
+:sideeffect-method: sideEffect:
+:addtograph-method: addToGraph
+:behavior-method: behaviorWithDemands:
+:update-method: updateValue:
+:momentupdate-method: update
+:momentjustupdated-method: justUpdated
+:statejustupdated-method: justUpdated
+:statejustupdatedto-method: justUpdatedTo:
+:statejustupdatedtofrom-method: justUpdatedTo:from:
+:asyncaction-method: action:requireSync:NO
+:dateprovider-property: dateProvider
+:dateprovider-class: BGDateProvider
+:example-loginextent: LoginExtent.m
+:example-loginextent-header: LoginExtent.h
+:example-loginpage: LoginPageViewController.m
+:example-chat-chat: ChatExtent.m
+:example-chat-chat-header: ChatExtent.h
+:example-chat-participant: ParticipantExtent.m
+:example-chat-participant-header: ParticipantExtent.h
+:constructor: initializer
+:string-class: NSString
+:source-resource-1: Lines 5 and 6
+:source-sideeffect-1: Line 9
+:source-statemanagement-1: Line 8
+:source-justupdated-1: Line 11
+:source-justupdated-2: Line 18
+:source-tracevalue-1: Line 9
+:source-sideeffect-1: Line 10
+:source-sideeffect-2: line 20
+:source-serialize-1: Line 21
+:source-serialize-2: line 23
+:source-serialize-3: line 19
+:source-async: Line 4
+:source-addtograph-1: line 9
+:source-addtograph-2: Line 19
+:source-extentcollections-1: lines 12 and 20
+:source-extentcollections-2: lines 13 and 21
+:source-dynamic-1: line 8
+:source-dynamic-2: lines 5 and 6
+:source-dynamic-3: line 10
+:uses-crtp:
+:readme-doc: readme-objc.html
+:guide-doc: objc.html
+:github-project: bgobjc
+end::objc[]
+
+tag::typescript[]
+:sourcedir: ./examples/typescript/BGExamples
+:source-language: typescript
+:false-bool: false
+:true-bool: true
+:resource-class: Resource
+:state-class: State
+:moment-class: Moment
+:extent-class: Extent
+:graph-class: Graph
+:event-class: GraphEvent
+:action-method: action
+:sideeffect-method: sideEffect
+:addtograph-method: addToGraph
+:behavior-method: makeBehavior
+:update-method: update
+:momentupdate-method: update
+:momentjustupdated-method: justUpdated
+:statejustupdated-method: justUpdated
+:statejustupdatedto-method: justUpdatedTo
+:statejustupdatedtofrom-method: justUpdatedToFrom
+:asyncaction-method: actionAsync
+:dateprovider-property: dateProvider
+:dateprovider-class: BehaviorGraphDateProvider
+:example-loginextent: LoginExtent.ts
+:example-loginextent-header: LoginExtent.ts
+:example-loginpage: LoginExtent.ts
+:example-chat-chat: ChatExample.ts
+:example-chat-chat-header: ChatExample.ts
+:example-chat-participant: ChatExample.ts
+:example-chat-participant-header: ChatExample.ts
+:constructor: constructor
+:string-class: string
+:source-resource-1: Lines 2 and 3
+:source-sideeffect-1: Line 6
+:source-statemanagement-1: Line 5
+:source-justupdated-1: Line 9
+:source-justupdated-2: Line 17
+:source-tracevalue-1: Line 7
+:source-sideeffect-1: Line 7
+:source-sideeffect-2: line 18
+:source-serialize-1: Line 19
+:source-serialize-2: line 21
+:source-serialize-3: line 18
+:source-async: Line 3
+:source-addtograph-1: line 8
+:source-addtograph-2: Line 17
+:source-extentcollections-1: lines 10 and 18
+:source-extentcollections-2: lines 11 and 19
+:source-dynamic-1: line 6
+:source-dynamic-2: lines 4 and 5
+:source-dynamic-3: line 9
+:readme-doc: readme-typescript.html
+:guide-doc: typescript.html
+:github-project: bgjs
+end::typescript[]
+
+tag::kotlin[]
+:sourcedir: ./examples/kotlin/BGExamples
+:source-language: kotlin
+:false-bool: false
+:true-bool: true
+:resource-class: Resource
+:state-class: State
+:moment-class: Moment
+:extent-class: Extent
+:graph-class: Graph
+:event-class: GraphEvent
+:action-method: action
+:sideeffect-method: sideEffect
+:addtograph-method: addToGraph
+:behavior-method: makeBehavior
+:update-method: update
+:momentupdate-method: update
+:momentjustupdated-method: justUpdated
+:statejustupdated-method: justUpdated
+:statejustupdatedto-method: justUpdatedTo
+:statejustupdatedtofrom-method: justUpdatedToFrom
+:asyncaction-method: actionAsync
+:dateprovider-property: dateProvider
+:dateprovider-class: BehaviorGraphDateProvider
+:example-loginextent: LoginExtent.kt
+:example-loginextent-header: LoginExtent.kt
+:example-loginpage: LoginExtent.kt
+:example-chat-chat: ChatExample.kt
+:example-chat-chat-header: ChatExample.kt
+:example-chat-participant: ChatExample.kt
+:example-chat-participant-header: ChatExample.kt
+:constructor: init
+:string-class: String
+:source-resource-1: Lines 2 and 3
+:source-sideeffect-1: Line 6
+:source-statemanagement-1: Line 5
+:source-justupdated-1: Line 9
+:source-justupdated-2: Line 17
+:source-tracevalue-1: Line 7
+:source-sideeffect-1: Line 7
+:source-sideeffect-2: line 18
+:source-serialize-1: Line 19
+:source-serialize-2: line 21
+:source-serialize-3: line 18
+:source-async: Line 3
+:source-addtograph-1: line 8
+:source-addtograph-2: Line 17
+:source-extentcollections-1: lines 10 and 18
+:source-extentcollections-2: lines 11 and 19
+:source-dynamic-1: line 6
+:source-dynamic-2: lines 4 and 5
+:source-dynamic-3: line 9
+:uses-crtp:
+:readme-doc: readme-kotlin.html
+:guide-doc: kotlin.html
+:github-project: bgkotlin
+end::kotlin[]
diff --git a/doc-site/content/en/ignore/intro.adoc b/doc-site/content/en/ignore/intro.adoc
new file mode 100644
index 0000000..398d6b1
--- /dev/null
+++ b/doc-site/content/en/ignore/intro.adoc
@@ -0,0 +1,273 @@
+---
+title: "Intro"
+---
+
+= Behavior Graph
+:icons: font
+:imagesdir: /images
+:sectlinks:
+:source-highlighter: rouge
+:rouge-theme: molokai
+:toc: left
+//:bg-doc-version: objc
+:bg-doc-version: typescript
+:github-root: https://github.com/yahoo/
+:pages-root: https://yahoo.github.io/bgdocs/docs/
+include::content/en/includes.adoc[tag={bg-doc-version}]
+
+== Links
+* link:{github-root}{github-project}[Github]
+* link:{pages-root}{bg-doc-version}/intro.html[Intro (this document)]
+* link:{pages-root}{bg-doc-version}/guide.html[Behavior Graph Programming Guide]
+ifeval::["{bg-doc-version}" == "objc"]
+* *Cocoapods* (Objective-C): BehaviorGraph
+endif::[]
+ifeval::["{bg-doc-version}" == "typescript"]
+* *NPM* link:https://www.npmjs.com/package/behavior-graph[`behavior-graph`]
+* *jsDelivr* https://cdn.jsdelivr.net/npm/behavior-graph/lib/behavior-graph.min.js
+endif::[]
+
+ifeval::["{bg-doc-version}" == "kotlin"]
+???
+endif::[]
+
+
+== Safe Mutable State
+
+**Behavior Graph** is a software library that greatly enhances our ability to program **user facing software** and **control systems**.
+Programs of this type quickly scale up in complexity as features are added. Behavior Graph directly addresses this complexity by shifting more of the burden to the computer.
+It works by offering the programmer a new unit of code organization called a **behavior**.
+Behaviors are blocks of code enriched with additional information about their stateful relationships.
+Using this information, Behavior Graph enforces _safe use of mutable state_, arguably the primary source of complexity in this class of software.
+It does this by taking on the responsibility of control flow between behaviors, ensuring they are are _run at the correct time and in the correct order_.
+
+=== State
+
+It helps to understand why user facing software, control systems, and similar programs present a particular challenge.
+
+We define these systems by three primary characteristics:
+
+1. *Asynchronous*: inputs happen over a period of time
+2. *Event-driven*: outputs occur over time in response to inputs
+3. *Stateful*: outputs depend on a history of prior inputs
+
+A thermostat controlling the temperature in a house is an example:
+
+image::thermostat-wall.svg[Login page]
+
+1. It runs continuously, responding to temperature changes as well as button presses in order to operate the heating equipment.
+2. Button presses will result in changes to the display.
+3. Button presses which set the desired temperature will determine when the heating equipment turns on in the future.
+
+The challenge comes from the large number of different inputs where order and history matter.
+A sequence of 10 presses on our Up and Down buttons can occur in over 1000 different ways.
+An interface that accepts 10 different types of input over a sequence of 10 events means we are facing 10 billion possible arrangements.
+And that is a tiny fraction of what a real user facing application is typically up against.
+
+The solution comes from the fact that we only need to remember just enough information to make decisions in the future.
+Instead of remembering each button press, we simply remember a desired temperature and update it as inputs happen.
+We don't care which sequence of button presses gets us to 68 degrees.
+To our program they are all the same.
+We call this compressed historical information *state.*
+With state we can compress 10 billion button presses into a single number.
+
+Inputs lead to state changes.
+Pressing the Up and Down button changes the desired temperature state.
+State changes lead to outputs.
+Changing the desired temperature means the disply will change.
+State changes also often lead to other state changes as our program grows in features.
+When the desired temperature changes, the desired state of the heating equipment may change.
+(And when that desired state of the heating equipment changes, our program will output to turn on or off the heating equipment.)
+
+A correctly functioning program will have a natural dependency graph between inputs, internal states, and outputs.
+Unfortunately, status quo programming techniques have no way of expressing this dependency graph directly.
+Programmers must implicitly build this graph out of the correct sequencing of method calls and state updates.
+In so doing, they throw away this valuable dependency information and the computer can no longer help us.
+**That is the root of the problem.**
+
+=== Behavior Graph
+
+With Behavior Graph, we build our programs out of units of functionality called *behaviors*.
+Behaviors manage state via components called *resources*.
+Behaviors are simple, easily understood blocks of code paired with any relationships to these resources.
+Resources are objects which encapsulate both state and how that state changes.
+A behavior for our thermostat would be "_when the user presses the *Up* or *Down* buttons, increase or decrease the desired temperature by one degree_."
+The _desired temperature_ is the resource that this behavior manages.
+
+image::thermostat-temp.svg[Login page]
+
+An entire thermostat program would be built out of many of these behaviors.
+So we add a second behavior, "_when the current temperature is below the desired temperature, turn on the heating equipment_."
+Our behaviors will collaborate to implement the complete thermostat functionality without knowing about each other directly.
+Instead, behaviors compose via resources, in this case _desired temperature_.
+The first behavior declares that it is responsible for setting the _desired temperature_.
+The second behavior declares that it uses the _desired temperature_ to know if it needs to turn on the heat.
+
+image::thermostat-heat.svg[Login page]
+
+We never run behaviors directly by calling them like we do with methods.
+Instead Behavior Graph uses the dependencies between behaviors and resources to determine which behaviors need to run and in which order.
+If the user presses the Up button to raise the desired temperature above the current temperature, the heating behavior will automatically run after the temperature behavior updates the _desired temperature_ resource.
+
+Here we can see the contrast to the status quo approach of nesting chains of method calls.
+In order to ensure the heat can be turned on when the up button is presset, the button press method needs to call the desired temperature setting method.
+And that method in turn needs to call the heating equipment method.
+Because no method runs unless another method calls it, we must explicitly weave these threads of control flow throughout our code.
+In large programs, separately maintaining control flow to ensure our dependency graph is respected is both difficult and error prone.
+
+Fred Brooks link:https://en.wikipedia.org/wiki/No_Silver_Bullet[famously pointed out] that software is necessarily complex because the problems themselves are complex.
+With Behavior Graph we overcome our human complexity limits by delegating more of that work to the computer itself.
+As programmers, we focus on individual behaviors and their immediate relationships.
+The computer in turn handles the complex chore of sorting through hundreds or thousands of those behaviors to ensure a working program.
+
+Behavior Graph gives us control flow for free.
+
+Behavior Graph is a compact and mature library with no external dependencies.
+It is used in production applications with millions of daily users.
+It is available for multiple languages and platforms (Objective C/Swift, Typescript/Ja*vascript, Kotlin).
+
+== Walkthrough
+
+We can illustrate how Behavior Graph code works in detail through another example application, a typical login screen.
+
+image::login-ui-2.svg[Login page]
+
+As a first feature, we would like the Login button to remain disabled until the user has entered both a reasonable email and password.
+If the user types in some password but an invalid email address (missing the '@' character, for example) the Login button will remain disabled.
+Once she corrects the email address by adding an '@' character, the Login button should immediately enable.
+
+In Behavior Graph, this unit of functionality constitutes a typical *behavior*.
+It looks like this
+
+[source, {source-language}, numbered, indent=0]
+----
+include::{sourcedir}/{example-loginextent}[tags=login_intro_short1]
+----
+
+Behaviors have dependencies on units of information called *resources*.
+This behavior depends on two resources, `email` and `password`.
+They appear as a list in the first parameter to `makeBehavior`.
+This list is called the behavior's *demands*.
+Our behavior has read only access to these resources.
+
+As stated before, behaviors are never called directly.
+In specifying a behavior's demands, we are saying, _"whenever any of these resources updates (changes), then this behavior needs to run"._
+In our example, when either `email` or `password` (or both) update, this behavior will run in response.
+
+`email` and `password` are a specific type of resource called a *state resource* which is designed for saving and retreiving information.
+The contents of these state resources is available via their `value` property.
+
+The block of code specified in the behavior is the code that will run.
+A typical behavior uses normal code to perform its work.
+Here we check the validity of the email with a normal function.
+We determine if the Login button should be enabled using normal boolean logic.
+
+This behavior is responsible for the enabled state of the Login button.
+This information is stored in another state resource called `loginEnabled`.
+We specify a behavior's responsibilites as a list in the second parameter to `makeBehavior`.
+This list is called the behavior's *supplies*.
+A behavior can read and write the contents of its supplies.
+The contents of a state resource can be written to by calling its `{update-method}` method.
+
+We can continue to develop our Login page by adding a second feature.
+When the user clicks the Login button and we are not already logging in, then we would like to enter into a logging in state.
+In order to prevent mistakes, when we are in a logging in state, we would also like the Login button to be disabled.
+
+To implement this new feature we introduce a second behavior and make a small change to our existing behavior.
+
+[source, {source-language}, numbered, indent=0]
+----
+include::{sourcedir}/{example-loginextent}[tags=login_intro_short2]
+----
+
+The new behavior has one demand, `loginClick`.
+This is a second type of resource called a *moment resource*.
+Moments are designed to track momentary happenings such as a button click or network call returning.
+We can check if a moment has just happened by accessing its `{momentjustupdated-method}` property.
+
+When the user clicks on the button, `loginClick` will update, and this new behavior will run.
+It performs a simple boolean check to determine if the `loggingIn` state resource needs to update to `{true-bool}`.
+It is allowed to update this resource because `loggingIn` is part of its supplies.
+
+We also modified our previous behavior to include `loggingIn` as one of its demands.
+This means it will run when the `loggingIn` resource updates as well as have permission to access the boolean `value` of `loggingIn`.
+Now the state of `loginEnabled` depends on all three pieces of information: `email`, `password`, and `loggingIn`.
+
+image::login-intro-graph.svg[Login Behavior Graph]
+
+
+Information comes into our system via *actions*.
+A typical UI library will provide some type of callback or event system to capture user inputs.
+In this example we will listen to a click handler to create a new action which updates the `loginClick` moment resource.
+
+[source, {source-language}, numbered, indent=0]
+----
+include::{sourcedir}/{example-loginextent}[tags=login_intro_action]
+----
+
+We would similarly connect `email` and `password` to their respective text fields.
+
+Once the user has entered a valid email and password, the Login button will enable.
+When the user subsequently clicks on the Login button, the behavior that supplies `loggingIn` will run.
+It will update the `loggingIn` resource to `{true-bool}`.
+This in turn will cause the behavior that supplies `loginEnabled` behavior to run.
+It will update the `loginEnabled` resource to `{false-bool}`.
+
+In order to perform real output to the UI library, we need to create a *side effect*.
+
+[source, {source-language}, numbered, indent=0]
+----
+include::{sourcedir}/{example-loginextent}[tags=login_intro_sideeffect]
+----
+
+Side effects are created directly inside behaviors.
+This side effect updates the `enabled` state of the `loginButton` based on the state of the `loginEnabled` resource.
+It does not run immediately, however.
+Behavior Graph defers the running of side effects until after all behaviors have run.
+Side effects are a practical way for Behavior Graph to create output while ensuring access to consistent state.
+
+This example covers the primary concepts when developing with Behavior Graph.
+There are, however, additional features that make Behavior Graph a practical software library.
+link:https://{guide-doc}[The Programming Guide] explains these features in detail.
+
+== Reactive Programming
+
+Behavior Graph graph has many characteristics of a link:https://en.wikipedia.org/wiki/Reactive_programming[reactive programming] library.
+If you are familiar with other libraries in this family you should find some similarities.
+There are some important distinctions:
+
+* Behaviors function as _observers_ and resources function as _observables_.
+They are always separate objects, however.
+* It is not based on streams.
+* It does not use a link:http://reactivex.io/documentation/operators.html[large library of functional combinators].
+* The principles are not programming language or platform specific.
+* It does not have link:https://en.wikipedia.org/wiki/Reactive_programming#Glitches[glitches].
+* It does not permit link:https://en.wikipedia.org/wiki/Reactive_programming#Cyclic_dependencies[cyclic dependencies] and provides tools for discovering and avoiding them.
+* It is a dynamic dataflow graph. Relationships between behaviors and resources can change at runtime which enables powerful modeling techniques.
+
+== Related Work
+
+* link:https://en.wikipedia.org/wiki/Cybernetics[Cybernetics] describes the "closed signaling loop", the essence of Unidirectional Data Flow architectures (aka MVI or Model View Intent).
+* link:https://redux.js.org[Redux] (and Flux) are popular Javscript state management frameworks organized around Unidirectional Data Flow. There are similar implementations for different platforms, eg link:https://github.com/day8/re-frame[re-frame/Clojure].
+* link:https://en.wikipedia.org/wiki/Functional_reactive_programming[Functional Reactive Programming] (FRP) is not new.
+* link:http://reactivex.io[ReactiveX] is a popular reactive programming implementation for many different imerative languages. * link:https://github.com/spotify/mobius[Mobius] is another take on FRP and Unidirectional Data Flow for the Android platform.
+* link:https://en.wikipedia.org/wiki/Observer_pattern[Observers] aren't new.
+* link:https://cycle.js.org[Cycle.js] Javascript reactive library.
+* link:https://mobx.js.org/README.html[MobX] Javascript reactive library.
+* link:https://www.rescala-lang.com[REScala].
+* link:https://developer.apple.com/documentation/combine[Apple's Combine].
+* link:https://docs.oracle.com/javase/9/docs/api/java/util/concurrent/Flow.html[Java Flow].
+* link:http://www.inf.ed.ac.uk/teaching/courses/seoc/2005_2006/resources/statecharts.pdf[State Charts(pdf)].
+* link:https://xstate.js.org[X State], state chart library.
+* link:https://en.wikipedia.org/wiki/Publish–subscribe_pattern[Event Bus/Pub-Sub] is a pattern for building reactive style architectures.
+* link:https://elm-lang.org[Elm], a functional programming language with reactive principles.
+* link:http://witheve.com[Eve], an innovative language organized around reactive blocks of code.
+* link:https://www.flapjax-lang.org/index.html[Flapjax] language.
+* link:https://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.14.9769&rep=rep1&type=pdf[Designware: Software Development by Refinement], this paper presents ideas for building a system through composition.
+* link:http://publications.csail.mit.edu/lcs/pubs/pdf/MIT-LCS-TM-061.pdf[Data flow programming] is not new.
+* link:https://blog.janestreet.com/introducing-incremental/[Incremental].
+* link:https://en.wikipedia.org/wiki/Temporal_logic[Temporal Logic].
+* link:https://arxiv.org/abs/1104.2293[Reactive Imperative Programming with Dataflow Constraints]. In this paper the authors build a very similar bipartite data flow dependency graph.
+* link:https://www.cs.cmu.edu/afs/cs/project/amulet/www/papers/toplas-constraint-experience.pdf[Garnet and Amulet] are toolkits for developing software using similar data flow techniques.
+* link:https://www.amazon.com/Picturing-Quantum-Processes-Diagrammatic-Reasoning/dp/110710422X[Picturing Quantum Processes].
+* link:https://www.amazon.com/Invitation-Applied-Category-Theory-Compositionality/dp/1108711820[An Invitation to Applied Category Theory].
diff --git a/doc-site/content/en/ignore/search.md b/doc-site/content/en/ignore/search.md
new file mode 100644
index 0000000..e3690fd
--- /dev/null
+++ b/doc-site/content/en/ignore/search.md
@@ -0,0 +1,6 @@
+---
+title: Search Results
+layout: search
+
+---
+
diff --git a/doc-site/content/en/questions.md b/doc-site/content/en/questions.md
new file mode 100644
index 0000000..9be7a41
--- /dev/null
+++ b/doc-site/content/en/questions.md
@@ -0,0 +1,35 @@
+---
+title: "Questions and Feedback"
+weight: 70
+---
+
+While we have given the current feature set a lot of consideration, we cannot understand all your constraints.
+Let us know what you are thinking.
+Your questions and feedback help us.
+
+### Ideas
+* You want to more about how something works?
+* You want to know why something works the way it does?
+* How to integrate with your favorite library?
+* What is a good pattern for expressing your particular problem?
+* Do you have an interesting use case?
+* Have you hit a barrier that makes it impossible to use?
+
+## Channels
+
+At the moment we would like to avoid creating yet one more community you need to sign up for.
+Use these existing channels instead.
+
+### GitHub
+
+For Javascript/Typescript related questions ask on our Github Page:
+
+[bgjs Github](https://github.com/yahoo/bgjs/issues)
+
+For questions or feedback related to this documentation we have a separate Github Repository:
+
+[bgdocs Github](https://github.com/yahoo/bgdocs/issues)
+
+### Stack Overflow
+
+We will monitor StackOverflow for [Behavior Graph related questions](https://stackoverflow.com/questions/tagged/behavior-graph) if you tag them with `behavior-graph`.
diff --git a/doc-site/content/en/quickstart.md b/doc-site/content/en/quickstart.md
new file mode 100644
index 0000000..b6d0fae
--- /dev/null
+++ b/doc-site/content/en/quickstart.md
@@ -0,0 +1,92 @@
+---
+title: "Quick Start"
+weight: 10
+---
+
+Using Behavior Graph is as simple as downloading it via your preferred format and importing it into your source.
+
+## Downloading
+
+### NPM
+
+Behavior Graph is hosted on NPM @ [behavior-graph](https://www.npmjs.com/package/behavior-graph).
+
+You may add it as a dependency in your project's __package.json__ manually or install it via the shell
+It supports both CommonJS and module imports.
+
+```sh
+npm install behavior-graph
+```
+
+Please search the web for any help using `npm`.
+
+### GitHub
+Behavior Graph is available in source form via Github @ [yahoo/bgjs](https://www.github.com/yahoo/bgjs).
+
+### Javascript CDNs
+
+Behavior Graph is also available via a number of popular CDN Services.
+You may prefer to use these when importing directly into the browser.
+
+* [Skypack.dev](https://www.skypack.dev/view/behavior-graph)
+* [JSDelivr](https://www.jsdelivr.com/package/npm/behavior-graph)
+
+
+## Importing
+
+Javascript imports require some knowledge of your environment which is beyond the scope of this guide.
+
+For modern environments:
+
+Node: `import * as bg from behavior-graph`
+
+or
+
+Browser: `import * as bg from "https://cdn.skypack.dev/behavior-graph";`
+
+Behavior Graph is also available as an IIFE which you can include as a script tag directly into the browser.
+
+``
+
+The default export name is `bg` when using this method.
+
+## Quicker Start
+
+To start exploring feel free to use any of the following which have been preconfigured to use Behavior Graph.
+
+* [JSFiddle](https://jsfiddle.net/slevin11/akevq4hm/)
+* [CodePen](https://codepen.io/slevin11/pen/XWzbMWZ)
+
+## Coding
+
+### Typescript or Javascript
+
+Behavior-Graph is written in Typescript.
+It is usable directly from Javascript or Typescript code.
+Type declaration files are provided for all APIs.
+
+### Hello, World!
+
+Once you've set up your environment, type in the following to see the magic inside the Javascript console.
+
+```javascript
+let g = new bg.Graph();
+let e = new bg.Extent(g);
+let m1 = e.moment();
+e.behavior()
+ .demands(m1)
+ .runs(() => {
+ console.log('Hello, World!')
+ });
+e.addToGraphWithAction();
+m1.updateWithAction();
+```
+
+If this does not work, double check your imports and your environment.
+
+## Tutorials
+
+It is unlikely you will get very far with Behavior Graph without working through a [tutorial]({{< ref "tutorials/tutorial-1" >}}).
+Please spend some time with them to practice writing Behavior Graph code.
+They don't take very long, and you will be mentally stimulated and spiritually rewarded for your time.
+
diff --git a/doc-site/content/en/relatedwork.md b/doc-site/content/en/relatedwork.md
new file mode 100644
index 0000000..a042022
--- /dev/null
+++ b/doc-site/content/en/relatedwork.md
@@ -0,0 +1,33 @@
+---
+title: "Related Work"
+weight: 60
+---
+
+We acknowledge the hard work and incredible contributions from everyone below.
+
+* [Cybernetics](https://en.wikipedia.org/wiki/Cybernetics) describes the "closed signaling loop", the essence of Unidirectional Data Flow architectures (aka MVI or Model View Intent).
+* [Redux](https://redux.js.org) (and Flux) are popular Javascript state management frameworks organized around Unidirectional Data Flow. There are similar implementations for different platforms
+* [re-frame/Clojure](https://github.com/day8/re-frame). Unidirectional Data Flow for Clojurescript
+* [Functional Reactive Programming](https://en.wikipedia.org/wiki/Functional_reactive_programming) (FRP) is not new.
+* [ReactiveX](http://reactivex.io) is a popular reactive programming implementation for many different imperative languages.
+* [Mobius](https://github.com/spotify/mobius) is another take on Unidirectional Data Flow for the Android platform.
+* [Observers](https://en.wikipedia.org/wiki/Observer_pattern) aren't new.
+* [Cycle.js](https://cycle.js.org) Javascript reactive library.
+* [MobX](https://mobx.js.org/README.html) Javascript reactive library.
+* [REScala](https://www.rescala-lang.com) Scala reactive library
+* [Apple's Combine](https://developer.apple.com/documentation/combine).
+* [Java Flow](https://docs.oracle.com/javase/9/docs/api/java/util/concurrent/Flow.html).
+* [State Charts(pdf)](http://www.inf.ed.ac.uk/teaching/courses/seoc/2005_2006/resources/statecharts.pdf).
+* [X State](https://xstate.js.org), state chart library.
+* [Event Bus/Pub-Sub](https://en.wikipedia.org/wiki/Publish–subscribe_pattern) is a pattern for building reactive style architectures.
+* [Elm](https://elm-lang.org), an elegant functional programming language for the web.
+* [Eve](http://witheve.com), an innovative language with reactive blocks of code.
+* [Flapjax](https://www.flapjax-lang.org/index.html) language.
+* [Designware: Software Development by Refinement (pdf)](https://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.14.9769&rep=rep1&type=pdf), this paper presents ideas for building a system through composition.
+* [Data flow programming (pdf)](http://publications.csail.mit.edu/lcs/pubs/pdf/MIT-LCS-TM-061.pdf) is not new.
+* [Incremental](https://blog.janestreet.com/introducing-incremental/).
+* [Temporal Logic](https://en.wikipedia.org/wiki/Temporal_logic).
+* [Reactive Imperative Programming with Dataflow Constraints](https://arxiv.org/abs/1104.2293). In this paper the authors build a very similar bipartite data flow dependency graph.
+* [Garnet and Amulet (pdf)](https://www.cs.cmu.edu/afs/cs/project/amulet/www/papers/toplas-constraint-experience.pdf) are toolkits for developing software using similar data flow techniques.
+* [Picturing Quantum Processes](https://www.amazon.com/Picturing-Quantum-Processes-Diagrammatic-Reasoning/dp/110710422X).
+* [An Invitation to Applied Category Theory](https://www.amazon.com/Invitation-Applied-Category-Theory-Compositionality/dp/1108711820).
diff --git a/doc-site/content/en/tutorials/_index.md b/doc-site/content/en/tutorials/_index.md
new file mode 100644
index 0000000..af0b432
--- /dev/null
+++ b/doc-site/content/en/tutorials/_index.md
@@ -0,0 +1,4 @@
+---
+title: "Tutorials"
+weight: 50
+---
diff --git a/doc-site/content/en/tutorials/tutorial-1.md b/doc-site/content/en/tutorials/tutorial-1.md
new file mode 100644
index 0000000..d250a9c
--- /dev/null
+++ b/doc-site/content/en/tutorials/tutorial-1.md
@@ -0,0 +1,589 @@
+---
+title: "Tutorial 1 - Basics"
+---
+
+Here we will introduce the essentials to get you started quickly using Behavior Graph.
+
+The recommended way to get started is to use our [preconfigured tutorial site](https://jsfiddle.net/slevin11/k2g6fov0/).
+
+If you prefer to set up your own environment please see the [Quick Start page]({{< ref quickstart >}}).
+Make sure to open up the Javascript console.
+That is where we will generate our output.
+
+## Hello, World!
+
+Type in the following into the editor.
+You will gain more by typing it, as it forces you to think about each line.
+
+
+{{< highlight javascript >}}
+import * as bg from "https://cdn.skypack.dev/behavior-graph";
+
+let g = new bg.Graph();
+
+class HelloExtent extends bg.Extent {
+ constructor(graph) {
+ super(graph);
+
+ this.person = this.state("Nobody");
+ this.behavior()
+ .demands(this.person)
+ .runs(() => {
+ console.log("Hello, " + this.person.value + "!");
+ });
+ }
+}
+
+let e = new HelloExtent(g);
+
+e.addToGraphWithAction();
+
+g.action(() => {
+ e.person.update("World");
+});
+{{< / highlight >}}
+
+Run the code.
+In the console you should see
+
+```
+"Hello, World!"
+```
+
+
+### The Parts
+
+Let's review this in pieces.
+
+{{< highlight javascript >}}
+import * as bg from "https://cdn.skypack.dev/behavior-graph";
+{{< / highlight >}}
+
+This is a standard Javascript import.
+You may need to adjust it depending on your Javascript environment which is beyond the scope of this tutorial.
+Behavior Graph is distributed though [NPM](https://www.npmjs.com/package/behavior-graph) and is available through a number of downstream CDNs.
+See the [Quick Start page]({{< ref quickstart >}}) for more information.
+Note that Behavior Graph is imported as `bg` here.
+We will reference that in a few places in the tutorial.
+Your import name may be different.
+
+
+{{< highlight javascript >}}
+let g = new bg.Graph();
+{{< / highlight >}}
+
+You must create an instance of a `Graph`.
+You may have more than one instance, but all Behavior Graph elements are associated with a specific instance.
+
+{{< highlight javascript >}}
+class HelloExtent extends bg.Extent {
+ constructor(graph) {
+ super(graph);
+{{< / highlight >}}
+
+You will modularize your Behavior Graph code into __Extents__.
+You do this by extending the built in `Extent` class and passing it your `Graph` instance in the constructor.
+Extents collect Behaviors and Resources together with the same lifetime.
+
+{{< highlight javascript >}}
+ this.person = this.state("Nobody");
+{{< / highlight >}}
+
+`person` is a __Resource__.
+Resources are containers for information.
+This specifically is a __State__ resource.
+State resources contain persistent information, i.e. it may be used in the future.
+You will define state resources as properties on your Extent subclass and create them with the `.state()` factory method.
+State resources always have an initial value.
+Our `person` resource has an initial value of `"Nobody"`.
+
+{{< highlight javascript >}}
+ this.behavior()
+ .demands(this.person)
+ .runs(() => {
+ console.log("Hello, " + this.person.value + "!");
+ });
+{{< / highlight >}}
+
+This is a __Behavior__.
+Behaviors are units of functionality.
+We create them with the `.behavior()` factory method that uses a fluent [`BehaviorBuilder`]({{< ref "api#behaviorbuildert" >}}) API.
+
+This behavior has two parts.
+The `.demands()` clause states that this behavior depends on the resource `person`.
+
+The `.runs()` clause is the code that gets run whenever one (or more) of the demands is updated (has new information).
+This one prints our greeting using `this.person.value`.
+The `.value` property returns the contents of our `person` state resource.
+A behavior must demand a resource in order to access its `.value` property.
+(It can also access it if it supplies the resource as well.)
+
+{{< highlight javascript >}}
+let e = new HelloExtent(g);
+e.addToGraphWithAction();
+{{< / highlight >}}
+
+We will need to create an instance of our `HelloExtent` in order to add it to the graph.
+Then we call `.addToGraphWithAction()` which adds the `HelloExtent`'s resource and behavior to our graph.
+They will not perform any work until their extent has been added.
+
+{{< highlight javascript >}}
+g.action(() => {
+ e.person.update("World");
+});
+{{< / highlight >}}
+
+Here we create a new Action using `.action()` on our Graph instance.
+Action blocks are how we introduce new information from the outside.
+In this case, we are providing the person's name by calling `.update()` on our `person` state resource.
+
+### How it works
+
+This call to `.action()` will start a series of steps.
+
+1. It will run the anonymous function given as a parameter.
+2. `e.person.update()` will tell the graph to mark any demanding behaviors as activated.
+In this case there is the only one demanding behavior.
+3. The anonymous function will complete.
+4. The graph will call the runs block on the one activated behavior.
+5. That runs block prints out "Hello, World!" by accessing the `.value` of `person` which is the value we passed into the `.update()` method.
+6. The `.action()` method completes and the program continues.
+
+All of these steps together make up a single Behavior Graph __Event__.
+
+## Doing More
+
+While this may seem like a tedious implementation of "Hello, World", we have already established a foundation that will let this program grow to arbitrary complexity.
+The computer can use these components to support us throughout the development process.
+
+Let's introduce a second reason why we may need to print our greeting.
+
+_The highlighted lines will always be the new or updated lines_
+
+{{< highlight javascript "hl_lines=2 4 6" >}}
+ this.person = this.state("Nobody");
+ this.greeting = this.state("Greetings");
+ this.behavior()
+ .demands(this.person, this.greeting)
+ .runs(() => {
+ console.log(this.greeting.value + ", " + this.person.value + "!");
+ });
+{{< / highlight >}}
+
+First we create a second state resource, `greeting`.
+Then we add `greeting` as an additional demand.
+We must add it as a demand because we access its `.value` property.
+Behavior Graph will raise an error if we do not.
+This explicitness is by design.
+Finally we modify our message to use `greeting.value`.
+
+When we run our program now it should produce
+
+```
+"Greetings, World!"
+```
+
+If we want to get back to our original message we can add more to our action.
+
+{{< highlight javascript "hl_lines=3">}}
+g.action(() => {
+ e.person.update("World");
+ e.greeting.update("Hello");
+});
+{{< / highlight >}}
+
+Now we have
+
+```
+"Hello, World!"
+```
+
+### Multiple Updates
+
+This illustrates an important distinction between Behavior Graph and many reactive libraries.
+Our behavior demands multiple resources.
+That means whenever either `person` or `greeting` __or both__ update our behavior should run.
+However it doesn't run immediately, it is only __activated__.
+
+Inside our action we update both `person` and `greeting`.
+Our behavior that demands them will not run until the entire action block has completed.
+All updates inside a single action are treated as if they happened at the same time.
+This means our behavior runs only once.
+
+We aren't required to provide both just because we demand both.
+State resources persist their value from action to action.
+Let's add an additional action to see this.
+
+{{< highlight javascript "hl_lines=5-7">}}
+g.action(() => {
+ e.person.update("World");
+ e.greeting.update("Hello");
+});
+g.action(() => {
+ e.greeting.update("Goodbye");
+});
+{{< / highlight >}}
+
+Now in our console it should print
+
+```
+"Hello, World!"
+"Goodbye, World!"
+```
+
+"World" persisted into the second event. While "Goodbye" replaced the previous greeting.
+
+## Moments
+
+Not all information persists.
+Sometimes things just happen.
+A button press is a typical example.
+We can model this information with __Moment__ resources.
+
+{{< highlight javascript "hl_lines=3 5 7-9">}}
+ this.person = this.state("Nobody");
+ this.greeting = this.state("Greetings");
+ this.button = this.moment();
+ this.behavior()
+ .demands(this.person, this.greeting, this.button)
+ .runs(() => {
+ if (this.button.justUpdated) {
+ console.log(this.greeting.value + ", " + this.person.value + "!");
+ }
+ });
+{{< / highlight >}}
+
+First we create a `button` resource.
+We create it with a `.moment()` factory method on our Extent subclass.
+Then we add it to our behavior's list of demands so it runs when `button` updates.
+
+Inside our runs block, we've gated our log statement by checking against `button.justUpdated`.
+This will be true only if some other part of our code called `button.update()` during the same event.
+
+### Press the Button
+
+If you run the program now you will get no output.
+This is because we only update the `person` and `greeting` resources and our `log` statement only runs when `button.justUpdated` is true.
+
+So lets add some additional lines to simulate a button press.
+
+{{< highlight javascript "hl_lines=8-10">}}
+g.action(() => {
+ e.person.update("World");
+ e.greeting.update("Hello");
+});
+g.action(() => {
+ e.greeting.update("Goodbye");
+});
+g.action(() => {
+ e.button.update();
+});
+{{< / highlight >}}
+
+Now our program outputs:
+
+```
+"Goodbye, World!"
+```
+
+The first two actions only update the state resources.
+Our behavior is run but the `if (this.button.justUpdated)` check prevents anything from happening.
+The third action causes the behavior to run as well.
+This time the `if` check passes and it logs the message based on prior updates.
+
+Of course they don't need to be in separate actions.
+
+{{< highlight javascript "hl_lines=3">}}
+g.action(() => {
+ e.button.update();
+ e.greeting.update("Nevermind");
+});
+{{< / highlight >}}
+
+Will output:
+
+```
+"Nevermind, World!"
+```
+
+The message changed because both `button` updated as well as `greeting` in that same action.
+The order in which they were updated inside the action is irrelevant to any demanding behaviors.
+
+## A Graph
+
+With only one behavior, it is difficult to call it a behavior graph.
+The real power of Behavior Graph comes with the ability to incrementally introduce related functionality.
+Behaviors will often depend on information provided by other behaviors.
+
+### Supplies
+
+Imagine, for security sake, that we would like to introduce logging into our "Hello, World" program.
+
+{{< highlight javascript "hl_lines=2 4 7 9">}}
+ this.button = this.moment();
+ this.message = this.state(null);
+ this.behavior()
+ .supplies(this.message)
+ .demands(this.person, this.greeting, this.button)
+ .runs(() => {
+ this.message.update(this.greeting.value + ", " + this.person.value + "!");
+ if (this.button.justUpdated) {
+ console.log(this.message.value);
+ }
+ });
+{{< / highlight >}}
+
+We add a new state resource, `message`, to save the current message.
+We add this resource to a new supplies clause of our behavior definition with `.supplies()`.
+__Supplies__ are resources that this behavior is solely responsible for.
+It means that a behavior can both read a resource's `.value` and `.update()` it as well.
+
+We call `message.update()` with the text contents of the greeting to save them for later.
+
+{{< alert title="Rule" color="primary" >}}
+A resource can only be supplied by one behavior.
+A behavior can supply multiple resources.
+It is an error to call `.update()` on a resource inside a behavior if it does not appear in the supplies clause.
+
+Actions can call `.update()` on a resource without specifying that they do so.
+Actions cannot `.update()` a resource if it is supplied by a behavior.
+{{< /alert >}}
+
+### Logging Behavior
+
+We can introduce the logging functionality by adding an additional behavior.
+
+{{< highlight javascript "hl_lines=6-10">}}
+ if (this.button.justUpdated) {
+ console.log(this.message.value);
+ }
+ });
+
+ this.behavior()
+ .demands(this.message)
+ .runs(() => {
+ console.log("Message changed to: " + this.message.value);
+ });
+{{< / highlight >}}
+
+This new behavior demands `message`.
+This means it will run whenever `message` updates.
+Our output shows this result:
+
+```
+"Message changed to: Hello, World!"
+"Message changed to: Goodbye, World!"
+"Nevermind, World!"
+"Message changed to: Nevermind, World!"
+```
+
+As you can see our new logging behavior runs and generates output each time the message changes.
+
+## Events
+
+In Behavior Graph we call a single pass through the graph (from the start of an action to the last output) an [__Event__]({{< ref "api#graphevent" >}}).
+Our current output is the result of three events.
+
+__First Event:__
+```mermaid
+graph LR
+ a["Action:
person=>World
greeting=>Hello"] --> b["Behavior 1:
message=>Hello, World!"] --> c["Behavior 2:
message change logged"]
+```
+
+__Second Event:__
+```mermaid
+graph LR
+ a["Action:
greeting=>Goodbye"] --> b["Behavior 1:
message=>Goodbye, World!"] --> c["Behavior 2:
message change logged"]
+```
+
+__Third Event:__
+```mermaid
+graph LR
+ a["Action:
button updates
greeting=>Nevermind"] --> b["Behavior 1:
message=>Nevermind, World!
message printed"] --> c["Behavior 2:
message change logged"]
+```
+
+### The Same Event
+
+Every time a resource is updated, it is assigned the current event.
+So in the __First Event__ example above, when `person` and `greeting` update, they get pointers to that same event in their `.event` property.
+Then when `message` updates in the first behavior it also gets a pointer to this same event.
+
+We can access this event inside any behavior that we demand (or supply).
+This can use this to append a timestamp to our log messages.
+
+{{< highlight javascript "hl_lines=4">}}
+ this.behavior()
+ .demands(this.message)
+ .runs(() => {
+ console.log("Message changed to: " + this.message.value + " : " + this.message.event.timestamp);
+ });
+{{< / highlight >}}
+
+You should now see something similar to the lines below
+
+```
+"Message changed to: Hello, World! : Fri Jan 28 2022 16:43:43 GMT-0800"
+"Message changed to: Goodbye, World! : Fri Jan 28 2022 16:43:43 GMT-0800"
+"Nevermind, World!"
+"Message changed to: Nevermind, World! : Fri Jan 28 2022 16:43:43 GMT-0800"
+```
+
+### What Just Happened?
+
+Using `.justUpdated` is a powerful tool for organizing our code into related functionality.
+We will add additional logging to see how this works.
+First we will track when we send the message.
+
+{{< highlight javascript "hl_lines=2 4 10 15 17-22">}}
+ this.message = this.state(null);
+ this.sentMessage = this.moment();
+ this.behavior()
+ .supplies(this.message, this.sentMessage)
+ .demands(this.person, this.greeting, this.button)
+ .runs(() => {
+ this.message.update(this.greeting.value + ", " + this.person.value + "!");
+ if (this.button.justUpdated) {
+ console.log(this.message.value);
+ this.sentMessage.update();
+ }
+ });
+{{< / highlight >}}
+
+We create a moment resource for `sentMessage`.
+Sending the message is a one off action, so we keep track of that with a moment.
+We will be calling `.update()` on `sentMessage` so we need to add it to the list of supplies.
+We call `this.sentMessage.update()` right after the `console.log` call to track when we actually print out our message.
+
+Note that a behavior can supply more than one resource.
+This is a common pattern that lets us group related logic together without having to jump through hoops to avoid duplication.
+
+Next we modify our logging message to demand this additional resource.
+
+{{< highlight javascript "hl_lines=2 4 6-9">}}
+ this.behavior()
+ .demands(this.message, this.sentMessage)
+ .runs(() => {
+ if (this.message.justUpdated) {
+ console.log("Message changed to: " + this.message.value + " : " + this.message.event.timestamp);
+ }
+ if (this.sentMessage.justUpdated) {
+ console.log("Message sent: " + this.message.value + " : " + this.message.event.timestamp);
+ }
+ });
+{{< / highlight >}}
+
+This behavior now demands `sentMessage` which means it will run whenever that resource is updated.
+Inside our run block we check to see which resource was updated and generate the correct log message.
+It may be the case that either one or both is updated.
+
+You will find yourself using this "what just happened?" pattern in many of your behaviors.
+
+Running your program should looks like this:
+
+```
+"Message changed to: Hello, World! : Sat Jan 29 2022 08:33:05 GMT-0800"
+"Message changed to: Goodbye, World! : Sat Jan 29 2022 08:33:05 GMT-0800"
+"Nevermind, World!"
+"Message changed to: Nevermind, World! : Sat Jan 29 2022 08:33:05 GMT-0800"
+"Message sent: Nevermind, World! : Sat Jan 29 2022 08:33:05 GMT-0800"
+```
+
+## Challenge
+
+Can you introduce a single resource that turns on or off our newly added logging?
+Try to do this challenge before looking at the answer.
+
+Here's some hints:
+* Try adding a state resource.
+* You'll need to demand it in a behavior and introduce some additional logic.
+
+### Answer
+
+{{< highlight javascript "hl_lines=2 15 17 24">}}
+ this.sentMessage = this.moment();
+ this.loggingEnabled = this.state(true);
+ this.behavior()
+ .supplies(this.message, this.sentMessage)
+ .demands(this.person, this.greeting, this.button)
+ .runs(() => {
+ this.message.update(this.greeting.value + ", " + this.person.value + "!");
+ if (this.button.justUpdated) {
+ console.log(this.message.value);
+ this.sentMessage.update();
+ }
+ });
+
+ this.behavior()
+ .demands(this.message, this.sentMessage, this.loggingEnabled)
+ .runs(() => {
+ if (this.loggingEnabled.value) {
+ if (this.message.justUpdated) {
+ console.log("Message changed to: " + this.message.value + " : " + this.message.event.timestamp);
+ }
+ if (this.sentMessage.justUpdated) {
+ console.log("Message sent: " + this.message.value + " : " + this.message.event.timestamp);
+ }
+ }
+ });
+{{< / highlight >}}
+
+`loggingEnabled` is our new resource.
+We want it to persist so we use a state resource.
+It defaults to `true` meaning logging is on.
+
+We then need to demand it inside our logging behavior in order to access its `.value` property.
+If we try to access `.value` without demanding it, Behavior Graph will raise an error.
+
+We can modify our last action to see it work.
+
+{{< highlight javascript "hl_lines=4">}}
+g.action(() => {
+ e.button.update();
+ e.greeting.update("Nevermind");
+ e.loggingEnabled.update(false);
+});
+{{< / highlight >}}
+
+After adding this additional line, running our code looks like this.
+
+```
+"Message changed to: Hello, World! : Sat Jan 29 2022 08:39:43 GMT-0800"
+"Message changed to: Goodbye, World! : Sat Jan 29 2022 08:39:43 GMT-0800"
+"Nevermind, World!"
+```
+
+We no longer log the last two messages because logging was turned off in the same action.
+Notice how even though `loggingEnabled.update(false)` comes after our updates, we still disable logging for the same event.
+If you were to do this without Behavior Graph, using status quo method calls and property updates, you would need to ensure that `loggingEnabled` changes to `false` _before_ the other updates.
+It would be a different result if you updated it _after_.
+_The ability to remove the hidden complexity that comes with sequencing is a programming superpower._
+Behavior Graph gives you this feature for free.
+
+### Ordering Resources
+
+You may notice that although `.loggingEnabled` is a demand, we don't actually need it to to be a reason for our logging behavior to run.
+We only need to check its `.value`.
+Behavior Graph lets us lighten this constraint.
+
+{{< highlight javascript "hl_lines=2">}}
+ this.behavior()
+ .demands(this.message, this.sentMessage, this.loggingEnabled.order)
+ .runs(() => {
+ if (this.loggingEnabled.value) {
+ if (this.message.justUpdated) {
+{{< / highlight >}}
+
+We can add `.order` to any resource inside our demands clause.
+When we do this, updating that resource will not activate this behavior.
+This can give us some additional accuracy when specifying how our behaviors are linked.
+
+## Complete
+
+Congratulations! You have completed this first tutorial.
+You can see the [finished tutorial code here](https://jsfiddle.net/slevin11/uej3y09f/).
+
+While you may feel that there were many new concepts introduced, we have already covered the majority of them.
+You will find they come naturally with some practice.
+The remaining tutorials give you a taste for how Behavior Graph works inside real interactive programs.
+
+
diff --git a/doc-site/content/en/tutorials/tutorial-2.md b/doc-site/content/en/tutorials/tutorial-2.md
new file mode 100644
index 0000000..8a494bf
--- /dev/null
+++ b/doc-site/content/en/tutorials/tutorial-2.md
@@ -0,0 +1,446 @@
+---
+title: "Tutorial 2 - IO"
+---
+
+This tutorial will show how Behavior Graph interacts with real inputs and outputs to produce a working application.
+In this case we will build the control system for a thermostat, the device in your house that controls the heat.
+
+
+
+This simplified thermostat has two buttons, __Up__ and __Down__ for raising and lowering the desired temperature.
+It also periodically gets external updates of the current temperature.
+If the desired temperature is above the current temperature, we will turn on the heat.
+And once they are the same, the heat will turn off.
+
+## Initial Code
+
+We have created a starter project using [JSFiddle](https://jsfiddle.net/slevin11/k3z2uysx/).
+You should use that for this tutorial.
+It has some simple HTML/CSS to represent the Thermostat's user interface.
+If you wish to use your own environment you will need to copy the HTML and CSS from this JSFiddle site into your own.
+
+The initial setup code has been provided for you.
+
+{{< highlight javascript "hl_lines=">}}
+import * as bg from "https://cdn.skypack.dev/behavior-graph";
+
+class Thermostat extends bg.Extent {
+ constructor(graph) {
+ super(graph);
+ }
+}
+
+let graph = new bg.Graph();
+let tm = new Thermostat(graph);
+tm.addToGraphWithAction();
+{{< / highlight >}}
+
+The bulk of our application will exist inside our `Thermostat` subclass of `Extent`.
+
+## Desired Temperature
+
+The first part of our logic will focus on setting the desired temperature.
+The related elements look something like this.
+
+
+
+First we need a state resource to track our desired temperature and a behavior to supply it.
+
+{{< highlight javascript "hl_lines=5-10">}}
+class Thermostat extends bg.Extent {
+ constructor(graph) {
+ super(graph);
+
+ this.desiredTemperature = this.state(60);
+ this.behavior()
+ .supplies(this.desiredTemperature)
+ .runs(() => {
+ // desired temperature logic will go here
+ });
+ }
+}
+{{< / highlight >}}
+
+`desiredTemperature` is a state resource with an initial value of 60.
+We want a state resource because it is information we intend to use in the future.
+Our new behavior supplies this resource because we plan on calling `desiredTemperature.update()` inside the runs block.
+
+### Button Presses
+
+Our thermostat will need to respond to the button press events that come from our HTML buttons.
+
+{{< highlight javascript "hl_lines=3-6">}}
+ super(graph);
+
+ this.up = this.moment();
+ document.querySelector('#up').addEventListener('click', () => {
+ this.up.updateWithAction();
+ });
+
+ this.desiredTemperature = this.state(60);
+{{< / highlight >}}
+
+We create an `up` moment resource to track when the Up button is pressed.
+Then we use standard DOM manipulation code to respond to the HTML click event.
+We call `this.up.updateWithAction()` to track this event in our moment resource.
+
+`.updateWithAction()` is syntactic sugar for creating a new action and calling `.update()`.
+It is the same as if we typed this instead:
+
+{{< highlight javascript "hl_lines=">}}
+ document.querySelector('#up').addEventListener('click', () => {
+ this.graph.action(() => {
+ this.up.update();
+ });
+ });
+{{< / highlight >}}
+
+### Responding to the Button
+
+We need to modify our behavior to respond to this update.
+
+{{< highlight javascript "hl_lines=4 6-8">}}
+ this.desiredTemperature = this.state(60);
+ this.behavior()
+ .supplies(this.desiredTemperature)
+ .demands(this.up)
+ .runs(() => {
+ if (this.up.justUpdated) {
+ this.desiredTemperature.update(this.desiredTemperature.value + 1);
+ }
+ });
+{{< / highlight >}}
+
+
+We add `up` to our list of demands.
+This ensures that this behavior activates whenever `up` is updated.
+Inside the run block we check for `.justUpdated`.
+If so, we update the `desiredTemperature` by incrementing it from its previous `.value`.
+
+{{< alert title="Access Rules" color="primary" >}}
+1. You can only access `.justUpdated` inside behaviors that demand (or supply) that resource.
+Otherwise Behavior Graph will raise an error.
+2. You can only access `.value` inside behaviors that demand (or supply) that resource.
+Otherwise Behavior Graph will raise an error.
+3. You can only call `.update()` inside a behavior that supplies that resource.
+A resource can only be supplied by one behavior.
+Behavior Graph will raise an error if you do this incorrectly.
+{{< /alert >}}
+
+These rules are essential to allowing Behavior Graph to ensure your resources are always in a consistent state.
+
+### Output
+
+At this point our `desiredTemperature` changes when you press the Up button.
+But we don't update the display.
+We add that here.
+
+{{< highlight javascript "hl_lines=8-10">}}
+ this.behavior()
+ .supplies(this.desiredTemperature)
+ .demands(this.up)
+ .runs(() => {
+ if (this.up.justUpdated) {
+ this.desiredTemperature.update(this.desiredTemperature.value + 1);
+ }
+ this.sideEffect(() => {
+ document.querySelector('#desiredTemperature').innerText = this.desiredTemperature.value;
+ });
+ });
+{{< / highlight >}}
+
+This behavior creates a __Side Effect__ block.
+Inside that block we use standard DOM methods to update the temperature.
+Now if you run this and click on the Up button you will see the temperature field appear and increment.
+
+Side effects are the correct way to generate output from inside a behavior.
+Although a side effect is created inside a behavior, it will only run after all other behaviors have completed running.
+This ensures that all our internal state has settled before calling code that may potentially access it.
+
+Side effects do not have a restriction on what resources they can access, unlike the behavior in which they are defined.
+
+### Down
+
+We can add the handling for our Down button in a similar way.
+
+{{< highlight javascript "hl_lines=6-9">}}
+ this.up = this.moment();
+ document.querySelector('#up').addEventListener('click', () => {
+ this.up.updateWithAction();
+ });
+
+ this.down = this.moment();
+ document.querySelector('#down').addEventListener('click', () => {
+ this.down.updateWithAction();
+ });
+{{< / highlight >}}
+
+And modify our behavior to respond.
+
+{{< highlight javascript "hl_lines=3 7-9">}}
+ this.behavior()
+ .supplies(this.desiredTemperature)
+ .demands(this.up, this.down)
+ .runs(() => {
+ if (this.up.justUpdated) {
+ this.desiredTemperature.update(this.desiredTemperature.value + 1);
+ } else if (this.down.justUpdated) {
+ this.desiredTemperature.update(this.desiredTemperature.value - 1);
+ }
+ this.sideEffect(() => {
+{{< / highlight >}}
+
+Run the program.
+Clicking on the Up and Down buttons should now move the desired temperature display up and down.
+
+### AddedToGraph
+
+You may have noticed that the desired temperature display doesn't show up until after we've tapped on one of the buttons.
+This is because our behavior only runs when one of its demands is updated.
+What we would like to do is also run it once at the beginning.
+
+{{< highlight javascript "hl_lines=3">}}
+ this.behavior()
+ .supplies(this.desiredTemperature)
+ .demands(this.up, this.down, this.addedToGraph)
+ .runs(() => {
+ if (this.up.justUpdated) {
+{{< / highlight >}}
+
+We add the `this.addedToGraph` resource to our list of demands.
+Now when you run the code you will see that the temperature appears at the beginning.
+
+`addedToGraph` is a built in state resource that is part of every Extent.
+It is updated to `true` when the Extent is added to the graph.
+Just like other resources you can demand it to get a behavior to run at the beginning.
+And you can check it's `.justUpdated` property to specialize your logic when necessary.
+
+## Heat
+
+Now we need to introduce a separate bit of functionality to control the heating equipment.
+This logic compares the current temperature to the desired temperature and turns on or off the heating equipment accordingly.
+
+
+
+### Current Temperature
+
+First we need a resource to track the current temperature,
+
+{{< highlight javascript "hl_lines=2">}}
+ this.desiredTemperature = this.state(60);
+ this.currentTemperature = this.state(60);
+{{< / highlight >}}
+
+and a new behavior to update the UI when that resource updates.
+
+{{< highlight javascript "hl_lines=6-12">}}
+ this.sideEffect(() => {
+ document.querySelector('#desiredTemperature').innerText = this.desiredTemperature.value;
+ });
+ });
+
+ this.behavior()
+ .demands(this.currentTemperature, this.addedToGraph)
+ .runs(() => {
+ this.sideEffect(() => {
+ document.querySelector('#currentTemperatureDisplay').innerText = this.currentTemperature.value;
+ });
+ });
+{{< / highlight >}}
+
+Like with `desiredTemperature` this behavior runs whenever `currentTemperature` updates as well as once at the beginning.
+It uses a side effect to update our UI.
+
+### Heat On
+
+Next we need a resource to track if the heat is on or not.
+
+{{< highlight javascript "hl_lines=3">}}
+ this.desiredTemperature = this.state(60);
+ this.currentTemperature = this.state(60);
+ this.heatOn = this.state(false);
+{{< / highlight >}}
+
+By default the `heatOn` state resource is `false` indicating that it is off.
+
+{{< highlight javascript "hl_lines=7-13">}}
+ .runs(() => {
+ this.sideEffect(() => {
+ document.querySelector('#currentTemperatureDisplay').innerText = this.currentTemperature.value;
+ });
+ });
+
+ this.behavior()
+ .supplies(this.heatOn)
+ .demands(this.currentTemperature, this.desiredTemperature)
+ .runs(() => {
+ let heatOn = this.desiredTemperature.value > this.currentTemperature.value;
+ this.heatOn.update(heatOn);
+ });
+{{< / highlight >}}
+
+Here we add another new behavior.
+It is responsible for updating `heatOn` so we add it as a supply.
+It uses both `currentTemperature` and `desiredTemperature` for its logic, so both are demands.
+When it runs, it updates `heatOn` to true if our `currentTemperature` is too low.
+
+### Heat Display
+
+We want our display to update alongside the `heatOn`.
+So we add that logic to our new behavior.
+
+{{< highlight javascript "hl_lines=3 7-9">}}
+ this.behavior()
+ .supplies(this.heatOn)
+ .demands(this.currentTemperature, this.desiredTemperature, this.addedToGraph)
+ .runs(() => {
+ let heatOn = this.desiredTemperature.value > this.currentTemperature.value;
+ this.heatOn.update(heatOn);
+ this.sideEffect(() => {
+ document.querySelector('#heatStatus').innerText = this.heatOn.value ? "On" : "Off"
+ });
+ });
+{{< / highlight >}}
+
+We demand `addedToGraph` to ensure we update the display when the thermostat starts.
+We also add a side effect block to update the UI.
+
+Now when you click the Up and Down buttons you should see the heating display change based on `desiredTemperature` changes.
+
+### Heating Equipment
+
+In a real thermostat, whenever `heatOn` changes, we would send a signal to real heating equipment somewhere else in the house.
+Since we don't have that available, we will simulate our own heat and demonstrate how we can mix in other asynchronous elements.
+
+We'll add a new behavior.
+
+{{< highlight javascript "hl_lines=6-14">}}
+ this.sideEffect(() => {
+ document.querySelector('#heatStatus').innerText = this.heatOn.value ? "On" : "Off"
+ });
+ });
+
+ this.behavior()
+ .demands(this.heatOn)
+ .runs(() => {
+ if (this.heatOn.justUpdatedTo(true)) {
+ // turn heat on
+ } else if (this.heatOn.justUpdatedTo(false)) {
+ // turn heat off
+ }
+ });
+{{< / highlight >}}
+
+This new behavior responds to `heatOn` updates.
+It uses `.justUpdatedTo()` to differentiate changing to true or false.
+
+At this point we want to make an important point about the way state resources work.
+Even though the behavior that supplies `heatOn` calls `.update()` every time it runs, it doesn't necessarily update the state resource.
+Behavior Graph uses `===` to check if the new value is different from the starting value.
+If they are the same, the state resource does not actually update.
+Therefore, demanding behaviors are not activated.
+
+As an example, if `heatOn.value` is currently `false`, calling `heatOn.update(true)` will update the resource and activate demanding behaviors. However, if in the next event we also call `heatOn.update(true)`, Behavior Graph will check `true === true` and therefore will not actually update or activate demanding behaviors.
+
+#### Turning On
+
+We can use the built-in Javascript API `setInterval()` to simulate our heat changing over a period of time.
+When on, this timer will increment our current temperature by 1 every 1.5 seconds.
+
+{{< highlight javascript "hl_lines=2-8">}}
+ if (this.heatOn.justUpdatedTo(true)) {
+ this.sideEffect(() => {
+ this.heatingIntervalId = setInterval(() => {
+ this.action(() => {
+ this.currentTemperature.update(this.currentTemperature.value + 1);
+ });
+ }, 1500);
+ });
+ } else if (this.heatOn.justUpdatedTo(false)) {
+{{< / highlight >}}
+
+
+This branch creates a side effect which starts the timer and saves that timer directly to a normal property `heatingIntervalId`.
+We will use this to stop the timer later.
+When the timer fires, we create an action to bring new information into Behavior Graph.
+In this case, the new information is that `currentTemperature` has increased by 1.
+Note we are accessing `currentTemperature.value` inside a side effect block which means we don't need to add it as a demand of the behavior.
+
+`heatingIntervalId` is a normal Javascript property.
+You are always welcome to use normal properties and methods inside behaviors however you like.
+You just won't get the additional support from Behavior Graph for those uses.
+In this case, we don't need to respond to any changes with it so we just save it to a property.
+
+
+#### Turning Off
+
+If you run this program now, the heat will start incrementing but it won't stop once the heat turns off.
+We will add an additional side effect for this.
+
+{{< highlight javascript "hl_lines=2-5">}}
+ } else if (this.heatOn.justUpdatedTo(false)) {
+ this.sideEffect(() => {
+ clearInterval(this.heatingIntervalId);
+ this.heatingIntervalId = null;
+ });
+ }
+{{< / highlight >}}
+
+This side effect cancels our timer when the heat turns off.
+`clearInterval()` is a built-in Javascript method to cancel the timer.
+We set the property to null for cleanliness.
+
+Now run the code and turn the desired temperature up a few degrees from the current temperature and wait.
+You will see the current temperature slowly increase until they equal and the heat will turn off.
+
+## Behavior Graph Programming
+
+This code is typical Behavior Graph programming style.
+The path we went through to get here is typical of the Behavior Graph programming process.
+The important step is learning how to organize code into behaviors.
+
+### Control Flow for Free
+
+Behaviors are never called directly, which can feel like a lack of control.
+This is a fair intuition, but also incorrect.
+Behavior Graph improves our ability to express the intent of our code.
+We do not think in terms of "do this now".
+Instead we think, "here are the reasons why this should run".
+
+Behavior Graph determines the behavior that should run next based on which behaviors have been activated and their dependencies.
+A behavior that may influence another behavior will always run first.
+
+Looking a the behaviors in our Thermostat program and their linked resources we can figure out exactly how things will run.
+Here's the sequence of steps when we click the Up button.
+
+1. Action: `up.update()`, activate Behavior 1
+2. Behavior 1: `desiredTemperature.update(61)`, activate Behavior 2, create Side Effect 1
+3. Behavior 2: `heatOn.update(true)`, activate Behavior 3, create Side Effect 2
+4. Behavior 3: create Side Effect 3
+5. Side Effect 1: `#desiredTemperature` HTML element changes to "61"
+6. Side Effect 2: `#heatStatus` HTML element changes to "On"
+7. Side Effect 3: Create simulated "heating" timer
+
+The status quo approach of organizing around method calls leaves us open to potential errors as dependencies change.
+With Behavior Graph, if we introduce a new dependency for Behavior 1, the rest of the control flow adapts.
+Everything will continue to run in the correct order.
+This is Behavior Graph doing the work for you.
+
+### Incremental Adoption
+
+Behavior Graph is not the solution to all programming problems.
+It is a tool for structuring the event driven logic portion of your software.
+Feel free to use it as much or as little as you like.
+
+When introducing it incrementally to an exiting codebase, it is easiest to work back from the output.
+1. Find a place were you update the UI, start an animation, or make a network call.
+2. Wrap that up in a side effect inside a behavior.
+3. Then figure out what new information should cause that behavior to run.
+4. Turn that information into resources.
+5. Either update those resources inside action blocks or write new behaviors to supply them.
+6. Repeat.
+
+## Congratulations
+
+Congratulations! You have completed the second tutorial.
+You can see the [finished tutorial code here](https://jsfiddle.net/slevin11/kfuwrmb8/).
diff --git a/doc-site/content/en/tutorials/tutorial-3.md b/doc-site/content/en/tutorials/tutorial-3.md
new file mode 100644
index 0000000..d5b0846
--- /dev/null
+++ b/doc-site/content/en/tutorials/tutorial-3.md
@@ -0,0 +1,736 @@
+---
+title: "Tutorial 3 - Extents"
+---
+
+In this tutorial we will create a simple todo list.
+
+
+
+A todo list is interesting because it has user interface elements with independent lifetimes.
+When we start, our list is empty.
+The visible interface elements are the header for adding new items and a footer for the remaining items.
+After we click Save, a new item will appear with a checkbox for completing it, and a button for deleting it.
+The list as a whole has a longer lifetime than the individual items.
+
+We will implement this by adding and removing Extents from the graph as we add and remove items from our list.
+This is a powerful and practical technique that gives Behavior Graph a unique expressiveness in the state management realm.
+
+## Initial Code
+
+We have created a starter project using [JSFiddle](https://jsfiddle.net/slevin11/kuw2h1no/).
+You should use that for this tutorial.
+It has some simple HTML/CSS to represent the Todo List's user interface.
+If you wish to use your own environment you will need to copy the HTML and CSS from this JSFiddle site into your own.
+
+We first want to set up the initial structure.
+
+{{< highlight javascript "hl_lines=">}}
+import * as bg from "https://cdn.skypack.dev/behavior-graph";
+
+class ListExtent extends bg.Extent {
+ constructor(graph) {
+ super(graph);
+
+ }
+}
+
+let graph = new bg.Graph();
+let list = new ListExtent(graph);
+list.addToGraphWithAction();
+{{< / highlight >}}
+
+## Adding Items
+
+_New code for you to add or modify will always be highlighted._
+
+In order to add a new item,
+
+{{< highlight javascript "hl_lines=5-16">}}
+class ListExtent extends bg.Extent {
+ constructor(graph) {
+ super(graph);
+
+ this.save = this.moment();
+ document.querySelector('#save').addEventListener('click', () => {
+ this.save.updateWithAction(document.querySelector('#new-item-text').value);
+ });
+
+ this.behavior()
+ .demands(this.save)
+ .runs(() => {
+ // do adding here
+ });
+{{< / highlight >}}
+
+We create a `save` moment resource to model the interaction of clicking on the Save button after typing in a new todo.
+We use a normal DOM event to call `save.updateWithAction()` when the button it pressed.
+
+Unlike in previous tutorials, with this moment resource we are passing our update method a parameter which contains the value of the text field.
+Moments often carry information along with them, in this case we would like to know the textual content of the new item.
+
+Next we create an empty behavior, demanding the `save` resource.
+When `save` is updated, we want this behavior to create a list item and update the UI.
+
+### What is an Item?
+
+A typical way to represent a list item is to create a data structure or object with the relevant data.
+We can do something similar with a new Extent subclass.
+
+{{< highlight javascript "hl_lines=4-10">}}
+ }
+}
+
+class ItemExtent extends bg.Extent {
+ constructor(graph, text, list) {
+ super(graph);
+ this.list = list;
+ this.itemText = this.state(text);
+ }
+}
+
+let graph = new bg.Graph();
+let list = new ListExtent(graph);
+list.addToGraphWithAction();
+{{< / highlight >}}
+
+We add a new `ItemExtent` subclass and pass in some information into its constructor.
+1. A required `Graph` instance
+2. The text of the new todo list item which we store in an `itemText` state resource
+3. A pointer to the parent `ListExtent` instance which we will use later
+
+### Creating an ItemExtent
+
+Back in our `ListExtent`, inside our behavior we can create an `ItemExtent` and add it to the graph.
+
+{{< highlight javascript "hl_lines=5-7">}}
+ this.behavior()
+ .demands(this.save)
+ .runs(() => {
+ if (this.save.justUpdated) {
+ let item = new ItemExtent(this.graph, this.save.value, this);
+ this.addChildLifetime(item);
+ item.addToGraph();
+ }
+ });
+{{< / highlight >}}
+
+We create the new item with the contents of the text field.
+Then we call `.addChildLifetime(item)`.
+This lets Behavior Graph know that our `ListExtent` instance will always be around longer than any individual `ItemExtent`.
+This will make it easier to connect behaviors in our `ItemExtent` to resources in our `ListExtent`.
+We will see more on that later.
+
+Next we call `item.addToGraph()`.
+Adding an extent to the graph is a necessary step.
+Until we do this, any behaviors or resources in that extent will not perform their expected roles.
+
+### Collecting the Items
+
+We also need a way to keep track of these items as they are added.
+
+{{< highlight javascript "hl_lines=1 4 11-12">}}
+ this.allItems = this.state([]);
+
+ this.behavior()
+ .supplies(this.allItems)
+ .demands(this.save)
+ .runs(() => {
+ if (this.save.justUpdated) {
+ let item = new ItemExtent(this.graph, this.save.value, this);
+ this.addChildLifetime(item);
+ item.addToGraph();
+ this.allItems.value.push(item);
+ this.allItems.updateForce(this.allItems.value);
+ }
+ });
+{{< / highlight >}}
+
+`allItems` is a state resource initialized with an empty array.
+We supply it because we will be updating it inside this behavior.
+Whenever we create a new item we will append that `ItemExtent` instance to the end of that array via its `.value` property and the built in `Array.push()` method.
+It is typical when working with extents that come and go to store them in a state resource or a collection inside a state resource.
+
+Lastly, changing the contents of a collection is not equivalent to calling `.update()` on the owning resource.
+We must update the resource so that demanding behaviors will be notified.
+To do this we update the resource with its own contents.
+We do this with `.updateForce()`.
+We cannot just call `.update()` because the contents are still the same array instance.
+`.update()` automatically filters out updates when the old value `===` the new value.
+`.updateForce()` works identically but ignores this check.
+This is a common pattern when storing collections inside a resource.
+
+### Updating the UI
+
+Adding an item still doesn't update the UI to match.
+Inside our `ItemExtent` we add some code to create a DOM node.
+
+{{< highlight javascript "hl_lines=6">}}
+class ItemExtent extends bg.Extent {
+ constructor(graph, text, list) {
+ super(graph);
+ this.list = list;
+ this.itemText = this.state(text);
+ this.itemElement = document.querySelector('#templates .list-item').cloneNode(true);
+ }
+}
+{{< / highlight >}}
+
+This is a normal property that points to a DOM element we create by cloning some template content inside the existing HTML document.
+Then inside our `ListExtent` behavior we can add our list item UI.
+
+{{< highlight javascript "hl_lines=8-11">}}
+ .runs(() => {
+ if (this.save.justUpdated) {
+ let item = new ItemExtent(this.graph, this.save.value, this);
+ this.addChildLifetime(item);
+ item.addToGraph();
+ this.allItems.value.push(item);
+ this.allItems.updateForce(this.allItems.value);
+ this.sideEffect(() => {
+ document.querySelector('#list').appendChild(item.itemElement);
+ document.querySelector('#new-item-text').value = '';
+ });
+ }
+{{< / highlight >}}
+
+This side effect adds the DOM element that our new `ListExtent` instance points to.
+It also clears the text field so we can add additional items.
+
+Try adding some items by typing in the box and clicking Save.
+It seems to add items but we only see empty text.
+We can fix that by using our `addedToGraph` built in resource.
+
+Inside `ItemExtent` a new behavior.
+
+{{< highlight javascript "hl_lines=4-10">}}
+ this.itemText = this.state(text);
+ this.itemElement = document.querySelector('#templates .list-item').cloneNode(true);
+
+ this.behavior()
+ .demands(this.itemText, this.addedToGraph)
+ .runs(() => {
+ this.sideEffect(() => {
+ this.itemElement.querySelector('.item-text').innerText = this.itemText.value;
+ });
+ });
+{{< / highlight >}}
+
+This behavior will run when the `ItemExtent` is added to the graph updating its `innerText` HTML content.
+We also added a demand on `itemText` since we would expect that to change the UI if it ever changes as well.
+Now running our code and adding a few items will show our list correctly.
+
+### Completing Items
+
+There's a checkbox to complete a todo list item.
+Checking it at this point does nothing.
+Let's fix that.
+
+Inside `ItemExtent`
+
+{{< highlight javascript "hl_lines=4-7">}}
+ this.list = list;
+ this.itemText = this.state(text);
+ this.itemElement = document.querySelector('#templates .list-item').cloneNode(true);
+ this.completed = this.state(false);
+ this.itemElement.querySelector('.completed-checkbox').addEventListener('change', () => {
+ this.completed.updateWithAction(!this.completed.value);
+ });
+{{< / highlight >}}
+
+We add a new `completed` state resource that defaults to false.
+We also add a DOM event for when the checkbox is checked so we can update `completed`.
+
+And we add an additional behavior inside `ItemExtent`
+
+{{< highlight javascript "hl_lines=1-12">}}
+ this.behavior()
+ .demands(this.completed, this.addedToGraph)
+ .runs(() => {
+ this.sideEffect(() => {
+ let completedClass = 'completed';
+ if (this.completed.value) {
+ this.itemElement.classList.add(completedClass);
+ } else {
+ this.itemElement.classList.remove(completedClass);
+ }
+ });
+ });
+{{< / highlight >}}
+
+This behavior creates a side effect which adds a "completed" class to our HTML item.
+This uses the existing CSS to strike-through a completed todo item.
+We also include `addedToGraph` as an additional demand.
+It is not strictly necessary at this point because all todo items start off as not completed.
+However, it is good practice to use it in behaviors that generate side effects to reflect the current state.
+If we were to introduce functionality later for saving and restoring todo lists, we may have items that start in a completed state.
+
+Running this and checking/unchecking todo list items should update the UI accordingly.
+
+## Dynamic Behaviors
+
+We will now introduce functionality that takes advantage of Behavior Graph's ability to dynamically adjust demands and supplies.
+
+### Remaining Items
+
+The "Remaining Items" footer of the UI does not update currently.
+First we need it to respond when adding items.
+
+Inside `ListExtent` we add a new behavior.
+
+{{< highlight javascript "hl_lines=1-8">}}
+ this.behavior()
+ .demands(this.allItems, this.addedToGraph)
+ .runs(() => {
+ this.sideEffect(() => {
+ let count = this.allItems.value.filter(item => !item.completed.value).length;
+ document.querySelector('#remaining-count').textContent = count;
+ });
+ });
+{{< / highlight >}}
+
+This behavior will create a side effect to update the remaining items text to match the current number of non-completed items in the list.
+`allItems` is a demand because we want to run it whenever we add a new item to the list.
+Inside the side effect we are able to iterate over the array of `ItemExtent` instances to check the `.value` of their `completed` state resource.
+`addedToGraph` ensures we have the correct starting value in there since it starts out empty.
+
+Now try adding a few items to the list and you will see the remaining items count increment.
+
+### Updating Remaining Items
+
+If you try to complete an item however our remaining items count does not change.
+This is because that new behavior does not demand the `completed` resource from our `ItemExtent` instances.
+However, we cannot just add it inside the list of demands because the set of `ItemExtent` instances changes over time.
+
+Behaviors have another clause for handling these situations.
+
+{{< highlight javascript "hl_lines=3-5">}}
+ this.behavior()
+ .demands(this.allItems, this.added)
+ .dynamicDemands([this.allItems], () => {
+ return this.allItems.value.map(item => item.completed);
+ })
+ .runs(() => {
+ this.sideEffect(() => {
+ let count = this.allItems.value.filter(item => !item.completed.value).length;
+ document.querySelector('#remaining-count').textContent = count;
+ });
+ });
+{{< / highlight >}}
+
+`.dynamicDemands()` is another clause when creating a behavior that lets you specify an additional list of demands that can change.
+It takes two parameters.
+The first is an array of resources.
+Whenever any of those update, it will run the anonymous function in the second parameter.
+That function returns a list of additional demands this behavior should have.
+
+Here our dynamic demands clause will return an array containing the `completed` resource from each `ItemExtent` instance.
+So each time we add a new item, our behavior will adapt so that it will run when the `completed` resource for that item updates.
+
+Now try running the code.
+You will see that adding new items updates the remaining items count.
+And you will see that checking and unchecking the box on those items affects the remaining count as well.
+
+### Remaining Items Revisited
+
+Its worth a little extra effort to consider what we just did.
+The _entire_ remaining items feature is defined by this single behavior.
+
+Let's compare this with a status quo implementation: methods, properties, and objects.
+First we might have a method similar to our run block to update the UI
+
+```javascript
+// Hypothetical Code -- Don't type this in
+updateRemainingCount() {
+ let count = this.allItems.filter(item => !item.completed).length;
+ document.querySelector('#remaining-count').textContent = count;
+}
+```
+
+That seems reasonable, but its not enough.
+We need to go inside another method somewhere else in this same class to ensure this gets called when we add a new item.
+Perhaps it might look like this:
+
+```javascript
+// Hypothetical Code -- Don't type this in
+saveButtonPressed(text) {
+ let save = new Item(text);
+ this.allItems.push(save);
+ this.updateRemainingCount();
+}
+```
+
+Ok, so the feature is in two places.
+That's manageable.
+Unfortunately, to handle completing, we still need to call in from another place in the code inside the Item class.
+
+```javascript
+// Hypothetical Code -- Don't type this in
+completeChecked(checked) {
+ this.completed = checked;
+ this.updateRemainingCount();
+}
+```
+
+Now our feature is spread across multiple classes.
+And when we add a way to remove items we will need to involve another code-path.
+This type of spread out logic is a real challenge for developers.
+It is incredibly easy to miss a case.
+
+Production software is significantly more complex than this trivial example.
+Most developers are swimming in control flows like this.
+Behavior Graph lets us collect the "what" and the "why" all together in the same behavior.
+The problem literally disappears.
+
+
+## Deleting Items
+
+We also have a Delete button on each list item which does nothing currently.
+We will fix that now.
+
+Inside `ItemExtent` we add some button click handling.
+
+{{< highlight javascript "hl_lines=6-8">}}
+ this.itemElement = document.querySelector('#templates .list-item').cloneNode(true);
+ this.completed = this.state(false);
+ this.itemElement.querySelector('.completed-checkbox').addEventListener('change', () => {
+ this.completed.updateWithAction(!this.completed.value);
+ });
+ this.itemElement.querySelector('.item-delete').addEventListener('click', () => {
+ this.list.removeItem.updateWithAction(this);
+ });
+{{< / highlight >}}
+
+This takes a DOM event and updates the `removeItem` resource on `ListExtent` (which we haven't added yet).
+It is common to interact with resources on other extents and a source of conceptual power.
+Here we are saying, "this list item is requesting to be removed."
+
+Note that each `ItemExtent`'s Delete button updates the same `list.removeItem` resource.
+We are allowed to update resources from multiple actions because only one action will happen during a single event.
+
+Now, inside `ListExtent`,
+
+{{< highlight javascript "hl_lines=2 6 8 18-25">}}
+ this.allItems = this.state([]);
+ this.removeItem = this.moment();
+
+ this.behavior()
+ .supplies(this.allItems)
+ .demands(this.save, this.removeItem)
+ .runs(() => {
+ if (this.save.justUpdated) {
+ let item = new ItemExtent(this.graph, this.save.value, this);
+ this.addChildLifetime(item);
+ item.addToGraph();
+ this.allItems.value.push(item);
+ this.allItems.updateForce(this.allItems.value);
+ this.sideEffect(() => {
+ document.querySelector('#list').appendChild(item.itemElement);
+ document.querySelector('#new-item-text').value = '';
+ });
+ } else if (this.removeItem.justUpdated) {
+ let item = this.removeItem.value;
+ item.removeFromGraph();
+ this.allItems.update(this.allItems.value.filter(listItem => listItem !== item));
+ this.sideEffect(() => {
+ document.querySelector('#list').removeChild(item.itemElement);
+ });
+ }
+ });
+{{< / highlight >}}
+
+These changes make up the remove item feature.
+First we have a new `removeItem` moment resource.
+`removeItem` models the concept of "a request to be removed happened".
+We add `removeItem` as an additional demand on our `allItems` behavior.
+This causes the behavior to run when a user clicks on a Delete button.
+
+It is common to refer to behaviors by the resources they supply.
+This is because a behavior uniquely supplies those resources.
+In this case we call this the `allItems` behavior.
+
+We are modifying this behavior because removing an item affects the list in `allItems`.
+We could not put this logic in another behavior because a resource can only be supplied by one behavior.
+Updating a resource can only happen in the one behavior that supplies it.
+
+Inside the runs block we add new checks for `save.justUpdated` and `removeItem.justUpdated`.
+It is a common pattern to iterate through `.justUpdated` checks of the various demands to determine what happened.
+In this case we remove the item from the list by building a new list without the item to remove.
+So we do not need to call `.forceUpdate()` like we did when adding the item.
+Our side effect ensures that the UI updates as well.
+
+Also note that we call`.removeFromGraph()` on the removed `ItemExtent`.
+Extents should always be removed from the graph if they are no longer needed otherwise their behaviors will continue to run.
+
+### Remaining Items Re-revisited
+
+You may also notice that the remaining items count now goes down if you remove an item that hasn't been completed yet.
+We get this for free because we defined the remaining items on the `allItems` list.
+If we were just using method calls to implement delete, we very likely would have removed the item from our list directly and then had to remember to call some `updateRemainingCount()` which could have easily been forgotten.
+
+This ability to make chances without introducing additional complexity is a hallmark of programming with Behavior Graph.
+Once you've experienced it a few times you will find it difficult to give up.
+
+## Editing
+
+Now we will introduce some editing functionality.
+This will cover some additional uses of dynamic behaviors.
+We will allow the user to click on a particular todo list item to select it.
+While selected, the user can edit the text inside the main text field.
+
+### Selecting
+
+The first step is to use a DOM event to identify when we have selected a particular item.
+
+Inside `ItemExtent` add another handler.
+
+{{< highlight javascript "hl_lines=4-6">}}
+ this.itemElement.querySelector('.item-delete').addEventListener('click', () => {
+ this.list.removeItem.updateWithAction(this);
+ });
+ this.itemElement.querySelector('.item-text').addEventListener('click', () => {
+ this.list.selectRequest.updateWithAction(this);
+ });
+{{< / highlight >}}
+
+`selectRequest` updates with the list item that was clicked as its `.value`.
+
+Inside `ListExtent` we add the related resources and a corresponding behavior.
+
+{{< highlight javascript "hl_lines=3-4">}}
+ this.allItems = this.state([]);
+ this.removeItem = this.moment();
+ this.selectRequest = this.moment();
+ this.selected = this.state(null);
+{{< / highlight >}}
+
+{{< highlight javascript "hl_lines=1-6">}}
+ this.behavior()
+ .supplies(this.selected)
+ .demands(this.selectRequest)
+ .runs(() => {
+ this.selected.update(this.selectRequest.value);
+ });
+{{< / highlight >}}
+
+Clicking on an item sets our new `selected` resource as the item that was just clicked.
+
+### Selected
+
+Now we want our items to visually reflect when they are selected.
+We can do this by demanding this resource in a behavior in each `ItemExtent`.
+
+{{< highlight javascript "hl_lines=1-13">}}
+ this.behavior()
+ .demands(this.list.selected)
+ .runs(() => {
+ let selected = this.list.selected.value === this;
+ this.sideEffect(() => {
+ let selectedClass = 'selected'
+ if (selected) {
+ this.itemElement.classList.add(selectedClass);
+ } else {
+ this.itemElement.classList.remove(selectedClass);
+ }
+ });
+ });
+{{< / highlight >}}
+
+When the `selected` state resource inside `ListExtent` updates, this behavior on every item will run.
+They each demand `this.list.selected`.
+Depending on if the item is the one that is selected we will change the UI accordingly.
+
+We want to refer back to the beginning where we called `.addChildLifetime(item)` inside `ListExtent`.
+
+{{< highlight javascript "hl_lines=">}}
+ let item = new ItemExtent(this.graph, this.save.value, this);
+ this.addChildLifetime(item);
+ item.addToGraph();
+{{< / highlight >}}
+
+This line says that the list will always be around when the item is.
+This gives us permission to add `this.list.selected`, a resource in `ListExtent`, as a demand on the behavior inside each `ItemExtent`.
+Behavior Graph ensures that we don't link to resources that may no longer be part of the graph and it uses lifetimes as a way to manage that.
+
+You can now try out this code by clicking on different items.
+
+Notice that we can take a list of many items and click on different ones and watch it switch.
+Each time, one of those behaviors adds the 'selected' CSS class while all the rest remove the 'selected' class.
+Removing a class when its already not there is valid and simplifies our logic.
+
+### Deselect
+
+Its easy enough to introduce deselecting by clicking on the already selected element.
+
+Inside `ListExtent` we modify our `selected` behavior.
+
+{{< highlight javascript "hl_lines=5-7 9">}}
+ this.behavior()
+ .supplies(this.selected)
+ .demands(this.selectRequest)
+ .runs(() => {
+ if (this.selected.value == this.selectRequest.value) {
+ this.selected.update(null);
+ } else {
+ this.selected.update(this.selectRequest.value);
+ }
+ });
+{{< / highlight >}}
+
+We check if the currently selected item is the one that was just clicked on and set it to `null` in that case.
+This communicates that nothing should be selected.
+Try running now and clicking on an item multiple times.
+
+### Updating The Text Field
+
+Now we want to introduce editing.
+Let's try updating the main text field when we select an item so we can edit it.
+
+{{< highlight javascript "hl_lines=10-18">}}
+ this.behavior()
+ .supplies(this.selected)
+ .demands(this.selectRequest)
+ .runs(() => {
+ if (this.selected.value == this.selectRequest.value) {
+ this.selected.update(null);
+ } else {
+ this.selected.update(this.selectRequest.value);
+ }
+
+ if (this.selected.justUpdated) {
+ this.sideEffect(() => {
+ let textField = document.querySelector('#new-item-text');
+ textField.value = this.selected.value === null ? '' : this.selected.value.itemText.value;
+ let actionText = document.querySelector('#action');
+ actionText.innerText = this.selected.value === null ? 'Add' : 'Edit'
+ });
+ }
+ });
+{{< / highlight >}}
+
+Whenever `selected` updates, we run this new side effect.
+It copies over the text of the item into the text field.
+It also updates the UI to indicate that we are editing and not adding.
+
+Conversely, when `selected` updates to `null`, it puts our UI state back to an Add mode.
+
+### Preventing Adding
+
+When we are in editing mode, the save button should cause the text in our item to update.
+However right now it will still create a new item.
+We want to prevent that from happening when we are in editing mode.
+
+{{< highlight javascript "hl_lines=5">}}
+ this.behavior()
+ .supplies(this.allItems)
+ .demands(this.save, this.removeItem)
+ .runs(() => {
+ if (this.save.justUpdated && this.selected.traceValue === null) {
+ let item = new ItemExtent(this.graph, this.save.value, this);
+ this.addChildLifetime(item);
+ item.addToGraph();
+ this.allItems.value.push(item);
+{{< / highlight >}}
+
+At the time we click the Save button, we want to know if we are in editing mode or not.
+We could check `selected.value` to see if it is null (ie not editing).
+However, here we use `selected.traceValue` instead.
+`.traceValue` is the value of the resource at the beginning of the graph event (the instant the action started).
+So if `selected` updates this event, `.traceValue` will still return what it was before it changed.
+
+This also removes the requirement that we demand `selected` in this behavior.
+Here we just care if an item is already selected or not, not that something was "just selected".
+So we don't need it as a demand.
+`.traceValue` of any resource is always accessible by any behavior without demanding (or supplying) it.
+
+The `if` check ignores the `save` click when there's something already selected.
+
+### Making Changes
+
+Now we can add a new behavior inside `ListExtent` that responds to our save and updates the text of the selected item.
+
+{{< highlight javascript "hl_lines=4-14">}}
+ }
+ });
+
+ this.behavior()
+ .dynamicSupplies([this.allItems], () => {
+ return this.allItems.value.map(item => item.itemText);
+ })
+ .demands(this.save)
+ .runs(() => {
+ if (this.save.justUpdated && this.selected.traceValue !== null) {
+ this.selected.traceValue.itemText.update(this.save.value);
+ }
+ });
+
+ this.behavior()
+ .demands(this.allItems, this.addedToGraph)
+{{< / highlight >}}
+
+This new behavior uses `.dyanmicSupplies()`.
+This is a clause we haven't seen before.
+It does work similarly to `.dynamicDemands()`.
+In this behavior, we update our supplies to be the `itemText` resource from each `ItemExtent` instance.
+Whenever we add our remove an item, this behavior will update its supplies.
+
+We will supply all of them because any one of them might become selected.
+When the user clicks the Save button, this behavior will update the `itemText` on the selected item to whats inside the text field.
+
+Notice that we use `selected.traceValue` again here, so it is not part of the demands.
+We want which item was selected at the time `save` was updated.
+We also do not need this behavior to run when `selected` updates.
+
+Notice that all we do is update `itemText`.
+We already have a behavior that knows how to change that UI when we change the text.
+It is common to make these kinds of changes and see everything work as expected.
+
+Run the code and you will see your working todo list.
+
+## Challenge
+
+After saving our changes, the item remains selected.
+It might be a better user experience to exit editing mode after saving.
+Can you implement this?
+
+Try your best before looking at the answer.
+
+Hints:
+1. You can do this by modifying a single behavior.
+2. Which behavior is responsible for `selected`?
+3. Which resource updates when we click the Save button?
+
+### Answer: Clearing Post Save
+
+We can modify our `selected` behavior inside `ListExtent`.
+
+{{< highlight javascript "hl_lines=3 5 11-13">}}
+ this.behavior()
+ .supplies(this.selected)
+ .demands(this.selectRequest, this.save)
+ .runs(() => {
+ if (this.selectRequest.justUpdated) {
+ if (this.selected.value == this.selectRequest.value) {
+ this.selected.update(null);
+ } else {
+ this.selected.update(this.selectRequest.value);
+ }
+ } else if (this.save.justUpdated) {
+ this.selected.update(null);
+ }
+{{< / highlight >}}
+
+Now our `selected` behavior also runs and deselects the current item when the Save button is pressed.
+Here see again the common pattern of checking various resources' `.justUpdated` property inside a behavior.
+
+Just before adding this feature we used `selected.traceValue` in a few behaviors.
+This change here is an additional motivation for that.
+If we used `selected.value` in those behaviors it means they would demand `selected` which is supplied by this behavior.
+Which means they would run _after_ this behavior.
+Which means by they time they ran, `selected.value` would be null.
+We don't want what `selected` changes to, we want what it was at the start.
+This is what `traceValue` provides.
+
+## Congratulations
+
+Congratulations! You have completed the third tutorial.
+You can see the [finished tutorial code here](https://jsfiddle.net/slevin11/vdu25ar9/).
diff --git a/doc-site/content/en/why-behavior-graph.md b/doc-site/content/en/why-behavior-graph.md
new file mode 100644
index 0000000..ebfe37b
--- /dev/null
+++ b/doc-site/content/en/why-behavior-graph.md
@@ -0,0 +1,95 @@
+---
+title: "Why Behavior Graph?"
+weight: 20
+---
+
+**Behavior Graph** is a software library that greatly enhances our ability to program **user facing software** and **control systems**. Programs of this type quickly scale up in complexity as features are added. Behavior Graph directly addresses this complexity by shifting more of the burden to the computer.
+It works by offering the programmer a new unit of code organization called a **behavior**.
+Behaviors are blocks of code enriched with additional information about their stateful relationships.
+Using this information, Behavior Graph enforces _safe use of mutable state_, arguably the primary source of complexity in this class of software.
+It does this by taking on the responsibility of control flow between behaviors, ensuring they are are _run at the correct time and in the correct order_.
+
+## Interactive Systems
+
+It helps to understand why user facing software, control systems, and similar programs present a particular challenge.
+
+We define these systems by three primary characteristics:
+
+1. *Asynchronous*: inputs can happen over a period of time and at any time
+2. *Event-driven*: outputs occur over time in response to inputs
+3. *Stateful*: outputs depend on a history of prior inputs
+
+A thermostat controlling the temperature in a house is an example:
+
+
+
+1. It runs continuously, responding to temperature changes as well as button presses in order to operate the heating equipment.
+2. Button presses will result in changes to the display.
+3. Button presses which set the desired temperature will determine when the heating equipment turns on in the future.
+
+The challenge comes from the large number of different inputs where order and history matter.
+A sequence of 10 presses on our Up and Down buttons can occur in over 1000 different ways.
+An interface that accepts 10 different types of input over a sequence of 10 events means we are facing 10 billion possible arrangements.
+And that is a tiny fraction of what a real user facing application is typically up against.
+
+The solution comes from the fact that we only need to remember just enough information to make decisions in the future.
+Instead of remembering each button press, we simply remember a desired temperature and update it as inputs happen.
+We don't care which sequence of button presses gets us to 68 degrees.
+To our program they are all the same.
+We call this compressed historical information *state.*
+With state we can compress 10 billion button presses into a single number.
+
+Inputs lead to state changes.
+Pressing the Up and Down button changes the desired temperature state.
+State changes lead to outputs.
+Changing the desired temperature means the display will change.
+State changes also often lead to other state changes as our program grows in features.
+When the desired temperature changes, the desired state of the heating equipment may change.
+(And when that desired state of the heating equipment changes, our program will output to turn on or off the heating equipment.)
+
+A correctly functioning program will have a natural dependency graph between inputs, internal states, and outputs.
+Unfortunately, status quo programming techniques have no way of expressing this dependency graph directly.
+Programmers must implicitly build this graph out of the correct sequencing of method calls and state updates.
+In so doing, they throw away this valuable dependency information and the computer can no longer help us.
+**That is the root of the problem.**
+
+## Behavior Graph
+
+With Behavior Graph, we build our programs out of units of functionality called *behaviors*.
+Behaviors manage state via components called *resources*.
+Behaviors are simple, easily understood blocks of code paired with any relationships to these resources.
+Resources are objects which encapsulate both state and how that state changes.
+A behavior for our thermostat would be "_when the user presses the *Up* or *Down* buttons, increase or decrease the desired temperature by one degree_."
+The _desired temperature_ is the resource that this behavior manages.
+
+
+
+An entire thermostat program would be built out of many of these behaviors.
+So we add a second behavior, "_when the current temperature is below the desired temperature, turn on the heating equipment_."
+Our behaviors will collaborate to implement the complete thermostat functionality without knowing about each other directly.
+Instead, behaviors compose via resources, in this case _desired temperature_.
+The first behavior declares that it is responsible for setting the _desired temperature_.
+The second behavior declares that it uses the _desired temperature_ to know if it needs to turn on the heat.
+
+
+
+We never run behaviors directly by calling them like we do with methods.
+Instead Behavior Graph uses the dependencies between behaviors and resources to determine which behaviors need to run and in which order.
+If the user presses the Up button to raise the desired temperature above the current temperature, the heating behavior will automatically run after the temperature behavior updates the _desired temperature_ resource.
+
+Here we can see the contrast to the status quo approach of nesting chains of method calls.
+In order to ensure the heat can be turned on when the up button is pressed, the button press method needs to call the desired temperature setting method.
+And that method in turn needs to call the heating equipment method.
+Because no method runs unless another method calls it, we must explicitly weave these threads of control flow throughout our code.
+In large programs, separately maintaining control flow to ensure our dependency graph is respected is both difficult and error prone.
+
+Fred Brooks [famously pointed out](https://en.wikipedia.org/wiki/No_Silver_Bullet) that software is necessarily complex because the problems themselves are complex.
+With Behavior Graph we overcome our human complexity limits by delegating more of that work to the computer itself.
+As programmers, we focus on individual behaviors and their immediate relationships.
+The computer in turn handles the complex chore of sorting through hundreds or thousands of those behaviors to ensure a working program.
+
+__Behavior Graph gives us control flow for free.__
+
+Behavior Graph is a compact and mature library with no external dependencies.
+It is used in production applications with millions of daily users.
+It is available for multiple languages and platforms (Objective C/Swift, Typescript/Javascript, Kotlin).
diff --git a/doc-site/data/terms.yaml b/doc-site/data/terms.yaml
new file mode 100644
index 0000000..7b01b55
--- /dev/null
+++ b/doc-site/data/terms.yaml
@@ -0,0 +1,167 @@
+objc:
+ sourcedir: "./examples/objc/BGExamples"
+ source-language: "objectivec"
+ false-bool: "@NO"
+ true-bool: "@YES"
+ resource-class: "BGResource"
+ state-class: "BGState"
+ moment-class: "BGMoment"
+ extent-class: "BGExtent"
+ graph-class: "BGGraph"
+ event-class: "BGEvent"
+ action-method: "action"
+ sideeffect-method: "sideEffect:"
+ addtograph-method: "addToGraph"
+ behavior-method: "behaviorWithDemands:"
+ update-method: "updateValue:"
+ omentupdate-method: "update"
+ momentjustupdated-method: "justUpdated"
+ statejustupdated-method: "justUpdated"
+ statejustupdatedto-method: "justUpdatedTo:"
+ statejustupdatedtofrom-method: "justUpdatedTo:from:"
+ asyncaction-method: "action:requireSync:NO"
+ dateprovider-property: "dateProvider"
+ dateprovider-class: "BGDateProvider"
+ example-loginextent: "LoginExtent.m"
+ example-loginextent-header: "LoginExtent.h"
+ example-loginpage: "LoginPageViewController.m"
+ example-chat-chat: "ChatExtent.m"
+ example-chat-chat-header: "ChatExtent.h"
+ example-chat-participant: "ParticipantExtent.m"
+ example-chat-participant-header: "ParticipantExtent.h"
+ constructor: "initializer"
+ string-class: "NSString"
+ source-resource-1: "Lines 5 and 6"
+ source-sideeffect-1: "Line 9"
+ source-statemanagement-1: "Line 8"
+ source-justupdated-1: "Line 11"
+ source-justupdated-2: "Line 18"
+ source-tracevalue-1: "Line 9"
+ source-sideeffect-1: "Line 10"
+ source-sideeffect-2: "line 20"
+ source-serialize-1: "Line 21"
+ source-serialize-2: "line 23"
+ source-serialize-3: "line 19"
+ source-async: "Line 4"
+ source-addtograph-1: "line 9"
+ source-addtograph-2: "Line 19"
+ source-extentcollections-1: "lines 12 and 20"
+ source-extentcollections-2: "lines 13 and 21"
+ source-dynamic-1: "line 8"
+ source-dynamic-2: "lines 5 and 6"
+ source-dynamic-3: "line 10"
+ uses-crtp: false
+ readme-doc: "readme-objc.html"
+ guide-doc: "objc.html"
+ github-project: "bgobjc"
+typescript:
+ sourcedir: "./examples/typescript/BGExamples"
+ source-language: "typescript"
+ false-bool: "false"
+ true-bool: "true"
+ resource-class: "Resource"
+ state-class: "State"
+ moment-class: "Moment"
+ extent-class: "Extent"
+ graph-class: "Graph"
+ event-class: "GraphEvent"
+ action-method: "action"
+ sideeffect-method: "sideEffect"
+ addtograph-method: "addToGraph"
+ behavior-method: "makeBehavior"
+ update-method: "update"
+ momentupdate-method: "update"
+ momentjustupdated-method: "justUpdated"
+ statejustupdated-method: "justUpdated"
+ statejustupdatedto-method: "justUpdatedTo"
+ statejustupdatedtofrom-method: "justUpdatedToFrom"
+ asyncaction-method: "actionAsync"
+ dateprovider-property: "dateProvider"
+ dateprovider-class: "BehaviorGraphDateProvider"
+ example-loginextent: "LoginExtent.ts"
+ example-loginextent-header: "LoginExtent.ts"
+ example-loginpage: "LoginExtent.ts"
+ example-chat-chat: "ChatExample.ts"
+ example-chat-chat-header: "ChatExample.ts"
+ example-chat-participant: "ChatExample.ts"
+ example-chat-participant-header: "ChatExample.ts"
+ constructor: "constructor"
+ string-class: "string"
+ source-resource-1: "Lines 2 and 3"
+ source-sideeffect-1: "Line 6"
+ source-statemanagement-1: "Line 5"
+ source-justupdated-1: "Line 9"
+ source-justupdated-2: "Line 17"
+ source-tracevalue-1: "Line 7"
+ source-sideeffect-1: "Line 7"
+ source-sideeffect-2: "line 18"
+ source-serialize-1: "Line 19"
+ source-serialize-2: "line 21"
+ source-serialize-3: "line 18"
+ source-async: "Line 3"
+ source-addtograph-1: "line 8"
+ source-addtograph-2: "Line 17"
+ source-extentcollections-1: "lines 10 and 18"
+ source-extentcollections-2: "lines 11 and 19"
+ source-dynamic-1: "line 6"
+ source-dynamic-2: "lines 4 and 5"
+ source-dynamic-3: "line 9"
+ readme-doc: "readme-typescript.html"
+ guide-doc: "typescript.html"
+ github-project: "bgjs"
+kotlin:
+ sourcedir: "./examples/kotlin/BGExamples"
+ source-language: "kotlin"
+ false-bool: "false"
+ true-bool: "true"
+ resource-class: "Resource"
+ state-class: "State"
+ moment-class: "Moment"
+ extent-class: "Extent"
+ graph-class: "Graph"
+ event-class: "GraphEvent"
+ action-method: "action"
+ sideeffect-method: "sideEffect"
+ addtograph-method: "addToGraph"
+ behavior-method: "makeBehavior"
+ update-method: "update"
+ momentupdate-method: "update"
+ momentjustupdated-method: "justUpdated"
+ statejustupdated-method: "justUpdated"
+ statejustupdatedto-method: "justUpdatedTo"
+ statejustupdatedtofrom-method: "justUpdatedToFrom"
+ asyncaction-method: "actionAsync"
+ dateprovider-property: "dateProvider"
+ dateprovider-class: "BehaviorGraphDateProvider"
+ example-loginextent: "LoginExtent.kt"
+ example-loginextent-header: "LoginExtent.kt"
+ example-loginpage: "LoginExtent.kt"
+ example-chat-chat: "ChatExample.kt"
+ example-chat-chat-header: "ChatExample.kt"
+ example-chat-participant: "ChatExample.kt"
+ example-chat-participant-header: "ChatExample.kt"
+ constructor: "init"
+ string-class: "String"
+ source-resource-1: "Lines 2 and 3"
+ source-sideeffect-1: "Line 6"
+ source-statemanagement-1: "Line 5"
+ source-justupdated-1: "Line 9"
+ source-justupdated-2: "Line 17"
+ source-tracevalue-1: "Line 7"
+ source-sideeffect-1: "Line 7"
+ source-sideeffect-2: "line 18"
+ source-serialize-1: "Line 19"
+ source-serialize-2: "line 21"
+ source-serialize-3: "line 18"
+ source-async: "Line 3"
+ source-addtograph-1: "line 8"
+ source-addtograph-2: "Line 17"
+ source-extentcollections-1: "lines 10 and 18"
+ source-extentcollections-2: "lines 11 and 19"
+ source-dynamic-1: "line 6"
+ source-dynamic-2: "lines 4 and 5"
+ source-dynamic-3: "line 9"
+ uses-crtp: false
+ readme-doc: "readme-kotlin.html"
+ guide-doc: "kotlin.html"
+ github-project: "bgkotlin"
diff --git a/doc-site/deploy.sh b/doc-site/deploy.sh
new file mode 100755
index 0000000..a2c28f6
--- /dev/null
+++ b/doc-site/deploy.sh
@@ -0,0 +1,17 @@
+#Copyright 2018 Google LLC
+#
+#Licensed under the Apache License, Version 2.0 (the "License");
+#you may not use this file except in compliance with the License.
+#You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+#Unless required by applicable law or agreed to in writing, software
+#distributed under the License is distributed on an "AS IS" BASIS,
+#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#See the License for the specific language governing permissions and
+#limitations under the License.
+#
+rm -rf public/
+HUGO_ENV="production" hugo --gc || exit 1
+s3deploy -source=public/ -region=eu-west-1 -bucket=bep.is -distribution-id=E8OKNT7W9ZYZ2 -path temp/td
diff --git a/doc-site/docker-compose.yaml b/doc-site/docker-compose.yaml
new file mode 100644
index 0000000..e8f211a
--- /dev/null
+++ b/doc-site/docker-compose.yaml
@@ -0,0 +1,13 @@
+version: "3.3"
+
+services:
+
+ site:
+ image: docsy/docsy-example
+ build:
+ context: .
+ command: server
+ ports:
+ - "1313:1313"
+ volumes:
+ - .:/src
diff --git a/doc-site/examples/kotlin/.gitignore b/doc-site/examples/kotlin/.gitignore
new file mode 100644
index 0000000..c6ef218
--- /dev/null
+++ b/doc-site/examples/kotlin/.gitignore
@@ -0,0 +1,2 @@
+.idea
+
diff --git a/doc-site/examples/kotlin/BGExamples/ChatExample.kt b/doc-site/examples/kotlin/BGExamples/ChatExample.kt
new file mode 100644
index 0000000..15f4791
--- /dev/null
+++ b/doc-site/examples/kotlin/BGExamples/ChatExample.kt
@@ -0,0 +1,156 @@
+import com.yahoo.behaviorgraph.*
+
+// @tag::chat_extent[]
+class ChatExtent(graph: Graph) : Extent(graph) {
+// @end::chat_extent[]
+
+ val participantJoined: Moment
+ val participantDisconnected: Moment
+
+ // @tag::chat_participants_resources[]
+ val participants: State>
+ // @end::chat_participants_resources[]
+
+ val pinnedParticipant: State
+
+ val participantsRelink: Resource
+
+ init {
+
+ // tag::participants[]
+ this.participantJoined = Moment(this)
+ this.participantDisconnected = Moment(this)
+ this.participants = State(this, mutableMapOf())
+ this.makeBehavior(listOf(this.participantJoined, this.participantDisconnected), listOf(this.participants)) { extent ->
+ if (extent.participantJoined.justUpdated) {
+ val participantId = extent.participantJoined.value
+ val participant = ParticipantExtent(extent.graph, participantId, extent)
+ participant.addToGraph()
+ extent.participants.value.set(participantId, participant)
+ extent.participants.update(extent.participants.value, false)
+ }
+
+ if (extent.participantDisconnected.justUpdated) {
+ val participantId = extent.participantDisconnected.value
+ val participant = extent.participants.value[participantId]
+ participant!!.removeFromGraph()
+ extent.participants.value.remove(participantId)
+ extent.participants.update(extent.participants.value, false)
+
+ }
+
+ }
+ // end::participants[]
+
+ // tag::chat_relink_pinned[]
+ participantsRelink = Resource(this)
+ makeBehavior(listOf(participants), listOf(participantsRelink)) { extent ->
+ val demands = mutableListOf()
+ demands.add(extent.participants)
+ demands.add(extent.participantsRelink)
+ extent.participants.value.values.forEach {
+ demands.add(it.pinTap)
+ }
+ extent.pinnedParticipant.suppliedBy!!.setDemands(demands)
+ }
+ // end::chat_relink_pinned[]
+
+ // tag::chat_pinned[]
+ pinnedParticipant = State(this, null)
+ makeBehavior(listOf(this.participants, this.participantsRelink), listOf(this.pinnedParticipant)) { chatExtent ->
+ val currentPinned = chatExtent.pinnedParticipant.value
+ var newPinned: ParticipantExtent? = null
+ for (particpantExtent in chatExtent.participants.value.values) {
+ if (particpantExtent.pinTap.justUpdated) {
+ newPinned = particpantExtent
+ break
+ } else if (particpantExtent == currentPinned) {
+ newPinned = currentPinned
+ }
+ }
+
+ chatExtent.pinnedParticipant.update(newPinned, true)
+ }
+ // end::chat_pinned[]
+
+
+ }
+}
+
+// @tag::participant_extent[]
+class ParticipantExtent(
+ graph: Graph,
+ participantId: String,
+ chatExtent: ChatExtent
+
+) : Extent(graph) {
+// @end::participant_extent[]
+
+ val chatExtent: ChatExtent
+ // @tag::participant_mute_resources[]
+ val muteTap: Moment
+ val muted: State
+ // @end::participant_mute_resources[]
+
+ // @tag::participant_pin_resources[]
+ val pinTap: Moment
+ // @end::participant_pin_resources[]
+ val participantId: String
+
+ init{
+ this.participantId = participantId
+ this.chatExtent = chatExtent
+
+ this.pinTap = Moment(this)
+
+ // tag::participant_mute[]
+ this.muteTap = Moment(this)
+ this.muted = State(this, false)
+ this.makeBehavior(listOf(this.muteTap), listOf(this.muted)){ extent ->
+ if (extent.muteTap.justUpdated) {
+ extent.muted.update(extent.muted.value, true)
+ if (extent.muted.justUpdated) {
+ extent.sideEffect("mute toggle") { extent ->
+
+ extent.muteParticipant(extent.muted.value)
+ extent.updateMuteUI(extent.muted.value)
+ }
+ }
+ }
+ }
+ // end::participant_mute[]
+
+ /*
+ // tag::participant_mute_alt[]
+ this.muteBehavior = this.makeBehavior(listOf(this.muteTap), listOf(this.muted), //...
+ // end::participant_mute_alt[]
+ */
+
+ // tag::participant_pinned[]
+ this.makeBehavior(listOf(this.chatExtent.pinnedParticipant), null) { participantExtent ->
+ if (participantExtent.chatExtent.pinnedParticipant.justUpdatedTo(participantExtent)) {
+ participantExtent.sideEffect("show as pinned") {
+ it.updatePinUI(true)
+ }
+ } else if (participantExtent.chatExtent.pinnedParticipant.justUpdatedFrom(participantExtent)) {
+ participantExtent.sideEffect("show as normal") {
+ it.updatePinUI(false)
+ }
+ }
+ }
+ // end::participant_pinned[]
+
+ }
+
+ fun updatePinUI(pinned: Boolean) {
+ // update UI
+ }
+
+ fun updateMuteUI(mute: Boolean) {
+ // update UI
+ }
+
+ fun muteParticipant(mute: Boolean) {
+ // external api call
+ }
+}
\ No newline at end of file
diff --git a/doc-site/examples/kotlin/BGExamples/LoginExtent.kt b/doc-site/examples/kotlin/BGExamples/LoginExtent.kt
new file mode 100644
index 0000000..af5cb64
--- /dev/null
+++ b/doc-site/examples/kotlin/BGExamples/LoginExtent.kt
@@ -0,0 +1,287 @@
+import com.yahoo.behaviorgraph.Extent
+import com.yahoo.behaviorgraph.Graph
+import com.yahoo.behaviorgraph.Moment
+import com.yahoo.behaviorgraph.State
+
+// tag::login_enable_extent[]
+class LoginExtent(graph: Graph) : Extent(graph) {
+ val email: State
+ val password: State
+// end::login_enable_extent[]
+
+ val loggingIn = State(this, false)
+ val emailValid = State(this, false)
+ val passwordValid = State(this, false)
+ val loginEnabled = State(this, false)
+ val loginClick = Moment(this)
+ val returnKey = Moment(this)
+ val loginComplete = Moment(this)
+
+ // tag::login_enable_init[]
+ init {
+
+ this.email = State(this, "")
+ this.password = State(this, "")
+
+ // tag::login_enable_behavior[]
+ this.makeBehavior(listOf(email, password), null) { loginExtent ->
+ // end::login_enable_init[]
+ val email = loginExtent.email.value
+ val password2: String = loginExtent.password.value
+ val hasPassword = password2.length > 0
+ val loginEnabled = loginExtent.validEmailAddress(email) && hasPassword
+ loginExtent.sideEffect("enable login button") {
+ loginExtent.enableLoginButton(loginEnabled)
+ }
+ }
+ // end::login_enable_behavior[]
+ }
+
+
+ fun validEmailAddress(email: String) : Boolean {
+ return (email.length > 0) && email.includes('@')
+ }
+
+ fun doLogin(email: String, password: String, complete: (Boolean) -> Unit) {
+ // login api calls
+ }
+
+ fun enableLoginButton(enabled: Boolean) {
+ // side effect to set the enabled state of the login button
+ }
+
+ // tag::login_sequence_compare[]
+ fun emailChangedSincePassword() : Boolean {
+ return this.email.event.sequence > this.password.event.sequence
+ }
+ // end::login_sequence_compare[]
+
+ // tag::login_timestamp[]
+ fun loginCompletedWhen() : Long {
+ return this.loginComplete.event!!.timestamp
+ }
+ // end::login_timestamp[]
+
+
+}
+class LoginCompleteExtent(graph: Graph) : Extent(graph) {
+ val email = State(this, "")
+ val password = State(this, "")
+
+ val loggingIn = State(this, false)
+ val emailValid = State(this, false)
+ val passwordValid = State(this, false)
+
+ //begin complete constructor
+
+ init {
+
+ // tag::login_complete_email[]
+ val emailValid = State(this, false)
+ makeBehavior(listOf(email), listOf(emailValid)) { loginExtent ->
+ val email = loginExtent.email.value
+ val emailValid = loginExtent.validEmailAddress(email)
+ loginExtent.emailValid.update(emailValid, true)
+ }
+ // end::login_complete_email[]
+
+ makeBehavior(listOf(password), listOf(passwordValid)) { loginExtent ->
+ val password = loginExtent.password.value
+ val passwordValid = password.length > 0
+ loginExtent.passwordValid.update(passwordValid, true)
+ }
+
+ // tag::login_complete_enable[]
+ val loginEnabled = State(this, false)
+ makeBehavior(listOf(emailValid, passwordValid, loggingIn), listOf(loginEnabled)) { loginExtent ->
+ val enabled = loginExtent.emailValid.value && loginExtent.passwordValid.value && !loginExtent.loggingIn.value;
+ loginExtent.loginEnabled.update(enabled, true);
+ loginExtent.sideEffect("enable login button") { loginExtent ->
+ loginExtent.enableLoginButton(loginExtent.loginEnabled.value);
+ }
+ }
+ // end::login_complete_enable[]
+
+ // tag::login_complete_login[]
+ val loginClick = Moment(this)
+ val returnKey = Moment(this)
+ val loginComplete = Moment(this)
+ makeBehavior(listOf(loginClick, returnKey, loginComplete), listOf(this.loggingIn)) { loginExtent ->
+ if ((loginExtent.loginClick.justUpdated || loginExtent.returnKey.justUpdated) &&
+ loginExtent.loginEnabled.traceValue) {
+ // Start login
+ loginExtent.loggingIn.update(true, true);
+ } else if (loginExtent.loginComplete.justUpdated &&
+ !loginExtent.loginComplete.value &&
+ loginExtent.loggingIn.value) {
+ // Login failed
+ loginExtent.loggingIn.update(false, true);
+ }
+
+ if (loginExtent.loggingIn.justUpdatedTo(true)) {
+ loginExtent.sideEffect("login api call") {
+ it.doLogin(it.email.value, it.password.value) { success ->
+ it.action("login call returned") {
+ it.loginComplete.update(success);
+ }
+ }
+ }
+ }
+ }
+ // end::login_complete_login[]
+
+ // Dont delete; its used in the documentation
+ // this has an example of requireSync
+ /*
+ makeBehavior(listOf(loginClick, returnKey, loginComplete), listOf(loggingIn)) { extent ->
+ if ((extent.loginClick.justUpdated || extent.returnKey.justUpdated) &&
+ extent.loginEnabled.traced.value) {
+ // Start login
+ extent.loggingIn.update(true, true)
+ } else if (extent.loginComplete.justUpdated &&
+ !extent.loginComplete.value &&
+ extent.loggingIn.value) {
+ // Login failed
+ extent.loggingIn.update(false, true)
+ }
+
+ if (extent.loggingIn.justUpdatedTo(true)) {
+ // tag::login_complete_loginalt[]
+ extent.sideEffect("login api call") {
+ it.doLogin(extent.email.value, extent.password.value) { success ->
+ it.actionAsync("login call returned") {
+ it.loginComplete.update(success)
+ })
+ })
+ })
+ // end::login_complete_loginalt[]
+ }
+ })
+ */
+
+ }
+
+
+ //end complete constructor
+
+
+ fun validEmailAddress(email: String) : Boolean {
+ return (email.length > 0) && email.includes('@')
+ }
+
+ fun doLogin(email: String, password: String, complete: (Boolean) -> Unit) {
+ // login api calls
+ }
+
+ fun enableLoginButton(enabled: Boolean) {
+ // side effect to set the enabled state of the login button
+ }
+
+}
+
+class LoginShortExtent {
+ val email = State(this, "")
+ val password = State(this, "")
+
+ val loggingIn = State(this, false)
+ val emailValid = State(this, false)
+ val passwordValid = State(this, false)
+
+ init {
+ // tag::login_intro_short1[]
+ makeBehavior(listOf(email, password), listOf(loginEnabled)) { extent ->
+
+ val emailValid = extent.validEmailAddress(email.value)
+ val passwordValid = extent.password.value.length > 0
+ val enabled = emailValid && passwordValid
+ extent.loginEnabled.update(enabled)
+
+ };
+ // end::login_intro_short1[]
+
+
+ // tag::login_intro_short2[]
+ makeBehavior(listOf(loginClick), listOf(loggingIn)) { extent ->
+
+ if (extent.loginClick.justUpdated && !extent.loggingIn.value) {
+ extent.loggingIn.update(true);
+ }
+
+ }
+
+ makeBehavior(listOf(email, password, loggingIn), listOf(loginEnabled)) { extent ->
+
+ val emailValid = extent.validEmailAddress(email.value)
+ val passwordValid = extent.password.value.length > 0
+ val enabled = emailValid && passwordValid && !extent.loggingIn.value
+ extent.loginEnabled.update(enabled)
+
+ };
+ // end::login_intro_short2[]
+
+ // tag::login_intro_sideeffect[]
+ makeBehavior(listOf(email, password, loggingIn), listOf(loginEnabled)) { extent ->
+
+ val emailValid = extent.validEmailAddress(email.value)
+ val passwordValid = extent.password.value.length > 0
+ val enabled = emailValid && passwordValid && !extent.loggingIn.value
+ extent.loginEnabled.update(enabled)
+
+ extent.sideEffect("login button enabled") { extent ->
+ extent.loginButton.enabled = extent.loginEnabled.value;
+ }
+
+ };
+ // end::login_intro_sideeffect[]
+
+ }
+
+ // tag::login_intro_action[]
+ fun loginButtonClicked() {
+ this.graph.action("login button clicked") {
+ this.loginClick.update(Unit)
+ }
+ }
+ // end::login_intro_action[]
+
+}
+
+class LoginPage {
+
+ val graph: Graph
+ val loginExtent: LoginExtent
+
+ init {
+ // tag::login_enable_setup[]
+ this.graph = Graph()
+ this.loginExtent = LoginExtent(graph)
+ this.graph.action("new login page") {
+ this.loginExtent.addToGraph()
+ }
+ // end::login_enable_setup[]
+ }
+
+ // tag::login_enable_actions[]
+ fun didUpdateEmailField(contents: String) {
+ this.graph.action("update email field") {
+ this.loginExtent.email.update(contents, true)
+ }
+ }
+
+ fun didUpdatePasswordField(contents: String) {
+ this.graph.action("update password field") {
+ this.loginExtent.password.update(contents, true)
+ }
+ }
+ // end::login_enable_actions[]
+
+ // tag::login_complete_click[]
+ fun loginButtonClicked() {
+ this.graph.action("login button clicked") {
+ this.loginExtent.loginClick.update(Unit)
+ }
+ }
+ // end::login_complete_click[]
+
+
+}
diff --git a/doc-site/examples/kotlin/BGExamples/com/yahoo/behaviorgraph/Action.kt b/doc-site/examples/kotlin/BGExamples/com/yahoo/behaviorgraph/Action.kt
new file mode 100644
index 0000000..9982496
--- /dev/null
+++ b/doc-site/examples/kotlin/BGExamples/com/yahoo/behaviorgraph/Action.kt
@@ -0,0 +1,3 @@
+package com.yahoo.behaviorgraph
+
+internal class Action(val impulse: String?, val block: () -> Unit)
diff --git a/doc-site/examples/kotlin/BGExamples/com/yahoo/behaviorgraph/Behavior.kt b/doc-site/examples/kotlin/BGExamples/com/yahoo/behaviorgraph/Behavior.kt
new file mode 100644
index 0000000..76d3fbc
--- /dev/null
+++ b/doc-site/examples/kotlin/BGExamples/com/yahoo/behaviorgraph/Behavior.kt
@@ -0,0 +1,39 @@
+package com.yahoo.behaviorgraph
+
+class Behavior(
+ val extent: Extent<*>, demands: List?, supplies: List?,
+ var block: (Extent<*>) -> Unit
+) : Comparable {
+ var demands: MutableSet? = null
+ var supplies: MutableSet? = null
+ var debugName: String? = null
+ var enqueuedWhen: Long? = null
+ var removedWhen: Long? = null
+ var added = false
+ internal var orderingState = OrderingState.Unordered
+ var order: Long = 0
+ var untrackedDemands: List?
+ var untrackedSupplies: List?
+
+ init {
+ extent.addBehavior(this)
+ this.untrackedDemands = demands
+ this.untrackedSupplies = supplies
+ }
+
+ override fun compareTo(other: Behavior): Int {
+ return order.compareTo(other.order)
+ }
+
+ override fun toString(): String {
+ return "Behavior(debugName=$debugName)"
+ }
+
+ fun setDemands(newDemands: List) {
+ this.extent.graph.updateDemands(this, newDemands)
+ }
+
+ fun setSupplies(newSupplies: List) {
+ this.extent.graph.updateSupplies(this, newSupplies)
+ }
+}
diff --git a/doc-site/examples/kotlin/BGExamples/com/yahoo/behaviorgraph/Event.kt b/doc-site/examples/kotlin/BGExamples/com/yahoo/behaviorgraph/Event.kt
new file mode 100644
index 0000000..fa1b1db
--- /dev/null
+++ b/doc-site/examples/kotlin/BGExamples/com/yahoo/behaviorgraph/Event.kt
@@ -0,0 +1,7 @@
+package com.yahoo.behaviorgraph
+
+data class Event(val sequence: Long, val timestamp: Long, val impulse: String?) {
+ companion object {
+ val InitialEvent: Event = Event(0, 0, "InitialEvent")
+ }
+}
diff --git a/doc-site/examples/kotlin/BGExamples/com/yahoo/behaviorgraph/Extent.kt b/doc-site/examples/kotlin/BGExamples/com/yahoo/behaviorgraph/Extent.kt
new file mode 100644
index 0000000..5f10227
--- /dev/null
+++ b/doc-site/examples/kotlin/BGExamples/com/yahoo/behaviorgraph/Extent.kt
@@ -0,0 +1,97 @@
+package com.yahoo.behaviorgraph
+
+import com.yahoo.behaviorgraph.exception.BehaviorGraphException
+import kotlin.system.measureTimeMillis
+
+/**
+ * A container for a group of related behaviors and resources
+ */
+open class Extent(val graph: Graph) { //TODO restrict SubclassType to subclass of Extent
+ var debugName: String = javaClass.simpleName
+ internal var behaviors: MutableList = mutableListOf()
+ internal var resources: MutableList = mutableListOf()
+ var addedToGraphWhen: Event? = null
+ internal set
+
+ fun addBehavior(behavior: Behavior) {
+ this.behaviors.add(behavior)
+ }
+
+ fun addResource(resource: Resource) {
+ this.resources.add(resource)
+ }
+
+ fun addToGraphWithAction() {
+ this.graph.action("add extent: $debugName") { this.addToGraph() }
+ }
+
+ fun addToGraph() {
+ if (graph.currentEvent != null) {
+ nameResources()
+ graph.addExtent(this)
+ } else {
+ throw BehaviorGraphException("addToGraph must be called within an event. Extent=$this")
+ }
+ }
+
+ fun removeFromGraphWithAction() {
+ this.graph.action("remove extent: $debugName") { this.removeFromGraph() }
+ }
+
+ fun removeFromGraph() {
+ if (graph.currentEvent != null) {
+ if (addedToGraphWhen != null) {
+ graph.removeExtent(this)
+ }
+ } else {
+ throw BehaviorGraphException("removeFromGraph must be called within an event. Extent=$this")
+ }
+ }
+
+ fun makeBehavior(
+ demands: List?,
+ supplies: List?,
+ block: (SubclassType) -> Unit
+ ): Behavior {
+ return Behavior(this, demands, supplies, block as (Extent<*>) -> Unit)
+ }
+
+ fun sideEffect(name: String?, block: (extent: SubclassType) -> Unit) {
+ graph.sideEffect(this, name, block as (Extent<*>) -> Unit)
+ }
+
+ fun actionAsync(impulse: String?, action: () -> Unit) {
+ if (this.addedToGraphWhen != null) {
+ this.graph.actionAsync(impulse, action)
+ } else {
+ throw BehaviorGraphException("Action on extent requires it be added to the graph. Extent=$this")
+ }
+ }
+
+ fun action(impulse: String?, action: () -> Unit) {
+ if (this.addedToGraphWhen != null) {
+ this.graph.action(impulse, action)
+ } else {
+ throw BehaviorGraphException("Action on extent requires it be added to the graph. Extent=$this")
+ }
+ }
+
+ /**
+ * Ensure all resources in self (not superclasses) have a debugName.
+ * future: move to platformSupport since this is not portable beyond the jvm
+ */
+ private fun nameResources() {
+ val timeMS = measureTimeMillis {
+ javaClass.declaredFields.forEach { field ->
+ if (field.type == Resource::class.java) {
+ val resource = field.get(this) as Resource
+ if (resource.debugName == null) {
+ println("setting debugName for ${field.name}")
+ resource.debugName = field.name
+ }
+ }
+ }
+ }
+ println("collectAndNameResources() time was $timeMS ms")
+ }
+}
diff --git a/doc-site/examples/kotlin/BGExamples/com/yahoo/behaviorgraph/Graph.kt b/doc-site/examples/kotlin/BGExamples/com/yahoo/behaviorgraph/Graph.kt
new file mode 100644
index 0000000..835a3a0
--- /dev/null
+++ b/doc-site/examples/kotlin/BGExamples/com/yahoo/behaviorgraph/Graph.kt
@@ -0,0 +1,472 @@
+package com.yahoo.behaviorgraph
+
+import com.yahoo.behaviorgraph.Event.Companion.InitialEvent
+import com.yahoo.behaviorgraph.exception.AllDemandsMustBeAddedToTheGraphExceptions
+import com.yahoo.behaviorgraph.exception.BehaviorDependencyCycleDetectedException
+import com.yahoo.behaviorgraph.exception.BehaviorGraphException
+import com.yahoo.behaviorgraph.exception.ExtentsCanOnlyBeAddedDuringAnEventException
+import com.yahoo.behaviorgraph.exception.ExtentsCanOnlyBeRemovedDuringAnEventException
+import com.yahoo.behaviorgraph.exception.ResourceCannotBeSuppliedByMoreThanOneBehaviorException
+import com.yahoo.behaviorgraph.platform.PlatformSupport
+import java.util.ArrayDeque
+import java.util.ArrayList
+import java.util.Deque
+import java.util.HashSet
+import java.util.PriorityQueue
+import kotlin.math.max
+
+class Graph constructor(private var platformSupport: PlatformSupport = PlatformSupport.platformSupport) {
+ internal var currentEvent: Event? = null
+ var lastEvent: Event
+ private var activatedBehaviors: PriorityQueue
+ internal var currentBehavior: Behavior? = null
+ private var effects: Deque
+ private var actions: Deque
+ private var untrackedBehaviors: MutableList
+ private var modifiedDemandBehaviors: MutableList
+ private var modifiedSupplyBehaviors: MutableList
+ private var updatedTransients: MutableList
+ private var needsOrdering: MutableList
+
+ init {
+ effects = ArrayDeque()
+ actions = ArrayDeque()
+ activatedBehaviors = PriorityQueue()
+ untrackedBehaviors = ArrayList()
+ modifiedDemandBehaviors = ArrayList()
+ modifiedSupplyBehaviors = ArrayList()
+ updatedTransients = ArrayList()
+ needsOrdering = ArrayList()
+ lastEvent = InitialEvent
+ }
+
+ //*****
+ fun actionAsync(impulse: String?, block: () -> Unit) {
+ val action = Action(impulse, block)
+ this.actions.addLast(action)
+ if (this.currentEvent == null) {
+ this.eventLoop()
+ }
+ }
+
+ fun action(impulse: String?, block: () -> Unit) {
+ val action = Action(impulse, block)
+ this.actions.addLast(action)
+ this.eventLoop()
+ }
+
+ fun eventLoop() {
+ while (true) {
+ try {
+ if (this.activatedBehaviors.size > 0 ||
+ this.untrackedBehaviors.size > 0 ||
+ this.modifiedDemandBehaviors.size > 0 ||
+ this.modifiedSupplyBehaviors.size > 0 ||
+ this.needsOrdering.size > 0
+ ) {
+ val sequence = this.currentEvent!!.sequence
+ this.addUntrackedBehaviors(sequence)
+ this.addUntrackedSupplies(sequence)
+ this.addUntrackedDemands(sequence)
+ this.orderBehaviors()
+ this.runNextBehavior(sequence)
+
+ continue
+ }
+
+ if (effects.isNotEmpty()) {
+ val effect = this.effects.removeFirst()
+ effect.block(effect.extent)
+ continue
+ }
+
+ currentEvent?.let { aCurrentEvent ->
+ this.clearTransients()
+ this.lastEvent = aCurrentEvent
+ this.currentEvent = null
+ this.currentBehavior = null
+ }
+
+ if (actions.isNotEmpty()) {
+ val action = actions.removeFirst()
+ val newEvent = Event(
+ this.lastEvent.sequence + 1,
+ platformSupport.getCurrentTimeMillis(),
+ action.impulse
+ )
+ this.currentEvent = newEvent
+ action.block()
+ continue
+ }
+ } catch (e: Exception) {
+ //put graph into clean state and rethrow exception
+ this.currentEvent = null
+ this.actions.clear()
+ this.effects.clear()
+ this.currentBehavior = null
+ this.activatedBehaviors.clear()
+ this.clearTransients()
+ this.modifiedDemandBehaviors.clear()
+ this.modifiedSupplyBehaviors.clear()
+ this.untrackedBehaviors.clear()
+ throw(e)
+ }
+ // no more tasks so we can exit the event loop
+ break
+ }
+ }
+
+ private fun clearTransients() {
+ updatedTransients.forEach {
+ it.clear()
+ }
+ updatedTransients.clear()
+ }
+
+ internal fun trackTransient(resource: Transient) {
+ this.updatedTransients.add(resource)
+ }
+
+ internal fun resourceTouched(resource: Resource) {
+ this.currentEvent?.let { aCurrentEvent ->
+ for (subsequent in resource.subsequents) {
+ this.activateBehavior(subsequent, aCurrentEvent.sequence)
+ }
+ }
+ }
+
+ private fun activateBehavior(behavior: Behavior, sequence: Long) {
+ if (behavior.enqueuedWhen == null || behavior.enqueuedWhen!! < sequence) {
+ behavior.enqueuedWhen = sequence
+ //note addLast() versus the javascript push(), which here would add first and in javascript appends
+ this.activatedBehaviors.add(behavior)
+ }
+ }
+
+ private fun runNextBehavior(sequence: Long) {
+ if (this.activatedBehaviors.isEmpty()) {
+ return
+ }
+ val behavior = this.activatedBehaviors.remove()
+ if (behavior.removedWhen != sequence) {
+ this.currentBehavior = behavior
+ behavior.block(behavior.extent!!)
+ this.currentBehavior = null
+ }
+ }
+
+ internal fun sideEffect(extent: Extent<*>, name: String?, block: (extent: Extent<*>) -> Unit) {
+ if (this.currentEvent == null) {
+ throw BehaviorGraphException("Effects can only be added during an event loop.")
+ } else {
+ this.effects.addLast(SideEffect(name, block, extent))
+ }
+ }
+
+ private fun addUntrackedBehaviors(sequence: Long) {
+ for (behavior in this.untrackedBehaviors) {
+ this.activateBehavior(behavior, sequence)
+ this.modifiedDemandBehaviors.add(behavior)
+ this.modifiedSupplyBehaviors.add(behavior)
+ }
+ this.untrackedBehaviors.clear()
+ }
+
+ //Note: parameter sequence not used. We'll keep because typescript uses it.
+ private fun addUntrackedSupplies(sequence: Long) {
+ modifiedSupplyBehaviors.forEach { behavior ->
+ behavior.untrackedSupplies?.let { behaviorUntrackedSupplies ->
+ behavior.supplies?.forEach { existingSupply ->
+ existingSupply.suppliedBy = null
+ }
+ behavior.supplies = HashSet(behaviorUntrackedSupplies)
+ behavior.supplies?.forEach { newSupply: Resource ->
+ if (newSupply.suppliedBy != null && newSupply.suppliedBy !== behavior) {
+ throw ResourceCannotBeSuppliedByMoreThanOneBehaviorException(
+ "Resource cannot be supplied by more than one behavior",
+ newSupply,
+ behavior
+ )
+ }
+ newSupply.suppliedBy = behavior
+ }
+ behavior.untrackedSupplies = null
+ // technically this doesn't need reordering but its subsequents will
+ // setting this to reorder will also adjust its subsequents if necessary
+ // in the sortDFS code
+ this.needsOrdering.add(behavior)
+ }
+ }
+ this.modifiedSupplyBehaviors.clear()
+ }
+
+ private fun addUntrackedDemands(sequence: Long) {
+ modifiedDemandBehaviors.forEach { behavior ->
+ behavior.untrackedDemands?.let { untrackedDemands ->
+ var removedDemands: MutableList? = null
+ behavior.demands?.forEach { demand ->
+ if (!untrackedDemands.contains(demand)) {
+ if (removedDemands == null) {
+ removedDemands = ArrayList()
+ }
+ removedDemands?.add(demand)
+ }
+ }
+ var addedDemands: MutableList? = null
+
+ for (untrackedDemand in untrackedDemands) {
+ if (!untrackedDemand.added) {
+ throw AllDemandsMustBeAddedToTheGraphExceptions(
+ "All demands must be added to the graph.",
+ behavior,
+ untrackedDemand
+ )
+ }
+ if (behavior.demands == null || !behavior.demands!!.contains(untrackedDemand)) {
+ if (addedDemands == null) {
+ addedDemands = ArrayList()
+ }
+ addedDemands.add(untrackedDemand)
+ }
+ }
+ var needsRunning = false
+
+ removedDemands?.let { localRemovedDemands ->
+ needsRunning = true
+ for (demand in localRemovedDemands) {
+ demand.subsequents.remove(behavior)
+ }
+ }
+ var orderBehavior = behavior.orderingState == OrderingState.Unordered
+
+ addedDemands?.let { localAddedDemands ->
+ needsRunning = true
+ for (demand in localAddedDemands) {
+ demand.subsequents.add(behavior)
+
+ if (!orderBehavior) {
+ val prior = demand.suppliedBy
+ if (prior != null && prior.orderingState == OrderingState.Ordered && prior.order >= behavior.order) {
+ orderBehavior = true
+ }
+ }
+ }
+ }
+
+ behavior.demands = HashSet(behavior.untrackedDemands)
+ behavior.untrackedDemands = null
+
+ if (orderBehavior) {
+ this.needsOrdering.add(behavior)
+ }
+ if (needsRunning) {
+ this.activateBehavior(behavior, sequence)
+ }
+ }
+ }
+ this.modifiedDemandBehaviors.clear()
+ }
+
+ /**
+ * find all behaviors that need ordering and their
+ // subsequents and mark them all as needing ordering
+ */
+ private fun orderBehaviors() {
+ if (needsOrdering.isEmpty()) {
+ return
+ }
+ val localNeedsOrdering = ArrayList()
+ val traversalQueue = ArrayDeque()
+ // first get behaviors that need ordering and mark them as
+ // ordered so they will be traversed when first encountered
+ for (behavior in needsOrdering) {
+ behavior.orderingState = OrderingState.Ordered
+ traversalQueue.addLast(behavior)
+ }
+
+ needsOrdering.clear()
+
+ while (traversalQueue.isNotEmpty()) {
+ var behavior = traversalQueue.removeFirst()
+
+ if (behavior.orderingState == OrderingState.Ordered) {
+ behavior.orderingState = OrderingState.Unordered
+ localNeedsOrdering.add(behavior)
+ behavior.supplies?.forEach { aResource ->
+ aResource.subsequents.forEach { aSubsequentBehavior ->
+ traversalQueue.push(aSubsequentBehavior)
+ }
+ }
+ }
+ }
+ //TODO is there a kotlin idiom for the following?
+ val needsReheap = mutableListOf(false) // this allows out parameter
+ for (behavior in localNeedsOrdering) {
+ this.sortDFS(behavior, needsReheap)
+ }
+
+ if (needsReheap.first()) {
+ //we've invalidated our current activatedBehaviors by changing the priority of
+ //some of the behaviors, so resort.
+ val newActivatedBehaviors = PriorityQueue()
+ activatedBehaviors.forEach {
+ newActivatedBehaviors.add(it)
+ }
+ activatedBehaviors = newActivatedBehaviors
+ }
+ }
+
+ private fun sortDFS(behavior: Behavior, needsReheap: MutableList) {
+ if (behavior.orderingState == OrderingState.Ordering) {
+ throw BehaviorDependencyCycleDetectedException(
+ "Behavior dependency cycle detected.", behavior,
+ cycleForBehavior(behavior)
+ )
+ }
+
+ if (behavior.orderingState == OrderingState.Unordered) {
+ behavior.orderingState = OrderingState.Ordering
+ var order = 0L
+ behavior.demands?.forEach { localResource ->
+ localResource.suppliedBy?.let { localBehavior ->
+ if (localBehavior.orderingState != OrderingState.Ordered) {
+ this.sortDFS(localBehavior, needsReheap)
+ }
+ order = max(order, localBehavior.order + 1)
+ }
+ }
+
+ behavior.orderingState = OrderingState.Ordered
+
+ if (order != behavior.order) {
+ behavior.order = order
+ needsReheap[0] = true
+ }
+ }
+ }
+
+ private fun cycleForBehavior(behavior: Behavior): List {
+ var stack = ArrayList() //we'll "push" and "pop" from the end
+ if (cycleDFS(behavior, behavior, stack)) {
+ var output = ArrayList()
+ while (stack.isNotEmpty()) {
+ var rez = stack.removeAt(stack.size - 1)
+ output.add(rez)
+ }
+ return output
+ } else {
+ return ArrayList()
+ }
+ }
+
+ private fun cycleDFS(
+ currentBehavior: Behavior,
+ target: Behavior,
+ stack: MutableList
+ ): Boolean {
+ currentBehavior.demands?.forEach { aResource ->
+ stack.add(aResource)
+ var b = aResource.suppliedBy
+ if (b != null) {
+ if (b == target) {
+ return true
+ }
+ if (this.cycleDFS(b, target, stack)) {
+ return true
+ }
+ stack.removeAt(stack.size - 1)
+ }
+ }
+
+ return false
+ }
+
+ private fun addBehavior(behavior: Behavior) {
+ behavior.added = true
+ this.untrackedBehaviors.add(behavior)
+ }
+
+ fun updateDemands(behavior: Behavior, newDemands: List) {
+ if (!behavior.added) {
+ throw BehaviorGraphException("Behavior must belong to graph before updating demands: $behavior")
+ } else if (this.currentEvent == null) {
+ throw BehaviorGraphException("Demands can only be updated during an event loop. Behavior=$behavior")
+ }
+ behavior.untrackedDemands = newDemands
+ this.modifiedDemandBehaviors.add(behavior)
+ }
+
+ fun updateSupplies(behavior: Behavior, newSupplies: List) {
+ if (!behavior.added) {
+ throw BehaviorGraphException("Behavior must belong to graph before updating supplies. Behavior=$behavior")
+ }
+
+ this.currentEvent
+ ?: throw BehaviorGraphException("Supplies can only be updated during an event loop. Behavior=$behavior")
+
+ behavior.untrackedSupplies = newSupplies
+ this.modifiedSupplyBehaviors.add(behavior)
+ }
+
+ private fun removeBehavior(behavior: Behavior, sequence: Long) {
+ // remove all behaviors supplies from subsequents demands
+ behavior.supplies?.forEach { supply ->
+ supply.subsequents.forEach { subsequent ->
+ subsequent.demands?.remove(supply)
+ }
+ supply.subsequents.clear()
+ }
+
+ behavior.demands?.forEach { demand ->
+ demand.subsequents.remove(behavior)
+ }
+ behavior.demands?.clear()
+
+
+ behavior.removedWhen = sequence
+ behavior.added = false
+ }
+
+ private fun addResource(resource: Resource) {
+ resource.added = true
+ }
+
+ fun addExtent(extent: Extent<*>) {
+ if (extent.addedToGraphWhen != null) {
+ throw BehaviorGraphException("Extent $extent has already been added to the graph: ${extent.graph}")
+ }
+
+ this.currentEvent?.let { localCurrentEvent ->
+ extent.addedToGraphWhen = localCurrentEvent
+ extent.resources.forEach {
+ this.addResource(it)
+ }
+ extent.behaviors.forEach {
+ addBehavior(it)
+ }
+ } ?: run {
+ throw ExtentsCanOnlyBeAddedDuringAnEventException(
+ "Extents can only be added during an event.",
+ extent
+ )
+ }
+ }
+
+ fun removeExtent(extent: Extent<*>) {
+ this.currentEvent?.let { localCurrentEvent ->
+ extent.resources.forEach { resource ->
+ resource.added = false
+ }
+ extent.behaviors.forEach { behavior ->
+ removeBehavior(behavior, localCurrentEvent.sequence)
+ }
+
+ extent.addedToGraphWhen = null
+ } ?: run {
+ throw ExtentsCanOnlyBeRemovedDuringAnEventException(
+ "Extents can only be removed during an event loop.",
+ extent
+ )
+ }
+ }
+}
diff --git a/doc-site/examples/kotlin/BGExamples/com/yahoo/behaviorgraph/Moment.kt b/doc-site/examples/kotlin/BGExamples/com/yahoo/behaviorgraph/Moment.kt
new file mode 100644
index 0000000..fc45ebc
--- /dev/null
+++ b/doc-site/examples/kotlin/BGExamples/com/yahoo/behaviorgraph/Moment.kt
@@ -0,0 +1,53 @@
+package com.yahoo.behaviorgraph
+
+import com.yahoo.behaviorgraph.exception.BehaviorGraphException
+
+class Moment(extent: Extent<*>, debugName: String? = null) : Resource(extent, debugName),
+ Transient {
+ private var _happened = false
+ private var _happenedValue: T? = null
+ private var _happenedWhen: Event? = null
+
+ /**
+ * return if we've just been updated
+ */
+ val justUpdated: Boolean
+ get() = this._happened
+
+ /**
+ * @throws BehaviorGraphException if not justUpdated())
+ */
+ val value: T
+ get() {
+ if (!justUpdated) {
+ throw BehaviorGraphException("Cannot access value unless it has been justUpdated()")
+ }
+ return this._happenedValue!!
+ }
+ val event: Event?
+ get() = this._happenedWhen
+
+ fun updateWithAction(value: T) {
+ graph.action(getImpulse()) { update(value) }
+ }
+
+ private fun getImpulse(): String? {
+ return if (this.debugName != null) {
+ "Impulse From happen(): $this)"
+ } else null
+ }
+
+ fun update(value: T) {
+ this.assertValidUpdater()
+ this._happened = true
+ this._happenedValue = value
+ this._happenedWhen = this.graph.currentEvent
+ this.graph.resourceTouched(this)
+ this.graph.trackTransient(this)
+ }
+
+ override fun clear() {
+ this._happened = false
+ this._happenedValue = null
+ }
+}
diff --git a/doc-site/examples/kotlin/BGExamples/com/yahoo/behaviorgraph/OrderingState.kt b/doc-site/examples/kotlin/BGExamples/com/yahoo/behaviorgraph/OrderingState.kt
new file mode 100644
index 0000000..8028fee
--- /dev/null
+++ b/doc-site/examples/kotlin/BGExamples/com/yahoo/behaviorgraph/OrderingState.kt
@@ -0,0 +1,7 @@
+package com.yahoo.behaviorgraph
+
+internal enum class OrderingState {
+ Unordered,
+ Ordered,
+ Ordering
+}
diff --git a/doc-site/examples/kotlin/BGExamples/com/yahoo/behaviorgraph/Resource.kt b/doc-site/examples/kotlin/BGExamples/com/yahoo/behaviorgraph/Resource.kt
new file mode 100644
index 0000000..687ddca
--- /dev/null
+++ b/doc-site/examples/kotlin/BGExamples/com/yahoo/behaviorgraph/Resource.kt
@@ -0,0 +1,139 @@
+package com.yahoo.behaviorgraph
+
+import com.yahoo.behaviorgraph.exception.BehaviorGraphException
+import java.util.HashSet
+
+open class Resource(val extent: Extent<*>, var debugName: String? = null) {
+ val graph: Graph = extent.graph
+ var added = false
+ var subsequents: MutableSet = HashSet()
+ var suppliedBy: Behavior? = null
+
+ init {
+ extent.addResource(this)
+ }
+
+ internal fun assertValidUpdater() {
+ val currentBehavior = graph.currentBehavior
+ val currentEvent = graph.currentEvent
+ if (currentBehavior == null && currentEvent == null) {
+ throw BehaviorGraphException("Resource $debugName must be updated inside a behavior or action")
+ }
+ if (this.suppliedBy != null && currentBehavior != this.suppliedBy) {
+ throw BehaviorGraphException("Supplied resource $debugName can only be updated by its supplying behavior. CurrentBehavior = $currentBehavior")
+
+ }
+ if (this.suppliedBy == null && currentBehavior != null) {
+ throw BehaviorGraphException("Unsupplied resource $debugName can only be updated in an action. CurrentBehavior=$currentBehavior")
+ }
+ }
+}
+
+/*
+open class Resource {
+ var value: T? = null
+ var event: Event? = null
+ var debugName: String? = null
+ var traced: Boolean = false
+ var valuePersistence: ValuePersistence
+ var previousValue: T? = null
+ var previousEvent: Event? = null
+ var graph: Graph
+ var added: Boolean = false
+ var extent: Extent? = null
+ var subsequents: MutableSet
+ var suppliedBy: Behavior? = null
+ var capturedUpdate: (() -> Unit)? = null
+ var traceValue: T?
+ get() {
+ if (!traced) {
+ throw BehaviorGraphException("Accessing traced value for non traced resource. + $this")
+ }
+ return if (justHappened()) this.previousValue else this.value;
+ }
+ var traceEvent: Event?
+ get() {
+ if (!this.traced) {
+ throw BehaviorGraphException("Accessing traced event for non traced resource: $this");
+ } else {
+ return if (this.justHappened()) this.previousEvent else this.event;
+ }
+ }
+
+ init {
+ valuePersistence = ValuePersistence.Persistent
+ subsequents = HashSet()
+ traceValue = null
+ traceEvent = null
+ }
+
+ @JvmOverloads
+ fun updateValue(newValue: T?, changesOnly: Boolean = false) {
+ if (this.graph == null) {
+ this.capturedUpdate = { this.updateValue(newValue, changesOnly) }
+ } else {
+ // Ensure valid graph structure
+ if (this.graph?.currentEvent == null) {
+ throw BehaviorGraphException("Added resources can only be updated during an event loop. Resource= $this")
+ } else {
+ if (this.suppliedBy != null && this.graph?.currentBehavior != this.suppliedBy) {
+ throw BehaviorGraphException("Resource can only be updated by its supplying behavior. Resource=$this currentBehavior= ${this.graph?.currentBehavior}");
+ }
+
+ if (changesOnly) {
+ if (this.value == newValue) {
+ return;
+ }
+ }
+
+ if (this.traced) {
+ val previousSequence = this.previousEvent?.sequence ?: 0
+ if (previousSequence < this.graph!!.currentEvent!!.sequence) {
+ this.previousValue = this.value;
+ this.previousEvent = this.event;
+ } else {
+ this.previousValue = null;
+ this.previousEvent = null;
+ }
+ }
+
+ this.value = newValue;
+ this.event = this.graph!!.currentEvent;
+ this.graph!!.resourceTouched(this);
+ }
+ }
+ }
+
+ fun justHappened(): Boolean {
+ val anEvent = this.event
+ val aCurrentEvent = this.graph?.currentEvent;
+
+ return if (anEvent != null && aCurrentEvent != null) {
+ anEvent.sequence == aCurrentEvent.sequence;
+ } else {
+ false;
+ }
+ }
+
+ fun hasHappened(): Boolean {
+ return this.event != null;
+ }
+
+ fun happenedSince(since: Resource): Boolean {
+ val thisSequence = this.event?.sequence ?: -1;
+ val sinceSequence = since.event?.sequence ?: 0;
+ return thisSequence >= sinceSequence;
+ }
+
+ fun happenedBetween(since: Resource, until: Resource): Boolean {
+ val thisSequence = this.event?.sequence ?: -1;
+ val sinceSequence = since.event?.sequence ?: 0;
+ val untilSequence = until.event?.sequence ?: Long.MAX_VALUE;
+ return thisSequence >= sinceSequence && thisSequence < untilSequence;
+ }
+
+ override fun toString(): String {
+ return "Resource(value=$value, debugName=$debugName, valuePersistence=$valuePersistence, extent=$extent)"
+ }
+}
+*/
diff --git a/doc-site/examples/kotlin/BGExamples/com/yahoo/behaviorgraph/SideEffect.kt b/doc-site/examples/kotlin/BGExamples/com/yahoo/behaviorgraph/SideEffect.kt
new file mode 100644
index 0000000..30019c1
--- /dev/null
+++ b/doc-site/examples/kotlin/BGExamples/com/yahoo/behaviorgraph/SideEffect.kt
@@ -0,0 +1,3 @@
+package com.yahoo.behaviorgraph
+
+data class SideEffect(val debugName: String?, val block: (extent: Extent<*>) -> Unit, val extent: Extent<*>) //todo contrain Extent more than "*"?
diff --git a/doc-site/examples/kotlin/BGExamples/com/yahoo/behaviorgraph/State.kt b/doc-site/examples/kotlin/BGExamples/com/yahoo/behaviorgraph/State.kt
new file mode 100644
index 0000000..6d2572a
--- /dev/null
+++ b/doc-site/examples/kotlin/BGExamples/com/yahoo/behaviorgraph/State.kt
@@ -0,0 +1,69 @@
+package com.yahoo.behaviorgraph
+
+import com.yahoo.behaviorgraph.Event.Companion.InitialEvent
+
+class State(extent: Extent<*>, initialState: T, debugName: String? = null) :
+ Resource(extent, debugName),
+ Transient {
+ private var currentState = StateHistory(initialState, InitialEvent)
+ private var priorStateDuringEvent: StateHistory? = null
+ val value: T
+ get() = currentState.value
+ val event: Event
+ get() = currentState.event
+ private val trace: StateHistory
+ get() = priorStateDuringEvent ?: currentState
+ val traceValue: T
+ get() = trace.value
+ val traceEvent: Event
+ get() = trace.event
+
+ fun updateWithAction(newValue: T, changesOnly: Boolean) {
+ graph.action(getImpulse()) { update(newValue, changesOnly) }
+ }
+
+ fun update(newValue: T, changesOnly: Boolean) {
+ this.assertValidUpdater()
+
+ if (changesOnly) {
+ if (newValue == currentState.value)
+ return
+ }
+ priorStateDuringEvent = currentState
+ currentState = StateHistory(newValue, this.graph.currentEvent!!)
+ this.graph.resourceTouched(this)
+ this.graph.trackTransient(this)
+ }
+
+ private fun getImpulse(): String? {
+ return if (this.debugName != null) {
+ "Impulse From happen(): $this)"
+ } else null
+ }
+
+ val justUpdated: Boolean
+ get() = currentState.event == graph.currentEvent
+
+ fun justUpdatedTo(toValue: T): Boolean {
+ return justUpdated &&
+ (currentState.value == toValue)
+ }
+
+ fun justUpdatedFrom(fromValue: T): Boolean {
+ return justUpdated &&
+ (priorStateDuringEvent!!.value == fromValue)
+ }
+
+ fun justUpdatedToFrom(toValue: T, fromValue: T): Boolean {
+ return justUpdatedTo(toValue) && justUpdatedFrom(fromValue)
+ }
+
+ override fun clear() {
+ priorStateDuringEvent = null
+ }
+
+ data class StateHistory(val value: T, val event: Event)
+}
+
+
+
diff --git a/doc-site/examples/kotlin/BGExamples/com/yahoo/behaviorgraph/Transient.kt b/doc-site/examples/kotlin/BGExamples/com/yahoo/behaviorgraph/Transient.kt
new file mode 100644
index 0000000..50b2fbb
--- /dev/null
+++ b/doc-site/examples/kotlin/BGExamples/com/yahoo/behaviorgraph/Transient.kt
@@ -0,0 +1,5 @@
+package com.yahoo.behaviorgraph
+
+interface Transient {
+ fun clear() : Unit
+}
diff --git a/doc-site/examples/kotlin/BGExamples/com/yahoo/behaviorgraph/ValuePersistence.kt b/doc-site/examples/kotlin/BGExamples/com/yahoo/behaviorgraph/ValuePersistence.kt
new file mode 100644
index 0000000..845e5ad
--- /dev/null
+++ b/doc-site/examples/kotlin/BGExamples/com/yahoo/behaviorgraph/ValuePersistence.kt
@@ -0,0 +1,7 @@
+package com.yahoo.behaviorgraph
+
+enum class ValuePersistence {
+ Persistent,
+ Transient,
+ TransientTrace
+}
diff --git a/doc-site/examples/kotlin/BGExamples/com/yahoo/behaviorgraph/exception/AllDemandsMustBeAddedToTheGraphExceptions.kt b/doc-site/examples/kotlin/BGExamples/com/yahoo/behaviorgraph/exception/AllDemandsMustBeAddedToTheGraphExceptions.kt
new file mode 100644
index 0000000..371ad66
--- /dev/null
+++ b/doc-site/examples/kotlin/BGExamples/com/yahoo/behaviorgraph/exception/AllDemandsMustBeAddedToTheGraphExceptions.kt
@@ -0,0 +1,6 @@
+package com.yahoo.behaviorgraph.exception
+
+import com.yahoo.behaviorgraph.Behavior
+import com.yahoo.behaviorgraph.Resource
+
+class AllDemandsMustBeAddedToTheGraphExceptions(s: String, val currentBehavior: Behavior, val untrackedDemand: Resource) : BehaviorGraphException("$s Behavior=$currentBehavior untrackedDemand=$untrackedDemand")
diff --git a/doc-site/examples/kotlin/BGExamples/com/yahoo/behaviorgraph/exception/BehaviorDependencyCycleDetectedException.kt b/doc-site/examples/kotlin/BGExamples/com/yahoo/behaviorgraph/exception/BehaviorDependencyCycleDetectedException.kt
new file mode 100644
index 0000000..7570204
--- /dev/null
+++ b/doc-site/examples/kotlin/BGExamples/com/yahoo/behaviorgraph/exception/BehaviorDependencyCycleDetectedException.kt
@@ -0,0 +1,7 @@
+package com.yahoo.behaviorgraph.exception
+
+import com.yahoo.behaviorgraph.Behavior
+import com.yahoo.behaviorgraph.Resource
+
+class BehaviorDependencyCycleDetectedException(s: String, val behavior: Behavior, val cycle: List) : BehaviorGraphException("$s Behavior=$behavior")
+
diff --git a/doc-site/examples/kotlin/BGExamples/com/yahoo/behaviorgraph/exception/BehaviorGraphException.kt b/doc-site/examples/kotlin/BGExamples/com/yahoo/behaviorgraph/exception/BehaviorGraphException.kt
new file mode 100644
index 0000000..50abd2c
--- /dev/null
+++ b/doc-site/examples/kotlin/BGExamples/com/yahoo/behaviorgraph/exception/BehaviorGraphException.kt
@@ -0,0 +1,7 @@
+package com.yahoo.behaviorgraph.exception
+
+open class BehaviorGraphException : RuntimeException {
+ constructor(message: String, ex: Exception?): super(message, ex)
+ constructor(message: String): super(message)
+ constructor(ex: Exception): super(ex)
+}
diff --git a/doc-site/examples/kotlin/BGExamples/com/yahoo/behaviorgraph/exception/ExtentsCanOnlyBeAddedDuringAnEventException.kt b/doc-site/examples/kotlin/BGExamples/com/yahoo/behaviorgraph/exception/ExtentsCanOnlyBeAddedDuringAnEventException.kt
new file mode 100644
index 0000000..5c00872
--- /dev/null
+++ b/doc-site/examples/kotlin/BGExamples/com/yahoo/behaviorgraph/exception/ExtentsCanOnlyBeAddedDuringAnEventException.kt
@@ -0,0 +1,6 @@
+package com.yahoo.behaviorgraph.exception
+
+import com.yahoo.behaviorgraph.Extent
+
+class ExtentsCanOnlyBeAddedDuringAnEventException(s: String, val extent: Extent<*>) : BehaviorGraphException("$s Extent=$extent")
+
diff --git a/doc-site/examples/kotlin/BGExamples/com/yahoo/behaviorgraph/exception/ExtentsCanOnlyBeRemovedDuringAnEventException.kt b/doc-site/examples/kotlin/BGExamples/com/yahoo/behaviorgraph/exception/ExtentsCanOnlyBeRemovedDuringAnEventException.kt
new file mode 100644
index 0000000..4980f0f
--- /dev/null
+++ b/doc-site/examples/kotlin/BGExamples/com/yahoo/behaviorgraph/exception/ExtentsCanOnlyBeRemovedDuringAnEventException.kt
@@ -0,0 +1,6 @@
+package com.yahoo.behaviorgraph.exception
+
+import com.yahoo.behaviorgraph.Extent
+
+class ExtentsCanOnlyBeRemovedDuringAnEventException(s: String, val extent: Extent<*>) : BehaviorGraphException("$s Extent=$extent")
+
diff --git a/doc-site/examples/kotlin/BGExamples/com/yahoo/behaviorgraph/exception/MissingInitialValuesException.kt b/doc-site/examples/kotlin/BGExamples/com/yahoo/behaviorgraph/exception/MissingInitialValuesException.kt
new file mode 100644
index 0000000..c393287
--- /dev/null
+++ b/doc-site/examples/kotlin/BGExamples/com/yahoo/behaviorgraph/exception/MissingInitialValuesException.kt
@@ -0,0 +1,5 @@
+package com.yahoo.behaviorgraph.exception
+
+import com.yahoo.behaviorgraph.Resource
+
+class MissingInitialValuesException(s: String, val resource: Resource) : BehaviorGraphException("$s Resource=$resource")
diff --git a/doc-site/examples/kotlin/BGExamples/com/yahoo/behaviorgraph/exception/ResourceCannotBeSuppliedByMoreThanOneBehaviorException.kt b/doc-site/examples/kotlin/BGExamples/com/yahoo/behaviorgraph/exception/ResourceCannotBeSuppliedByMoreThanOneBehaviorException.kt
new file mode 100644
index 0000000..9c062a9
--- /dev/null
+++ b/doc-site/examples/kotlin/BGExamples/com/yahoo/behaviorgraph/exception/ResourceCannotBeSuppliedByMoreThanOneBehaviorException.kt
@@ -0,0 +1,6 @@
+package com.yahoo.behaviorgraph.exception
+
+import com.yahoo.behaviorgraph.Behavior
+import com.yahoo.behaviorgraph.Resource
+
+class ResourceCannotBeSuppliedByMoreThanOneBehaviorException(s: String, val alreadySupplied: Resource, val desiredSupplier: Behavior) : BehaviorGraphException("$s alreadySupplied=$alreadySupplied desiredSupplier=$desiredSupplier")
diff --git a/doc-site/examples/kotlin/BGExamples/com/yahoo/behaviorgraph/platform/PlatformSupport.kt b/doc-site/examples/kotlin/BGExamples/com/yahoo/behaviorgraph/platform/PlatformSupport.kt
new file mode 100644
index 0000000..39c3492
--- /dev/null
+++ b/doc-site/examples/kotlin/BGExamples/com/yahoo/behaviorgraph/platform/PlatformSupport.kt
@@ -0,0 +1,11 @@
+package com.yahoo.behaviorgraph.platform
+
+interface PlatformSupport {
+ fun isMainThread(): Boolean
+ fun getCurrentTimeMillis(): Long
+ fun now() = getCurrentTimeMillis()
+
+ companion object {
+ lateinit var platformSupport: PlatformSupport
+ }
+}
diff --git a/doc-site/examples/kotlin/kotlin.iml b/doc-site/examples/kotlin/kotlin.iml
new file mode 100644
index 0000000..7b6c2b0
--- /dev/null
+++ b/doc-site/examples/kotlin/kotlin.iml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/doc-site/examples/objc/.gitignore b/doc-site/examples/objc/.gitignore
new file mode 100644
index 0000000..8ee1ded
--- /dev/null
+++ b/doc-site/examples/objc/.gitignore
@@ -0,0 +1 @@
+**/xcuserdata
diff --git a/doc-site/examples/objc/BGExamples.xcodeproj/project.pbxproj b/doc-site/examples/objc/BGExamples.xcodeproj/project.pbxproj
new file mode 100644
index 0000000..d433fd9
--- /dev/null
+++ b/doc-site/examples/objc/BGExamples.xcodeproj/project.pbxproj
@@ -0,0 +1,545 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 50;
+ objects = {
+
+/* Begin PBXBuildFile section */
+ 7301269A2422C0810031659C /* LoginPageViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 730126992422C0810031659C /* LoginPageViewController.m */; };
+ 7301269D2422C2780031659C /* ImperativeLoginPageViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 7301269C2422C2780031659C /* ImperativeLoginPageViewController.m */; };
+ 730126A02422E2900031659C /* LoginExtent.m in Sources */ = {isa = PBXBuildFile; fileRef = 7301269F2422E2900031659C /* LoginExtent.m */; };
+ 73265A3825A13B8700B0BF84 /* BGPriorityQueue.m in Sources */ = {isa = PBXBuildFile; fileRef = 73265A3625A13B8700B0BF84 /* BGPriorityQueue.m */; };
+ 73C56CB7241293A300066BC4 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 73C56CB6241293A300066BC4 /* AppDelegate.m */; };
+ 73C56CBA241293A300066BC4 /* SceneDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 73C56CB9241293A300066BC4 /* SceneDelegate.m */; };
+ 73C56CBD241293A300066BC4 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 73C56CBC241293A300066BC4 /* ViewController.m */; };
+ 73C56CC0241293A300066BC4 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 73C56CBE241293A300066BC4 /* Main.storyboard */; };
+ 73C56CC2241293A500066BC4 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 73C56CC1241293A500066BC4 /* Assets.xcassets */; };
+ 73C56CC5241293A500066BC4 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 73C56CC3241293A500066BC4 /* LaunchScreen.storyboard */; };
+ 73C56CC8241293A500066BC4 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 73C56CC7241293A500066BC4 /* main.m */; };
+ 73C56CD2241293A600066BC4 /* BGExamplesTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 73C56CD1241293A600066BC4 /* BGExamplesTests.m */; };
+ 73C56CEC241296F000066BC4 /* BGProfiler.m in Sources */ = {isa = PBXBuildFile; fileRef = 73C56CE7241296EF00066BC4 /* BGProfiler.m */; };
+ 73C56CED241296F000066BC4 /* BGGraph.m in Sources */ = {isa = PBXBuildFile; fileRef = 73C56CEB241296F000066BC4 /* BGGraph.m */; };
+ 73C56CF42412981700066BC4 /* SigngupPageViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 73C56CF22412981700066BC4 /* SigngupPageViewController.m */; };
+ 73C56CF8241298A600066BC4 /* SignupExtent.m in Sources */ = {isa = PBXBuildFile; fileRef = 73C56CF7241298A600066BC4 /* SignupExtent.m */; };
+ 73F5F5D5246C5F12006236D8 /* ChatExtent.m in Sources */ = {isa = PBXBuildFile; fileRef = 73F5F5D4246C5F12006236D8 /* ChatExtent.m */; };
+ 73F5F5D8246C5F56006236D8 /* ParticipantExtent.m in Sources */ = {isa = PBXBuildFile; fileRef = 73F5F5D7246C5F56006236D8 /* ParticipantExtent.m */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXContainerItemProxy section */
+ 73C56CCE241293A600066BC4 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 73C56CAA241293A300066BC4 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 73C56CB1241293A300066BC4;
+ remoteInfo = BGExamples;
+ };
+/* End PBXContainerItemProxy section */
+
+/* Begin PBXFileReference section */
+ 730126982422C0810031659C /* LoginPageViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LoginPageViewController.h; sourceTree = ""; };
+ 730126992422C0810031659C /* LoginPageViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = LoginPageViewController.m; sourceTree = ""; };
+ 7301269B2422C2780031659C /* ImperativeLoginPageViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ImperativeLoginPageViewController.h; sourceTree = ""; };
+ 7301269C2422C2780031659C /* ImperativeLoginPageViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ImperativeLoginPageViewController.m; sourceTree = ""; };
+ 7301269E2422E2900031659C /* LoginExtent.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LoginExtent.h; sourceTree = ""; };
+ 7301269F2422E2900031659C /* LoginExtent.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = LoginExtent.m; sourceTree = ""; };
+ 73265A3625A13B8700B0BF84 /* BGPriorityQueue.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BGPriorityQueue.m; sourceTree = ""; };
+ 73265A3725A13B8700B0BF84 /* BGPriorityQueue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BGPriorityQueue.h; sourceTree = ""; };
+ 73C56CB2241293A300066BC4 /* BGExamples.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = BGExamples.app; sourceTree = BUILT_PRODUCTS_DIR; };
+ 73C56CB5241293A300066BC4 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; };
+ 73C56CB6241293A300066BC4 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; };
+ 73C56CB8241293A300066BC4 /* SceneDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SceneDelegate.h; sourceTree = ""; };
+ 73C56CB9241293A300066BC4 /* SceneDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SceneDelegate.m; sourceTree = ""; };
+ 73C56CBB241293A300066BC4 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; };
+ 73C56CBC241293A300066BC4 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; };
+ 73C56CBF241293A300066BC4 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
+ 73C56CC1241293A500066BC4 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
+ 73C56CC4241293A500066BC4 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
+ 73C56CC6241293A500066BC4 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
+ 73C56CC7241293A500066BC4 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; };
+ 73C56CCD241293A600066BC4 /* BGExamplesTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = BGExamplesTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
+ 73C56CD1241293A600066BC4 /* BGExamplesTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BGExamplesTests.m; sourceTree = ""; };
+ 73C56CD3241293A600066BC4 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
+ 73C56CE7241296EF00066BC4 /* BGProfiler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BGProfiler.m; sourceTree = ""; };
+ 73C56CE8241296EF00066BC4 /* BGGraph.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BGGraph.h; sourceTree = ""; };
+ 73C56CE9241296EF00066BC4 /* BGGraph+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "BGGraph+Private.h"; sourceTree = ""; };
+ 73C56CEA241296EF00066BC4 /* BGProfiler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BGProfiler.h; sourceTree = ""; };
+ 73C56CEB241296F000066BC4 /* BGGraph.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BGGraph.m; sourceTree = ""; };
+ 73C56CF12412981700066BC4 /* SigngupPageViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SigngupPageViewController.h; sourceTree = ""; };
+ 73C56CF22412981700066BC4 /* SigngupPageViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SigngupPageViewController.m; sourceTree = ""; };
+ 73C56CF6241298A600066BC4 /* SignupExtent.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SignupExtent.h; sourceTree = ""; };
+ 73C56CF7241298A600066BC4 /* SignupExtent.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SignupExtent.m; sourceTree = ""; };
+ 73F5F5D3246C5F12006236D8 /* ChatExtent.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ChatExtent.h; sourceTree = ""; };
+ 73F5F5D4246C5F12006236D8 /* ChatExtent.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ChatExtent.m; sourceTree = ""; };
+ 73F5F5D6246C5F56006236D8 /* ParticipantExtent.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ParticipantExtent.h; sourceTree = ""; };
+ 73F5F5D7246C5F56006236D8 /* ParticipantExtent.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ParticipantExtent.m; sourceTree = ""; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ 73C56CAF241293A300066BC4 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 73C56CCA241293A600066BC4 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ 73C56CA9241293A300066BC4 = {
+ isa = PBXGroup;
+ children = (
+ 73C56CB4241293A300066BC4 /* BGExamples */,
+ 73C56CD0241293A600066BC4 /* BGExamplesTests */,
+ 73C56CB3241293A300066BC4 /* Products */,
+ );
+ sourceTree = "";
+ };
+ 73C56CB3241293A300066BC4 /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ 73C56CB2241293A300066BC4 /* BGExamples.app */,
+ 73C56CCD241293A600066BC4 /* BGExamplesTests.xctest */,
+ );
+ name = Products;
+ sourceTree = "";
+ };
+ 73C56CB4241293A300066BC4 /* BGExamples */ = {
+ isa = PBXGroup;
+ children = (
+ 73C56CDC241293BD00066BC4 /* BehaviorGraph */,
+ 73C56CB5241293A300066BC4 /* AppDelegate.h */,
+ 73C56CB6241293A300066BC4 /* AppDelegate.m */,
+ 73C56CF12412981700066BC4 /* SigngupPageViewController.h */,
+ 73C56CF22412981700066BC4 /* SigngupPageViewController.m */,
+ 730126982422C0810031659C /* LoginPageViewController.h */,
+ 730126992422C0810031659C /* LoginPageViewController.m */,
+ 7301269B2422C2780031659C /* ImperativeLoginPageViewController.h */,
+ 7301269C2422C2780031659C /* ImperativeLoginPageViewController.m */,
+ 73C56CF6241298A600066BC4 /* SignupExtent.h */,
+ 73C56CF7241298A600066BC4 /* SignupExtent.m */,
+ 7301269E2422E2900031659C /* LoginExtent.h */,
+ 7301269F2422E2900031659C /* LoginExtent.m */,
+ 73C56CB8241293A300066BC4 /* SceneDelegate.h */,
+ 73C56CB9241293A300066BC4 /* SceneDelegate.m */,
+ 73C56CBB241293A300066BC4 /* ViewController.h */,
+ 73C56CBC241293A300066BC4 /* ViewController.m */,
+ 73F5F5D3246C5F12006236D8 /* ChatExtent.h */,
+ 73F5F5D4246C5F12006236D8 /* ChatExtent.m */,
+ 73F5F5D6246C5F56006236D8 /* ParticipantExtent.h */,
+ 73F5F5D7246C5F56006236D8 /* ParticipantExtent.m */,
+ 73C56CBE241293A300066BC4 /* Main.storyboard */,
+ 73C56CC1241293A500066BC4 /* Assets.xcassets */,
+ 73C56CC3241293A500066BC4 /* LaunchScreen.storyboard */,
+ 73C56CC6241293A500066BC4 /* Info.plist */,
+ 73C56CC7241293A500066BC4 /* main.m */,
+ );
+ path = BGExamples;
+ sourceTree = "";
+ };
+ 73C56CD0241293A600066BC4 /* BGExamplesTests */ = {
+ isa = PBXGroup;
+ children = (
+ 73C56CD1241293A600066BC4 /* BGExamplesTests.m */,
+ 73C56CD3241293A600066BC4 /* Info.plist */,
+ );
+ path = BGExamplesTests;
+ sourceTree = "";
+ };
+ 73C56CDC241293BD00066BC4 /* BehaviorGraph */ = {
+ isa = PBXGroup;
+ children = (
+ 73C56CE8241296EF00066BC4 /* BGGraph.h */,
+ 73C56CEB241296F000066BC4 /* BGGraph.m */,
+ 73C56CE9241296EF00066BC4 /* BGGraph+Private.h */,
+ 73C56CEA241296EF00066BC4 /* BGProfiler.h */,
+ 73C56CE7241296EF00066BC4 /* BGProfiler.m */,
+ 73265A3725A13B8700B0BF84 /* BGPriorityQueue.h */,
+ 73265A3625A13B8700B0BF84 /* BGPriorityQueue.m */,
+ );
+ path = BehaviorGraph;
+ sourceTree = "";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+ 73C56CB1241293A300066BC4 /* BGExamples */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 73C56CD6241293A600066BC4 /* Build configuration list for PBXNativeTarget "BGExamples" */;
+ buildPhases = (
+ 73C56CAE241293A300066BC4 /* Sources */,
+ 73C56CAF241293A300066BC4 /* Frameworks */,
+ 73C56CB0241293A300066BC4 /* Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = BGExamples;
+ productName = BGExamples;
+ productReference = 73C56CB2241293A300066BC4 /* BGExamples.app */;
+ productType = "com.apple.product-type.application";
+ };
+ 73C56CCC241293A600066BC4 /* BGExamplesTests */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 73C56CD9241293A600066BC4 /* Build configuration list for PBXNativeTarget "BGExamplesTests" */;
+ buildPhases = (
+ 73C56CC9241293A600066BC4 /* Sources */,
+ 73C56CCA241293A600066BC4 /* Frameworks */,
+ 73C56CCB241293A600066BC4 /* Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ 73C56CCF241293A600066BC4 /* PBXTargetDependency */,
+ );
+ name = BGExamplesTests;
+ productName = BGExamplesTests;
+ productReference = 73C56CCD241293A600066BC4 /* BGExamplesTests.xctest */;
+ productType = "com.apple.product-type.bundle.unit-test";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ 73C56CAA241293A300066BC4 /* Project object */ = {
+ isa = PBXProject;
+ attributes = {
+ LastUpgradeCheck = 1230;
+ ORGANIZATIONNAME = "Verizon Media";
+ TargetAttributes = {
+ 73C56CB1241293A300066BC4 = {
+ CreatedOnToolsVersion = 11.3;
+ };
+ 73C56CCC241293A600066BC4 = {
+ CreatedOnToolsVersion = 11.3;
+ TestTargetID = 73C56CB1241293A300066BC4;
+ };
+ };
+ };
+ buildConfigurationList = 73C56CAD241293A300066BC4 /* Build configuration list for PBXProject "BGExamples" */;
+ compatibilityVersion = "Xcode 9.3";
+ developmentRegion = en;
+ hasScannedForEncodings = 0;
+ knownRegions = (
+ en,
+ Base,
+ );
+ mainGroup = 73C56CA9241293A300066BC4;
+ productRefGroup = 73C56CB3241293A300066BC4 /* Products */;
+ projectDirPath = "";
+ projectRoot = "";
+ targets = (
+ 73C56CB1241293A300066BC4 /* BGExamples */,
+ 73C56CCC241293A600066BC4 /* BGExamplesTests */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+ 73C56CB0241293A300066BC4 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 73C56CC5241293A500066BC4 /* LaunchScreen.storyboard in Resources */,
+ 73C56CC2241293A500066BC4 /* Assets.xcassets in Resources */,
+ 73C56CC0241293A300066BC4 /* Main.storyboard in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 73C56CCB241293A600066BC4 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+ 73C56CAE241293A300066BC4 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 73C56CBD241293A300066BC4 /* ViewController.m in Sources */,
+ 73C56CB7241293A300066BC4 /* AppDelegate.m in Sources */,
+ 73C56CC8241293A500066BC4 /* main.m in Sources */,
+ 73C56CEC241296F000066BC4 /* BGProfiler.m in Sources */,
+ 73C56CBA241293A300066BC4 /* SceneDelegate.m in Sources */,
+ 73F5F5D8246C5F56006236D8 /* ParticipantExtent.m in Sources */,
+ 730126A02422E2900031659C /* LoginExtent.m in Sources */,
+ 73265A3825A13B8700B0BF84 /* BGPriorityQueue.m in Sources */,
+ 73C56CF8241298A600066BC4 /* SignupExtent.m in Sources */,
+ 7301269A2422C0810031659C /* LoginPageViewController.m in Sources */,
+ 7301269D2422C2780031659C /* ImperativeLoginPageViewController.m in Sources */,
+ 73C56CED241296F000066BC4 /* BGGraph.m in Sources */,
+ 73C56CF42412981700066BC4 /* SigngupPageViewController.m in Sources */,
+ 73F5F5D5246C5F12006236D8 /* ChatExtent.m in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 73C56CC9241293A600066BC4 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 73C56CD2241293A600066BC4 /* BGExamplesTests.m in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXTargetDependency section */
+ 73C56CCF241293A600066BC4 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 73C56CB1241293A300066BC4 /* BGExamples */;
+ targetProxy = 73C56CCE241293A600066BC4 /* PBXContainerItemProxy */;
+ };
+/* End PBXTargetDependency section */
+
+/* Begin PBXVariantGroup section */
+ 73C56CBE241293A300066BC4 /* Main.storyboard */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 73C56CBF241293A300066BC4 /* Base */,
+ );
+ name = Main.storyboard;
+ sourceTree = "";
+ };
+ 73C56CC3241293A500066BC4 /* LaunchScreen.storyboard */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 73C56CC4241293A500066BC4 /* Base */,
+ );
+ name = LaunchScreen.storyboard;
+ sourceTree = "";
+ };
+/* End PBXVariantGroup section */
+
+/* Begin XCBuildConfiguration section */
+ 73C56CD4241293A600066BC4 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_ENABLE_OBJC_WEAK = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = dwarf;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ ENABLE_TESTABILITY = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu11;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "DEBUG=1",
+ "$(inherited)",
+ );
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 13.2;
+ MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
+ MTL_FAST_MATH = YES;
+ ONLY_ACTIVE_ARCH = YES;
+ SDKROOT = iphoneos;
+ };
+ name = Debug;
+ };
+ 73C56CD5241293A600066BC4 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_ENABLE_OBJC_WEAK = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ ENABLE_NS_ASSERTIONS = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu11;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 13.2;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ MTL_FAST_MATH = YES;
+ SDKROOT = iphoneos;
+ VALIDATE_PRODUCT = YES;
+ };
+ name = Release;
+ };
+ 73C56CD7241293A600066BC4 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CODE_SIGN_STYLE = Automatic;
+ DEVELOPMENT_TEAM = "";
+ INFOPLIST_FILE = BGExamples/Info.plist;
+ IPHONEOS_DEPLOYMENT_TARGET = 12.1;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ );
+ PRODUCT_BUNDLE_IDENTIFIER = com.yahoo.BGExamples;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ TARGETED_DEVICE_FAMILY = "1,2";
+ };
+ name = Debug;
+ };
+ 73C56CD8241293A600066BC4 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CODE_SIGN_STYLE = Automatic;
+ DEVELOPMENT_TEAM = "";
+ INFOPLIST_FILE = BGExamples/Info.plist;
+ IPHONEOS_DEPLOYMENT_TARGET = 12.1;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ );
+ PRODUCT_BUNDLE_IDENTIFIER = com.yahoo.BGExamples;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ TARGETED_DEVICE_FAMILY = "1,2";
+ };
+ name = Release;
+ };
+ 73C56CDA241293A600066BC4 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ BUNDLE_LOADER = "$(TEST_HOST)";
+ CODE_SIGN_STYLE = Automatic;
+ INFOPLIST_FILE = BGExamplesTests/Info.plist;
+ IPHONEOS_DEPLOYMENT_TARGET = 13.2;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ "@loader_path/Frameworks",
+ );
+ PRODUCT_BUNDLE_IDENTIFIER = com.yahoo.BGExamplesTests;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ TARGETED_DEVICE_FAMILY = "1,2";
+ TEST_HOST = "$(BUILT_PRODUCTS_DIR)/BGExamples.app/BGExamples";
+ };
+ name = Debug;
+ };
+ 73C56CDB241293A600066BC4 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ BUNDLE_LOADER = "$(TEST_HOST)";
+ CODE_SIGN_STYLE = Automatic;
+ INFOPLIST_FILE = BGExamplesTests/Info.plist;
+ IPHONEOS_DEPLOYMENT_TARGET = 13.2;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ "@loader_path/Frameworks",
+ );
+ PRODUCT_BUNDLE_IDENTIFIER = com.yahoo.BGExamplesTests;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ TARGETED_DEVICE_FAMILY = "1,2";
+ TEST_HOST = "$(BUILT_PRODUCTS_DIR)/BGExamples.app/BGExamples";
+ };
+ name = Release;
+ };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+ 73C56CAD241293A300066BC4 /* Build configuration list for PBXProject "BGExamples" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 73C56CD4241293A600066BC4 /* Debug */,
+ 73C56CD5241293A600066BC4 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 73C56CD6241293A600066BC4 /* Build configuration list for PBXNativeTarget "BGExamples" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 73C56CD7241293A600066BC4 /* Debug */,
+ 73C56CD8241293A600066BC4 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 73C56CD9241293A600066BC4 /* Build configuration list for PBXNativeTarget "BGExamplesTests" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 73C56CDA241293A600066BC4 /* Debug */,
+ 73C56CDB241293A600066BC4 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+/* End XCConfigurationList section */
+ };
+ rootObject = 73C56CAA241293A300066BC4 /* Project object */;
+}
diff --git a/doc-site/examples/objc/BGExamples.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/doc-site/examples/objc/BGExamples.xcodeproj/project.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 0000000..5fe0322
--- /dev/null
+++ b/doc-site/examples/objc/BGExamples.xcodeproj/project.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,7 @@
+
+
+
+
+
diff --git a/doc-site/examples/objc/BGExamples.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/doc-site/examples/objc/BGExamples.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
new file mode 100644
index 0000000..18d9810
--- /dev/null
+++ b/doc-site/examples/objc/BGExamples.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
@@ -0,0 +1,8 @@
+
+
+
+
+ IDEDidComputeMac32BitWarning
+
+
+
diff --git a/doc-site/examples/objc/BGExamples.xcworkspace/contents.xcworkspacedata b/doc-site/examples/objc/BGExamples.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 0000000..4ccb116
--- /dev/null
+++ b/doc-site/examples/objc/BGExamples.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,7 @@
+
+
+
+
+
diff --git a/doc-site/examples/objc/BGExamples.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/doc-site/examples/objc/BGExamples.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
new file mode 100644
index 0000000..18d9810
--- /dev/null
+++ b/doc-site/examples/objc/BGExamples.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
@@ -0,0 +1,8 @@
+
+
+
+
+ IDEDidComputeMac32BitWarning
+
+
+
diff --git a/doc-site/examples/objc/BGExamples/AppDelegate.h b/doc-site/examples/objc/BGExamples/AppDelegate.h
new file mode 100644
index 0000000..ca9673c
--- /dev/null
+++ b/doc-site/examples/objc/BGExamples/AppDelegate.h
@@ -0,0 +1,15 @@
+//
+// AppDelegate.h
+// BGExamples
+//
+// Created by Sean Levin on 3/6/20.
+// Copyright © 2020 Verizon Media. All rights reserved.
+//
+
+#import
+
+@interface AppDelegate : UIResponder
+
+
+@end
+
diff --git a/doc-site/examples/objc/BGExamples/AppDelegate.m b/doc-site/examples/objc/BGExamples/AppDelegate.m
new file mode 100644
index 0000000..7d1d3f7
--- /dev/null
+++ b/doc-site/examples/objc/BGExamples/AppDelegate.m
@@ -0,0 +1,41 @@
+//
+// AppDelegate.m
+// BGExamples
+//
+// Created by Sean Levin on 3/6/20.
+// Copyright © 2020 Verizon Media. All rights reserved.
+//
+
+#import "AppDelegate.h"
+
+@interface AppDelegate ()
+
+@end
+
+@implementation AppDelegate
+
+
+- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
+ // Override point for customization after application launch.
+ return YES;
+}
+
+
+#pragma mark - UISceneSession lifecycle
+
+
+- (UISceneConfiguration *)application:(UIApplication *)application configurationForConnectingSceneSession:(UISceneSession *)connectingSceneSession options:(UISceneConnectionOptions *)options {
+ // Called when a new scene session is being created.
+ // Use this method to select a configuration to create the new scene with.
+ return [[UISceneConfiguration alloc] initWithName:@"Default Configuration" sessionRole:connectingSceneSession.role];
+}
+
+
+- (void)application:(UIApplication *)application didDiscardSceneSessions:(NSSet *)sceneSessions {
+ // Called when the user discards a scene session.
+ // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
+ // Use this method to release any resources that were specific to the discarded scenes, as they will not return.
+}
+
+
+@end
diff --git a/doc-site/examples/objc/BGExamples/Assets.xcassets/AppIcon.appiconset/Contents.json b/doc-site/examples/objc/BGExamples/Assets.xcassets/AppIcon.appiconset/Contents.json
new file mode 100644
index 0000000..d8db8d6
--- /dev/null
+++ b/doc-site/examples/objc/BGExamples/Assets.xcassets/AppIcon.appiconset/Contents.json
@@ -0,0 +1,98 @@
+{
+ "images" : [
+ {
+ "idiom" : "iphone",
+ "size" : "20x20",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "iphone",
+ "size" : "20x20",
+ "scale" : "3x"
+ },
+ {
+ "idiom" : "iphone",
+ "size" : "29x29",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "iphone",
+ "size" : "29x29",
+ "scale" : "3x"
+ },
+ {
+ "idiom" : "iphone",
+ "size" : "40x40",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "iphone",
+ "size" : "40x40",
+ "scale" : "3x"
+ },
+ {
+ "idiom" : "iphone",
+ "size" : "60x60",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "iphone",
+ "size" : "60x60",
+ "scale" : "3x"
+ },
+ {
+ "idiom" : "ipad",
+ "size" : "20x20",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "ipad",
+ "size" : "20x20",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "ipad",
+ "size" : "29x29",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "ipad",
+ "size" : "29x29",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "ipad",
+ "size" : "40x40",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "ipad",
+ "size" : "40x40",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "ipad",
+ "size" : "76x76",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "ipad",
+ "size" : "76x76",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "ipad",
+ "size" : "83.5x83.5",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "ios-marketing",
+ "size" : "1024x1024",
+ "scale" : "1x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/doc-site/examples/objc/BGExamples/Assets.xcassets/Contents.json b/doc-site/examples/objc/BGExamples/Assets.xcassets/Contents.json
new file mode 100644
index 0000000..da4a164
--- /dev/null
+++ b/doc-site/examples/objc/BGExamples/Assets.xcassets/Contents.json
@@ -0,0 +1,6 @@
+{
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/doc-site/examples/objc/BGExamples/Base.lproj/LaunchScreen.storyboard b/doc-site/examples/objc/BGExamples/Base.lproj/LaunchScreen.storyboard
new file mode 100644
index 0000000..f2b70d1
--- /dev/null
+++ b/doc-site/examples/objc/BGExamples/Base.lproj/LaunchScreen.storyboard
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/doc-site/examples/objc/BGExamples/Base.lproj/Main.storyboard b/doc-site/examples/objc/BGExamples/Base.lproj/Main.storyboard
new file mode 100644
index 0000000..e8a10d1
--- /dev/null
+++ b/doc-site/examples/objc/BGExamples/Base.lproj/Main.storyboard
@@ -0,0 +1,121 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/doc-site/examples/objc/BGExamples/BehaviorGraph/BGGraph+Private.h b/doc-site/examples/objc/BGExamples/BehaviorGraph/BGGraph+Private.h
new file mode 100644
index 0000000..d294ff5
--- /dev/null
+++ b/doc-site/examples/objc/BGExamples/BehaviorGraph/BGGraph+Private.h
@@ -0,0 +1,144 @@
+//
+// BGGraph+Private.h
+// YVideoSDK
+//
+// Created by James Lou on 4/11/19.
+// Copyright © 2019 Yahoo. All rights reserved.
+//
+
+#import "BGGraph.h"
+#import "BGPriorityQueue.h"
+#import
+
+static NSObject * _Nonnull NullPushedValue;
+
+typedef NS_ENUM(NSInteger, BGOrderingState) {
+ BGOrderingStateUnordered,
+ BGOrderingStateOrdering,
+ BGOrderingStateOrdered,
+};
+
+typedef NS_ENUM(NSInteger, BGResourceValuePersistence) {
+ BGResourcePersistent,
+ BGResourceTransient,
+ BGResourceTransientTrace,
+};
+
+
+@interface BGEvent ()
+- (instancetype _Nonnull)initWithImpulse:(NSString * _Nullable )impulse sequence:(NSUInteger)sequence timestamp:(NSDate * _Nonnull)timestamp;
+@end
+
+@interface BGResource<__covariant ValueType> () {
+ @protected
+ __weak BGGraph *_graph;
+ __weak BGExtent *_extent;
+ __weak BGBehavior *_behavior;
+ NSHashTable *_subsequents;
+ BGEvent *_added;
+ NSString *_staticDebugName;
+}
+@property (nonatomic, readwrite, weak, nullable) BGGraph *graph;
+@property (nonatomic, readwrite, weak, nullable) BGExtent *extent;
+@property (nonatomic, readwrite, weak, nullable) BGBehavior *behavior;
+@property (nonatomic, readwrite, nullable) BGEvent *added;
+@property (nonatomic, readonly, nonnull) NSHashTable *subsequents;
+@property (nonatomic, nullable) void (^capturedInitialUpdate)(void);
+@property (nonatomic, readonly) BOOL traced;
+@property (nonatomic, readonly) BGResourceValuePersistence valuePersistence;
+@property (nonatomic, nullable) ValueType previousValue;
+@property (nonatomic, nullable) BGEvent *previousEvent;
+- (instancetype _Nonnull)initWithExtent:(BGExtent * _Nonnull)extent value:(ValueType _Nullable)value event:(BGEvent * _Nullable)event;
+- (void)_updateValue:(ValueType _Nullable)value;
+- (void)_forceUpdateValue:(ValueType _Nullable)value;
+- (void)clearTransient;
+@end
+
+
+@interface BGMoment<__covariant ValueType> ()
+@end
+
+@interface BGState<__covariant ValueType> ()
+@end
+
+@interface BGBehavior ()
+@property (nonatomic, readonly, nonnull) NSHashTable *supplies;
+@property (nonatomic, readwrite, nullable) BGGraph *graph;
+@property (nonatomic, readwrite, weak, nullable) BGExtent *extent;
+@property (nonatomic, nullable) NSMutableArray *modifiedDemands;
+@property (nonatomic, readonly, nullable) NSHashTable *demands;
+@property (nonatomic) NSUInteger removedSequence;
+@property (nonatomic) NSUInteger lastUpdateSequence;
+@property (nonatomic) NSUInteger order;
+@property (nonatomic) BGOrderingState orderingState;
+@property (nonatomic) NSUInteger enqueuedSequence;
+@end
+
+@interface BGAction : NSObject
+@property (nonatomic, nullable) NSString *name;
+@property (nonatomic, nonnull) dispatch_block_t block;
+- (instancetype _Nonnull)initWithName:(NSString * _Nullable)name block:(dispatch_block_t _Nonnull)block;
+@end
+
+@interface BGSideEffect : NSObject {
+@protected
+ NSString * _Nullable _name;
+ BGEvent * _Nonnull _event;
+}
+@property (nonatomic, readonly, nullable) NSString *name;
+@property (nonatomic, readonly, nonnull) BGEvent *event;
+- (void)run;
+@end
+
+@interface BGBehaviorSideEffect : BGSideEffect
+@property (nonatomic, readonly, nonnull) BGExtent *extent;
+@property (nonatomic, readonly, nonnull) void(^block)(BGExtent * _Nonnull extent);
+- (instancetype _Nonnull)initWithName:(NSString * _Nullable)name event:(BGEvent * _Nonnull)event extent:(BGExtent * _Nonnull)extent block:(void(^ _Nonnull)(BGExtent * _Nullable))block;
+@end
+
+@interface BGGraphSideEffect : BGSideEffect
+@property (nonatomic, readonly, nonnull) dispatch_block_t block;
+- (instancetype _Nonnull)initWithName:(NSString * _Nullable)name event:(BGEvent * _Nonnull)event block:(dispatch_block_t _Nonnull)block;
+@end
+
+@interface BGEventLoopState : NSObject
+@property (nonatomic, nonnull) BGEvent *event;
+@property (nonatomic, readonly) NSUInteger sequence;
+@property (nonatomic) unsigned long long eventSid;
+@property (nonatomic) BOOL processingAction;
+@property (nonatomic) BOOL processingChanges;
+@end
+
+@interface BGGraph ()
+@property (nonatomic, nullable) BGEventLoopState *eventLoopState;
+@property (nonatomic, readonly) NSUInteger sequence;
+@property (nonatomic, readonly) BOOL processingAction;
+@property (nonatomic, readonly) BOOL processingChanges;
+@property (nonatomic) NSUInteger eventLoopDrivers;
+@property (nonatomic, nonnull) NSMutableSet *needsOrdering;
+@property (nonatomic, readonly, nonnull) NSMutableArray *afterChanges;
+@property (nonatomic, readonly, nonnull) NSMutableSet *untrackedBehaviors;
+@property (nonatomic, readonly, nonnull) NSMutableSet *modifiedDemands;
+@property (nonatomic, readonly, nonnull) NSMutableArray *updatedTransientResources;
+@property (nonatomic, readonly, nonnull) NSMutableArray *deferredRelease;
+@property (nonatomic, readonly, nonnull) BGPriorityQueue *behaviorQueue;
+@property (nonatomic, readonly, nullable) BGBehavior * currentBehavior;
+@property (nonatomic, readonly, nullable) os_log_t aclog;
+@property (nonatomic, readonly, nullable) os_log_t splog;
+
+@property (nonatomic, readonly, nullable) NSMutableArray *actionQueue;
+@property (nonatomic, readonly, nullable) NSMutableArray *sideEffectQueue;
+
+- (void)submitToQueue:(BGBehavior * _Nonnull)subsequent;
+- (void)removeBehavior:(BGBehavior * _Nonnull)behavior;
+- (void)trackTransient:(BGResource * _Nonnull)rez;
+@end
+
+@interface BGExtent ()
+@property (nonatomic, nullable) BGEvent *addedToGraph;
+@property (nonatomic, readonly, nullable) NSMutableSet *allBehaviors;
+@property (nonatomic, readonly, nullable) NSMutableSet *allResources;
+- (void)addBehavior:(BGBehavior * _Nonnull)behavior;
+- (void)addResource:(BGResource * _Nonnull)resource;
+- (void)nameComponents;
+@end
diff --git a/doc-site/examples/objc/BGExamples/BehaviorGraph/BGGraph.h b/doc-site/examples/objc/BGExamples/BehaviorGraph/BGGraph.h
new file mode 100644
index 0000000..7d8444b
--- /dev/null
+++ b/doc-site/examples/objc/BGExamples/BehaviorGraph/BGGraph.h
@@ -0,0 +1,117 @@
+//
+// BGGraph.h
+// YVideoSDK
+//
+// Created by James Lou on 2/7/19.
+// Copyright © 2019 Yahoo. All rights reserved.
+//
+
+#import
+
+#define _fname ([NSString stringWithFormat:@"%@.%@", NSStringFromClass(self.class), NSStringFromSelector(_cmd)])
+
+@class BGBehavior;
+@class BGGraph;
+
+@class BGExtent;
+
+@interface BGEvent : NSObject
+@property (nonatomic, readonly) NSUInteger sequence;
+@property (nonatomic, readonly, nonnull) NSDate *timestamp;
+@property (nonatomic, readonly, nullable) NSString *impulse;
+@property (nonatomic, readonly, class, nonnull) BGEvent *unknownPast;
+- (BOOL)happenedSince:(NSUInteger)since;
+@end
+
+
+@interface BGResource<__covariant ValueType> : NSObject
+@property (nonatomic, readonly, weak, nullable) BGGraph *graph;
+@property (nonatomic, readonly, weak, nullable) BGExtent *extent;
+@property (nonatomic, readonly, weak, nullable) BGBehavior *behavior;
+@property (nonatomic, readonly, nullable) BGEvent *added;
+@property (nonatomic, nullable) NSString *staticDebugName;
+
+@property (nonatomic, readonly, nullable) ValueType value;
+@property (nonatomic, readonly, nullable) ValueType traceValue;
+@property (nonatomic, readonly, nonnull) BGEvent *event;
+@property (nonatomic, readonly, nonnull) BGEvent *traceEvent;
+
+@property (nonatomic, readonly) BOOL justAdded;
+@property (nonatomic, readonly) BOOL justUpdated;
+@property (nonatomic, readonly) BOOL hasUpdated;
+@property (nonatomic, readonly) BOOL traceHasUpdated;
+- (BOOL)hasUpdatedSince:(BGResource * _Nonnull)since;
+- (instancetype _Nonnull)initWithExtent:(BGExtent * _Nonnull)extent;
+- (instancetype _Nonnull)init NS_UNAVAILABLE;
++ (instancetype _Nonnull)new NS_UNAVAILABLE;
+- (BOOL)justUpdatedTo:(ValueType _Nullable)toValue;
+@end
+
+@interface BGMoment<__covariant ValueType> : BGResource
+- (void)update;
+- (void)updateValue:(ValueType _Nullable)value;
+@end
+
+@interface BGState<__covariant ValueType> : BGResource
+- (instancetype _Nonnull)initWithExtent:(BGExtent * _Nonnull)extent value:(ValueType _Nullable)value NS_DESIGNATED_INITIALIZER;
+- (void)updateValue:(ValueType _Nullable)value;
+- (void)updateValueForce:(ValueType _Nullable)value;
+- (BOOL)justUpdatedFrom:(ValueType _Nullable)fromValue;
+- (BOOL)justUpdatedTo:(ValueType _Nullable)toValue from:(ValueType _Nullable)fromValue;
+@end
+
+@interface BGBehavior : NSObject
+@property (nonatomic, nullable) void(^runBlock)(BGExtent * _Nonnull extent);
+@property (nonatomic, nullable) NSString *staticDebugName;
+@property (nonatomic, readonly, weak, nullable) BGGraph *graph;
+@property (nonatomic, readonly, weak, nullable) BGExtent *extent;
+- (instancetype _Nonnull)initWithExtent:(BGExtent * _Nonnull)extent
+ demands:(NSArray * _Nullable)demands
+ supplies:(NSArray * _Nullable)supplies
+ runBlock:(void(^_Nullable)(BGExtent * _Nonnull extent))runBlock;
+- (void)setDemands:(NSArray * _Nullable)demands;
+- (void)addDemand:(BGResource * _Nonnull)demand;
+- (void)removeDemand:(BGResource * _Nonnull)demand;
+
+- (void)setSupplies:(NSArray * _Nullable)supplies;
+@end
+
+@protocol BGDateProvider
+- (NSDate * _Nonnull)bg_currentDate;
+@end
+
+@interface BGGraph : NSObject
+@property (nonatomic, readonly, nonnull) BGBehavior *mainNode;
+@property (nonatomic, readonly, nullable) BGEvent *currentEvent;
+@property (nonatomic, readonly, nullable) BGEvent *lastEvent;
+@property (nonatomic, readonly, nonnull) BGState *currentEventResource;
+@property (nonatomic, weak, nullable) id dateProvider;
+@property (nonatomic, readonly, nonnull) BGExtent *rootExtent;
+@property (nonatomic) BOOL defaultRequireSync;
+@property (nonatomic) BOOL assertOnLeakedSideEffects;
+
+- (void)action:(NSString * _Nullable)impulse runBlock:(dispatch_block_t _Nonnull)changes;
+- (void)action:(NSString * _Nullable)impulse requireSync:(BOOL)requireSync runBlock:(dispatch_block_t _Nonnull)changes;
+- (void)sideEffect:(NSString * _Nullable)name runBlock:(dispatch_block_t _Nonnull)block;
+@end
+
+@interface BGExtent<__covariant SubType> : NSObject
+@property (nonatomic, readonly, weak, nullable) BGGraph *graph;
+@property (nonatomic, readonly, nullable) NSString *debugHere;
+
+- (instancetype _Nonnull)initWithGraph:(BGGraph * _Nonnull)graph NS_DESIGNATED_INITIALIZER;
+- (instancetype _Nonnull)init NS_UNAVAILABLE;
++ (instancetype _Nonnull)new NS_UNAVAILABLE;
+- (void)addToGraph;
+- (void)removeFromGraph;
+- (BGBehavior * _Nonnull)behaviorWithDemands:(NSArray * _Nullable)demands
+ supplies:(NSArray * _Nullable)supplies
+ runBlock:(void(^_Nullable)(SubType _Nonnull extent))runBlock;
+
+- (void)sideEffect:(NSString * _Nullable)name runBlock:(void(^ _Nonnull)(SubType _Nonnull extent))block;
+- (BGMoment * _Nonnull)moment;
+- (BGResource * _Nonnull)resource;
+- (BGState * _Nonnull)stateWithValue:(id _Nullable)value;
+@end
+
+
diff --git a/doc-site/examples/objc/BGExamples/BehaviorGraph/BGGraph.m b/doc-site/examples/objc/BGExamples/BehaviorGraph/BGGraph.m
new file mode 100644
index 0000000..f7cdbc4
--- /dev/null
+++ b/doc-site/examples/objc/BGExamples/BehaviorGraph/BGGraph.m
@@ -0,0 +1,1157 @@
+//
+// BGGraph.m
+// YVideoSDK
+//
+// Created by James Lou on 2/7/19.
+// Copyright © 2019 Yahoo. All rights reserved.
+//
+
+#import "BGGraph+Private.h"
+
+#import
+
+#define PriorlessOrder 0
+
+
+BOOL bg_equal(NSObject *obj1, NSObject *obj2) {
+ return (obj1 && obj2 && [obj1 isEqual:obj2]) || (!obj1 && !obj2);
+}
+
+static BGEvent* BGUnknownPast;
+
+@implementation BGEvent
+
++ (void)initialize {
+ static dispatch_once_t onceToken;
+ dispatch_once(&onceToken, ^{
+ if (self == BGEvent.class) {
+ BGUnknownPast = [[BGEvent alloc] initWithImpulse:@"UnknownPast" sequence:0 timestamp:[NSDate dateWithTimeIntervalSince1970:0]];
+ }
+ });
+}
+
++ (BGEvent *)unknownPast {
+ return BGUnknownPast;
+}
+
+- (instancetype _Nonnull)initWithImpulse:(NSString *)impulse sequence:(NSUInteger)sequence timestamp:(NSDate * _Nonnull)timestamp {
+ self = [super init];
+ _impulse = impulse;
+ _sequence = sequence;
+ _timestamp = timestamp;
+ return self;
+}
+
+- (BOOL)happenedSince:(NSUInteger)since {
+ return _sequence > 0 && _sequence >= since;
+}
+
+- (NSString *)description {
+ return self.debugDescription;
+}
+
+- (NSString *)debugDescription {
+ return [NSString stringWithFormat:@"<%@:%p (%lu), %@>", NSStringFromClass(self.class), self, (unsigned long)self.sequence, self.impulse];
+}
+
+- (NSString *)debugLine {
+ return [NSString stringWithFormat:@"Event: (%lu), %@", (unsigned long)_sequence, _impulse];
+}
+
+@end
+
+
+@implementation BGResource
+@dynamic traced;
+@dynamic valuePersistence;
+
+- (instancetype)initWithExtent:(BGExtent *)extent {
+ return [self initWithExtent:extent value:nil event:nil];
+}
+
+- (instancetype)initWithExtent:(BGExtent *)extent value:(id)value event:(BGEvent *)event {
+ self = [super init];
+ _subsequents = [NSHashTable weakObjectsHashTable];
+ _value = value;
+ _event = event;
+ [extent addResource:self];
+ return self;
+}
+
+- (BOOL)justUpdated {
+ return [self justUpdated:nil useTo:NO from:nil useFrom:NO];
+}
+
+- (BOOL)justUpdatedTo:(id _Nullable)toValue {
+ return [self justUpdated:toValue useTo:YES from:nil useFrom:NO];
+}
+
+- (BOOL)justAdded {
+ return _graph && _added && _added == _graph.lastEvent;
+}
+
+- (BOOL)hasUpdated {
+ return _graph && _event && _event != BGUnknownPast;
+}
+
+- (BOOL)traceHasUpdated {
+ BGEvent *traceEvent;
+ return _graph && (traceEvent = self.traceEvent) && traceEvent != BGUnknownPast;
+}
+
+- (BOOL)hasUpdatedSince:(BGResource *)since {
+ return self.event && self.event.sequence >= since.event.sequence;
+}
+
+- (id)traceValue {
+ return self.traced && _event == _graph.currentEvent ? _previousValue : _value;
+}
+
+- (BGEvent *)traceEvent {
+ return self.traced && _event == _graph.currentEvent ? _previousEvent : _event;
+}
+
+- (BOOL)justUpdated:(id)toValue useTo:(BOOL)useTo from:(id _Nullable)fromValue useFrom:(BOOL)useFrom {
+ if (_graph.currentEvent == nil || self.event != _graph.currentEvent) {
+ return NO;
+ }
+ if (useTo && !bg_equal(self.value, toValue)) {
+ return NO;
+ }
+ if (useFrom && !bg_equal(self.traceValue, fromValue)) {
+ return NO;
+ }
+ return YES;
+}
+
+- (void)notifySubsequents {
+ for (BGBehavior *subsequent in _subsequents) {
+ [_graph submitToQueue:subsequent];
+ }
+}
+
+- (BOOL)assertUpdateable {
+ NSAssert(_extent.addedToGraph, @"A resource can only be updated after its been added to a graph.");
+ NSAssert(_graph.currentEvent, @"A resources's value can only be updated during an event loop.");
+ NSAssert(_behavior ? _behavior == _graph.currentBehavior : YES, @"A resource's value can only be updated while its behavior is responding.");
+ NSAssert(!_behavior ? _graph.currentBehavior == nil : YES, @"A non supplied resource can only be updated at the start of a new event loop inside a submitChanges block.");
+ return YES;
+}
+
+- (void)_updateValue:(id)value {
+ NSAssert([self assertUpdateable], @"Cannot update");
+ if (!bg_equal(_value, value)) {
+ [self _forceUpdateValue:value];
+ }
+}
+
+- (void)_forceUpdateValue:(id)value {
+ NSAssert([self assertUpdateable], @"Cannot update");
+
+ if (self.traced) {
+ if (_previousEvent.sequence < self.graph.sequence) {
+ _previousValue = _value;
+ _previousEvent = _event;
+ }
+ } else {
+ if (_value) {
+ [self.graph.deferredRelease addObject:_value];
+ }
+ _previousValue = nil;
+ _previousEvent = nil;
+ }
+
+
+ _value = value;
+ _event = _graph.currentEvent;
+ [self notifySubsequents];
+ [self logUpdate];
+
+ if (self.valuePersistence != BGResourcePersistent) {
+ [_graph trackTransient:self];
+ }
+}
+
+- (void)clearTransient {
+ switch (self.valuePersistence) {
+ case BGResourceTransient:
+ if (_value) {
+ [self.graph.deferredRelease addObject:_value];
+ }
+ _value = nil;
+ break;
+ case BGResourceTransientTrace:
+ if (_previousValue) {
+ [self.graph.deferredRelease addObject:_previousValue];
+ }
+ _previousValue = nil;
+ break;
+ case BGResourcePersistent:
+ break;
+ }
+}
+
+- (void)logUpdate {
+ if (@available(ios 12, *)) {
+ os_signpost_id_t eventSid = os_signpost_id_generate(_graph.splog);
+ os_signpost_event_emit(_graph.splog, eventSid, "resource updated", "name=\"%@\" value=\"%@\"", self.staticDebugName, self.value);
+ }
+ os_log_debug(_graph.aclog, " - %@ => %@ (%@(%p))", _staticDebugName, _value, NSStringFromClass([_extent class]), _extent);
+}
+
+- (NSString *)description {
+ return [self debugDescription];
+}
+
+- (NSString *)debugDescription {
+ return [NSString stringWithFormat:@"<%@:%p (%@) value=%@>", NSStringFromClass(self.class), self, self.staticDebugName, _value];
+}
+
+- (NSString *)debugLine {
+ return [NSString stringWithFormat:@" %@ (%lu): %@", self.staticDebugName, (unsigned long)_event.sequence, _value];
+}
+
+@end
+
+@implementation BGMoment
+
+- (void)update {
+ [self _forceUpdateValue:nil];
+}
+
+- (void)updateValue:(id)value {
+ [self _forceUpdateValue:value];
+}
+
+- (void)logUpdate {
+ if (@available(ios 12, *)) {
+ if (os_signpost_enabled(_graph.splog)) {
+ os_signpost_id_t eventSid = os_signpost_id_generate(_graph.splog);
+ if (self.value) {
+ os_signpost_event_emit(_graph.splog, eventSid, "BGMoment happened", "name=\"%@\" value=\"%@\"", self.staticDebugName, self.value);
+ } else {
+ os_signpost_event_emit(_graph.splog, eventSid, "BGMoment happened", "name=\"%@\"", self.staticDebugName);
+ }
+ }
+ }
+ os_log_debug(_graph.aclog, " - %@ => %@ (%@(%p))", _staticDebugName, self.value, NSStringFromClass([_extent class]), _extent);
+}
+
+- (BOOL)traced {
+ return NO;
+}
+
+- (BGResourceValuePersistence)valuePersistence {
+ return BGResourceTransient;
+}
+
+@end
+
+@implementation BGState
+@synthesize valuePersistence = _valuePersistence;
+
+- (instancetype)initWithExtent:(BGExtent *)extent {
+ return [self initWithExtent:(BGExtent *)extent value:nil];
+}
+
+- (instancetype)initWithExtent:(BGExtent *)extent value:(id _Nullable)value {
+ return [super initWithExtent:extent value:value event:BGUnknownPast];
+}
+
+- (BGResourceValuePersistence)valuePersistence {
+ return BGResourceTransientTrace;
+}
+
+- (BOOL)traced {
+ return YES;
+}
+
+- (BOOL)justUpdatedFrom:(id _Nullable)fromValue {
+ return [self justUpdated:nil useTo:NO from:fromValue useFrom:YES];
+}
+
+- (BOOL)justUpdatedTo:(id _Nullable)toValue from:(id _Nullable)fromValue {
+ return [self justUpdated:toValue useTo:YES from:fromValue useFrom:YES];
+}
+
+- (void)updateValue:(id)value {
+ [self _updateValue:value];
+}
+
+- (void)updateValueForce:(id)value {
+ [self _forceUpdateValue:value];
+}
+
+@end
+
+
+@implementation BGBehavior
+
+- (instancetype _Nonnull)initWithExtent:(BGExtent *)extent
+ demands:(NSArray * _Nullable)demands
+ supplies:(NSArray * _Nullable)supplies
+ runBlock:(void (^ _Nullable)(BGExtent * _Nonnull extent))runBlock {
+ self = [super init];
+ _extent = extent;
+ _runBlock = runBlock;
+ _demands = [NSHashTable weakObjectsHashTable];
+ _supplies = [NSHashTable new];
+ [self setDemands:demands];
+ [self setSupplies:supplies];
+ [extent addBehavior:self];
+ return self;
+}
+
+
+- (NSString *)description {
+ return [self debugDescription];
+}
+
+- (void)setExtent:(BGExtent *)extent {
+ _extent = extent;
+}
+
+- (NSString *)debugDescription {
+ NSString *suppliesString = _supplies.count == 0 ? @"{}" : ({
+ NSMutableString *string = [NSMutableString stringWithString:@"{"];
+ for (BGResource *supply in _supplies) {
+ [string appendFormat:@"\n\t\t%@", supply];
+ }
+ [string appendString:@"\n}"];
+ string;
+ });
+ return [NSString stringWithFormat:@"<%@:%p (%@) supplies=%@>", NSStringFromClass(self.class), self, self.staticDebugName, suppliesString];
+}
+
+- (NSString *)debugLine {
+ NSMutableArray *sups = [NSMutableArray new];
+ for (BGResource *supply in _supplies) {
+ [sups addObject:supply.staticDebugName];
+ }
+ return [NSString stringWithFormat:@"| %@ >", [sups componentsJoinedByString:@","]];
+}
+
+- (NSString *)debugCurrentState {
+ NSMutableArray *info = [NSMutableArray new];
+ [info addObject:[_graph.currentEvent debugLine]];
+ [info addObject:@"Demands:"];
+ for (BGResource *demand in _demands) {
+ [info addObject:[demand debugLine]];
+ }
+ [info addObject:@"Supplies:"];
+ for (BGResource *supply in _supplies) {
+ [info addObject:[supply debugLine]];
+ }
+ return [info componentsJoinedByString:@"\n"];
+}
+
+- (void)setDemands:(NSArray * _Nullable)demands {
+ NSAssert(self.extent.addedToGraph == nil || self.graph.processingChanges, @"Demands can only be modified before adding the behavior to the graph or during an event.");
+
+ _modifiedDemands = [demands mutableCopy];
+ [_graph.modifiedDemands addObject:self];
+}
+
+- (void)addDemand:(BGResource * _Nonnull)demand {
+ NSAssert(self.extent.addedToGraph == nil || self.graph.processingChanges, @"Demands can only be modified before adding the behavior to the graph or during an event.");
+
+ if (!demand) {
+ // useful instead of having to do the if check everywhere you want to add it
+ // although in theory if you meant to add something but were adding nil then you wouldn't be alerted to it
+ return;
+ }
+
+ if (_modifiedDemands) {
+ if (![_modifiedDemands containsObject:demand]) {
+ [_modifiedDemands addObject:demand];
+ [_graph.modifiedDemands addObject:self];
+ }
+ } else {
+ if (![_demands containsObject:demand]) {
+ _modifiedDemands = [_demands.allObjects mutableCopy];
+ [_modifiedDemands addObject:demand];
+ [_graph.modifiedDemands addObject:self];
+ }
+ }
+}
+
+- (void)removeDemand:(BGResource * _Nonnull)demand {
+ NSAssert(self.extent.addedToGraph == nil || self.graph.processingChanges, @"Demands can only be modified before adding the behavior to the graph or during an event.");
+
+ if (_modifiedDemands) {
+ if ([_modifiedDemands containsObject:demand]) {
+ [_modifiedDemands removeObject:demand];
+ [_graph.modifiedDemands addObject:self];
+ }
+ } else {
+ if ([_demands containsObject:demand]) {
+ _modifiedDemands = [_demands.allObjects mutableCopy];
+ [_modifiedDemands removeObject:demand];
+ [_graph.modifiedDemands addObject:self];
+ }
+ }
+}
+
+- (void)setSupplies:(NSArray * _Nullable)supplies {
+ NSAssert(self.extent.addedToGraph == nil || self.graph.processingChanges, @"Supplies can only be modified before adding the behavior to the graph or during an event.");
+
+ for (BGResource *supply in _supplies) {
+ if (![supplies containsObject:supply]) {
+ // Removed supply
+ supply.behavior = nil;
+ }
+ }
+
+ for (BGResource *supply in supplies) {
+ if (![_supplies containsObject:supply]) {
+ // Added supply
+ NSAssert(!supply.behavior, @"Supply already added to a different behavior.");
+ supply.behavior = self;
+
+ for (BGBehavior *subsequent in supply.subsequents) {
+ [self.graph.modifiedDemands addObject:subsequent];
+ }
+ }
+ }
+
+ [_supplies removeAllObjects];
+ for (BGResource *supply in supplies) {
+ NSAssert(supply.behavior == self, nil);
+ [_supplies addObject:supply];
+ }
+}
+
+@end
+
+@implementation BGAction
+
+- (instancetype)initWithName:(NSString *)name block:(dispatch_block_t)block {
+ self = [super init];
+ _name = name;
+ _block = block;
+ return self;
+}
+
+@end
+
+@implementation BGSideEffect
+
+- (instancetype)initWithName:(NSString *)name event:(BGEvent *)event {
+ NSAssert(self.class != BGSideEffect.class, @"Abstract");
+ return self;
+}
+
+- (void)run {
+ NSAssert(NO, @"Abstract");
+}
+
+- (NSString *)description {
+ return self.debugDescription;
+}
+
+- (NSString *)debugDescription {
+ return [NSString stringWithFormat:@"<%@:%p name=%@ event=%@>", NSStringFromClass(self.class), self, _name, _event];
+}
+
+@end
+
+@implementation BGBehaviorSideEffect
+
+- (instancetype)initWithName:(NSString *)name event:(BGEvent *)event extent:(BGExtent *)extent block:(void (^)(BGExtent * _Nullable))block {
+ self = [super init];
+ _name = name;
+ _event = event;
+ _extent = extent;
+ _block = block;
+ return self;
+}
+
+- (void)run {
+ _block(_extent);
+}
+
+@end
+
+@implementation BGGraphSideEffect
+
+- (instancetype)initWithName:(NSString *)name event:(BGEvent *)event block:(dispatch_block_t)block {
+ self = [super init];
+ _name = name;
+ _event = event;
+ _block = block;
+ return self;
+}
+
+- (void)run {
+ _block();
+}
+
+@end
+
+@implementation BGEventLoopState
+
+- (NSUInteger)sequence {
+ return _event.sequence;
+}
+
+@end
+
+@implementation BGGraph
+
++ (void)initialize {
+ if (self == BGGraph.class) {
+ NullPushedValue = [NSObject new];
+ }
+}
+
+- (instancetype)init {
+ self = [super init];
+
+ NSString *subsystem = [NSBundle mainBundle].bundleIdentifier;
+ if ([[[NSProcessInfo processInfo] arguments] containsObject:@"-signpostBehaviorGraphActivity"]) {
+ _splog = os_log_create(subsystem.UTF8String, "bg-signpost");
+ } else {
+ _splog = OS_LOG_DISABLED;
+ }
+ if ([[[NSProcessInfo processInfo] arguments] containsObject:@"-logBehaviorGraphActivity"]) {
+ _aclog = os_log_create(subsystem.UTF8String, "bg-log");
+ } else {
+ _aclog = OS_LOG_DISABLED;
+ }
+ if (@available(iOS 12, *)) {
+ os_signpost_event_emit(_splog, os_signpost_id_generate(_splog), "graph created", "id=%p", self);
+ }
+ os_log_debug(_aclog, "graph created: id=%p", self);
+
+ _behaviorQueue = [[BGPriorityQueue alloc] initWithComparisonBlock:^CFComparisonResult(BGBehavior * _Nonnull obj1, BGBehavior * _Nonnull obj2) {
+ if (obj1.order < obj2.order) {
+ return kCFCompareLessThan;
+ } else if (obj1.order > obj2.order) {
+ return kCFCompareGreaterThan;
+ } else {
+ return kCFCompareEqualTo;
+ }
+ }];
+
+ _actionQueue = [NSMutableArray new];
+ _sideEffectQueue = [NSMutableArray new];
+
+ _untrackedBehaviors = [NSMutableSet new];
+ _modifiedDemands = [NSMutableSet new];
+ _updatedTransientResources = [NSMutableArray new];
+ _deferredRelease = [NSMutableArray new];
+ _needsOrdering = [NSMutableSet new];
+
+ _rootExtent = [[BGExtent alloc] initWithGraph:self];
+ _currentEventResource = [_rootExtent stateWithValue:nil];
+ _currentEventResource.staticDebugName = @"currentEventResource";
+ [self addExtent:_rootExtent event:BGUnknownPast];
+
+ return self;
+}
+
+- (void)action:(NSString * _Nullable)impulse runBlock:(dispatch_block_t _Nonnull)changes {
+ [self action:impulse requireSync:_defaultRequireSync runBlock:changes];
+}
+
+- (void)action:(NSString *)impulse requireSync:(BOOL)requireSync runBlock:(dispatch_block_t)changes {
+ [self _action:impulse requireSync:requireSync changes:changes];
+}
+
+- (void)_action:(NSString *)impulse requireSync:(BOOL)requireSync changes:(dispatch_block_t)changes {
+ if (self.processingAction) {
+ NSAssert(!requireSync, @"Cannot synchronously complete nested action. Either relax the synchronous requirement for this action or fold changes into current action.");
+ requireSync = NO;
+ }
+
+ NSAssert(!(self.assertOnLeakedSideEffects && self.processingChanges), @"Side effect leaked while processing changes.");
+
+ BGAction *action = [[BGAction alloc] initWithName:impulse block:changes];
+ [_actionQueue addObject:action];
+
+ if (_eventLoopDrivers == 0 || requireSync) {
+ [self eventLoop];
+ }
+}
+
+- (void)eventLoop {
+ ++_eventLoopDrivers;
+ while (YES) {
+ @autoreleasepool {
+ if (_eventLoopState.processingChanges) {
+ if (_untrackedBehaviors.count > 0) {
+ [self addUntrackedBehaviors];
+ }
+
+ if (_modifiedDemands.count > 0) {
+ [self commitModifiedDemands];
+ }
+
+ if (_needsOrdering.count > 0) {
+ [self orderBehaviors];
+ }
+
+ if (_behaviorQueue.count > 0) {
+ BGBehavior *subsequent = [_behaviorQueue pop];
+ [self runBehavior:subsequent];
+ continue;
+ }
+ }
+
+ _eventLoopState.processingChanges = NO;
+
+ if (_sideEffectQueue.count > 0) {
+ BGSideEffect *sideEffect = _sideEffectQueue[0];
+ [_sideEffectQueue removeObjectAtIndex:0];
+ [sideEffect run];
+ continue;
+ }
+
+ if (_updatedTransientResources.count > 0) {
+ [self clearUpdatedTransientResources];
+ continue;
+ }
+
+ if (_deferredRelease.count > 0) {
+ // Retaining any objects that might be going to nil until the end of the event loop
+ // This limits ways that resource updates and clearing transients might cause
+ // deallocs which could potentially force new events during the update phase
+ [_deferredRelease removeAllObjects];
+ continue;
+ }
+
+ if (_eventLoopState) {
+ NSAssert(!self.processingAction && !self.processingChanges, nil);
+
+ BGEventLoopState *eventLoopState = _eventLoopState;
+ _eventLoopState = nil;
+
+ if (@available(ios 12, *)) {
+ os_signpost_interval_end(_splog, eventLoopState.eventSid, "event loop");
+ }
+ }
+
+ if (_actionQueue.count > 0) {
+ NSDate *timestamp = [_dateProvider bg_currentDate] ?: [NSDate date];
+
+ BGAction *action = _actionQueue[0];
+ [_actionQueue removeObjectAtIndex:0];
+
+ BGEvent *event = [[BGEvent alloc] initWithImpulse:action.name sequence:(_lastEvent.sequence + 1) timestamp:timestamp];
+ _lastEvent = event;
+
+ _eventLoopState = [BGEventLoopState new];
+ _eventLoopState.event = event;
+ _eventLoopState.processingAction = YES;
+ _eventLoopState.processingChanges = YES;
+
+ if (@available(ios 12, *)) {
+ _eventLoopState.eventSid = os_signpost_id_generate(_splog);
+ os_signpost_interval_begin(_splog, _eventLoopState.eventSid, "event loop", "impulse=\"%@\" sequence=%lu timestamp=\"%@\"", event.impulse, (unsigned long)event.sequence, event.timestamp);
+ }
+ os_log_debug(_aclog, "=== event %@ sequence=%lu timestamp=%@ ===", event.impulse, (unsigned long)event.sequence, event.timestamp);
+
+ action.block();
+ [_currentEventResource updateValueForce:event];
+
+ _eventLoopState.processingAction = NO;
+
+ // NOTE: We keep the action block around because it may capture capture and retain some external objects
+ // If it were to go away right after running then that might cause a dealloc to be called as it goes out of scope internal
+ // to the event loop and thus create a side effect during the update phase.
+ // So we keep it around until after all updates are processed.
+ [_deferredRelease addObject:action];
+
+ continue;
+ }
+ break;
+ }
+ }
+ --_eventLoopDrivers;
+}
+
+- (BGEvent *)currentEvent {
+ return _eventLoopState.event;
+}
+
+- (BOOL)processingChanges {
+ return _eventLoopState.processingChanges;
+}
+
+- (BOOL)processingAction {
+ return _eventLoopState.processingAction;
+}
+
+- (NSUInteger)sequence {
+ return _eventLoopState.sequence;
+}
+
+- (void)submitToQueue:(BGBehavior *)behavior {
+ // @SAL 8/26/2019-- I'm not sure how either of these would trigger, it seems they are both a result of a broken
+ // algorithm, not a misconfigured graph
+ // jlou 2/5/19 - These asserts are checking for graph implementation bugs, not for user error.
+ NSAssert(self.processingChanges, @"Should not be activating behaviors in current phase.");
+ NSAssert(behavior.lastUpdateSequence != _eventLoopState.sequence, @"Behavior already ran.");
+ if (behavior.enqueuedSequence < self.sequence) {
+ behavior.enqueuedSequence = self.sequence;
+ [_behaviorQueue push:behavior];
+ }
+}
+
+- (void)runBehavior:(BGBehavior *)behavior {
+ // jlou 2/5/19 - This assert checks for graph implementation bugs, not for user error.
+ NSAssert(behavior.lastUpdateSequence < self.sequence, @"Behaviors should only run once per cycle.");
+ if (behavior.removedSequence != self.sequence) {
+ _currentBehavior = behavior;
+ behavior.lastUpdateSequence = self.sequence;
+ if (behavior.runBlock) {
+ unsigned long long behaviorSid = 0;
+ if (@available(iOS 12, *)) {
+ behaviorSid = os_signpost_id_generate(_splog);
+ os_signpost_interval_begin(_splog, behaviorSid, "behavior run begin", "name=\"%@\"", [behavior debugLine]);
+ }
+ os_log_debug(_aclog, " %@ (%@(%p))", [behavior debugLine], NSStringFromClass([behavior.extent class]), behavior.extent);
+
+ behavior.runBlock(behavior.extent);
+ if (@available(iOS 12, *)) {
+ os_signpost_interval_end(_splog, behaviorSid, "behavior run end");
+ }
+ }
+ _currentBehavior = nil;
+ }
+}
+
+- (void)sideEffect:(NSString *)name runBlock:(dispatch_block_t)block {
+ __auto_type sideEffect = [[BGGraphSideEffect alloc] initWithName:name event:self.currentEvent block:block];
+ [self submitSideEffect:sideEffect];
+}
+
+- (void)submitSideEffect:(BGSideEffect * _Nonnull)sideEffect {
+ NSAssert(self.processingChanges, @"Can only submit an after-changes block during a graph cycle.");
+ [_sideEffectQueue addObject:sideEffect];
+}
+
+- (void)addUntrackedBehaviors {
+ for (BGBehavior *behavior in _untrackedBehaviors) {
+ [_modifiedDemands addObject:behavior];
+ [self submitToQueue:behavior];
+ }
+ [_untrackedBehaviors removeAllObjects];
+}
+
+- (void)commitModifiedDemands {
+ for (BGBehavior *subsequent in _modifiedDemands) {
+ NSMutableSet *addedDemands;
+ NSMutableSet *removedDemands;
+ if (subsequent.modifiedDemands) {
+ for (BGResource *demand in subsequent.demands) {
+ if (![subsequent.modifiedDemands containsObject:demand]) {
+ if (!removedDemands) {
+ removedDemands = [NSMutableSet new];
+ }
+ [removedDemands addObject:demand];
+ }
+ }
+
+ for (BGResource *demand in subsequent.modifiedDemands) {
+ NSAssert(demand.graph == self && demand.extent.addedToGraph != nil, @"Added demands must be added to the graph.");
+ if (![subsequent.demands containsObject:demand]) {
+ if (!addedDemands) {
+ addedDemands = [NSMutableSet new];
+ }
+ [addedDemands addObject:demand];
+ }
+ }
+
+ for (BGResource *demand in removedDemands) {
+ [demand.subsequents removeObject:subsequent];
+ [subsequent.demands removeObject:demand];
+ }
+
+ for (BGResource *demand in addedDemands) {
+ [demand.subsequents addObject:subsequent];
+ [subsequent.demands addObject:demand];
+ }
+ }
+
+ BOOL needsOrdering = (subsequent.orderingState == BGOrderingStateUnordered);
+ if (!needsOrdering) {
+ for (BGResource *demand in subsequent.demands) {
+ BGBehavior *orderedPrior = (BGBehavior *)demand.behavior;
+ if (orderedPrior.orderingState == BGOrderingStateOrdered && orderedPrior.order >= subsequent.order) {
+ needsOrdering = YES;
+ }
+ }
+ }
+
+ if (needsOrdering) {
+ [_needsOrdering addObject:subsequent];
+ }
+
+ subsequent.modifiedDemands = nil;
+ }
+
+ [_modifiedDemands removeAllObjects];
+}
+
+- (void)orderBehaviors {
+ __auto_type needsOrdering = [NSMutableArray new];
+
+ // Walk subsequents and add to needs ordering list
+ {
+ __auto_type traversalQueue = [NSMutableArray new];
+
+ // Add unsorted behaviors to traversal queue and temporarily mark each as 'ordered'
+ // so that it will be traversed when first encountered.
+ for (BGBehavior *behavior in _needsOrdering) {
+ behavior.orderingState = BGOrderingStateOrdered;
+ [traversalQueue addObject:behavior];
+ }
+ [_needsOrdering removeAllObjects];
+
+ while (traversalQueue.count > 0) {
+ BGBehavior *behavior = traversalQueue[0];
+ [traversalQueue removeObjectAtIndex:0];
+
+ if (behavior.orderingState != BGOrderingStateUnordered) {
+ behavior.orderingState = BGOrderingStateUnordered;
+
+ [needsOrdering addObject:behavior];
+
+ for (BGResource *supply in behavior.supplies) {
+ for (BGBehavior *subsequent in supply.subsequents) {
+ [traversalQueue addObject:subsequent];
+ }
+ }
+ }
+ }
+ }
+
+ BOOL needsReheap = NO;
+ for (BGBehavior *behavior in needsOrdering) {
+ [self sortDFS:behavior needsReheap:&needsReheap];
+ }
+
+ if (needsReheap) {
+ [_behaviorQueue needsResort];
+ }
+}
+
+- (void)clearUpdatedTransientResources {
+ while (_updatedTransientResources.count > 0) {
+ BGResource *rez = [_updatedTransientResources objectAtIndex:0];
+ [_updatedTransientResources removeObjectAtIndex:0];
+ [rez clearTransient];
+ }
+}
+
+- (NSString *)cycleStringForBehavior:(BGBehavior *)behavior {
+ NSMutableArray *stack = [NSMutableArray new];
+ BOOL found = [self cyclePrinterDFSCurrent:behavior target:behavior resourceStack:stack];
+ if (found) {
+ NSMutableArray *output = [NSMutableArray new];
+ while (stack.count > 0) {
+ BGResource *resource = [stack lastObject];
+ [stack removeLastObject];
+ [output addObject:[NSString stringWithFormat:@"| (%@) %@ >",
+ NSStringFromClass(resource.behavior.extent.class), resource.staticDebugName ?: resource.debugDescription]];
+ }
+ NSMutableArray *behaviorSupplies = [NSMutableArray new];
+ for (BGResource *resource in behavior.supplies) {
+ [behaviorSupplies addObject:resource.staticDebugName ?: resource.debugDescription];
+ }
+ NSString *outputLine = [NSString stringWithFormat:@"| (%@) %@ >",
+ NSStringFromClass(behavior.extent.class), [behaviorSupplies componentsJoinedByString:@","]];
+ [output addObject:outputLine];
+ return [output componentsJoinedByString:@"\n"];
+ } else {
+ // no cycle found
+ return nil;
+ }
+}
+
+- (BOOL)cyclePrinterDFSCurrent:(BGBehavior *)currentBehavior target:(BGBehavior *)targetBehavior resourceStack:(NSMutableArray *)stack {
+ for (BGResource *resource in currentBehavior.demands) {
+ [stack addObject:resource];
+ if (resource.behavior == targetBehavior) {
+ return YES; // cycle detected
+ }
+ if ([self cyclePrinterDFSCurrent:resource.behavior target:targetBehavior resourceStack:stack]) {
+ return YES;
+ }
+ [stack removeLastObject];
+ }
+ return NO;
+}
+
+- (void)sortDFS:(BGBehavior * _Nonnull)behavior needsReheap:(BOOL * _Nonnull)needsReheap {
+ NSAssert(behavior.orderingState != BGOrderingStateOrdering, @"Dependency cycle detected:\n%@", [self cycleStringForBehavior:behavior]);
+ if (behavior.orderingState == BGOrderingStateUnordered) {
+ behavior.orderingState = BGOrderingStateOrdering;
+ NSUInteger order = PriorlessOrder + 1;
+ for (BGResource *demand in behavior.demands) {
+ BGBehavior * orderedPrior = demand.behavior;
+ if (orderedPrior.orderingState != BGOrderingStateOrdered) {
+ [self sortDFS:orderedPrior needsReheap:needsReheap];
+ }
+ order = MAX(order, orderedPrior.order + 1);
+ }
+ behavior.orderingState = BGOrderingStateOrdered;
+ if (order != behavior.order) {
+ behavior.order = order;
+ *needsReheap = YES;
+ }
+ }
+}
+
+- (void)removeBehavior:(BGBehavior *)behavior {
+ for (BGResource *supply in behavior.supplies) {
+ for (BGBehavior * subsequent in supply.subsequents) {
+ [subsequent.demands removeObject:supply];
+ }
+ [supply.subsequents removeAllObjects];
+ }
+
+ BGBehavior * subsequent = (BGBehavior *)behavior;
+ for (BGResource *demand in subsequent.demands) {
+ [demand.subsequents removeObject:subsequent];
+ }
+ [subsequent.demands removeAllObjects];
+
+ behavior.removedSequence = self.sequence;
+}
+
+- (void)addExtent:(BGExtent *)extent event:(BGEvent *)event {
+ NSAssert(extent.addedToGraph == nil, @"Extent cannot be added to the graph twice.");
+ extent.addedToGraph = event;
+ for (BGResource *resource in extent.allResources.allObjects) {
+ resource.added = event;
+ }
+ for (BGBehavior * behavior in extent.allBehaviors.allObjects) {
+ [_untrackedBehaviors addObject:behavior];
+ }
+}
+
+- (void)removeExtent:(BGExtent *)extent {
+ NSAssert(self.processingChanges, @"Extents must be removed during an event.");
+ NSAssert(extent.addedToGraph != nil, @"Extent cannot be removed from graph twice.");
+ for (BGBehavior * behavior in extent.allBehaviors) {
+ [self removeBehavior:behavior];
+ }
+ extent.addedToGraph = nil;
+}
+
+- (void)trackTransient:(BGResource *)rez {
+ [_updatedTransientResources addObject:rez];
+}
+
+@end
+
+@interface YVSelector : NSObject
+@property (nonatomic) NSString *name;
+@property (nonatomic) SEL selector;
+@end
+
+@implementation YVSelector
+- (NSString *)description {
+ return [self debugDescription];
+}
+
+- (NSString *)debugDescription {
+ return [NSString stringWithFormat:@"<%@:%p %@>", NSStringFromClass(self.class), self, _name];
+}
+
+- (NSUInteger)hash {
+ return [_name hash];
+}
+
+- (BOOL)isEqual:(id)object {
+ return [_name isEqual:object];
+}
+@end
+
+@implementation BGExtent
+
+static char kDemandableSelectorsKey;
+static char kNodeableSelectorsKey;
+
+
+- (NSString *)debugHere {
+ return [self.graph.currentBehavior debugCurrentState];
+}
+
+- (void)dealloc {
+ // @SAL 8/29/2019-- Removing is a bit tricky right now. If the signal to remove is the dealloc of the extent
+ // then we need to capture the allBehaviors before starting submitChanges otherwise it goes away
+ // with extent and then we can't access them.
+ // Possibly dealloc shouldn't be the signal (but it might be error prone to not use it). Its hard to model
+ // when the dealloc happens and we are specifically trying to keep track of those details.
+
+ NSSet *allBehaviors = self->_allBehaviors; // save pointer so it won't disappear on us
+ BGGraph *graph = self.graph;
+ [self.graph action:_fname requireSync:NO runBlock:^{
+ for (BGBehavior * behavior in allBehaviors) {
+ [graph removeBehavior:behavior];
+ }
+ }];
+}
+
+- (instancetype _Nonnull)initWithGraph:(BGGraph *)graph {
+ if (self = [super init]) {
+ _graph = graph;
+ _allBehaviors = [NSMutableSet new];
+ _allResources = [NSMutableSet new];
+ }
+ return self;
+}
+
+- (void)addToGraph {
+ NSAssert(_graph.eventLoopState.processingChanges, @"Extents must be added to a graph during an event.");
+ [self nameComponents];
+ [_graph addExtent:self event:_graph.currentEvent];
+}
+
+- (void)removeFromGraph {
+ // @SAL 8/29/2019-- See dealloc for alternate removal process because of memory complications
+ [_graph removeExtent:self];
+}
+
+- (void)addBehavior:(BGBehavior *)behavior {
+ NSAssert(_addedToGraph == nil, @"Cannot add behavior after extent has been added to graph.");
+ NSAssert(behavior.extent == nil || behavior.extent == self, @"Behavior can only belong to one extent");
+ behavior.extent = self;
+ behavior.graph = self.graph;
+ [_allBehaviors addObject:behavior];
+}
+
+- (void)addResource:(BGResource *)resource {
+ NSAssert(_addedToGraph == nil, @"Cannot add resource after extent has been added to graph.");
+ NSAssert(resource.extent == nil || resource.extent == self, @"Resource can only belong to one extent");
+ if (resource.extent == nil) {
+ resource.extent = self;
+ resource.graph = self.graph;
+ [_allResources addObject:resource];
+ }
+
+}
+
+- (BGBehavior * _Nonnull)behaviorWithDemands:(NSArray * _Nullable)demands
+ supplies:(NSArray * _Nullable)supplies
+ runBlock:(void(^_Nullable)(id _Nonnull extent))runBlock {
+ BGBehavior *bhv = [[BGBehavior alloc] initWithExtent:self demands:demands supplies:supplies runBlock:runBlock];
+ return bhv;
+}
+
+- (void)nameComponents {
+
+ NSArray *nodeableSelectors = objc_getAssociatedObject([self class], &kNodeableSelectorsKey);
+ NSArray *demandableSelectors = objc_getAssociatedObject([self class], &kDemandableSelectorsKey);
+
+ for (YVSelector *selector in nodeableSelectors) {
+ IMP implementation = [self methodForSelector:selector.selector];
+ BGBehavior *(*function)(id, SEL) = (void *)implementation;
+ BGBehavior * behavior = function(self, selector.selector);
+ NSAssert(behavior, @"Process not initialized (%@)", selector.name);
+ if (behavior) {
+ if (behavior.staticDebugName == nil) {
+ behavior.staticDebugName = selector.name;
+ }
+ }
+ }
+
+ for (YVSelector *selector in demandableSelectors) {
+ IMP implementation = [self methodForSelector:selector.selector];
+ BGResource *(*function)(id, SEL) = (void *)implementation;
+ BGResource *resource = function(self, selector.selector);
+ NSAssert(resource, @"Resource not initialized (%@)", selector.name);
+ if (resource) {
+ if (resource.staticDebugName == nil) {
+ resource.staticDebugName = selector.name;
+ }
+ }
+ }
+}
+
+- (void)sideEffect:(NSString *)name runBlock:(void (^)(id _Nonnull))block {
+ __auto_type sideEffect = [[BGBehaviorSideEffect alloc] initWithName:name event:self.graph.currentEvent extent:self block:block];
+ [self.graph submitSideEffect:sideEffect];
+}
+
+- (BGState * _Nonnull)stateWithValue:(id)value {
+ return [[BGState alloc] initWithExtent:self value:value];
+}
+
+- (BGMoment * _Nonnull)moment {
+ return [[BGMoment alloc] initWithExtent:self];
+}
+
+- (BGResource * _Nonnull)resource {
+ return [[BGResource alloc] initWithExtent:self];
+}
+
+
++ (void)initialize {
+
+ NSMutableSet *demandableSelectors = [NSMutableSet new];
+ // this captures in case we have superclass that has the demandables
+ // we count on initialize happening from superclass down
+ NSMutableSet *parentDemandableSelectors = objc_getAssociatedObject([self superclass], &kDemandableSelectorsKey);
+ if (parentDemandableSelectors.count > 0) {
+ [demandableSelectors addObjectsFromArray:parentDemandableSelectors.allObjects];
+ }
+ NSMutableSet *nodeableSelectors = [NSMutableSet new];
+ NSMutableSet *parentNodeableSelectors = objc_getAssociatedObject([self superclass], &kNodeableSelectorsKey);
+ if (parentNodeableSelectors.count > 0) {
+ [nodeableSelectors addObjectsFromArray:parentNodeableSelectors.allObjects];
+ }
+
+ unsigned int count;
+ objc_property_t *properties = class_copyPropertyList(self, &count);
+ for (unsigned int i = 0; i < count; i++) {
+ objc_property_t property = properties[i];
+
+ NSString *typeEncoding = ({
+ char *typeEncodingCString = property_copyAttributeValue(property, "T");
+ NSString *typeEncoding = [NSString stringWithCString:typeEncodingCString encoding:NSASCIIStringEncoding];
+ free(typeEncodingCString);
+ typeEncoding;
+ });
+ if ([typeEncoding hasPrefix:@"@"] && typeEncoding.length > 3) {
+ Class cls = NSClassFromString([typeEncoding substringWithRange:NSMakeRange(2, typeEncoding.length - 3)]);
+
+ BOOL demandable = [cls isSubclassOfClass:BGResource.class];
+ BOOL nodeable = [cls isSubclassOfClass:BGBehavior.class];
+
+ if (demandable || nodeable) {
+ NSString *getterMethodName = ({
+ NSString *methodName;
+ char *getter = property_copyAttributeValue(property, "G"); // Getter
+ if (getter != NULL) {
+ methodName = [NSString stringWithCString:getter encoding:NSASCIIStringEncoding];
+ } else {
+ methodName = [NSString stringWithCString:property_getName(property) encoding:NSASCIIStringEncoding];
+ }
+ free(getter);
+ methodName;
+ });
+
+ YVSelector *selector = [YVSelector new];
+ selector.name = getterMethodName;
+ selector.selector = NSSelectorFromString(selector.name);
+
+ if (demandable) {
+ [demandableSelectors addObject:selector];
+ }
+ if (nodeable) {
+ [nodeableSelectors addObject:selector];
+ }
+ }
+ }
+ }
+ free(properties);
+
+ objc_setAssociatedObject(self, &kNodeableSelectorsKey, nodeableSelectors, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
+ objc_setAssociatedObject(self, &kDemandableSelectorsKey, demandableSelectors, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
+}
+
+@end
diff --git a/doc-site/examples/objc/BGExamples/BehaviorGraph/BGPriorityQueue.h b/doc-site/examples/objc/BGExamples/BehaviorGraph/BGPriorityQueue.h
new file mode 100644
index 0000000..403a1a2
--- /dev/null
+++ b/doc-site/examples/objc/BGExamples/BehaviorGraph/BGPriorityQueue.h
@@ -0,0 +1,18 @@
+//
+// BGPriorityQueue.h
+// YVideoSDK
+//
+// Created by James Lou on 6/9/19.
+// Copyright © 2019 Yahoo. All rights reserved.
+//
+
+#import
+
+@interface BGPriorityQueue : NSObject
+@property (nonatomic, readonly, nullable) ObjectType top;
+@property (nonatomic, readonly) NSUInteger count;
+- (instancetype _Nonnull)initWithComparisonBlock:(CFComparisonResult(^ _Nullable)(ObjectType _Nonnull obj1, ObjectType _Nonnull obj2))comparisonFunction NS_DESIGNATED_INITIALIZER;
+- (ObjectType _Nullable)pop;
+- (void)push:(ObjectType _Nonnull)object;
+- (void)needsResort;
+@end
diff --git a/doc-site/examples/objc/BGExamples/BehaviorGraph/BGPriorityQueue.m b/doc-site/examples/objc/BGExamples/BehaviorGraph/BGPriorityQueue.m
new file mode 100644
index 0000000..52122f4
--- /dev/null
+++ b/doc-site/examples/objc/BGExamples/BehaviorGraph/BGPriorityQueue.m
@@ -0,0 +1,91 @@
+//
+// BGPriorityQueue.m
+// YVideoSDK
+//
+// Created by James Lou on 6/9/19.
+// Copyright © 2019 Yahoo. All rights reserved.
+//
+
+#import "BGPriorityQueue.h"
+
+#define ElementCount (CFBinaryHeapGetCount(_heap) + _unheapedElements.count)
+
+CFComparisonResult bg_priorityQueueCompare(const void *ptr1, const void *ptr2, void *context) {
+ CFComparisonResult(^comparisonBlock)(id _Nonnull, id _Nonnull) = *(__unsafe_unretained CFComparisonResult(^ *)(id _Nonnull, id _Nonnull))context;
+ return comparisonBlock((__bridge id)ptr1, (__bridge id)ptr2);
+}
+
+@interface BGPriorityQueue ()
+@property (nonatomic, readonly, nonnull) CFBinaryHeapRef heap;
+@property (nonatomic, readonly, nonnull) CFComparisonResult(^comparisonBlock)(id _Nonnull, id _Nonnull);
+@property (nonatomic, readonly) CFBinaryHeapCompareContext compareContext;
+@property (nonatomic, readonly) NSMutableArray *unheapedElements;
+@end
+
+@implementation BGPriorityQueue
+
+- (instancetype)init {
+ return [self initWithComparisonBlock:nil];
+}
+
+- (instancetype)initWithComparisonBlock:(CFComparisonResult (^)(id _Nonnull, id _Nonnull))comparisonBlock {
+ _comparisonBlock = comparisonBlock;
+
+ CFBinaryHeapCallBacks callbacks = { .compare = &bg_priorityQueueCompare};
+ CFBinaryHeapCompareContext context = { .info = &_comparisonBlock };
+ _heap = CFBinaryHeapCreate(kCFAllocatorDefault, 0, &callbacks, &context);
+
+ _unheapedElements = [NSMutableArray new];
+ return self;
+}
+
+- (void)dealloc {
+ CFRelease(_heap);
+}
+
+- (id _Nullable)top {
+ if (ElementCount == 0) {
+ return nil;
+ }
+
+ [self heapify];
+ return (__bridge_transfer id)CFBinaryHeapGetMinimum(_heap);
+}
+
+- (id _Nullable)pop {
+ if (ElementCount == 0) {
+ return nil;
+ }
+
+ [self heapify];
+ id top = (__bridge_transfer id)CFBinaryHeapGetMinimum(_heap);
+ CFBinaryHeapRemoveMinimumValue(_heap);
+ return top;
+}
+
+- (void)push:(id)object {
+ [_unheapedElements addObject:object];
+}
+
+- (NSUInteger)count {
+ return ElementCount;
+}
+
+- (void)heapify {
+ if (_unheapedElements.count > 0) {
+ for (id obj in _unheapedElements) {
+ CFBinaryHeapAddValue(_heap, (__bridge_retained void *)obj);
+ }
+ [_unheapedElements removeAllObjects];
+ }
+}
+
+- (void)needsResort {
+ while (CFBinaryHeapGetCount(_heap) > 0) {
+ id value = (__bridge_transfer id)CFBinaryHeapGetMinimum(_heap);
+ CFBinaryHeapRemoveMinimumValue(_heap);
+ [_unheapedElements addObject:value];
+ }
+}
+
+@end
diff --git a/doc-site/examples/objc/BGExamples/BehaviorGraph/BGProfiler.h b/doc-site/examples/objc/BGExamples/BehaviorGraph/BGProfiler.h
new file mode 100644
index 0000000..6d2c245
--- /dev/null
+++ b/doc-site/examples/objc/BGExamples/BehaviorGraph/BGProfiler.h
@@ -0,0 +1,25 @@
+//
+// BGBehaviorGraphProfiler.h
+// YVideoSDK
+//
+// Created by James Lou on 6/10/19.
+// Copyright © 2019 Yahoo. All rights reserved.
+//
+
+#if DEBUG
+
+#import
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface BGProfiler : NSObject
+@property (nonatomic, readonly, class) BGProfiler *sharedInstance;
+@property (nonatomic, readonly, class) BOOL testUndeclaredDemands;
+@property (nonatomic, readonly, class) BOOL foundUndeclaredDemands;
+- (NSString *)cycleTimeStats;
+- (NSString *)sortTimeStats;
+@end
+
+NS_ASSUME_NONNULL_END
+
+#endif
diff --git a/doc-site/examples/objc/BGExamples/BehaviorGraph/BGProfiler.m b/doc-site/examples/objc/BGExamples/BehaviorGraph/BGProfiler.m
new file mode 100644
index 0000000..2f00cf5
--- /dev/null
+++ b/doc-site/examples/objc/BGExamples/BehaviorGraph/BGProfiler.m
@@ -0,0 +1,334 @@
+//
+// BGProfiler.m
+// YVideoSDK
+//
+// Created by James Lou on 6/10/19.
+// Copyright © 2019 Yahoo. All rights reserved.
+//
+
+#if DEBUG
+
+#import "BGProfiler.h"
+#import "BGGraph+Private.h"
+
+#import
+#include
+
+#define NS_PER_MS 1000000.0
+
+
+static BOOL assertUndeclaredDemands;
+static BOOL testUndeclaredDemands;
+static BOOL foundUndeclaredDemands;
+
+@interface BGCycleStats : NSObject
+@property (nonatomic) uint64_t totalTime;
+@property (nonatomic) uint64_t count;
+@property (nonatomic) uint64_t totalSortTime;
+@property (nonatomic) uint64_t sortCount;
+@end
+
+@implementation BGCycleStats
+@end
+
+@interface BGSortStats : NSObject
+@property (nonatomic) uint64_t totalTime;
+@property (nonatomic) uint64_t count;
+@end
+
+@implementation BGSortStats
+@end
+
+@interface BGProfiler ()
+
+@property (nonatomic, readonly) NSMutableDictionary *cycleStats;
+@property (nonatomic, readonly) uint64_t totalCycleTime;
+@property (nonatomic, readonly) uint64_t cycleCount;
+
+@property (nonatomic, readonly) NSMutableDictionary *sortStats;
+@property (nonatomic, readonly) uint64_t totalSortTime;
+@property (nonatomic, readonly) uint64_t sortCount;
+
+@property (nonatomic, readonly) NSMutableArray *currentCycleSortTimes;
+
+- (void)addCycleWithImpulse:(NSString *)impulse time:(uint64_t)time;
+- (void)addSortWithUnsortedCount:(uint64_t)unsortedCount time:(uint64_t)time;
+@end
+
+@interface BGResource (Profiler)
+@end
+
+@implementation BGResource (Profiler)
+
+- (void)profiler_verifyDemands {
+ // Looks at every access to ensure that things are being accessed properly in the graph.
+ BGBehavior *currentBehavior = self.behavior.graph.currentBehavior;
+
+ if (currentBehavior && self.behavior != currentBehavior && ![currentBehavior.demands containsObject:self]) {
+ foundUndeclaredDemands = YES;
+ if (assertUndeclaredDemands) {
+ NSAssert(NO, @"Resource %@ (%p) must be a demanded or supplied by behavior %@ (%p) to read its value or event",
+ self.staticDebugName, self, currentBehavior.staticDebugName, currentBehavior);
+ }
+ }
+}
+
+- (id)profiler_value {
+ [self profiler_verifyDemands];
+ return [self profiler_value];
+}
+
+- (BGEvent *)profiler_event {
+ [self profiler_verifyDemands];
+ return [self profiler_event];
+}
+
+@end
+
+@interface BGGraph ()
+- (void)orderBehaviors;
+- (void)processChanges:(dispatch_block_t)changeBlock impulse:(NSString *)impulse;
+@end
+
+@interface BGGraph (Profiler)
+@end
+
+@implementation BGGraph (Profiler)
+
+- (void)profiler_processChanges:(dispatch_block_t _Nonnull)changeBlock impulse:(NSString * _Nonnull)impulse {
+ mach_timebase_info_data_t info;
+ mach_timebase_info(&info);
+
+ uint64_t start = mach_absolute_time();
+ [self profiler_processChanges:changeBlock impulse:impulse];
+ uint64_t end = mach_absolute_time();
+
+ uint64_t duration = (end - start) * info.numer / info.denom;
+ [BGProfiler.sharedInstance addCycleWithImpulse:impulse time:duration];
+}
+
+- (void)profiler_orderBehaviors {
+ NSUInteger unsortedCount = self.needsOrdering.count;
+ if (unsortedCount > 0) {
+ mach_timebase_info_data_t info;
+ mach_timebase_info(&info);
+
+ uint64_t start = mach_absolute_time();
+ [self profiler_orderBehaviors];
+ uint64_t end = mach_absolute_time();
+
+ uint64_t duration = (end - start) * info.numer / info.denom;
+ [BGProfiler.sharedInstance addSortWithUnsortedCount:unsortedCount time:duration];
+ } else {
+ [self profiler_orderBehaviors];
+ }
+}
+
+@end
+
+@implementation BGProfiler
+
+static BGProfiler *sharedInstance;
+
++ (void)load {
+ sharedInstance = [BGProfiler new];
+
+ if ([NSProcessInfo.processInfo.arguments containsObject:@"-graphProfileTime"]) {
+ {
+ Method original = class_getInstanceMethod(BGGraph.class, NSSelectorFromString(@"processChanges:impulse:"));
+ Method swizzled = class_getInstanceMethod(BGGraph.class, @selector(profiler_processChanges:impulse:));
+ method_exchangeImplementations(original, swizzled);
+ }
+
+ {
+ Method original = class_getInstanceMethod(BGGraph.class, @selector(orderBehaviors));
+ Method swizzled = class_getInstanceMethod(BGGraph.class, @selector(profiler_orderBehaviors));
+ method_exchangeImplementations(original, swizzled);
+ }
+ }
+
+ assertUndeclaredDemands = [NSProcessInfo.processInfo.arguments containsObject:@"-graphVerifyDemands"];
+ testUndeclaredDemands = [NSProcessInfo.processInfo.environment[@"test_undeclared_demands"] isEqualToString:@"1"];
+
+ if (assertUndeclaredDemands || testUndeclaredDemands) {
+ {
+ Method original = class_getInstanceMethod(BGResource.class, @selector(value));
+ Method swizzled = class_getInstanceMethod(BGResource.class, @selector(profiler_value));
+ method_exchangeImplementations(original, swizzled);
+ }
+
+ {
+ Method original = class_getInstanceMethod(BGResource.class, @selector(event));
+ Method swizzled = class_getInstanceMethod(BGResource.class, @selector(profiler_event));
+ method_exchangeImplementations(original, swizzled);
+ }
+ }
+}
+
++ (BGProfiler *)sharedInstance {
+ return sharedInstance;
+}
+
++ (BOOL)testUndeclaredDemands {
+ return testUndeclaredDemands;
+}
+
++ (BOOL)foundUndeclaredDemands {
+ return foundUndeclaredDemands;
+}
+
+- (instancetype)init {
+ _cycleStats = [NSMutableDictionary new];
+ _sortStats = [NSMutableDictionary new];
+ _currentCycleSortTimes = [NSMutableArray new];
+ return self;
+}
+
+- (void)addCycleWithImpulse:(NSString *)impulse time:(uint64_t)time {
+ impulse = impulse ?: @"none";
+ BGCycleStats *stats = _cycleStats[impulse];
+ if (!stats) {
+ stats = [BGCycleStats new];
+ _cycleStats[impulse] = stats;
+ }
+ stats.totalTime += time;
+ ++stats.count;
+
+ _totalCycleTime += time;
+ ++_cycleCount;
+
+ for (NSNumber *sortTime in _currentCycleSortTimes) {
+ ++stats.sortCount;
+ stats.totalSortTime += sortTime.unsignedLongLongValue;
+ }
+ [_currentCycleSortTimes removeAllObjects];
+}
+
+- (void)addSortWithUnsortedCount:(uint64_t)unsortedCount time:(uint64_t)time {
+ BGSortStats *stats = _sortStats[@(unsortedCount)];
+ if (!stats) {
+ stats = [BGSortStats new];
+ _sortStats[@(unsortedCount)] = stats;
+ }
+ stats.totalTime += time;
+ ++stats.count;
+
+ _totalSortTime += time;
+ ++_sortCount;
+
+ [_currentCycleSortTimes addObject:@(time)];
+}
+
+- (NSString *)cycleTimeStats {
+ if (![[[NSProcessInfo processInfo] arguments] containsObject:@"-graphProfileTime"]) {
+ return @"Use run argument '-graphProfileTime' to enable behavior graph time profiling.";
+ }
+
+ NSMutableArray *lines = [NSMutableArray new];
+
+ NSString *(^newLine)(NSString *, NSString *, NSString *, NSString *, NSString *, NSString *) = ^NSString *(NSString *name, NSString *averageTime, NSString *totalTime, NSString *count, NSString *sortTime, NSString *sortCount) {
+ NSMutableString *string = [NSMutableString new];
+ [string appendString:name];
+ for (int i = 0; i < 140 - (NSInteger)name.length - (NSInteger)averageTime.length; ++i) {
+ [string appendString:@" "];
+ }
+ [string appendString:averageTime];
+
+ for (int i = 0; i < 20 - (NSInteger)totalTime.length; ++i) {
+ [string appendString:@" "];
+ }
+ [string appendString:totalTime];
+
+ for (int i = 0; i < 20 - (NSInteger)count.length; ++i) {
+ [string appendString:@" "];
+ }
+ [string appendString:count];
+
+ for (int i = 0; i < 20 - (NSInteger)sortTime.length; ++i) {
+ [string appendString:@" "];
+ }
+ [string appendString:sortTime];
+
+ for (int i = 0; i < 20 - (NSInteger)sortCount.length; ++i) {
+ [string appendString:@" "];
+ }
+ [string appendString:sortCount];
+
+ return string;
+ };
+
+ [lines addObject:newLine(@"Impulse", @"Avg (ms)", @"Total (ms)", @"Count", @"Sort Time", @"Sort Count")];
+ for (NSString *name in [_cycleStats.allKeys sortedArrayUsingComparator:^NSComparisonResult(NSString * _Nonnull obj1, NSString * _Nonnull obj2) {
+ NSTimeInterval time1 = ({ BGCycleStats *stats = _cycleStats[obj1]; stats.totalTime / (double)stats.count; });
+ NSTimeInterval time2 = ({ BGCycleStats *stats = _cycleStats[obj2]; stats.totalTime / (double)stats.count; });
+ if (time1 > time2) {
+ return NSOrderedAscending;
+ } else if (time1 < time2) {
+ return NSOrderedDescending;
+ } else {
+ return NSOrderedSame;
+ }
+ }]) {
+ BGCycleStats *cycle = _cycleStats[name];
+ [lines addObject:newLine(name, [NSString stringWithFormat:@"%f", cycle.totalTime / 1000000 / (double)cycle.count], [NSString stringWithFormat:@"%f", cycle.totalTime / 1000000.0], [NSString stringWithFormat:@"%llu", cycle.count], [NSString stringWithFormat:@"%f", cycle.totalSortTime / NS_PER_MS], [NSString stringWithFormat:@"%llu", cycle.sortCount])];
+ }
+ [lines addObject:newLine(@"Total", _cycleCount > 0 ? [NSString stringWithFormat:@"%f", _totalCycleTime / 1000000 / (double)_cycleCount] : @"0", [NSString stringWithFormat:@"%f", _totalCycleTime / 1000000.0], [NSString stringWithFormat:@"%llu", _cycleCount], [NSString stringWithFormat:@"%f", _totalSortTime / NS_PER_MS], [NSString stringWithFormat:@"%llu", _sortCount])];
+
+ return [@"\n" stringByAppendingString:[lines componentsJoinedByString:@"\n"]];
+}
+
+- (NSString *)sortTimeStats {
+ if (![[[NSProcessInfo processInfo] arguments] containsObject:@"-graphProfileTime"]) {
+ return @"Use run argument '-graphProfileTime' to enable behavior graph time profiling.";
+ }
+
+ NSMutableArray *lines = [NSMutableArray new];
+
+ NSString *(^newLine)(NSString *, NSString *, NSString *, NSString *) = ^NSString *(NSString *unsortedCount, NSString *averageTime, NSString *totalTime, NSString *count) {
+ NSMutableString *string = [NSMutableString new];
+ for (int i = 0; i < 20 - (NSInteger)unsortedCount.length; ++i) {
+ [string appendString:@" "];
+ }
+ [string appendString:unsortedCount];
+
+ for (int i = 0; i < 20 - (NSInteger)averageTime.length; ++i) {
+ [string appendString:@" "];
+ }
+ [string appendString:averageTime];
+
+ for (int i = 0; i < 20 - (NSInteger)totalTime.length; ++i) {
+ [string appendString:@" "];
+ }
+ [string appendString:totalTime];
+
+ for (int i = 0; i < 20 - (NSInteger)count.length; ++i) {
+ [string appendString:@" "];
+ }
+ [string appendString:count];
+
+ return string;
+ };
+
+ [lines addObject:newLine(@"# Behaviors Sorted", @"Avg (ms)", @"Total (ms)", @"Count")];
+ for (NSNumber *unsortedCount in [_sortStats.allKeys sortedArrayUsingComparator:^NSComparisonResult(NSNumber * _Nonnull obj1, NSNumber * _Nonnull obj2) {
+ NSTimeInterval time1 = ({ BGSortStats *stats = _sortStats[obj1]; stats.totalTime / (double)stats.count; });
+ NSTimeInterval time2 = ({ BGSortStats *stats = _sortStats[obj2]; stats.totalTime / (double)stats.count; });
+ if (time1 > time2) {
+ return NSOrderedAscending;
+ } else if (time1 < time2) {
+ return NSOrderedDescending;
+ } else {
+ return NSOrderedSame;
+ }
+ }]) {
+ BGSortStats *cycle = _sortStats[unsortedCount];
+ [lines addObject:newLine([NSString stringWithFormat:@"%llu", unsortedCount.unsignedLongLongValue], [NSString stringWithFormat:@"%f", cycle.totalTime / NS_PER_MS / (double)cycle.count], [NSString stringWithFormat:@"%f", cycle.totalTime / NS_PER_MS], [NSString stringWithFormat:@"%llu", cycle.count])];
+ }
+ [lines addObject:newLine(@"Total", _sortCount > 0 ? [NSString stringWithFormat:@"%f", _totalSortTime / NS_PER_MS / (double)_sortCount] : @"0", [NSString stringWithFormat:@"%f", _totalSortTime / NS_PER_MS], [NSString stringWithFormat:@"%llu", _sortCount])];
+
+ return [@"\n" stringByAppendingString:[lines componentsJoinedByString:@"\n"]];
+}
+
+@end
+
+#endif
diff --git a/doc-site/examples/objc/BGExamples/ChatExtent.h b/doc-site/examples/objc/BGExamples/ChatExtent.h
new file mode 100644
index 0000000..65ff10c
--- /dev/null
+++ b/doc-site/examples/objc/BGExamples/ChatExtent.h
@@ -0,0 +1,33 @@
+//
+// ChatExtent.h
+// BGExamples
+//
+// Created by Sean Levin on 5/13/20.
+// Copyright © 2020 Verizon Media. All rights reserved.
+//
+
+#import "BGGraph.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+@class ParticipantExtent;
+
+// @tag::chat_extent[]
+@class ChatExtent;
+@interface ChatExtent : BGExtent
+// @end::chat_extent[]
+
+@property (nonatomic, readonly) BGMoment *participantJoined;
+@property (nonatomic, readonly) BGMoment *participantDisconnected;
+
+// @tag::chat_participants_resources[]
+@property (nonatomic, readonly) BGState *participants;
+// @end::chat_participants_resources[]
+@property (nonatomic, readonly) BGState *pinnedParticipant;
+
+@property (nonatomic, readonly) BGResource *participantsRelink;
+@property (nonatomic, readonly) BGBehavior *pinnedBehavior;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/doc-site/examples/objc/BGExamples/ChatExtent.m b/doc-site/examples/objc/BGExamples/ChatExtent.m
new file mode 100644
index 0000000..e592be6
--- /dev/null
+++ b/doc-site/examples/objc/BGExamples/ChatExtent.m
@@ -0,0 +1,81 @@
+//
+// ChatExtent.m
+// BGExamples
+//
+// Created by Sean Levin on 5/13/20.
+// Copyright © 2020 Verizon Media. All rights reserved.
+//
+
+#import "ChatExtent.h"
+#import "ParticipantExtent.h"
+
+@implementation ChatExtent
+
+- (instancetype)initWithGraph:(BGGraph *)graph {
+ self = [super initWithGraph:graph];
+ if (self) {
+
+
+ // tag::participants[]
+ _participantJoined = [self moment];
+ _participantDisconnected = [self moment];
+ _participants = [self stateWithValue:[NSMutableDictionary new]];
+ [self behaviorWithDemands:@[_participantJoined, _participantDisconnected] supplies:@[_participants] runBlock:^(ChatExtent * _Nonnull extent) {
+
+ if (extent.participantJoined.justUpdated) {
+ NSString *participantId = extent.participantJoined.value;
+ ParticipantExtent *participant = [[ParticipantExtent alloc] initWithGraph:extent.graph participantId:participantId chat:extent];
+ [participant addToGraph];
+ extent.participants.value[participantId] = participant;
+ [extent.participants updateValueForce:extent.participants.value];
+ }
+
+ if (extent.participantDisconnected.justUpdated) {
+ NSString *participantId = extent.participantDisconnected.value;
+ ParticipantExtent *participant = extent.participants.value[participantId];
+ [participant removeFromGraph];
+ extent.participants.value[participantId] = nil;
+ [extent.participants updateValueForce:extent.participants.value];
+ }
+
+ }];
+ // end::participants[]
+
+ // tag::chat_relink_pinned[]
+ _participantsRelink = [self resource];
+ [self behaviorWithDemands:@[_participants] supplies:@[_participantsRelink] runBlock:^(ChatExtent * _Nonnull extent) {
+ NSMutableArray *demands = [NSMutableArray new];
+ [demands addObject:extent.participants];
+ [demands addObject:extent.participantsRelink];
+ for (ParticipantExtent *p in extent.participants.value) {
+ [demands addObject:p.pinTap];
+ }
+ [extent.pinnedBehavior setDemands:demands];
+ }];
+ // end::chat_relink_pinned[]
+
+ // tag::chat_pinned[]
+ _pinnedParticipant = [self stateWithValue:nil];
+ _pinnedBehavior = [self behaviorWithDemands:@[_participants, _participantsRelink] supplies:@[_pinnedParticipant] runBlock:^(ChatExtent * _Nonnull extent) {
+
+ ParticipantExtent *currentPinned = extent.pinnedParticipant.value;
+ ParticipantExtent *newPinned = nil;
+ for (ParticipantExtent *participant in extent.participants.value.allValues) {
+ if (participant.pinTap.justUpdated) {
+ newPinned = participant;
+ break;
+ } else if (participant == currentPinned) {
+ newPinned = currentPinned;
+ }
+ }
+
+ [extent.pinnedParticipant updateValue:newPinned];
+
+ }];
+ // end::chat_pinned[]
+
+ }
+ return self;
+}
+
+@end
diff --git a/doc-site/examples/objc/BGExamples/ImperativeLoginPageViewController.h b/doc-site/examples/objc/BGExamples/ImperativeLoginPageViewController.h
new file mode 100644
index 0000000..fb5d41a
--- /dev/null
+++ b/doc-site/examples/objc/BGExamples/ImperativeLoginPageViewController.h
@@ -0,0 +1,22 @@
+//
+// ImperativeLoginPageViewController.h
+// BGExamples
+//
+// Created by Sean Levin on 3/18/20.
+// Copyright © 2020 Verizon Media. All rights reserved.
+//
+
+#import
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface ImperativeLoginPageViewController : UIViewController
+
+@property (nonatomic) IBOutlet UITextField *emailField;
+@property (nonatomic) IBOutlet UITextField *passwordField;
+@property (nonatomic) IBOutlet UIButton *loginButton;
+
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/doc-site/examples/objc/BGExamples/ImperativeLoginPageViewController.m b/doc-site/examples/objc/BGExamples/ImperativeLoginPageViewController.m
new file mode 100644
index 0000000..91cbd86
--- /dev/null
+++ b/doc-site/examples/objc/BGExamples/ImperativeLoginPageViewController.m
@@ -0,0 +1,41 @@
+//
+// ImperativeLoginPageViewController.m
+// BGExamples
+//
+// Created by Sean Levin on 3/18/20.
+// Copyright © 2020 Verizon Media. All rights reserved.
+//
+
+#import "ImperativeLoginPageViewController.h"
+
+@interface ImperativeLoginPageViewController ()
+
+@end
+
+@implementation ImperativeLoginPageViewController
+
+- (BOOL)validEmailAddress:(NSString *)email {
+ return email.length > 0 && [email containsString:@"@"];
+}
+
+- (void)viewDidLoad {
+ [super viewDidLoad];
+ // Do any additional setup after loading the view.
+}
+
+- (IBAction)didUpdateEmailField:(id)sender {
+ [self checkEnableLoginButton];
+}
+
+- (IBAction)didUpdatePasswordField:(id)sender {
+ [self checkEnableLoginButton];
+}
+
+- (void)checkEnableLoginButton {
+ NSString *email = self.emailField.text;
+ NSString *password = self.passwordField.text;
+ BOOL hasPassword = password.length > 0;
+ self.loginButton.enabled = [self validEmailAddress:email] && hasPassword;
+}
+
+@end
diff --git a/doc-site/examples/objc/BGExamples/Info.plist b/doc-site/examples/objc/BGExamples/Info.plist
new file mode 100644
index 0000000..7b6037c
--- /dev/null
+++ b/doc-site/examples/objc/BGExamples/Info.plist
@@ -0,0 +1,64 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ $(DEVELOPMENT_LANGUAGE)
+ CFBundleExecutable
+ $(EXECUTABLE_NAME)
+ CFBundleIdentifier
+ $(PRODUCT_BUNDLE_IDENTIFIER)
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ $(PRODUCT_NAME)
+ CFBundlePackageType
+ $(PRODUCT_BUNDLE_PACKAGE_TYPE)
+ CFBundleShortVersionString
+ 1.0
+ CFBundleVersion
+ 1
+ LSRequiresIPhoneOS
+
+ UIApplicationSceneManifest
+
+ UIApplicationSupportsMultipleScenes
+
+ UISceneConfigurations
+
+ UIWindowSceneSessionRoleApplication
+
+
+ UISceneConfigurationName
+ Default Configuration
+ UISceneDelegateClassName
+ SceneDelegate
+ UISceneStoryboardFile
+ Main
+
+
+
+
+ UILaunchStoryboardName
+ LaunchScreen
+ UIMainStoryboardFile
+ Main
+ UIRequiredDeviceCapabilities
+
+ armv7
+
+ UISupportedInterfaceOrientations
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+ UISupportedInterfaceOrientations~ipad
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationPortraitUpsideDown
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+
+
diff --git a/doc-site/examples/objc/BGExamples/LoginExtent.h b/doc-site/examples/objc/BGExamples/LoginExtent.h
new file mode 100644
index 0000000..4b1dded
--- /dev/null
+++ b/doc-site/examples/objc/BGExamples/LoginExtent.h
@@ -0,0 +1,31 @@
+//
+// LoginExtent.h
+// BGExamples
+//
+// Created by Sean Levin on 3/18/20.
+// Copyright © 2020 Verizon Media. All rights reserved.
+//
+
+#import "BGGraph.h"
+#import "LoginPageViewController.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+// tag::login_enable_extent[]
+@class LoginExtent;
+@interface LoginExtent : BGExtent
+@property (nonatomic, readonly) BGState *email;
+@property (nonatomic, readonly) BGState *password;
+// end::login_enable_extent[]
+
+@property (nonatomic, readonly) BGMoment *loginClick;
+@property (nonatomic, readonly) BGMoment *returnKey;
+@property (nonatomic, readonly) BGState *emailValid;
+@property (nonatomic, readonly) BGState *passwordValid;
+@property (nonatomic, readonly) BGState *loginEnabled;
+@property (nonatomic, readonly) BGState *loggingIn;
+@property (nonatomic, readonly) BGMoment *loginComplete;
+@property (nonatomic, readwrite, weak) LoginPageViewController *login;
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/doc-site/examples/objc/BGExamples/LoginExtent.m b/doc-site/examples/objc/BGExamples/LoginExtent.m
new file mode 100644
index 0000000..ec0d84e
--- /dev/null
+++ b/doc-site/examples/objc/BGExamples/LoginExtent.m
@@ -0,0 +1,244 @@
+//
+// LoginExtent.m
+// BGExamples
+//
+// Created by Sean Levin on 3/18/20.
+// Copyright © 2020 Verizon Media. All rights reserved.
+//
+
+#import "LoginExtent.h"
+
+@implementation LoginExtent
+
+/*
+- (instancetype)init {
+ self = [super init];
+ if (self) {
+
+
+ }
+ return self;
+}
+*/
+ // tag::login_enable_init[]
+- (instancetype)initWithGraph:(BGGraph *)graph {
+ self = [super initWithGraph:graph];
+
+ _email = [self stateWithValue:@""];
+ _password = [self stateWithValue:@""];
+
+ // tag::login_enable_behavior[]
+ [self behaviorWithDemands:@[self.email, self.password]
+ supplies:nil
+ runBlock:^(LoginExtent * _Nonnull extent) {
+ // end::login_enable_init[]
+
+ NSString *email = extent.email.value;
+ NSString *password = extent.password.value;
+ BOOL hasPassword = password.length > 0;
+ BOOL loginEnabled = [extent validEmailAddress:email] && hasPassword;
+ [extent sideEffect:@"enable login button" runBlock:^(LoginExtent * _Nonnull extent) {
+ extent.login.loginButton.enabled = loginEnabled;
+ }];
+
+ }];
+ // end::login_enable_behavior[]
+
+ return self;
+}
+
+- (void)initBehaviorGraphComplete {
+
+ _loggingIn = [self stateWithValue:@NO];
+
+ // tag::login_complete_email[]
+ _emailValid = [self stateWithValue:@NO];
+ [self behaviorWithDemands:@[self.email]
+ supplies:@[self.emailValid]
+ runBlock:^(LoginExtent * _Nonnull extent) {
+
+ NSString *email = extent.email.value;
+ BOOL emailValid = [self validEmailAddress:email];
+ [extent.emailValid updateValue:@(emailValid)];
+
+ }];
+ // end::login_complete_email[]
+
+ _passwordValid = [self stateWithValue:@NO];
+ [self behaviorWithDemands:@[self.password]
+ supplies:@[self.passwordValid]
+ runBlock:^(LoginExtent * _Nonnull extent) {
+
+ NSString *password = extent.password.value;
+ BOOL passwordValid = password.length > 0;
+ [extent.passwordValid updateValue:@(passwordValid)];
+
+ }];
+
+
+ // tag::login_complete_enable[]
+ _loginEnabled = [self stateWithValue:@NO];
+ [self behaviorWithDemands:@[self.emailValid, self.passwordValid, self.loggingIn]
+ supplies:@[self.loginEnabled]
+ runBlock:^(LoginExtent * _Nonnull extent) {
+
+ BOOL enabled = (extent.emailValid.value.boolValue &&
+ extent.passwordValid.value.boolValue &&
+ !extent.loggingIn.value.boolValue);
+ [extent.loginEnabled updateValue:@(enabled)];
+ [extent sideEffect:@"enable login button" runBlock:^(LoginExtent * _Nonnull extent) {
+ extent.login.loginButton.enabled = extent.loginEnabled.value.boolValue;
+ }];
+
+ }];
+ // end::login_complete_enable[]
+
+ // tag::login_complete_login[]
+ _loginClick = [self moment];
+ _returnKey = [self moment];
+ _loginComplete = [self moment];
+ [self behaviorWithDemands:@[self.loginClick, self.returnKey, self.loginComplete]
+ supplies:@[self.loggingIn]
+ runBlock:^(LoginExtent * _Nonnull extent) {
+
+ if ((extent.loginClick.justUpdated || extent.returnKey.justUpdated) &&
+ extent.loginEnabled.traceValue.boolValue) {
+ // Start login
+ [extent.loggingIn updateValue:@YES];
+ } else if (extent.loginComplete.justUpdated &&
+ extent.loggingIn.value.boolValue) {
+ // Complete login
+ [extent.loggingIn updateValue:@NO];
+ }
+
+ if ([extent.loggingIn justUpdatedTo:@YES]) {
+ [extent sideEffect:@"login api call" runBlock:^(LoginExtent * _Nonnull extent) {
+ [extent login:extent.email.value password:extent.password.value complete:^(BOOL success) {
+ [extent action:@"login complete" runBlock:^{
+ [extent.loginComplete updateValue:@(success)];
+ }];
+ }];
+ }];
+ }
+
+ }];
+ // end::login_complete_login[]
+
+ /*
+ // Dont delete; its used in the documentation
+ // this has an example of requireSync
+
+ [self behaviorWithDemands:@[self.loginClick, self.returnKey, self.loginComplete]
+ supplies:@[self.loggingIn]
+ runBlock:^(LoginExtent t* _Nonnull extent) {
+
+ if ((extent.loginClick.justHappened || extent.returnKey.justHappened) &&
+ extent.loginEnabled.traceValue.boolValue) {
+ // Start login
+ [extent.loggingIn updateValue:@YES];
+ } else if (extent.loginComplete.justHappened &&
+ extent.loggingIn.value.boolValue) {
+ // Complete login
+ [extent.loggingIn updateValue:@NO];
+ }
+
+ if ([extent.loggingIn justChangedTo:@YES]) {
+ // tag::login_complete_loginalt[]
+ [extent sideEffect:@"login api call" block:^(LoginExtent * _Nonnull extent) {
+ [extent login:extent.email.value password:extent.password.value complete:^(BOOL success) {
+ [extent.graph action:@"login complete" requireSync:NO runBlock:^{
+ [extent.loginComplete happen:@(success)];
+ }];
+ }];
+ }];
+ // end::login_complete_loginalt[]
+ }
+
+ }];
+ */
+}
+
+- (instancetype)initShortWithGraph:(BGGraph *)graph {
+ // tag::login_intro_short1[]
+ [self behaviorWithDemands:@[self.email, self.password]
+ supplies:@[self.loginEnabled]
+ runBlock:^(LoginExtent *extent) {
+
+ BOOL emailValid = [extent validEmailAddress:extent.email.value];
+ BOOL passwordValid = extent.password.value.length > 0;
+ BOOL enabled = emailValid && passwordValid;
+ [extent.loginEnabled updateValue:@(enabled)];
+
+ }];
+ // end::login_intro_short1[]
+
+ // tag::login_intro_short2[]
+ [self behaviorWithDemands:@[self.loginClick]
+ supplies:@[self.loggingIn]
+ runBlock:^(LoginExtent * _Nonnull extent) {
+ if (extent.loginClick.justUpdated && !extent.loggingIn.value.boolValue) {
+ [extent.loggingIn updateValue:@YES];
+ }
+ }];
+
+ [self behaviorWithDemands:@[self.email, self.password, self.loggingIn]
+ supplies:@[self.loginEnabled]
+ runBlock:^(LoginExtent *extent) {
+
+ BOOL emailValid = [extent validEmailAddress:extent.email.value];
+ BOOL passwordValid = extent.password.value.length > 0;
+ BOOL enabled = emailValid && passwordValid && !self.loggingIn.value.boolValue;
+ [extent.loginEnabled updateValue:@(enabled)];
+
+ }];
+ // end::login_intro_short2[]
+
+
+ // tag::login_intro_sideeffect[]
+ [self behaviorWithDemands:@[self.email, self.password, self.loggingIn]
+ supplies:@[self.loginEnabled]
+ runBlock:^(LoginExtent *extent) {
+
+ BOOL emailValid = [extent validEmailAddress:extent.email.value];
+ BOOL passwordValid = extent.password.value.length > 0;
+ BOOL enabled = emailValid && passwordValid && !self.loggingIn.value.boolValue;
+ [extent.loginEnabled updateValue:@(enabled)];
+
+ [extent sideEffect:@"enable login button" runBlock:^(LoginExtent * _Nonnull extent) {
+ extent.loginButton.enabled = extent.loginEnabled.value.boolValue;
+ }];
+ }];
+ // end::login_intro_sideeffect[]
+
+ return self;
+}
+
+// tag::login_intro_action[]
+- (void)loginButtonClicked:(id)sender {
+ [self.graph action:@"loginButtonClicked" runBlock:^{
+ [self.loginClick update];
+ }];
+}
+// end::login_intro_action[]
+
+- (BOOL)validEmailAddress:(NSString *)email {
+ return email.length > 0 && [email containsString:@"@"];
+}
+
+- (void)login:(NSString *)email password:(NSString *)password complete:(void(^)(BOOL success))complete {
+ // some login api
+}
+
+// tag::login_sequence_compare[]
+- (BOOL)emailChangedSincePassword {
+ return self.email.event.sequence > self.password.event.sequence;
+}
+// end::login_sequence_compare[]
+
+// tag::login_timestamp[]
+- (NSDate *)loginCompletedWhen {
+ return self.loginComplete.event.timestamp;
+}
+// end::login_timestamp[]
+
+@end
diff --git a/doc-site/examples/objc/BGExamples/LoginPageViewController.h b/doc-site/examples/objc/BGExamples/LoginPageViewController.h
new file mode 100644
index 0000000..e67ecc3
--- /dev/null
+++ b/doc-site/examples/objc/BGExamples/LoginPageViewController.h
@@ -0,0 +1,23 @@
+//
+// LoginPageViewController.h
+// BGExamples
+//
+// Created by Sean Levin on 3/18/20.
+// Copyright © 2020 Verizon Media. All rights reserved.
+//
+
+#import
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface LoginPageViewController : UIViewController
+
+@property (nonatomic) IBOutlet UITextField *emailField;
+@property (nonatomic) IBOutlet UITextField *passwordField;
+@property (nonatomic) IBOutlet UIButton *loginButton;
+
+@property (nonatomic) IBOutlet UILabel *emailFeedback;
+@property (nonatomic) IBOutlet UILabel *passwordFeedback;
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/doc-site/examples/objc/BGExamples/LoginPageViewController.m b/doc-site/examples/objc/BGExamples/LoginPageViewController.m
new file mode 100644
index 0000000..59bed9d
--- /dev/null
+++ b/doc-site/examples/objc/BGExamples/LoginPageViewController.m
@@ -0,0 +1,62 @@
+//
+// LoginPageViewController.m
+// BGExamples
+//
+// Created by Sean Levin on 3/18/20.
+// Copyright © 2020 Verizon Media. All rights reserved.
+//
+
+#import "LoginPageViewController.h"
+#import "BGGraph.h"
+#import "LoginExtent.h"
+
+@interface LoginPageViewController ()
+@property (nonatomic) BGGraph *graph;
+@property (nonatomic) LoginExtent *loginExtent;
+
+@end
+
+@implementation LoginPageViewController
+
+- (instancetype)initWithCoder:(NSCoder *)coder {
+ self = [super initWithCoder:coder];
+ if (self) {
+ // tag::login_enable_setup[]
+ _graph = [[BGGraph alloc] init];
+ _loginExtent = [[LoginExtent alloc] initWithGraph:_graph];
+ [_graph action:@"new login page" runBlock:^{
+ [self.loginExtent addToGraph];
+ }];
+ // end::login_enable_setup[]
+ }
+ return self;
+}
+
+- (void)viewDidLoad {
+ [super viewDidLoad];
+ // Do any additional setup after loading the view.
+}
+
+// tag::login_enable_actions[]
+- (IBAction)didUpdateEmailField:(id)sender {
+ [self.graph action:@"update email" runBlock:^{
+ [self.loginExtent.email updateValue:self.emailField.text];
+ }];
+}
+
+- (IBAction)didUpdatePasswordField:(id)sender {
+ [self.graph action:@"update password" runBlock:^{
+ [self.loginExtent.password updateValue:self.passwordField.text];
+ }];
+}
+// end::login_enable_actions[]
+
+// tag::login_complete_click[]
+- (IBAction)loginButtonClicked:(id)sender {
+ [self.graph action:@"login button" runBlock:^{
+ [self.loginExtent.loginClick update];
+ }];
+}
+// end::login_complete_click[]
+
+@end
diff --git a/doc-site/examples/objc/BGExamples/ParticipantExtent.h b/doc-site/examples/objc/BGExamples/ParticipantExtent.h
new file mode 100644
index 0000000..c3cfb87
--- /dev/null
+++ b/doc-site/examples/objc/BGExamples/ParticipantExtent.h
@@ -0,0 +1,38 @@
+//
+// ParticipantExtent.h
+// BGExamples
+//
+// Created by Sean Levin on 5/13/20.
+// Copyright © 2020 Verizon Media. All rights reserved.
+//
+
+#import "BGGraph.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+@class ChatExtent;
+
+// @tag::participant_extent[]
+@class ParticipantExtent;
+@interface ParticipantExtent : BGExtent
+// @end::participant_extent[]
+
+@property (nonatomic, weak, readonly) ChatExtent *chatExtent;
+// @tag::participant_mute_resources[]
+@property (nonatomic, readonly) BGMoment *muteTap;
+@property (nonatomic, readonly) BGState *muted;
+// @end::participant_mute_resources[]
+
+// @tag::participant_pin_resources[]
+@property (nonatomic, readonly) BGMoment *pinTap;
+// @end::participant_pin_resources[]
+
+@property (nonatomic) BGBehavior *muteBehavior;
+
+@property (nonatomic, readonly) NSString *participantId;
+
+- (instancetype)initWithGraph:(BGGraph *)graph participantId:(NSString *)participantId chat:(ChatExtent *)chatExtent;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/doc-site/examples/objc/BGExamples/ParticipantExtent.m b/doc-site/examples/objc/BGExamples/ParticipantExtent.m
new file mode 100644
index 0000000..f6f498c
--- /dev/null
+++ b/doc-site/examples/objc/BGExamples/ParticipantExtent.m
@@ -0,0 +1,79 @@
+//
+// ParticipantExtent.m
+// BGExamples
+//
+// Created by Sean Levin on 5/13/20.
+// Copyright © 2020 Verizon Media. All rights reserved.
+//
+
+#import "ParticipantExtent.h"
+#import "ChatExtent.h"
+
+@implementation ParticipantExtent
+
+- (instancetype)initWithGraph:(BGGraph *)graph participantId:(NSString *)participantId chat:(ChatExtent *)chatExtent {
+ self = [super initWithGraph:graph];
+ if (self) {
+
+ _chatExtent = chatExtent;
+ _participantId = participantId;
+
+ // tag::participant_mute[]
+ _muteTap = [self moment];
+ _muted = [self stateWithValue:@NO];
+ [self behaviorWithDemands:@[_muteTap] supplies:@[_muted] runBlock:^(ParticipantExtent * _Nonnull extent) {
+ if (extent.muteTap.justUpdated) {
+ [extent.muted updateValue:@(!extent.muted.value.boolValue)];
+ if (extent.muted.justUpdated) {
+ [extent sideEffect:@"mute toggle" runBlock:^(ParticipantExtent * _Nonnull extent) {
+ [extent muteParticipant:extent.muted.value.boolValue];
+ [extent updateMuteUI:extent.muted.value.boolValue];
+ }];
+ }
+ }
+ }];
+ // end::participant_mute[]
+
+ _pinTap = [self moment];
+ // tag::participant_pinned[]
+ [self behaviorWithDemands:@[_chatExtent.pinnedParticipant] supplies:nil runBlock:^(ParticipantExtent * _Nonnull extent) {
+
+ if ([extent.chatExtent.pinnedParticipant justUpdatedTo:extent]) {
+ [extent sideEffect:@"show as pinned" runBlock:^(ParticipantExtent * _Nonnull extent) {
+ [extent updatePinUI:YES];
+ }];
+ } else if ([extent.chatExtent.pinnedParticipant justUpdatedFrom:extent]) {
+ [extent sideEffect:@"show as normal" runBlock:^(ParticipantExtent * _Nonnull extent) {
+ [extent updatePinUI:NO];
+ }];
+ }
+ }];
+ // end::participant_pinned[]
+
+ }
+ return self;
+}
+
+- (void)alts {
+ // tag::participant_mute_alt[]
+ _muteBehavior = [self behaviorWithDemands:@[_muteTap] supplies:@[_muted] //...
+ // end::participant_mute_alt[]
+ runBlock:^(ParticipantExtent * _Nonnull extent) {
+
+ }];
+
+}
+
+- (void)updatePinUI:(BOOL)pinned {
+
+}
+
+- (void)updateMuteUI:(BOOL)mute {
+ // update ui
+}
+
+- (void)muteParticipant:(BOOL)mute {
+ // external api call
+}
+
+@end
diff --git a/doc-site/examples/objc/BGExamples/SceneDelegate.h b/doc-site/examples/objc/BGExamples/SceneDelegate.h
new file mode 100644
index 0000000..fe7e723
--- /dev/null
+++ b/doc-site/examples/objc/BGExamples/SceneDelegate.h
@@ -0,0 +1,16 @@
+//
+// SceneDelegate.h
+// BGExamples
+//
+// Created by Sean Levin on 3/6/20.
+// Copyright © 2020 Verizon Media. All rights reserved.
+//
+
+#import
+
+@interface SceneDelegate : UIResponder
+
+@property (strong, nonatomic) UIWindow * window;
+
+@end
+
diff --git a/doc-site/examples/objc/BGExamples/SceneDelegate.m b/doc-site/examples/objc/BGExamples/SceneDelegate.m
new file mode 100644
index 0000000..d6698dd
--- /dev/null
+++ b/doc-site/examples/objc/BGExamples/SceneDelegate.m
@@ -0,0 +1,50 @@
+#import "SceneDelegate.h"
+
+@interface SceneDelegate ()
+
+@end
+
+@implementation SceneDelegate
+
+
+- (void)scene:(UIScene *)scene willConnectToSession:(UISceneSession *)session options:(UISceneConnectionOptions *)connectionOptions {
+ // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
+ // If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
+ // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).
+}
+
+
+- (void)sceneDidDisconnect:(UIScene *)scene {
+ // Called as the scene is being released by the system.
+ // This occurs shortly after the scene enters the background, or when its session is discarded.
+ // Release any resources associated with this scene that can be re-created the next time the scene connects.
+ // The scene may re-connect later, as its session was not neccessarily discarded (see `application:didDiscardSceneSessions` instead).
+}
+
+
+- (void)sceneDidBecomeActive:(UIScene *)scene {
+ // Called when the scene has moved from an inactive state to an active state.
+ // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive.
+}
+
+
+- (void)sceneWillResignActive:(UIScene *)scene {
+ // Called when the scene will move from an active state to an inactive state.
+ // This may occur due to temporary interruptions (ex. an incoming phone call).
+}
+
+
+- (void)sceneWillEnterForeground:(UIScene *)scene {
+ // Called as the scene transitions from the background to the foreground.
+ // Use this method to undo the changes made on entering the background.
+}
+
+
+- (void)sceneDidEnterBackground:(UIScene *)scene {
+ // Called as the scene transitions from the foreground to the background.
+ // Use this method to save data, release shared resources, and store enough scene-specific state information
+ // to restore the scene back to its current state.
+}
+
+
+@end
diff --git a/doc-site/examples/objc/BGExamples/SigngupPageViewController.h b/doc-site/examples/objc/BGExamples/SigngupPageViewController.h
new file mode 100644
index 0000000..dba437c
--- /dev/null
+++ b/doc-site/examples/objc/BGExamples/SigngupPageViewController.h
@@ -0,0 +1,27 @@
+//
+// SigngupPageViewController.h
+// BGExamples
+//
+// Created by Sean Levin on 3/6/20.
+// Copyright © 2020 Verizon Media. All rights reserved.
+//
+
+#import
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface SigngupPageViewController : UIViewController
+
+@property (nonatomic) IBOutlet UILabel *usernameErrorLabel;
+@property (nonatomic) IBOutlet UITextField *usernameTextField;
+@property (nonatomic) IBOutlet UILabel *passwordErrorLabel;
+@property (nonatomic) IBOutlet UITextField *password1Field;
+@property (nonatomic) IBOutlet UITextField *password2Field;
+@property (nonatomic) IBOutlet UIButton *signupButton;
+
+
+- (void)startSignupWithUsername:(NSString *)username password:(NSString *)password;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/doc-site/examples/objc/BGExamples/SigngupPageViewController.m b/doc-site/examples/objc/BGExamples/SigngupPageViewController.m
new file mode 100644
index 0000000..983bed5
--- /dev/null
+++ b/doc-site/examples/objc/BGExamples/SigngupPageViewController.m
@@ -0,0 +1,57 @@
+//
+// SigngupPageViewController.m
+// BGExamples
+//
+// Created by Sean Levin on 3/6/20.
+// Copyright © 2020 Verizon Media. All rights reserved.
+//
+
+#import "SigngupPageViewController.h"
+#import "BGGraph.h"
+#import "SignupExtent.h"
+
+@interface SigngupPageViewController ()
+@property (nonatomic) BGGraph *graph;
+@property (nonatomic) SignupExtent *signupExtent;
+@end
+
+@implementation SigngupPageViewController
+
+- (void)viewDidLoad {
+ [super viewDidLoad];
+
+ _graph = [[BGGraph alloc] init];
+ _signupExtent = [[SignupExtent alloc] initWithGraph:_graph];
+ _signupExtent.signup = self;
+ [_graph action:@"initial setup" runBlock:^{
+ [self.signupExtent addToGraph];
+ }];
+}
+
+- (void)startSignupWithUsername:(NSString *)username password:(NSString *)password {
+ NSLog(@"Signup api call sent.");
+}
+
+- (IBAction)didTapSubmitButton:(id)sender {
+ [self.graph action:_fname runBlock:^{
+ [self.signupExtent.signupButtonClicked update];
+ }];
+}
+
+- (IBAction)didUpdateUsernameField:(id)sender {
+ [self.graph action:_fname runBlock:^{
+ [self.signupExtent.username updateValue:self.usernameTextField.text];
+ }];
+}
+
+- (IBAction)didUpdatePasswordField:(id)sender {
+ [self.graph action:_fname runBlock:^{
+ if (sender == self.password1Field) {
+ [self.signupExtent.password1 updateValue:self.password1Field.text];
+ } else if (sender == self.password2Field) {
+ [self.signupExtent.password2 updateValue:self.password2Field.text];
+ }
+ }];
+}
+
+@end
diff --git a/doc-site/examples/objc/BGExamples/SignupExtent.h b/doc-site/examples/objc/BGExamples/SignupExtent.h
new file mode 100644
index 0000000..570c3c5
--- /dev/null
+++ b/doc-site/examples/objc/BGExamples/SignupExtent.h
@@ -0,0 +1,31 @@
+//
+// SignupExtent.h
+// BGExamples
+//
+// Created by Sean Levin on 3/6/20.
+// Copyright © 2020 Verizon Media. All rights reserved.
+//
+
+#import "BGGraph.h"
+#import "SigngupPageViewController.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+@class SignupExtent;
+@interface SignupExtent : BGExtent
+
+@property (nonatomic, readonly) BGState *username;
+@property (nonatomic, readonly) BGState *password1;
+@property (nonatomic, readonly) BGState *password2;
+@property (nonatomic, readonly) BGMoment *signupButtonClicked;
+
+@property (nonatomic, readonly) BGState *usernameValid;
+@property (nonatomic, readonly) BGState *passwordsValid;
+@property (nonatomic, readonly) BGState *signupEnabled;
+@property (nonatomic, readonly) BGState *signingUp;
+
+@property (nonatomic, readwrite, weak) SigngupPageViewController *signup;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/doc-site/examples/objc/BGExamples/SignupExtent.m b/doc-site/examples/objc/BGExamples/SignupExtent.m
new file mode 100644
index 0000000..f695320
--- /dev/null
+++ b/doc-site/examples/objc/BGExamples/SignupExtent.m
@@ -0,0 +1,99 @@
+//
+// SignupExtent.m
+// BGExamples
+//
+// Created by Sean Levin on 3/6/20.
+// Copyright © 2020 Verizon Media. All rights reserved.
+//
+
+#import "SignupExtent.h"
+
+@implementation SignupExtent
+
+- (instancetype)initWithGraph:(BGGraph *)graph {
+ self = [super initWithGraph:graph];
+ if (self) {
+
+ _username = [self stateWithValue:nil];
+ _password1 = [self stateWithValue:nil];
+ _password2 = [self stateWithValue:nil];
+ _signupButtonClicked = [self moment];
+
+ _usernameValid = [self stateWithValue:@NO];
+ _passwordsValid = [self stateWithValue:@NO];
+ _signupEnabled = [self stateWithValue:@NO];
+ _signingUp = [self stateWithValue:@NO];
+
+
+ [self behaviorWithDemands:@[self.username] supplies:@[self.usernameValid] runBlock:^(SignupExtent * _Nonnull extent) {
+
+ // Validate username and offer feedback
+ NSString *username = extent.username.value;
+ BOOL valid = [username length] >= 10 && ![username containsString:@" "];
+ [extent.usernameValid updateValue:@(valid)];
+ [extent sideEffect:@"update username error message" runBlock:^(SignupExtent * _Nonnull extent) {
+ if (extent.usernameValid.value.boolValue) {
+ extent.signup.usernameErrorLabel.hidden = YES;
+ } else {
+ extent.signup.usernameErrorLabel.hidden = NO;
+ extent.signup.usernameErrorLabel.text = @"Username must be at least 10 characters and contain no empty spaces.";
+ [extent.signup.usernameErrorLabel sizeToFit];
+
+ }
+ }];
+
+ }];
+
+
+ [self behaviorWithDemands:@[self.password1, self.password2] supplies:@[self.passwordsValid] runBlock:^(SignupExtent * _Nonnull extent) {
+
+ // Validate passwords and offer feedback
+ NSString *password1 = extent.password1.value;
+ NSString *password2 = extent.password2.value;
+ BOOL valid = [password1 length] >= 10 && [password1 isEqualToString:password2];
+ [extent.passwordsValid updateValue:@(valid)];
+ [extent sideEffect:@"update password valid error message" runBlock:^(SignupExtent * _Nonnull extent) {
+ if (extent.passwordsValid.value.boolValue) {
+ extent.signup.passwordErrorLabel.hidden = YES;
+ } else {
+ extent.signup.passwordErrorLabel.hidden = NO;
+ extent.signup.passwordErrorLabel.text = @"Passwords must match and be at least 10 characters long.";
+ [extent.signup.passwordErrorLabel sizeToFit];
+ }
+ }];
+
+ }];
+
+
+ [self behaviorWithDemands:@[self.usernameValid, self.passwordsValid, self.signingUp] supplies:@[self.signupEnabled] runBlock:^(SignupExtent * _Nonnull extent) {
+
+ // Signup only allowed when username and password fields are valid
+ [extent.signupEnabled updateValue:@(extent.usernameValid.value.boolValue && extent.passwordsValid.value.boolValue && !extent.signingUp.value.boolValue)];
+ [extent sideEffect:@"enable signup button" runBlock:^(SignupExtent * _Nonnull extent) {
+ extent.signup.signupButton.enabled = extent.signupEnabled.value.boolValue;
+ }];
+
+ }];
+
+
+ [self behaviorWithDemands:@[self.signupButtonClicked] supplies:@[self.signingUp] runBlock:^(SignupExtent * _Nonnull extent) {
+
+ // On signup switch to signing up mode which will disable more user interaction
+ // Note signupEnabled uses trace value to prevent graph cycle with signupEnabled behavior
+ if (extent.signupButtonClicked.justUpdated && extent.signupEnabled.traceValue.boolValue) {
+ [extent.signingUp updateValue:@YES];
+ [extent sideEffect:@"submit signup form" runBlock:^(SignupExtent * _Nonnull extent) {
+ [extent.signup startSignupWithUsername:extent.username.value password:extent.password1.value];
+ extent.signup.usernameTextField.enabled = !extent.signingUp.value.boolValue;
+ extent.signup.password1Field.enabled = !extent.signingUp.value.boolValue;
+ extent.signup.password2Field.enabled = !extent.signingUp.value.boolValue;
+ }];
+ }
+
+ }];
+ }
+ return self;
+}
+
+
+@end
diff --git a/doc-site/examples/objc/BGExamples/ViewController.h b/doc-site/examples/objc/BGExamples/ViewController.h
new file mode 100644
index 0000000..cf8093b
--- /dev/null
+++ b/doc-site/examples/objc/BGExamples/ViewController.h
@@ -0,0 +1,15 @@
+//
+// ViewController.h
+// BGExamples
+//
+// Created by Sean Levin on 3/6/20.
+// Copyright © 2020 Verizon Media. All rights reserved.
+//
+
+#import
+
+@interface ViewController : UIViewController
+
+
+@end
+
diff --git a/doc-site/examples/objc/BGExamples/ViewController.m b/doc-site/examples/objc/BGExamples/ViewController.m
new file mode 100644
index 0000000..e54e8f9
--- /dev/null
+++ b/doc-site/examples/objc/BGExamples/ViewController.m
@@ -0,0 +1,23 @@
+//
+// ViewController.m
+// BGExamples
+//
+// Created by Sean Levin on 3/6/20.
+// Copyright © 2020 Verizon Media. All rights reserved.
+//
+
+#import "ViewController.h"
+
+@interface ViewController ()
+
+@end
+
+@implementation ViewController
+
+- (void)viewDidLoad {
+ [super viewDidLoad];
+ // Do any additional setup after loading the view.
+}
+
+
+@end
diff --git a/doc-site/examples/objc/BGExamples/main.m b/doc-site/examples/objc/BGExamples/main.m
new file mode 100644
index 0000000..ace61c4
--- /dev/null
+++ b/doc-site/examples/objc/BGExamples/main.m
@@ -0,0 +1,19 @@
+//
+// main.m
+// BGExamples
+//
+// Created by Sean Levin on 3/6/20.
+// Copyright © 2020 Verizon Media. All rights reserved.
+//
+
+#import
+#import "AppDelegate.h"
+
+int main(int argc, char * argv[]) {
+ NSString * appDelegateClassName;
+ @autoreleasepool {
+ // Setup code that might create autoreleased objects goes here.
+ appDelegateClassName = NSStringFromClass([AppDelegate class]);
+ }
+ return UIApplicationMain(argc, argv, nil, appDelegateClassName);
+}
diff --git a/doc-site/examples/objc/BGExamplesTests/BGExamplesTests.m b/doc-site/examples/objc/BGExamplesTests/BGExamplesTests.m
new file mode 100644
index 0000000..89df1af
--- /dev/null
+++ b/doc-site/examples/objc/BGExamplesTests/BGExamplesTests.m
@@ -0,0 +1,37 @@
+//
+// BGExamplesTests.m
+// BGExamplesTests
+//
+// Created by Sean Levin on 3/6/20.
+// Copyright © 2020 Verizon Media. All rights reserved.
+//
+
+#import
+
+@interface BGExamplesTests : XCTestCase
+
+@end
+
+@implementation BGExamplesTests
+
+- (void)setUp {
+ // Put setup code here. This method is called before the invocation of each test method in the class.
+}
+
+- (void)tearDown {
+ // Put teardown code here. This method is called after the invocation of each test method in the class.
+}
+
+- (void)testExample {
+ // This is an example of a functional test case.
+ // Use XCTAssert and related functions to verify your tests produce the correct results.
+}
+
+- (void)testPerformanceExample {
+ // This is an example of a performance test case.
+ [self measureBlock:^{
+ // Put the code you want to measure the time of here.
+ }];
+}
+
+@end
diff --git a/doc-site/examples/objc/BGExamplesTests/Info.plist b/doc-site/examples/objc/BGExamplesTests/Info.plist
new file mode 100644
index 0000000..64d65ca
--- /dev/null
+++ b/doc-site/examples/objc/BGExamplesTests/Info.plist
@@ -0,0 +1,22 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ $(DEVELOPMENT_LANGUAGE)
+ CFBundleExecutable
+ $(EXECUTABLE_NAME)
+ CFBundleIdentifier
+ $(PRODUCT_BUNDLE_IDENTIFIER)
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ $(PRODUCT_NAME)
+ CFBundlePackageType
+ $(PRODUCT_BUNDLE_PACKAGE_TYPE)
+ CFBundleShortVersionString
+ 1.0
+ CFBundleVersion
+ 1
+
+
diff --git a/doc-site/examples/typescript/BGExamples/ChatExample.ts b/doc-site/examples/typescript/BGExamples/ChatExample.ts
new file mode 100644
index 0000000..6b5ec38
--- /dev/null
+++ b/doc-site/examples/typescript/BGExamples/ChatExample.ts
@@ -0,0 +1,158 @@
+import { Graph, InitialEvent, ValuePersistence } from './behaveg/behavegjs';
+import { Behavior } from "./behaveg/behavior"
+import { State, Moment, Resource } from "./behaveg/resource"
+import { Extent } from "./behaveg/extent"
+
+// @tag::chat_extent[]
+class ChatExtent extends Extent {
+// @end::chat_extent[]
+
+ participantJoined: Moment;
+ participantDisconnected: Moment;
+
+ // @tag::chat_participants_resources[]
+ participants: State