diff --git a/.changeset/README.md b/.changeset/README.md
new file mode 100644
index 0000000000..4f3b76b096
--- /dev/null
+++ b/.changeset/README.md
@@ -0,0 +1,8 @@
+# Changesets
+
+Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works
+with multi-package repos, or single-package repos to help you version and publish your code. You can
+find the full documentation for it [in our repository](https://github.com/changesets/changesets)
+
+We have a quick list of common questions to get you started engaging with this project in
+[our documentation](https://github.com/changesets/changesets/blob/master/docs/common-questions.md)
diff --git a/.changeset/config.json b/.changeset/config.json
new file mode 100644
index 0000000000..aa045f6555
--- /dev/null
+++ b/.changeset/config.json
@@ -0,0 +1,9 @@
+{
+ "changelog": ["@changesets/changelog-github", { "repo": "mobxjs/mobx" }],
+ "commit": false,
+ "access": "public",
+ "baseBranch": "main",
+ "___experimentalUnsafeOptions_WILL_CHANGE_IN_PATCH": {
+ "onlyUpdatePeerDependentsWhenOutOfRange": true
+ }
+}
diff --git a/.editorconfig b/.editorconfig
index 7419bd422e..8ef6aaa7be 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -6,6 +6,6 @@ charset = utf-8
indent_style = space
tab_width = 4
-[{package.json,.travis.yml}]
+[{package.json}]
indent_style = space
-indent_size = 2
\ No newline at end of file
+indent_size = 2
diff --git a/.eslintrc.js b/.eslintrc.js
new file mode 100644
index 0000000000..b0e009e162
--- /dev/null
+++ b/.eslintrc.js
@@ -0,0 +1,36 @@
+module.exports = {
+ parser: "@typescript-eslint/parser",
+ plugins: ["@typescript-eslint"],
+ extends: "eslint:recommended",
+ ignorePatterns: ["**/__tests__/**/*"],
+ env: {
+ browser: true,
+ es6: true,
+ node: true
+ },
+ parserOptions: {
+ ecmaVersion: 6,
+ sourceType: "module"
+ },
+ rules: {
+ "no-fallthrough": "off",
+ "no-constant-condition": "off",
+ curly: "error",
+ "getter-return": "off",
+ "no-console": "off",
+ "no-var": "error",
+ "no-undef": "off",
+ "no-extra-semi": "off", // doesn't get along well with prettier
+ "no-unused-vars": "off", // got typescript for that,
+ "no-redeclare": "off", // No idea what it does, but it dies
+ "require-yield": "off" // Doesn't work with TS
+ },
+ globals: {
+ process: "readable",
+ global: "readable",
+ console: "readable",
+ setTimeout: "readable",
+ clearTimeout: "readable",
+ module: "writable"
+ }
+}
diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
new file mode 100644
index 0000000000..95d7eb2031
--- /dev/null
+++ b/.github/FUNDING.yml
@@ -0,0 +1 @@
+open_collective: mobx
diff --git a/.github/ISSUE_TEMPLATE/bug.md b/.github/ISSUE_TEMPLATE/bug.md
new file mode 100644
index 0000000000..51c8ea614e
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug.md
@@ -0,0 +1,45 @@
+---
+name: 🐛 Reporting a Bug
+about: Open a new issue if something isn't working as expected.
+labels: 🐛 bug
+---
+
+
+
+**Intended outcome:**
+
+
+
+**Actual outcome:**
+
+
+
+**How to reproduce the issue:**
+
+
+
+**Versions**
+
+
diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
new file mode 100644
index 0000000000..f16ede9fe8
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/config.yml
@@ -0,0 +1,14 @@
+blank_issues_enabled: false
+contact_links:
+ - name: ✨ Feature Request / idea
+ url: https://github.com/mobxjs/mobx/discussions/new
+ about: Missing something in MobX? Let us know.
+ - name: 💬 Question / Discussion
+ about: Feel free to ask anything
+ url: https://github.com/mobxjs/mobx/discussions/new
+ - name: 📖 View documentation
+ url: https://mobx.js.org
+ about: Official Mobx documentation
+ - name: ❓ StackOverflow
+ url: https://stackoverflow.com/questions/tagged/mobx
+ about: Ask question or find answers on Stack overflow
\ No newline at end of file
diff --git a/.github/ISSUE_TEMPLATE/documentation.md b/.github/ISSUE_TEMPLATE/documentation.md
new file mode 100644
index 0000000000..d8a6c9a754
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/documentation.md
@@ -0,0 +1,9 @@
+---
+name: ✏ Documentation improvement
+about: Please open the PR instead!
+labels: 📖 documentation
+---
+
+Documentation lives in the `/docs` folder. Please, send in PR directly with a change instead of describing what you want to change.
+
+Ask here only if your change is bigger and there is a chance for rejecting it.
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
new file mode 100644
index 0000000000..01d242671e
--- /dev/null
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -0,0 +1,21 @@
+
+
+### Code change checklist
+
+- [ ] Added/updated unit tests
+- [ ] Updated `/docs`. For new functionality, at least `API.md` should be updated
+- [ ] Verified that there is no significant performance drop (`yarn mobx test:performance`)
+
+
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
new file mode 100644
index 0000000000..5ace4600a1
--- /dev/null
+++ b/.github/dependabot.yml
@@ -0,0 +1,6 @@
+version: 2
+updates:
+ - package-ecosystem: "github-actions"
+ directory: "/"
+ schedule:
+ interval: "weekly"
diff --git a/.github/lock.yml b/.github/lock.yml
new file mode 100644
index 0000000000..23ddfcbe92
--- /dev/null
+++ b/.github/lock.yml
@@ -0,0 +1,26 @@
+# Configuration for Lock Threads - https://github.com/dessant/lock-threads
+
+# Number of days of inactivity before a closed issue or pull request is locked
+daysUntilLock: 60
+
+# Skip issues and pull requests created before a given timestamp. Timestamp must
+# follow ISO 8601 (`YYYY-MM-DD`). Set to `false` to disable
+skipCreatedBefore: 2019-01-01
+
+# Issues and pull requests with these labels will be ignored. Set to `[]` to disable
+exemptLabels: []
+
+# Label to add before locking, such as `outdated`. Set to `false` to disable
+lockLabel: false
+
+# Comment to post before locking. Set to `false` to disable
+lockComment: >
+ This thread has been automatically locked since there has not been
+ any recent activity after it was closed. Please open a new issue for
+ related bugs or questions.
+
+# Assign `resolved` as the reason for locking. Set to `false` to disable
+setLockReason: true
+
+# Limit to only `issues` or `pulls`
+only: issues
diff --git a/.github/stale.yml b/.github/stale.yml
new file mode 100644
index 0000000000..480b2a213f
--- /dev/null
+++ b/.github/stale.yml
@@ -0,0 +1,30 @@
+# Number of days of inactivity before an issue becomes stale
+daysUntilStale: 14
+# Number of days of inactivity before a stale issue is closed
+daysUntilClose: 4
+# Issues with these labels will never be considered stale
+exemptLabels:
+ - 📌 pinned
+ - 💬 discuss
+ - 🍗 enhancement
+ - 📖 documentation
+ - 🔨 breaking-change
+ - 🚧 experimental
+ - 🙏 help wanted
+ - 👓 needs investigation
+ - ✋ on hold
+
+# Label to use when marking an issue as stale
+staleLabel: 🚶 stale
+# Comment to post when marking an issue as stale. Set to `false` to disable
+markComment: >
+ This issue has been automatically marked as stale because it has not had
+ recent activity. It will be closed if no further activity occurs. Thank you
+ for your contributions.
+# Comment to post when closing a stale issue. Set to `false` to disable
+closeComment: false
+# Limit to only `issues` or `pulls`
+only: issues
+# Comment to post when removing the stale label.
+unmarkComment: >
+ This issue has been automatically unmarked as stale. Please disregard previous warnings.
diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml
new file mode 100644
index 0000000000..1be48508eb
--- /dev/null
+++ b/.github/workflows/build_and_test.yml
@@ -0,0 +1,45 @@
+name: Build and test
+
+on:
+ push:
+ branches: [main]
+ pull_request:
+ branches: [main]
+ workflow_dispatch: {}
+
+jobs:
+ build:
+ name: Build and test
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout Repo
+ uses: actions/checkout@master
+
+ - name: Setup Node.js 22.x
+ uses: actions/setup-node@master
+ with:
+ node-version: 22.x
+
+ - name: Install Dependencies
+ run: yarn --frozen-lockfile --ignore-scripts
+
+ - name: Lint
+ run: yarn lint
+
+ - name: Build check
+ run: yarn lerna run build:check
+
+ - name: Build packages
+ run: yarn lerna run build:test
+
+ - name: Test
+ run: yarn test -i
+
+ - name: Test size
+ run: yarn lerna run test:size
+
+ - name: Test flow
+ run: yarn mobx test:flow
+
+ - name: Test performance
+ run: yarn mobx test:performance
diff --git a/.github/workflows/coveralls.yml b/.github/workflows/coveralls.yml
new file mode 100644
index 0000000000..ec1f27100f
--- /dev/null
+++ b/.github/workflows/coveralls.yml
@@ -0,0 +1,33 @@
+name: Coveralls
+
+on: ["push"]
+
+jobs:
+ mobx:
+ name: Packages coverage
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout Repo
+ uses: actions/checkout@master
+ with:
+ # This makes Actions fetch all Git history so that Changesets can generate changelogs with the correct commits
+ fetch-depth: 0
+
+ - name: Setup Node.js 22.x
+ uses: actions/setup-node@master
+ with:
+ node-version: 22.x
+
+ - name: Install Dependencies
+ run: yarn --frozen-lockfile --ignore-scripts
+
+ - name: Build packages
+ run: yarn lerna run build:test
+
+ # - name: Run Coverage
+ # run: yarn coverage
+
+ # - name: Upload to coveralls
+ # uses: coverallsapp/github-action@v2.3.6
+ # with:
+ # github-token: ${{ secrets.GITHUB_TOKEN }}
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
new file mode 100644
index 0000000000..4336bf7e61
--- /dev/null
+++ b/.github/workflows/release.yml
@@ -0,0 +1,40 @@
+name: Release
+
+on:
+ push:
+ branches:
+ - main
+
+jobs:
+ release:
+ name: Release
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout Repo
+ uses: actions/checkout@main
+ with:
+ # This makes Actions fetch all Git history so that Changesets can generate changelogs with the correct commits
+ fetch-depth: 0
+
+ - name: Setup Node.js 22.x
+ uses: actions/setup-node@master
+ with:
+ node-version: 22.x
+
+ - name: Install Dependencies
+ run: yarn
+
+ - name: Build packages
+ run: yarn lerna run build
+
+ - name: Create Release Pull Request or Publish to npm
+ id: changesets
+ uses: changesets/action@v1
+ with:
+ # This expects you to have a script called release which does a build for your packages and calls changeset publish
+ publish: yarn release
+ commit: Version release
+ title: Next release
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
diff --git a/.gitignore b/.gitignore
index 4c811618c0..0880b4500c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,16 +3,16 @@
node_modules
npm-debug.log
coverage
-notes.md
-lib
test/babel-tests.js
test/typescript/typescript-tests.js
-test/perf/perf.txt
dist/
-.build*/
-.idea
+**/.idea/*
+!/.idea/icon.png
+!/.idea/vcs.xml
.wp-build*/
*.iml
*.ipr
*.iws
yarn-error.log
+build/
+.DS_Store
diff --git a/.idea/icon.png b/.idea/icon.png
new file mode 100644
index 0000000000..a2af3a5933
Binary files /dev/null and b/.idea/icon.png differ
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000000..d5bf7140c3
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.prettierignore b/.prettierignore
index bd3dc91687..7957417edb 100644
--- a/.prettierignore
+++ b/.prettierignore
@@ -1 +1,6 @@
-/**/package.json
\ No newline at end of file
+/**/package.json
+website/**/*
+dist/
+docs/assets/
+*.yml
+coverage
\ No newline at end of file
diff --git a/.prettierrc b/.prettierrc
index baa8b38f80..1f85f3d765 100644
--- a/.prettierrc
+++ b/.prettierrc
@@ -3,5 +3,7 @@
"semi": false,
"tabWidth": 4,
"singleQuote": false,
+ "trailingComma": "none",
+ "arrowParens": "avoid",
"useTabs": false
}
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 3e971df616..0000000000
--- a/.travis.yml
+++ /dev/null
@@ -1,12 +0,0 @@
-language: node_js
-install:
- - yarn install
-script: CI=true yarn test:travis
-after_success:
- - cat ./coverage/lcov.info|./node_modules/coveralls/bin/coveralls.js
-node_js:
- - 9.3
-addons:
- apt:
- packages:
- - xvfb
diff --git a/.vscode/settings.json b/.vscode/settings.json
index 3297ba797f..618db460de 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -1,18 +1,17 @@
{
- "[typescript]": {
- "editor.formatOnSave": true,
- "editor.formatOnPaste": false
- },
- "[javascript]": {
- "editor.formatOnSave": true,
- "editor.formatOnPaste": false
- },
- // Note, these settings should match lint-staged settings!
- "prettier.semi": false,
- "prettier.tabWidth": 4,
- "prettier.useTabs": false,
- "prettier.printWidth": 100,
- "prettier.singleQuote": false,
- "typescript.tsdk": "node_modules/typescript/lib"
- // "javascript.validate.enable": false // enable for flow
-}
\ No newline at end of file
+ "[typescript]": {
+ "editor.defaultFormatter": "esbenp.prettier-vscode",
+ "editor.formatOnSave": true,
+ "editor.formatOnPaste": false
+ },
+ "[javascript]": {
+ "editor.defaultFormatter": "esbenp.prettier-vscode",
+ "editor.formatOnSave": true,
+ "editor.formatOnPaste": false
+ },
+ "typescript.tsdk": "node_modules/typescript/lib",
+ // "javascript.validate.enable": false // enable for flow
+ "eslint.validate": ["javascript", "javascriptreact", "typescript", "typescriptreact"],
+ "fb.doctor.disabled-tests": ["CorporateNetworkDoctor"],
+ "cSpell.enabled": true
+}
diff --git a/.watchmanconfig b/.watchmanconfig
new file mode 100644
index 0000000000..4da29c3841
--- /dev/null
+++ b/.watchmanconfig
@@ -0,0 +1,3 @@
+{
+ "ignore_dirs": ["node_modules", "_site", "dist", "coverage"]
+}
diff --git a/CHANGELOG.md b/CHANGELOG.md
deleted file mode 100644
index 12258884bb..0000000000
--- a/CHANGELOG.md
+++ /dev/null
@@ -1,1423 +0,0 @@
-# 5.8.0 / 4.8.0
-
-* MobX now requires TypeScript 3 (this was already the case in 5.7.0, but in this version the difference is actually noticeable in the typings).
-* Fixed array dehancer sometimes skipping. Fixes [#1839](https://github.com/mobxjs/mobx/issues/1839) through [#1841](https://github.com/mobxjs/mobx/pull/1841) by [k-g-a](https://github.com/k-g-a)
-* Fixed issue where webpack 4 wouldn't use the ESM module [#1834](https://github.com/mobxjs/mobx/pull/1834) by [mrtnbroder](https://github.com/mrtnbroder)
-* Improved type inference for `flow` in TypeScript 3. Fixes [#1816](https://github.com/mobxjs/mobx/issue/1816) through [#1825](https://github.com/mobxjs/mobx/pull/1825) by [ismailhabib](https://github.com/ismailhabib)
-* Introduced support for global environment variable `IGNORE_MOBX_MINIFIY_WARNING=true` to skip the built-in minification warning. See [#1835](https://github.com/mobxjs/mobx/pull/1835) by [fi3ework](https://github.com/fi3ework)
-* Fixed onBecome(Un)Observed dispoer cleanup. Fixes [#1537](https://github.com/mobxjs/mobx/issues/1537) through [#1833](https://github.com/mobxjs/mobx/pull/1833) by [fi3ework](https://github.com/fi3ework)
-
-# 5.7.1 / 4.7.1
-* Fixed [#1839](https://github.com/mobxjs/mobx/issues/1839), ObservableArrayAdministration.dehanceValues does not dehance last value.
-
-# 5.7.0 / 4.7.0
-
-* Upgraded typings to TypeScript 3
-* Fixed [#1742](https://github.com/mobxjs/mobx/issues/1742), change detection fails when multiple mobx instances were active.
-* Fixed [#1624](https://github.com/mobxjs/mobx/issues/1624), use built-in flow types for iterators
-* Fixed [#1777](https://github.com/mobxjs/mobx/issues/1777) through [#1826](https://github.com/mobxjs/mobx/pull/1826), stack overflow exception, in development mode, when using `@computed` on a React component. The MobX 5 behavior here has been reverted to the MobX 4 behavior.
-
-
-# 5.6.0 / 4.6.0
-
-* `keepAlive` has become smarter and won't recomputed computed values that are kept alive, as long as they aren't read. Implements [#1534](https://github.com/mobxjs/mobx/issues/1534)
-* Fixed [#1796](https://github.com/mobxjs/mobx/issues/1796), undeleting a property that had an initial value of `undefined` was undetected
-* Improved Flow typings, see [#1794](https://github.com/mobxjs/mobx/pull/1794) and [#1786](https://github.com/mobxjs/mobx/pull/1786)
-
-# 5.5.2 / 4.5.2
-
-* Fixed bug in `toJS` not handling `null` values correctly. Fixes [#1557](https://github.com/mobxjs/mobx/issues/1557) through [#1783](https://github.com/mobxjs/mobx/pull/1783) by [@wangyiz4262](https://github.com/wangyiz4262)
-
-# 5.5.1 / 4.5.1
-
-* `toJS` now has a `recurseEverything` everything option, that even detects and converts observable objects that are "behind" non-observable objects. See [#1699](https://github.com/mobxjs/mobx/pull/1699) by [wangyiz4262](https://github.com/wangyiz4262)
-* Added flow typings form `comparer`, see [#1751](https://github.com/mobxjs/mobx/pull/1752) by [pdong](https://github.com/pdong)
-* Update flow typings for configuration options, [#1772](https://github.com/mobxjs/mobx/pull/1772) by [alexandersorokin](https://github.com/alexandersorokin)
-
-# 5.5.0 / 4.5.0
-
-(Minor version of `5` was bumped significantly to make the number better correlate together :-))
-
-* Fixed [#1740](https://github.com/mobxjs/mobx/issues/1740): combining decorators and `extendObservable` in a class constructor caused errors to be thrown
-* Fixed [#1739](https://github.com/mobxjs/mobx/issues/1740):
- * Proxies: `delete`-ing a property was not always picked up by the reactivity system
- * Non-proxies: `remove()`-ing a property was not always picked up by the `has()` and `get()` utilities
- * `has` now returns `true` for computed fields
- * `get` now returns a value for computed fields
-* Introduced `_allowStateChangeInsideComputed`. Don't use it :-).
-* MobX is now transpiled using babel 7
-
-# 5.1.2 / 4.4.2
-
-* Fixed [#1650](https://github.com/mobxjs/mobx/issues/1650), decorating fields with the name `toString` does not behave correctly.
-
-# 5.1.1 / 4.4.1
-
-* Improved typings of `decorate`, see [#1711](https://github.com/mobxjs/mobx/pull/1711) by [makepost](https://github.com/makepost)
-
-# 5.1.0 / 4.4.0
-
-* Improved handling of multiple MobX instances. MobX will no longer complain about being loaded multiple times (one should still prevent it though, to prevent blowing up bundle size!), but merge internal state by default. If multiple MobX versions need to be loaded, call `configure({ isolateGlobalState: true })`. Note that this means that observables from the different MobX instances won't cooperate. Fixes [#1681](https://github.com/mobxjs/mobx/issues/1681), [#1082](https://github.com/mobxjs/mobx/issues/1082)
-* `enforceActions` options now supports the values: `"never"`, `"observed"` and `"always"` to make the behavior more clear. Fixes [#1680](https://github.com/mobxjs/mobx/issues/1680), [#1473](https://github.com/mobxjs/mobx/issues/1473)
-
-# 5.0.5
-
-* Fixed [#1667](https://github.com/mobxjs/mobx/issues/1667): creating a large array could result in undefined items (MobX 4.* was not affected)
-
-# 4.3.2 / 5.0.4
-
-* Fixed [#1685](https://github.com/mobxjs/mobx/issues/1685): expose `IAutorunOptions`
-* `decorate` now can apply multiple decorators, by accepting an array and applying them right to left: `decorate(Todo, { title: [serializable(primitive), persist('object'), observable] })`. By [@ramybenaroya](https://github.com/ramybenaroya) through [#1691](https://github.com/mobxjs/mobx/pull/1691) and [#1686](https://github.com/mobxjs/mobx/pull/1686)
-* Improved typings of `flow` so that it accepts async generators. By [@dannsam](https://github.com/dannsam) through [#1656](https://github.com/mobxjs/mobx/pull/1656) and [#1655](https://github.com/mobxjs/mobx/pull/1655)
-* `keys()` now also supports arrays. Fixes [#1600](https://github.com/mobxjs/mobx/pull/1600) through [#1601](https://github.com/mobxjs/mobx/pull/1601) by [@nunocastromartins](https://github.com/nunocastromartins)
-
-# 5.0.3
-
-* Fixed issue where it was no longer possible to define custom properties on observable arrays
-
-# 5.0.2
-
-* Fixed issue where iterators where not compiled to ES5, breaking the ES5 based builds.
-
-# 5.0.1 (Unpublished)
-
-* Fixed regression bug: `ObservableMap.size` was no longer observable. Fixes [#1583](https://github.com/mobxjs/mobx/issues/1583)
-* Downgraded lib export from ES6 to ES5. To many build tools still trip over ES6. Fixes [#1584](https://github.com/mobxjs/mobx/issues/1584). A modern build is available through `import ... from "mobx/lib/mobx.es6"` (or setup an alias in your build system)
-* Added support for mobx-react-devtools
-
-# 5.0.0
-
-[Release blogpost](https://medium.com/p/4852bce05572/)
-
-### Proxy support!
-
-MobX 5 is the first MobX version fully leveraging Proxies. This has two big advantages
-
-1. MobX can now detect the addition of properties on plain observable objects, so it is now possible to use plain observable objects as dynamic collections.
-2. Observable arrays are now recognized as arrays by all third party libraries, which will avoid the need to slice them.
-
-### The system requirements to run MobX has been upped
-
-* MobX 5 can only be used on environments that support `Proxies`. In practice this means, no Internet Explorer (Edge is fine). No nodejs < 6. React Native on Android only when JavaScript core is [upgraded](https://github.com/react-community/jsc-android-buildscripts#how-to-use-it-with-my-react-native-app). All modern browsers are supported.
-* Since MobX no longer runs on older browsers, the compilation target has been upgraded to ES2015 syntax supporting browsers. This means that MobX is not loadable on older browsers without down compilation to ES5.
-* If for whatever reason your project cannot meet this requirements, please stick to MobX 4. It will be actively maintained. All current features of MobX 5 are expressable in MobX 4 as well, but it means that for example to use dynamic objects some [additional APIs](https://mobx.js.org/refguide/object-api.html) are needed.
-* The performance footprint of MobX 5 should be pretty similar to MobX 4. In our performance tests we saw some minor improvements in memory footprint, but overall it should be pretty comparable.
-
-### Breaking changes
-
-* The required runtime needs to support the non-polyfillable `Proxy` API.
-* The minimum runtime target is now ES2015, not ES5
-* `spy` has become a no-op in production builds
-* All earlier deprecated APIs are dropped. Make sure to not have any deprecation warnings before upgrading.
-* `array.move` and `array.peek` are removed from the API
-* Dropped the third argument to `array.find` and `array.findIndex` since they were not standardized in ES.
-* `.$mobx` property has been dropped from all observables and replaced by a Symbol. Instead of using `x.$mobx.name`, use `import { $mobx } from "mobx"; x[$mobx].name` etc.
-* In some cases, the order in which autoruns are fired could have changed due to some internal optimizations (note that MobX never had a guarantee about the order in which autoruns fired!)
-
-### New features
-
-* It is possible to pass the `proxy: false` argument to `observable.object` to disable proxying (theoretically slightly faster, but removes no dynamic key addition)
-
-### Known Issues
-
-* Observable objects can no longer be frozen (otherwise they would become un-observable😎). If you are actually trying to do so MobX will now throw an exception like: `[mobx] Dynamic observable objects cannot be frozen]`. A place where that might happen unexpectedly is when passing an observable object as `style` property to a React component. Like ``, since React will freeze all style objects. The work-around is to simply pass a fresh, non-observable object for styling like: ``.
-* ~~If you are using `mobx` with `mobx-react`, and you are upgrading `mobx-react` to the MobX 5 compatible version (`mobx-react@5.2.0`) you will notice that `this.props` or `this.state` are not yet observable in the `constructor` or `componentWillMount`. This is for forward compatibility with React 16.3 where `componentWillMount` has been deprecated. In most cases using `componentDidMount` instead will suffice, especially when the goal is to setup reactions. For more info see [#478](https://github.com/mobxjs/mobx-react/issues/478).~~ Fixed in mobx-react 5.2.1. But note that you should still migrate away from `componentWillMount`😎.
-* Jest `toEqual` might throw an error `allKeys[x].match is not a function` when trying to equal observable arrays. This is a bug in Jest [report](https://github.com/facebook/jest/issues/6398). The simple work around for now is to slice (or `toJS` if the problem is recursive) the array first.
-* Jest `toEqual` matcher might no longer correctly equal your class instances, complaining about differences in the MobX adminstration. This is due to a bug with the processing of symbols: [report](https://github.com/facebook/jest/issues/6392). For now you might want to use a custom matcher if you are directly equalling observable objects. As a work around `toJS(object)` could be used before diffing.
-
-_Note June 7th, 2018:_ Both issues are already in Jest master and should be released soon.
-
-### Migration guide
-
-* Make sure to not use any API that produces deprecation warnings in MobX 4. Beyond that MobX 5 should pretty well as drop-in replacement of MobX 4.
-* You _could_ perform the following clean ups:
- * Don't `slice()` arrays when passing them to external libraries. (Note you still shouldn't pass observable data structures to non-`observer` React components, which is an orthogonal concept)
- * You could replace observable maps with observable objects if you are only using string-based keys.
-* Don't call the `reverse` or `sort` operations directly on observableArray's anymore, as it's behavior slightly differed from the built-in implementations of those methods. Instead use `observableArray.slice().sort()` to perform the sort on a copy. This gives no additional performance overhead compared to MobX 4. (The reason behind this is that built-in `sort` updates the array in place, but the observable array implementation always performed the sort on a defensive copy, and this change makes that explicit).
-
-
-### API's that have been dropped
-
-* The `arrayBuffer` setting is no longer supported by `configure` (it has become irrelevant)
-* `observable.shallowBox`, `observable.shallowArray`, `observable.shallowMap`, `observable.shallowObject`, `extendShallowObservable` api's have been removed. Instead, pass `{ deep: false }` to their non-shallow counter parts.
-* `observableArray.peek`, `observableArray.move`
-
-# 4.3.1
-
-* Fixed [#1534](Fixes https://github.com/mobxjs/mobx/issues/1534): @computed({keepAlive: true}) no long calculates before being accessed.
-* Added the `$mobx` export symbol for MobX 5 forward compatibity
-
-# 4.3.0
-
-* Introduced the `entries(observable)` API, by @samjacobclift through [#1536](https://github.com/mobxjs/mobx/pull/1536)
-* Fixed [#1535](https://github.com/mobxjs/mobx/issues/1535): Change in nested computed value was not propagated if read outside action context when there is a pending reaction. For more details see the exact test case.
-* Illegal property access through prototypes is now a warning instead of an error. Fixes [#1506](https://github.com/mobxjs/mobx/issues/1506). By @AmazingTurtle through [#1529](https://github.com/mobxjs/mobx/pull/1529)
-* Fixed issue where providing a custom setter to `@computed({ set: ... })` wasn't picked up
-* Fixed #1545: Actions properties where not re-assignable when using TypeScript
-* Illegal Access checks are now a warning instead of an error. Fix
-
-# 4.2.1
-
-* Fixed flow typings for `mobx.configure` [#1521](https://github.com/mobxjs/mobx/pull/1521) by @andrew--r
-* Improved typings for `mobx.flow`, fixes [#1527](https://github.com/mobxjs/mobx/issues/1527)
-* Throw error when using `@observable` in combination with a getter. [#1511](https://github.com/mobxjs/mobx/pull/1511) by @quanganhtran
-* `toJS` now uses Map internally, for faster detection of cycles. [#1517](https://github.com/mobxjs/mobx/pull/1517) by @loatheb
-* Fixed [#1512](https://github.com/mobxjs/mobx/issues/1512): `observe` hooks not being triggered when using `mobx.set`, Fixed in [#1514](https://github.com/mobxjs/mobx/pull/1514) by @quanganhtran
-* Several minor improvements, additional tests and doc improvements.
-
-# 4.2.0
-
-* Introduced `configure({ enforceActions: "strict" })`, which is more strict then `enforceActions: true`, as it will also throw on non-observed changes to observables. See also [#1473](https://github.com/mobxjs/mobx/issues/1473)
-* Fixed [#1480](https://github.com/mobxjs/mobx/issues/1480): Exceptions in the effect handler of `reaction` where not properly picked up by the global reaction system
-* Fixed a bug where computed values updated their cached value, even when the comparer considered the new value equal to the previous one. Thanks @kuitos for finding this and fixing it! [#1499](https://github.com/mobxjs/mobx/pull/1499)
-* Undeprecated `ObservableMap`, fixes [#1496](https://github.com/mobxjs/mobx/issues/1496)
-* Observable arrays now support `Symbol.toStringTag` (if available / polyfilled). This allows libraries like Ramda to detect automatically that observable arrays are arrays. Fixes [#1490](https://github.com/mobxjs/mobx/issues/1490). Note that `Array.isArray` will keep returning false for the entire MobX 4 range.
-* Actions are now always `configurable` and `writable`, like in MobX 3. Fixes [#1477](https://github.com/mobxjs/mobx/issues/1477)
-* Merged several improvements to the flow typings. [#1501](https://github.com/mobxjs/mobx/pull/1501) by @quanganhtran
-* Fixed several accidental usages of the global `fail`, by @mtaran-google through [#1483](https://github.com/mobxjs/mobx/pull/1483) and [#1482](https://github.com/mobxjs/mobx/pull/1482)
-
-# 4.1.1
-
-* Import `default` from MobX will no longer throw, but only warn instead. This fixes some issues with tools that reflect on the `default` export of a module
-* Disposing a spy listener inside a spy handler no longer causes an exception. Fixes [#1459](https://github.com/mobxjs/mobx/issues/1459) through [#1460](https://github.com/mobxjs/mobx/pull/1460) by [farwayer](https://github.com/farwayer)
-* Added a missing `runInAction` overload in the flow typings. [#1451](https://github.com/mobxjs/mobx/pull/1451) by [AMilassin](https://github.com/mobxjs/mobx/issues?q=is%3Apr+author%3AAMilassin)
-* Improved the typings of `decorate`. See [#1450](https://github.com/mobxjs/mobx/pull/1450) by [makepost](https://github.com/mobxjs/mobx/issues?q=is%3Apr+author%3Amakepost)
-
-# 4.1.0
-
-* Introduced `keepAlive` as option to `computed`
-* All observable api's now default to `any` for their generic arguments
-* Improved `flow` cancellation
-* The effect of `when` is now automatically an action.
-* `@computed` properties are now declared on their owner rather then the protoptype. Fixes an issue where `@computed` fields didn't work in React Native on proxied objects. See [#1396](https://github.com/mobxjs/mobx/issues/1396)
-* `action` and `action.bound` decorated fields are now reassignable, so that they can be stubbed
-
-# 4.0.2
-
-* Fixed issue where exceptions like `TypeError: Cannot define property:__mobxDidRunLazyInitializers, object is not extensible.` were thrown. Fixes [#1404](https://github.com/mobxjs/mobx/issues/1404)
-* Improved flow typings for `flow`, [#1399](https://github.com/mobxjs/mobx/pull/1399) by @ismailhabib
-
-# 4.0.1
-
-* Updated flow typings, see [#1393](https://github.com/mobxjs/mobx/pull/1393) by [andrew--r](https://github.com/andrew--r)
-
-# 4.0.0
-
-* For the highlights of this release, read the [blog](https://medium.com/p/c1fbc08008da/):
-* For migration notes: see the [wiki page](https://github.com/mobxjs/mobx/wiki/Migrating-from-mobx-3-to-mobx-4)
-* Note; many things that were removed to make the api surface smaller. If you think some feature shouldn't have been removed, feel free to open an issue!
-
-This is the extensive list of all changes.
-
-### New features
-
-The changes mentioned here are discussed in detail in the [release highlights](https://medium.com/p/c1fbc08008da/), or were simply updated in the docs.
-
-* MobX 4 introduces separation between the production and non production build. The production build strips most typechecks, resulting in a faster and smaller build. Make sure to substitute process.env.NODE_ENV = "production" in your build process! If you are using MobX in a react project, you most probably already have set this up. Otherwise, the idea is explained [here](https://reactjs.org/docs/add-react-to-an-existing-app.html).
-* Introduced `flow` to create a chain of async actions. This is the same function as [`asyncActions`](https://github.com/mobxjs/mobx-utils#asyncaction) of the mobx-utils package
-* These `flow`'s are now cancellable, by calling `.cancel()` on the returned promise, which will throw a cancellation exception into the generator function.
-* `flow` also has experimental support for async iterators (`async * function`)
-* Introduced `decorate(thing, decorators)` to decorate classes or object without needing decorator syntax.
-* Introduced `onBecomeObserved` and `onBecomeUnobserved`. These API's enable hooking into the observability system and get notified about when an observable starts / stops becoming used. This is great to automaticaly fetch data from external resources, or stop doing so.
-* `computed` / `@computed` now accepts a `requiresReaction` option. If it set, the computed value will throw an exception if it is being read while not being tracked by some reaction.
-* To make `requiresReaction` the default, use `mobx.configure({ computedRequiresReaction: true })`
-* Introduced `mobx.configure({ disableErrorBoundaries })`, for easier debugging of exceptoins. By [NaridaL](https://github.com/NaridaL) through [#1262](https://github.com/mobxjs/mobx/pull/1262)
-* `toJS` now accepts the options: `{ detectCycles?: boolean, exportMapsAsObjects?: boolean }`, both `true` by default
-* Observable maps are now backed by real ES6 Maps. This means that any value can be used as key now, not just strings and numbers.
-* The flow typings have been updated. Since this is a manual effort, there can be mistakes, so feel free to PR!
-
-* `computed(fn, options?)` / `@computed(options) get fn()` now accept the following options:
- * `set: (value) => void` to set a custom setter on the computed property
- * `name: "debug name"`
- * `equals: fn` the equality value to use for the computed to determine whether its output has changed. The default is `comparer.default`. Alternatives are `comparer.structural`, `comparer.identity` or just your own comparison function.
- * `requiresReaction: boolean` see above.
-
-* `autorun(fn, options?)` now accepts the following options:
- * `delay: number` debounce the autorun with the given amount of milliseconds. This replaces the MobX 3 api `autorunAsync`
- * `name: "debug name"`
- * `scheduler: function` a custom scheduler to run the autorun. For example to connect running the autorun to `requestAnimationFrame`. See the docs for more details
- * `onError`. A custom error handler to be notified when an autorun throws an exception.
-
-* `reaction(expr, effect, options?)` now accepts the following options:
- * `delay: number` debounce the autorun with the given amount of milliseconds. This replaces the MobX 3 api `autorunAsync`
- * `fireImmediately`. Immediately fire the effect function after the first evaluation of `expr`
- * `equals`. Custom equality function to determine whether the `expr` function differed from its previous result, and hence should fire `effect`. Accepts the same options as the `equals` option of computed.
- * All the options `autorun` accepts
-
-* `when(predicate, effect?, options?)` now accepts the following options:
- * `name: "debug name"`
- * `onError`. A custom error handler to be notified when an autorun throws an exception.
- * `timeout: number` a timeout in milliseconds, after which the `onError` handler will be triggered to signal the condition not being met within a certain time
-* The `effect` parameter of `when` has become optional. If it is omitted, `when` will return a promise. This makes it easy to `await` a condition, for example: `await when(() => user.profile.loaded)`. The returned promise can be cancelled using `promise.cancel()`
-
-* There is now an utility API that enables manipulating observable maps, objects and arrays with the same api. These api's are fully reactive, which means that even new property declarations can be detected by mobx if `set` is used to add them, and `values` or `keys` to iterate them.
- * `values(thing)` returns all values in the collection as array
- * `keys(thing)` returns all keys in the collection as array
- * `set(thing, key, value)` or `set(thing, { key: value })` Updates the given collection with the provided key / value pair(s).
- * `remove(thing, key)` removes the specified child from the collection. For arrays splicing is used.
- * `has(thing, key)` returns true if the collection has the specified _observable_ property.
- * `get(thing, key)` returns the chlid under the specified key.
-
-* `observable`, `observable.array`, `observable.object`, `observable.map` and `extendObservable` now accept an additional options object, which can specify the following attributes:
- * `name: "debug name"`
- * `deep: boolean`. `true` by default, indicates whether the children of this collection are automatically converted into observables as well.
- * `defaultDecorator: ` specifies the default decorator used for new children / properties, by default: `observable.deep`, but could be changed to `observable.ref`, `observable.struct` etc. (The `deep` property is just a short-hand for switching between `observable.deep` or `observable.ref` as default decorator for new properties)
-
-
-### Breaking changes
-
-The changes mentioned here are discussed in detail in the [migration notes](https://github.com/mobxjs/mobx/wiki/Migrating-from-mobx-3-to-mobx-4)
-
-* MobX 4 requires `Map` to be globally available. Polyfill it if targeting IE < 11 or other older browsers.
-* For typescript users, MobX now requires `Map` and several `Symbol`s to exist for its typings. So make sure that the `lib` configuration of your project is set to `"es6"`. (The compilation target can still be `"es5"`)
-* `observable.shallowArray(values)` has been removed, instead use `observable.array(values, { deep: false })`
-* `observable.shallowMap(values)` has been removed, instead use `observable.map(values, { deep: false })`
-* `observable.shallowObject(values)` has been removed, instead use `observable.object(values, {}, { deep: false })`
-* `extendShallowObservable(target, props)`, instead use `extendObservable(target, props, {}, { deep: false })`
-* The decorators `observable.ref`, `observable.shallow`, `observable.deep`, `observable.struct` can no longer be used as functions. Instead, they should be passed as part of the `decorators` param to resp. `observable.object` and `extendObservable`
-* The new signature of `extendObservable` is `extendObservable(target, props, decorators?, options?)`. This also means it is no longer possible to pass multiple bags of properties to `extendObservable`. `extendObservable` can no longer be used to re-declare properties. Use `set` instead to update existing properties (or introduce new ones).
-* Iterating maps now follows the spec, that is, `map.values()`, `map.entries()`, `map.keys()`, `map[@@iterator]()` and `array[@@iterator]()` no longer return an array, but an iterator. Use `mobx.values(map)` or `Array.from(map)` to convert the iterators to arrays.
-* dropped `@computed.equals`, instead, you can now use `@computed({ equals: ... })`
-* `useStrict(boolean)` was dropped, use `configure({ enforceActions: boolean })` instead
-* `isolateGlobalState` was dropped, use `configure({ isolateGlobalState: true})` instead
-* If there are multiple mobx instances active in a single project, an exception will be thrown. Previously only a warning was printed. Fixes #1098. For details, see [#1082](https://github.com/mobxjs/mobx/issues/1082).
-* Dropped the `shareGlobalState` feature. Instead, projects should be setup properly and it is up to the hosting package to make sure that there is only one MobX instance
-* `expr` has been moved to mobx-utils. Remember, `expr(fn)` is just `computed(fn).get()`
-* `createTransformer` has been moved to mobx-utils
-* Passing `context` explicitly to `autorun`, `reaction` etc is no longer supported. Use arrow functions or function.bind instead.
-* Removed `autorunAsync`. Use the `delay` option of `autorun` instead.
-* `autorun`, `when`, `reaction` don't support name as first argument anymore, instead pass the `name` option.
-* The `extras.` namespace has been dropped to enable tree-shaking non-used MobX features. All methods that where there originally are now exported at top level. If they are part of the official public API (you are encouraged to use them) they are exported as is. If they are experimental or somehow internal (you are discouraged to use them), they are prefixed with `_`.
-* Dropped bower support. Fixes #1263
-* The `spyReportStart`, `spyReportEnd`, `spyReport` and `isSpyEnabled` are no longer public. It is no longer possible to emit custom spy events as to avoid confusing in listeners what the possible set of events is.
-* Dropped `isStrictModeEnabled`
-* `observable(value)` will only succeed if it can turn the value into an observable data structure (a Map, Array or observable object). But it will no longer create an observable box for other values to avoid confusion. Call `observable.box(value)` explictly in such cases.
-* `isComputed` and `isObservable` no longer accept a property as second argument. Instead use `isComputedProp` and `isObservableProp`.
-* Removed `whyRun`, use `trace` instead
-* The spy event signature has slightly changed
-* The `Atom` class is no longer exposed. Use `createAtom` instead (same signature).
-* Calling reportObserved() on a self made atom will no longer trigger the hooks if reportObserved is triggered outside a reactive context.
-* The options `struct` and `compareStructural` for computed values are deprecated, use `@computed.struct` or `computed({ equals: comparer.structural})` instead.
-* `isModifierDescriptor` is no longer exposed.
-* `deepEqual` is no longer exposed, use `comparer.structural` instead.
-* `setReactionScheduler` -> `configure({ reactionScheduler: fn })`
-* `reserveArrayBuffer` -> `configure({ reactionErrorHandler: fn })`
-* `ObservableMap` is no longer exposed as constructor, use `observable.map` or `isObservableMap` instead
-* `map` -> `observable.map`
-* `runInAction` no longer accepts a custom scope
-* Dropped the already deprecated and broken `default` export that made it harder to tree-shake mobx. Make sure to always use `import { x } from "mobx"` and not `import mobx from "mobx"`.
-* Killed the already deprecated modifiers `asFlat` etc. If you war still using this, see the MobX 2 -> 3 migration notes.
-* Observable maps now fully implement the map interface. See [#1361](https://github.com/mobxjs/mobx/pull/1361) by [Marc Fallows](https://github.com/marcfallows)
-* Observable arrays will no longer expose the `.move` method
-* Dropped the `observable.deep.struct` modifier
-* Dropped the `observable.ref.struct` modifier
-* `observable.struct` now behaves like `observable.ref.struct` (this used to be `observable.deep.struct`). That is; values in an `observable.struct` field will be stored as is, but structural comparison will be used when assigning a new value
-* IReactionDisposer.onError has been removed, use the `onError` option of reactions instead
-
-### Issues fixed in this release:
-
-The issues are incoprorated in the above notes.
-
-* [#1316](https://github.com/mobxjs/mobx/issues/1316) - Improve `observable` api
-* [#992](https://github.com/mobxjs/mobx/issues/992) - `onBecomeObserved` & `onBecomeUnobserved`
-* [#1301](https://github.com/mobxjs/mobx/issues/1301) - Set `onError` handler when creating reactions
-* [#817](https://github.com/mobxjs/mobx/issues/817) - Improve typings of `observe`
-* [#800](https://github.com/mobxjs/mobx/issues/800) - Use `Map` as backend implementation of observable maps
-* [#1361](https://github.com/mobxjs/mobx/issues/1361) - Make observableMaps structurally correct maps
-* [#813](https://github.com/mobxjs/mobx/issues/813) - Create separate dev and production builds
-* [#961](https://github.com/mobxjs/mobx/issues/961), [#1197](https://github.com/mobxjs/mobx/issues/1197) - Make it possible to forbid reading an untracked computed value
-* [#1098](https://github.com/mobxjs/mobx/issues/1098) - Throw instead of warn if multiple MobX instances are active
-* [#1122](https://github.com/mobxjs/mobx/issues/1122) - Atom hooks fired to often for observable maps
-* [#1148](https://github.com/mobxjs/mobx/issues/1148) - Disposer of reactions should also cancel all scheduled effects
-* [#1241](https://github.com/mobxjs/mobx/issues/1241) - Make it possible to disable error boundaries, to make it easier to find exceptions
-* [#1263](https://github.com/mobxjs/mobx/issues/1263) - Remove bower.json
-
-# 3.6.2
-
-* Fixed accidental dependency on the `node` typings. Fixes [#1387](https://github.com/mobxjs/mobx/issues/1387) / [#1362](https://github.com/mobxjs/mobx/issues/1387)
-
-# 3.6.1
-
-* Fixed [#1358](https://github.com/mobxjs/mobx/pull/1359): Deep comparison failing on IE11. By [le0nik](https://github.com/le0nik) through [#1359](https://github.com/mobxjs/mobx/pull/1359)
-
-# 3.6.0
-
-* Fixed [#1118](https://github.com/mobxjs/mobx/issues/1118), the deepEquals operator build into mobx gave wrong results for non-primitive objects. This affected for example `computed.struct`, or the `compareStructural` of `reaction`
-
-# 3.5.0/1
-
-* Introduced `trace` for easier debugging of reactions / computed values. See the [docs](https://mobx.js.org/best/trace.html) for details.
-* Improved typings of `observableArray.find`, see [#1324](https://github.com/mobxjs/mobx/pull/1324) for details.
-
-# 3.4.1
-
-* Republished 3.4.0, because the package update doesn't seem to distibute consistently through yarn / npm
-
-# 3.4.0
-
-* Improve Flow support by exposing typings regularly. Flow will automatically include them now. In your `.flowconfig` will have to remove the import in the `[libs]` section (as it's done [here](https://github.com/mobxjs/mobx/pull/1254#issuecomment-348926416)). Fixes [#1232](https://github.com/mobxjs/mobx/issues/1232).
-
-# 3.3.3
-
-* Fixed regression bug where observable map contents could not be replaced using another observable map [#1258](https://github.com/mobxjs/mobx/issues/1258)
-* Fixed weird exception abot not being able to read `length` property of a function, see[#1238](https://github.com/mobxjs/mobx/issues/1238) through [#1257](https://github.com/mobxjs/mobx/issues/1238) by @dannsam
-
-# 3.3.2
-
-* Fix bug where custom comparers could be invoked with `undefined` values. Fixes [#1208](https://github.com/mobxjs/mobx/issues/1208)
-* Make typings for observable stricter when using flow [#1194](https://github.com/mobxjs/mobx/issues/1194), [#1231](https://github.com/mobxjs/mobx/issues/1231)
-* Fix a bug where `map.replace` would trigger reactions for unchanged values, fixes [#1243](https://github.com/mobxjs/mobx/issues/1243)
-* Fixed issue where `NaN` was considered unequal to `NaN` when a deep compare was made [#1249](https://github.com/mobxjs/mobx/issues/1249)
-
-# 3.3.1
-
-* Fix bug allowing maps to be modified outside actions when using strict mode, fixes [#940](https://github.com/mobxjs/mobx/issues/940)
-* Fixed [#1139](https://github.com/mobxjs/mobx/issues/1139) properly: `transaction` is no longer deprecated and doesn't disable tracking properties anymore
-* Fixed [#1120](https://github.com/mobxjs/mobx/issues/1139): `isComputed` should return false for non-existing properties
-
-# 3.3.0
-
-* Undeprecated `transaction`, see [#1139](https://github.com/mobxjs/mobx/issues/1139)
-* Fixed typings of reaction [#1136](https://github.com/mobxjs/mobx/issues/1136)
-* It is now possible to re-define a computed property [#1121](https://github.com/mobxjs/mobx/issues/1121)
-* Print an helpful error message when using `@action` on a getter [#971](https://github.com/mobxjs/mobx/issues/971)
-* Improved typings of intercept [#1119](https://github.com/mobxjs/mobx/issues/1119)
-* Made code base Prettier [#1103](https://github.com/mobxjs/mobx/issues/1103)
-* react-native will now by default use the es module build as well.
-* Added support for Weex, see [#1163](https://github.com/mobxjs/mobx/pull/1163/)
-* Added workaround for Firefox issue causing MobX to crash, see [#614](https://github.com/mobxjs/mobx/issues/614)
-
-# 3.2.2
-
-* Fixes a bug (or a known limitation) described in [#1092](https://github.com/mobxjs/mobx/issue/1092/). It is now possible to have different observable administration on different levels of the prototype chain. By @guillaumeleclerc
-* Fixed a build issue when using mobx in a project that was using rollup, fixes [#1099](https://github.com/mobxjs/mobx/issue/1099/) by @rossipedia
-* Fixed typings of `useStrict`, by @rickbeerendonk
-
-# 3.2.1
-
-* Introduced customizable value comperators to reactions and computed values. `reaction` and `computed` now support an additional option, `equals`, which takes a comparision function. See [#951](https://github.com/mobxjs/mobx/pull/951/) by @jamiewinder. Fixes #802 and #943. See the updated [`computed` docs](https://mobx.js.org/refguide/computed-decorator.html) for more details.
-
-# 3.2.0
-
-* MobX will warn again when there are multiple instances of MobX loaded, as this lead to often to confusing bugs if the project setup was not properly. The signal mobx that multiple instances are loaded on purpose, use `mobx.extras.runInSandbox`. See [#1082](https://github.com/mobxjs/mobx/issues/1082) for details.
-
-# 3.1.17
-
-* Improved typings of `IObservableArray.intercept`: use more restrictive types for `change` parameter of `handler`, by @bvanreeven
-* Fixed [#1072](https://github.com/mobxjs/mobx/issues/1072), fields without a default value could not be observed yet when using TypeScript
-
-# 3.1.16
-
-* Restored `default` export (and added warning), which broke code that was importing mobx like `import mobx from "mobx"`. Use `import * as mobx from "mobx"` or use named importes instead. By @andykog, see #1043, #1050
-* Fixed several typos in exceptions and documentation
-
-# 3.1.15
-
-* Fixed issue where `array.remove` did not work correctly in combination with `extras.interceptReads`
-
-# 3.1.14
-
-* Fixed 3.1.12 / 3.1.13 module packing. See #1039; `module` target is now transpiled to ES5 as well
-
-# 3.1.13 (Unpublished: Uglify chokes on it in CRA)
-
-* Fixed build issue with webpack 2, see #1040
-
-# 3.1.12 (Unpublished: wasn't being bundled correctly by all bundlers)
-
-* Added support for ES modules. See #1027 by @rossipedia
-* Improved flow typings. See #1019 by @fb55
-* Introduced experimental feature `extras.interceptReads(observable: ObservableMap | ObservableArray | ObservableObject | ObservableBox, property?: string, handler: value => value): Disposer` that can be used to intercept _reads_ from observable objects, to transform values on the fly when a value is read. One can achieve similar things with this as with proxying reads. See #1036
-
-# 3.1.11
-
-* Using rollup as bundler, instead of custom hacked build scripts, by @rossipedia, see #1023
-
-# 3.1.10
-
-* Fixed flow typings for `when`, by @jamsea
-* Add flow typings for `map.replace`, by @leader22
-* Added `observableArray.findIndex`, by @leader22
-* Improved typings of `autorun` / `autorunAsync` to better support async / await, by @capaj
-* Fixed typings of `action.bound`, see #803
-
-# 3.1.9
-
-* Introduced explicit `.get(index)` and `.set(index, value)` methods on observable arrays, for issues that have trouble handling many property descriptors on objects. See also #734
-* Made sure it is safe to call `onBecomeObserved` twice in row, fixes #874, #898
-* Fixed typings of `IReactionDisposer`
-
-# 3.1.8
-
-* Fixed edge case where `autorun` was not triggered again if a computed value was invalidated by the reaction itself, see [#916](https://github.com/mobxjs/mobx/issues/916), by @andykog
-* Added support for primtive keys in `createTransformer`, See #920 by @dnakov
-* Improved typings of `isArrayLike`, see #904, by @mohsen1
-
-# 3.1.7
-
-* Reverted ES2015 module changes, as they broke with webpack 2 (will be re-released later)
-
-# 3.1.6 (Unpublished)
-
-* Expose ES2015 modules to be used with advanced bundlers, by @mohsen1, fixes #868
-* Improved typings of `IObservableArray.intercept`: remove superflous type parameter, by @bvanreeven
-* Improved typings of map changes, by @hediet
-
-# 3.1.5
-
-* Improved typings of map changes, see #847, by @hediet
-* Fixed issue with `reaction` if `fireImmediately` was combined with `delay` option, see #837, by @SaboteurSpk
-
-# 3.1.4
-
-* Observable maps initialized from ES6 didn't deeply convert their values to observables. (fixes #869,by @ggarek)
-
-# 3.1.3
-
-* Make sure that `ObservableArray.replace` can handle large arrays by not using splats internally. (See e.g. #859)
-* Exposed `ObservableArray.spliceWithArray`, that unlike a normal splice, doesn't use a variadic argument list so that it is possible to splice in new arrays that are larger then allowed by the callstack.
-
-# 3.1.2
-
-* Fixed incompatiblity issue with `mobx-react@4.1.0`
-
-# 3.1.1 (unpublished)
-
-* Introduced `isBoxedObservable(value)`, fixes #804
-
-# 3.1.0
-
-### Improved strict mode
-
-Strict mode has been relaxed a bit in this release. Also computed values can now better handle creating new observables (in an action if needed). The semantics are now as follows:
-
-* In strict mode, it is not allowed to modify state that is already being _observed_ by some reaction.
-* It is allowed to create and modify observable values in computed blocks, as long as they are not _observed_ yet.
-
-In order words: Observables that are not in use anywhere yet, are not protected by MobX strict mode.
-This is fine as the main goal of strict mode is to avoid kicking of reactions at undesired places.
-Also strict mode enforces batched mutations of observables (through action).
-However, for unobserved observables this is not relevant; they won't kick of reactions at all.
-
-This fixes some uses cases where one now have to jump through hoops like:
-* Creating observables in computed properties was fine already, but threw if this was done with the aid of an action. See issue [#798](https://github.com/mobxjs/mobx/issues/798).
-* In strict mode, it was not possible to _update_ observable values without wrapping the code in `runInAction` or `action`. See issue [#563](https://github.com/mobxjs/mobx/issues/563)
-
-Note that the following constructions are still anti patterns, although MobX won't throw anymore on them:
-* Changing unobserved, but not just created observables in a computed value
-* Invoke actions in computed values. Use reactions like `autorun` or `reaction` instead.
-
-Note that observables that are not in use by a reaction, but that have `.observe` listeners attached, do *not* count towards being observed.
-Observe and intercept callbacks are concepts that do not relate to strict mode, actions or transactions.
-
-### Other changes
-
-* Reactions and observable values now consider `NaN === NaN`, See #805 by @andykog
-* Merged #783: extract error messages to seperate file, so that they can be optimized in production builds (not yet done), by @reisel, #GoodnessSquad
-* Improved typings of actions, see #796 by @mattiamanzati
-
-# 3.0.2
-
-* Fixed issue where MobX failed on environments where `Map` is not defined, #779 by @dirtyrolf
-* MobX can now be compiled on windows as well! #772 by @madarauchiha #GoodnessSquad
-* Added documentation on how Flow typings can be used, #766 by @wietsevenema
-* Added support for `Symbol.toPrimitive()` and `valueOf()`, see #773 by @eladnava #GoodnessSquad
-* Supressed an exception that was thrown when using the Chrome Developer tools to inspect arrays, see #752
-
-Re-introduced _structural comparison_. Seems we couldn't part from it yet :). So the following things have been added:
-
-* `struct` option to `reaction` (alias for `compareStructural`, to get more consistency in naming)
-* `observable.struct`, as alias for `observable.deep.struct`
-* `observable.deep.struct`: Only stores a new value and notify observers if the new value is not structurally the same as the previous value. Beware of cycles! Converts new values automatically to observables (like `observable.deep`)
-* `observable.ref.struct`: Only stores a new value and notify observers if the new value is not structurally the same as the previous value. Beware of cycles! Doesn't convert the new value into observables.
-* `extras.deepEquals`: Check if two data structures are deeply equal. supports observable and non observable data structures.
-
-# 3.0.1
-
-* `toString()` of observable arrays now behaves like normal arrays (by @capaj, see #759)
-* Improved flow types of `toJS`by @jamsea (#758)
-
-# 3.0.0
-
-The changelog of MobX 3 might look quite overwhelming, but migrating to MobX 3 should be pretty straight forward nonetheless.
-The api has now become more layered, and the api is more uniform and modifiers are cleaned up.
-In practice, you should check your usage of modifiers (`asFlat`, `asMap` etc.). Besides that the migration should be pretty painless.
-Please report if this isn't the case!
-Note that no changes to the runtime algorithm where made, almost all changes evolve in making the creation of observables more uniform, and removing deprecated stuff.
-
-## `observable` api has been redesigned
-
-The api to create observables has been redesigned.
-By default, it keeps the automatic conversion behavior from MobX 2.
-However, one can now have more fine grained control on how / which observables are constructed.
-Modifiers still exists, but they are more regular, and there should be less need for them.
-
-### `observable(plainObject)` will no longer enhance objects, but clone instead
-
-When passing a plain object to `observable`, MobX used to modify that object inplace and give it observable capabilities.
-This also happened when assigning a plain object to an observable array etc.
-However, this behavior has changed for a few reasons
-
-1. Both arrays and maps create new data structure, however, `observable(object)` didn't
-2. It resulted in unnecessary and confusing side effects. If you passed an object you received from some api to a function that added it, for example, to an observable collection. Suddenly your object would be modified as side effect of passing it down to that function. This was often confusing for beginners and could lead to subtle bugs.
-3. If MobX in the future uses Proxies behind the scenes, this would need to change as well
-
-If you want, you can still enhance existing plainObjects, but simply using `extendObservable(data, data)`. This was actually the old implementation, which has now changed to `extendObservable({}, data)`.
-
-As always, it is best practice not to have transportation objects etc lingering around; there should be only one source of truth, and that is the data that is in your observable state.
-If you already adhered to this rule, this change won't impact you.
-
-See [#649](https://github.com/mobxjs/mobx/issues/649)
-
-### Factories per observable type
-
-There are now explicit methods to create an observable of a specific type.
-
-* `observable.object(props, name?)` creates a new observable object, by cloning the give props and making them observable
-* `observable.array(initialValues, name?)`. Take a guess..
-* `observable.map(initialValues, name?)`
-* `observable.box(initialValue, name?)`. Creates a [boxed](http://mobxjs.github.io/mobx/refguide/boxed.html) value, which can be read from / written to using `.get()` and `.set(newValue)`
-* `observable(value)`, as-is, based on the type of `value`, uses any of the above four functions to create a new observable.
-
-### Shallow factories per type
-
-The standard observable factories create observable structures that will try to turn any plain javascript value (arrays, objects or Maps) into observables.
-Allthough this is fine in most cases, in some cases you might want to disable this autoconversion.
-For example when storing objects from external libraries.
-In MobX 2 you needed to use `asFlat` or `asReference` modifiers for this.
-In MobX 3, there are factories to directly create non-converting data structures:
-
-* `observable.shallowObject(props, name?)`
-* `observable.shallowArray(initialValues, name?)`
-* `observable.shallowMap(initialValues, name?)`
-* `observable.shallowBox(initialValue, name?)`
-
-So for example, `observable.shallowArray([todo1, todo2])` will create an observable array, but it won't try to convert the todos inside the array into observables as well.
-
-### Shallow properties
-
-The `@observable` decorator can still be used to introduce observable properties. And like in MobX 2, it will automatically convert its values.
-
-However, sometimes you want to create an observable property that does not convert its _value_ into an observable automatically.
-Previously that could be written as `@observable x = asReference(value)`.
-
-### Structurally comparison of observables have been removed
-
-This was not for a technical reason, but they just seemed hardly used.
-Structural comparision for computed properties and reactions is still possible.
-Feel free to file an issue, including use case, to re-introduce this feature if you think you really need it.
-However, we noticed that in practice people rarely use it. And in cases where it is used `reference` / `shallow` is often a better fit (when using immutable data for example).
-
-### Modifiers
-
-Modifiers can be used in combination `@observable`, `extendObservable` and `observable.object` to change the autoconversion rules of specific properties.
-
-The following modifiers are available:
-
-* `observable.deep`: This is the default modifier, used by any observable. It converts any assigned, non-primitive value into an observable value if it isn't one yet.
-* `observable.ref`: Disables automatic observable conversion, just creates an observable reference instead.
-* `observable.shallow`: Can only used in combination with collections. Turns any assigned collection into an collection, which is shallowly observable (instead of deep)
-
-Modifiers can be used as decorator:
-
-```javascript
-class TaskStore {
- @observable.shallow tasks = []
-}
-```
-
-Or as property modifier in combination with `observable.object` / `observable.extendObservable`.
-Note that modifiers always 'stick' to the property. So they will remain in effect even if a new value is assigned.
-
-```javascript
-const taskStore = observable({
- tasks: observable.shallow([])
-})
-```
-
-See [modifiers](http://mobxjs.github.io/mobx/refguide/modifiers.html)
-
-### `computed` api has been simplified
-
-Using `computed` to create boxed observables has been simplified, and `computed` can now be invoked as follows:
-* `computed(expr)`
-* `computed(expr, setter)`
-* `computed(expr, options)`, where options is an object that can specify one or more of the following fields: `name`, `setter`, `compareStructural` or `context` (the "this").
-
-Computed can also be used as a decorator:
-
-* `@computed`
-* `@computed.struct` when you want to compareStructural (previously was `@computed({asStructure: true})`)
-
-### `reaction` api has been simplified
-
-The signature of `reaction` is now `reaction(dataFunc, effectFunc, options?)`, where the following options are accepted:
-
-* `context`: The `this` to be used in the functions
-* `fireImmediately`
-* `delay`: Number in milliseconds that can be used to debounce the effect function.
-* `compareStructural`: `false` by default. If `true`, the return value of the *data* function is structurally compared to its previous return value, and the *effect* function will only be invoked if there is a structural change in the output.
-* `name`: String
-
-### Bound actions
-
-It is now possible to create actions and bind them in one go using `action.bound`. See [#699](https://github.com/mobxjs/mobx/issues/699).
-This means that now the following is possible:
-
-```javascript
-class Ticker {
- @observable tick = 0
-
- @action.bound
- increment() {
- this.tick++ // 'this' will always be correct
- }
-}
-
-const ticker = new Ticker()
-setInterval(ticker.increment, 1000)
-```
-
-### Improve error handling
-
-Error handling in MobX has been made more consistent. In MobX 2 there was a best-effort recovery attempt if a derivation throws, but MobX 3 introduced
-more consistent behavior:
-
-* Computed values that throw, store the exception and throw it to the next consumer(s). They keep tracking their data, so they are able to recover from exceptions in next re-runs.
-* Reactions (like `autorun`, `when`, `reaction`, `render()` of `observer` components) will always catch their exceptions, and just log the error. They will keep tracking their data, so they are able to recover in next re-runs.
-* The disposer of a reaction exposes an `onError(handler)` method, which makes it possible to attach custom error handling logic to an reaction (that overrides the default logging behavior).
-* `extras.onReactionError(handler)` can be used to register a global onError handler for reactions (will fire after spy "error" event). This can be useful in tests etc.
-
-See [#731](https://github.com/mobxjs/mobx/issues/731)
-
-### Removed error handling, improved error recovery
-
-MobX always printed a warning when an exception was thrown from a computed value, reaction or react component: `[mobx] An uncaught exception occurred while calculating....`.
-This warning was often confusing for people because they either had the impression that this was a mobx exception, while it actually is just informing about an exception that happened in userland code.
-And sometimes, the actual exception was silently caught somewhere else.
-MobX now does not print any warnings anymore, and just makes sure its internal state is still stable.
-Not throwing or handling an exception is now entirely the responsibility of the user.
-
-Throwing an exception doesn't revert the causing mutation, but it does reset tracking information, which makes it possible to recover from exceptions by changing the state in such a way that a next run of the derivation doesn't throw.
-
-### Flow-Types Support 🎉🎉🎉
-
-Flow typings have been added by [A-gambit](https://github.com/A-gambit).
-Add flow types for methods and interfaces of observable variables:
-
-```js
-const observableValue: IObservableValue = observable(1)
-const observableArray: IObservableArray = observable([1,2,3])
-
-const sum: IComputedValue = computed(() => {
- return observableArray.reduce((a: number, b: number): number => a + b, 0)
-})
-```
-
-See [#640](https://github.com/mobxjs/mobx/issues/640)
-
-### MobX will no longer share global state by default
-
-For historical reasons (at Mendix), MobX had a feature that it would warn if different versions of the MobX package are being loaded into the same javascript runtime multiple times.
-This is because multiple instances by default try to share their state.
-This allows reactions from one package to react to observables created by another package,
-even when both packages where shipped with their own (embedded) version of MobX (!).
-
-Obviously this is a nasty default as it breaks package isolation and might actually start to throw errors unintentionally when MobX is loaded multiple times in the same runtime by completely unrelated packages.
-So this sharing behavior is now by default turned off.
-Sharing MobX should be achieved by means of proper bundling, de-duplication of packages or using peer dependencies / externals if needed.
-This is similar to packages like React, which will also bail out if you try to load it multiple times.
-
-If you still want to use the old behavior, this can be achieved by running `mobx.extras.shareGlobalState()` on _all_ packages that want to share state with each other.
-Since this behavior is probably not used outside Mendix, it has been deprecated immediately, so if you rely on this feature, please report in #621, so that it can be undeprecated if there is no more elegant solution.
-
-See [#621](https://github.com/mobxjs/mobx/issues/621)
-
-### Using the @action decorator inside individual objects
-
-Don't use the `@action` decorator on an individual object that you pass to `observable()` or `extendObservable()`. If you have code that looks like `observable({ @action f: () => {})`, you should change it to `observable({ f: action(() => {})`.
-
-Whether or not this was ever a good idea is debatable, but it stopped working in this version. If you're using classes, it's a non-issue.
-
-### Other changes
-
-* **Breaking change:** The arguments to `observe` listeners for computed and boxed observables have changed and are now consistent with the other apis. Instead of invoking the callback with `(newValue: T, oldValue: T)` they are now invoked with a single change object: `(change: {newValue: T, oldValue: T, object, type: "update"})`
-* Using transaction is now deprecated, use `action` or `runInAction` instead. Transactions now will enter an `untracked` block as well, just as actions, which removes the conceptual difference.
-* Upgraded to typescript 2
-* It is now possible to pass ES6 Maps to `observable` / observable maps. The map will be converted to an observable map (if keys are string like)
-* Made `action` more debug friendly, it should now be easier to step through
-* ObservableMap now has an additional method, `.replace(data)`, which is a combination of `clear()` and `merge(data)`.
-* Passing a function to `observable` will now create a boxed observable refering to that function
-* Fixed #603: exceptions in transaction breaks future reactions
-* Fixed #698: createTransformer should support default arguments
-* Transactions are no longer reported grouped in spy events. If you want to group events, use actions instead.
-* Normalized `spy` events further. Computed values and actions now report `object` instead of `target` for the scope they have been applied to.
-* The following deprecated methods have been removed:
- * `transaction`
- * `autorunUntil`
- * `trackTransitions`
- * `fastArray`
- * `SimpleEventEmitter`
- * `ObservableMap.toJs` (use `toJS`)
- * `toJSlegacy`
- * `toJSON` (use `toJS`)
- * invoking `observe` and `inject` with plain javascript objects
-
----
-
-# 2.7.0
-
-### Automatic inference of computed properties has been deprecated.
-
-A deprecation message will now be printed if creating computed properties while relying on automatical inferrence of argumentless functions as computed values. In other words, when using `observable` or `extendObservable` in the following manner:
-
-```javascript
-const x = observable({
- computedProp: function() {
- return someComputation
- }
-})
-
-// Due to automatic inferrence now available as computed property:
-x.computedProp
-// And not !
-x.computedProp()
-```
-
-Instead, to create a computed property, use:
-
-```javascript
-observable({
- get computedProp() {
- return someComputation
- }
-})
-```
-
-or alternatively:
-
-```javascript
-observable({
- computedProp: computed(function() {
- return someComputation
- })
-})
-```
-
-This change should avoid confusing experiences when trying to create methods that don't take arguments.
-The current behavior will be kept as-is in the MobX 2.* range,
-but from MobX 3 onward the argumentless functions will no longer be turned
-automatically into computed values; they will be treated the same as function with arguments.
-An observable _reference_ to the function will be made and the function itself will be preserved.
-See for more details [#532](https://github.com/mobxjs/mobx/issues/532)
-
-N.B. If you want to introduce actions on an observable that modify its state, using `action` is still the recommended approach:
-
-```javascript
-observable({
- counter: 0,
- increment: action(function() {
- this.counter++
- })
-})
-```
-
-By the way, if you have code such as:
-
-```
-observable({
- @computed get someProp() { ... }
-});
-```
-
-That code will no longer work. Rather, reactions will fail silently. Remove `@computed`.
-Note, this only applies when using observable in this way; it doesn't apply when using
-`@observable` on a property within a class declaration.
-
-### Misc
-
-* Fixed #701: `toJS` sometimes failing to convert objects decorated with `@observable` (cause: `isObservable` sometimes returned false on these object)
-* Fixed typings for `when` / `autorun` / `reaction`; they all return a disposer function.
-
-
-# 2.6.5
-
-* Added `move` operation to observable array, see [#697](https://github.com/mobxjs/mobx/pull/697)
-
-# 2.6.4
-
-* Fixed potential clean up issue if an exception was thrown from an intercept handler
-* Improved typings of `asStructure` (by @nidu, see #687)
-* Added support for `computed(asStructure(() => expr))` (by @yotambarzilay, see #685)
-
-# 2.6.3
-
-* Fixed #603: exceptions in transaction breaks future reactions
-* Improved typings of `toJS`
-* Introduced `setReactionScheduler`. Internal api used by mobx-react@4 to be notified when reactions will be run
-
-# 2.6.2
-
-* Changes related to `toJS` as mentioned in version `2.6.0` where not actually shipped. This has been fixed, so see release notes below.
-
-# 2.6.1
-
-* Introduced convenience `isArrayLike`: returns whether the argument is either a JS- or observable array. By @dslmeinte
-* Improved readme. By @DavidLGoldberg
-* Improved assertion message, by @ncammarate (See [#618](https://github.com/mobxjs/mobx/pull/618))
-* Added HashNode badge, by @sandeeppanda92
-
-# 2.6.0
-
-_Marked as minor release as the behavior of `toJS` has been changed, which might be interpreted both as bug-fix or as breaking change, depending of how you interpreted the docs_
-
-* Fixed [#566](https://github.com/mobxjs/mobx/pull/566): Fixed incorrect behavior of `toJS`: `toJS` will now only recurse into observable object, not all objects. The new behavior is now aligned with what is suggested in the docs, but as a result the semantics changed a bit. `toJSlegacy` will be around for a while implementing the old behavior. See [#589](See https://github.com/mobxjs/mobx/pull/589) for more details.
-* Fixed [#571](https://github.com/mobxjs/mobx/pull/571): Don't use `instanceof` operator. Should fix issues if MobX is included multiple times in the same bundle.
-* Fixed [#576](https://github.com/mobxjs/mobx/pull/576): disallow passing actions directly to `autorun`; as they won't be tracked by @jeffijoe
-* Extending observable objects with other observable (objects) is now explicitly forbidden, fixes [#540](https://github.com/mobxjs/mobx/pull/540).
-
-# 2.5.2
-
-* Introduced `isComputed`
-* Observable objects can now have a type: `IObservableObject`, see [#484](https://github.com/mobxjs/mobx/pull/484) by @spiffytech
-* Restored 2.4 behavior of boxed observables inside observable objects, see [#558](https://github.com/mobxjs/mobx/issues/558)
-
-# 2.5.1
-
-* Computed properties can now be created by using getter / setter functions. This is the idiomatic way to introduce computed properties from now on:
-
-```javascript
-const box = observable({
- length: 2,
- get squared() {
- return this.length * this.length
- },
- set squared(value) {
- this.length = Math.sqrt(value)
- }
-})
-```
-
-# 2.5.0
-
-* Core derivation algorithm has received some majore improvements by @asterius1! See below. Pr #452, 489
-* Introduced setters for computed properties, use `computed(expr, setter)` or `@computed get name() { return expr } set name (value) { action }`. `computed` can now be used as modifier in `observable` / `extendObservable`, #421, #463 (see below for example)
-* Introduced `isStrictModeEnabled()`, deprecated `useStrict()` without arguments, see #464
-* Fixed #505, accessing an observable property throws before it is initialized
-
-MobX is now able track and memoize computed values while an (trans)action is running.
-Before 2.5, accessing a computed value during a transaction always resulted in a recomputation each time the computed value was accessed, because one of the upstream observables (might) have changed.
-In 2.5, MobX actively tracks whether one of the observables has changed and won't recompute computed values unnecessary.
-This means that computed values are now always memoized for the duration of the current action.
-In specific cases, this might signficantly speed up actions that extensively make decisions based on computed values.
-
-Example:
-```javascript
-class Square {
- @observable length = 2
- @computed get squared() {
- return this.length * this.length
- }
- // mobx now supports setters for computed values
- set squared(surfaceSize) {
- this.length = Math.sqrt(surfaceSize)
- }
-
- // core changes make actions more efficient if extensively using computed values:
- @action stuff() {
- this.length = 3
- console.log(this.squared) // recomputes in both 2.5 and before
- console.log(this.squared) // no longer recomputes
- this.length = 4
- console.log(this.squared) // recomputes in both 2.5 and before
- // after the action, before 2.5 squared would compute another time (if in use by a reaction), that is no longer the case
- }
-}
-```
-
-ES5 example for setters:
-```javascript
-function Square() {
- extendObservable(this, {
- length: 2,
- squared: computed(
- function() {
- return this.squared * this.squared
- },
- function(surfaceSize) {
- this.length = Math.sqrt(surfaceSize)
- }
- )
- })
-}
-```
-
-# 2.4.4
-
-* Fixed #503: map.delete returns boolean
-* Fix return type of `runInAction`, #499 by @Strate
-* Fixed enumerability of observable array methods, see #496.
-* Use TypeScript typeguards, #487 by @Strate
-* Added overloads to `action` for better type inference, #500 by @Strate
-* Fixed #502: `extendObservable` fails on objects created with `Object.create(null)`
-* Implemented #480 / #488: better typings for `asMap`, by @Strate
-
-# 2.4.3
-
-* Objects with a `null` prototype are now considered plain objects as well
-* Improved error message for non-converging cyclic reactions
-* Fixed potential HMR issue
-
-# 2.4.2
-
-* Improved error message when wrongly using `@computed`, by @bb (#450)
-* `observableArray.slice` now automatically converts observable arrays to plain arrays, fixes #460
-* Improved error message when an uncaught exception is thrown by a MobX tracked function
-
-# 2.4.1
-
-* `@action` decorated methods are now configurable. Fixes #441
-* The `onBecomeObserved` event handler is now triggered when an atom is observed, instead of when it is bound as dependency. Fixes #427 and makes atoms easier to extend.
-* if `useStrict()` is invoked without arguments, it now returns the current value of strict mode.
-* the current reaction is now always passed as first argument to the callbacks of `autorun`, `autorunAsync`, `when` and `reaction`. This allows reactions to be immediately disposed during the first run. See #438, by @andykog
-
-# 2.4.0
-
-* _Note: the internal version of MobX has been bumped. This version has no breaking api changes, but if you have MobX loaded multiple times in your project, they all have to be upgraded to `2.4.0`. MobX will report this when starting._
-* Made dependency tracking and binding significant faster. Should result in huge performance improvements when working with large collections.
-* Fixed typescript decorator issue, #423, #425? (by @bb)
-
-# 2.3.7
-
-* Fixed issue where computed values were tracked and accidentally kept alive during actions
-
-# 2.3.6
-* Fixed #406: Observable maps doesn't work with empty initial value in Safari
-* Implemented #357, #348: ObservableMap and ObservableArray now support iterators. Use [`@@iterator()` or iterall](https://github.com/leebyron/iterall) in ES5 environments.
-
-# 2.3.5
-
-* Fixed #364: Observable arrays not reacting properly to index assignments under iOS safari (mobile) 9.1.1 By @andykog
-* Fixed #387: Typings of boxed values
-* Added warning when reading array entries out of bounds. See #381
-
-# 2.3.4
-
-* Fixed #360: Removed expensive cycle detection (cycles are still detected, but a bit later)
-* Fixed #377: `toJS` serialization of Dates and Regexes preserves the original values
-* Fixed #379: `@action` decorated methods can now be inherited / overriden
-
-# 2.3.3
-
-* Fixed #186: Log a warning instead of an error if an exception is thrown in a derivation. Fixes issue where React Native would produce unusable error screens (because it shows the first logged error)
-* Fixed #333: Fixed some interoperability issues in combination with `Reflect` / `InversifyJS` decorators. @andykog
-* Fixed #333: `@observable` class properties are now _owned_ by their instance again, meaning they will show up in `Object.keys()` and `.hasOwnProperty` @andykog
-
-# 2.3.2
-
-* Fixed #328: Fixed exception when inspecting observable in `onBecomeObserved`
-* Fixed #341: `array.find` now returns `undefined` instead of `null` when nothing was found, behavior now matches the docs. (By @hellectronic)
-
-# 2.3.1
-
-* Fixed #327: spy not working with runInAction
-
-# 2.3.0
-
-### Introduced `whyRun`:
-Usage:
-* `whyRun()`
-* `whyRun(Reaction object / ComputedValue object / disposer function)`
-* `whyRun(object, "computed property name")`
-
-`whyRun` is a small utility that can be used inside computed value or reaction (`autorun`, `reaction` or the `render` method of an `observer` React component)
-and prints why the derivation is currently running, and under which circumstances it will run again.
-This should help to get a deeper understanding when and why MobX runs stuff, and prevent some beginner mistakes.
-
-This feature can probably be improved based on your feedback, so feel free to file issues with suggestions!
-
-### Semantic changes:
-* `@observable` is now always defined on the class prototypes and not in the instances. This means that `@observable` properties are enumerable, but won't appear if `Object.keys` or `hasOwnProperty` is used on a class _instance_.
-* Updated semantics of `reaction` as discussed in `#278`. The expression now needs to return a value and the side effect won't be triggered if the result didn't change. `asStructure` is supported in these cases. In contrast to MobX 2.2, effects will no longer be run if the output of the expression didn't change.
-
-### Enhancements
-
-* Introduces `isAction(fn)` #290
-* If an (argumentless) action is passed to `observable` / `extendObservable`, it will not be converted into a computed property.
-* Fixed #285: class instances are now also supported by `toJS`. Also members defined on prototypes which are enumerable are converted.
-* Map keys are now always coerced to strings. Fixes #308
-* `when`, `autorun` and `autorunAsync` now accept custom debug names (see #293, by @jamiewinder)
-* Fixed #286: autoruns no longer stop working if an action throws an exception
-* Implemented `runInAction`, can be used to create on the fly actions (especially useful in combination with `async/await`, see #299
-* Improved performance and reduced mem usage of decorators signficantly (by defining the properties on the prototype if possible), and removed subtle differences between the implementation and behavior in babel and typescript.
-* Updated logo as per #244. Tnx @osenvosem!
-
-# 2.2.2:
-
-* Fixed issue #267: exception when `useStrict(true)` was invoked in combination with `@observable` attributes when using Babel
-* Fixed issue #269: @action in combination with typescript targeting ES6 and reflect.ts
-* Improved compatibility with `JSON.stringify`, removed incorrect deprecation message
-* Improved some error messages
-
-# 2.2.1
-
-* Fixed issue where typescript threw a compile error when using `@action` without params on a field
-* Fixed issue where context was accidentally shared between class instances when using `@action` on a field
-
-# 2.2.0
-
-See the [release announcement](https://medium.com/@mweststrate/45cdc73c7c8d) for the full details of this release:
-
-Introduced:
-* `action` / `@action`
-* `intercept`
-* `spy`
-* `reaction`
-* `useStrict`
-* improved debug names
-* `toJSON` was renamed to `toJS`
-* `observable(asMap())` is the new idiomatic way to create maps
-* the effect of `when` is now untracked, similar to `reaction.
-* `extras.trackTransations` is deprecated, use `spy` instead
-* `untracked` has been undeprecated
-* introduced / documented: `getAtom`, `getDebugName`, `isSpyEnabled`, `spyReport`, `spyReportStart`, `spyReportEnd`
-* deprecated `extras.SimpleEventEmitter`
-* array splice events now also report the `added` collection and `removedCount`
-
-# 2.1.7
-
-* Fixed a false negative in cycle detection, as reported in #236
-
-# 2.1.6
-
-* Fixed #236, #237 call stack issues when working with large arrays
-
-# 2.1.5
-
-* Fix #222 (by @andykog) run `observe` callback of computed properties in untracked mode.
-
-# 2.1.4
-
-* Fixed #201 (see also #160), another iOS enumerability issue... By @luosong
-
-# 2.1.3
-
-* Fixed #191, when using babel, complex field initializers where shared. By @andykog
-* Added `lib/mobx.umd.min.js` for minified cdn builds, see #85
-
-# 2.1.2
-
-* Improved debug names of objects created using a constructor
-* Fixed(?) some issues with iOS7 as reported in #60 by @bstst
-
-# 2.1.1
-
-* Fixed issue where `autorun`s created inside `autorun`s were not always kicked off. (`mobx-react`'s `observer` was not affected). Please upgrade if you often use autorun.
-* Fixed typings of `mobx.map`, a list of entries is also acceptable.
-* (Experimental) Improved error recovery a bit further
-
-# 2.1.0
-
-* MobX is now chatty again when an exception occurs inside a autorun / computed value / React.render. Previously this was considered to be the responsibility of the surrounding code. But if exceptions were eaten this would be really tricky to debug.
-* (Experimental) MobX will now do a poor attempt to recover from exceptions that occured in autorun / computed value / React.render.
-
-# 2.0.6
-
-* `resetGlobalState` is now part of the `mobx.extras` namespace, as it is useful for test setup, to restore inconsistent state after test failures.
-* `resetGlobalState` now also resets the caches of `createTransformer`, see #163.
-
-# 2.0.5
-
-* WIP on bower support
-
-# 2.0.4
-
-* `$transformId` property on transformed objects should be non-enumerable. Fixes #170.
-
-# 2.0.3
-
-* Always peek if inspecting a stale, computed value. Fixes #165.
-
-# 2.0.2
-
-* Fixed issue where changing an object property was tracked, which could lead to unending loops in `autorunAsync`.
-
-# 2.0.1
-
-* Undeprecated `observable(scalar)` (see 143)
-* `expr` no longer prints incorrect deprecated messages (see 143)
-* Requires `mobx` twice no longer fails.
-
-# 2.0.0
-
-## A new name...
-Welcome to ~Mobservable~ MobX 2! First of all, there is the name change.
-The new name is shorter and funnier and it has the right emphasis: MobX is about reactive programming.
-Not about observability of data structures, which is just a technical necessity.
-MobX now has its own [mobxjs](https://github.com/mobxjs) organization on GitHub. Just report an issue if you want to join.
-
-All MobX 2.0 two compatible packages and repos have been renamed. So `mobx-react`, `mobx-react-devtools` etc.
-For the 1.0 versions, use the old `mobservable` based names.
-
-## Migrating from Mobservable 1.x to MobX 2.0
-
-Migrating from Mobservable should be pretty straight-forward as the public api is largely the same.
-However there are some conceptual changes which justifies a Major version bump as it might alter the behavior of MobX in edge cases.
-Besides that, MobX is just a large collection of minor improvements over Mobservable.
-Make sure to remove your old `mobservable` dependencies when installing the new `mobx` dependencies!
-
-## `autorun`s are now allowed to cause cycles!
-`autorun` is now allowed to have cycles. In Mobservable 1 an exception was thrown as soon as an autorun modified a variable which it was reading as well.
-In MobX 2 these situations are now allowed and the autorun will trigger itself to be fired again immediately after the current execution.
-This is fine as long as the autorun terminates within a reasonable amount of iterations (100).
-This should avoid the need for work-arounds involving `setTimeout` etc.
-Note that computed values (created using `observable(func)` are still not allowed to have cycles.
-
-## [Breaking] `observable(scalar)` returns an object instead of a function and has been deprecated.
-
-Creating an observable from a primitive or a reference no longer returns a getter/setter function, but a method with a `.get` and `.set` method.
-This is less confusing, easier to debug and more efficient.
-
-So to read or write from an observable scalar use:
-```javascript
-const temperature = observable(27);
-temperature.set(15); // previously: temperature(15)
-temperature.get(); // previously: temperature()
-```
-
-`observable(scalar)` has been deprecated to make the api smaller and the syntax more uniform. In practice having observable objects, arrays and decorators seems to suffice in 99% of the cases. Deprecating this functionality means that people have simply less concepts to learn. Probably creating observable scalars will continue to work for a long time, as it is important to the internals of MobX and very convenient for testing.
-
-## Introduced `@computed`
-
-MobX introduced the `@computed` decorator for ES6 class properties with getter functions.
-It does technically the same as `@observable` for getter properties. But having a separate decorator makes it easier to communicate about the code.
-`@observable` is for mutable state properties, `@computed` is for derived values.
-
-`@computed` can now also be parameterized. `@computed({asStructure: true})` makes sure that the result of a derivation is compared structurally instead of referentially with its preview value. This makes sure that observers of the computation don't re-evaluate if new structures are returned that are structurally equal to the original ones. This is very useful when working with point, vector or color structures for example. It behaves the same as the `asStructure` modifier for observable values.
-
-`@computed` properties are no longer enumerable.
-
-## MobX is now extensible!
-
-The core algorithm of MobX has been largely rewritten to improve the clarity, extensibility, performance and stability of the source code.
-It is now possible to define your own custom observable data sources by using the `Atom` class.
-It is also possible to create your own reactive functions using the `Reaction` class. `autorun`, `autorunAsync` and `@observer` have now all been implemented using the concept of Reactions.
-So feel free to write your own reactive [constructions](http://mobxjs.github.io/mobx/refguide/extending.html)!
-
-## Mobservable now fails fast
-
-In Mobservable 1 exceptions would be caught and sometimes re-thrown after logging them.
-This was confusing and not all derivations were able to recover from these exceptions.
-In MobX 2 it is no longer allowed for a computed function or `autorun` to throw an exception.
-
-## Improved build
-
-* MobX is roughly 20% faster
-* MobX is smaller: 75KB -> 60KB unminified, and 54KB -> 30KB minified.
-* Distributable builds are no longer available in the git repository, use unpkg instead:
-* Commonjs build: https://unpkg.com/mobx@^2.0.0/lib/mobx.js
-* Minified commonjs build: https://unpkg.com/mobx@^2.0.0/lib/mobx.min.js
-* UMD build: https://unpkg.com/mobx@^2.0.0/lib/mobx.umd.js
-* To use the minified build, require / import the lib from `"mobx/lib/mobx.min.js"` (or set up an alias in your webpack configuration if applicable)
-
-## Other changes
-
-* Improved debug names of all observables. This is especially visible when using `mobx-react-devtools` or `extras.trackTransitions`.
-* Renamed `extras.SimpleEventEmitter` to `SimpleEventEmitter`
-* Removed already deprecated methods: `isReactive`, `makeReactive`, `observeUntil`, `observeAsync`
-* Removed `extras.getDNode`
-* Invoking `ObservableArray.peek` is no longer registered as listener
-* Deprecated `untracked`. It wasn't documented and nobody seems to miss it.
-
-# 1.2.5
-
-* Map no longer throws when `.has`, `.get` or `.delete` is invoked with an invalid key (#116)
-* Files are now compiled without sourcemap to avoid issues when loading mobservable in a debugger when `src/` folder is not available.
-
-# 1.2.4
-
-* Fixed: observable arrays didn't properly apply modifiers if created using `asFlat([])` or `fastArray([])`
-* Don't try to make frozen objects observable (by @andykog)
-* `observableArray.reverse` no longer mutates the arry but just returns a sorted copy
-* Updated tests to use babel6
-
-# 1.2.3
-
-* observableArray.sort no longer mutates the array being sorted but returns a sorted clone instead (#90)
-* removed an incorrect internal state assumption (#97)
-
-# 1.2.2
-
-* Add bower support
-
-# 1.2.1
-
-* Computed value now yields consistent results when being inspected while in transaction
-
-# 1.2.0
-
-* Implemented #67: Reactive graph transformations. See: http://mobxjs.github.io/mobservable/refguide/create-transformer.html
-
-# 1.1.8
-
-* Implemented #59, `isObservable` and `observe` now support a property name as second param to observe individual values on maps and objects.
-
-# 1.1.7
-
-* Fixed #77: package consumers with --noImplicitAny should be able to build
-
-# 1.1.6
-
-* Introduced `mobservable.fastArray(array)`, in addition to `mobservable.observable(array)`. Which is much faster when adding items but doesn't support enumerability (`for (var idx in ar) ..` loops).
-* Introduced `observableArray.peek()`, for fast access to the array values. Should be used read-only.
-
-# 1.1.5
-
-* Fixed 71: transactions should not influence running computations
-
-# 1.1.4
-
-* Fixed #65; illegal state exception when using a transaction inside a reactive function. Credits: @kmalakoff
-
-# 1.1.3
-
-* Fixed #61; if autorun was created during a transaction, postpone execution until the end of the transaction
-
-# 1.1.2
-
-* Fixed exception when autorunUntil finished immediately
-
-# 1.1.1
-
-* `toJSON` now serializes object trees with cycles as well. If you know the object tree is acyclic, pass in `false` as second parameter for a performance gain.
-
-# 1.1.0
-
-* Exposed `ObservableMap` type
-* Introduced `mobservable.untracked(block)`
-* Introduced `mobservable.autorunAsync(block, delay)`
-
-# 1.0.9
-
-Removed accidental log message
-
-# 1.0.7 / 1.0.8
-
-Fixed inconsistency when using `transaction` and `@observer`, which sometimes caused stale values to be displayed.
-
-# 1.0.6
-
-Fix incompatibility issue with systemjs bundler (see PR 52)
-
-# 1.0.4/5
-
-* `map.size` is now a property instead of a function
-* `map()` now accepts an array as entries to construct the new map
-* introduced `isObservableObject`, `isObservableArray` and `isObservableMap`
-* introduced `observe`, to observe observable arrays, objects and maps, similarly to Object.observe and Array.observe
-
-# 1.0.3
-
-* `extendObservable` now supports passing in multiple object properties
-
-# 1.0.2
-
-* added `mobservable.map()`, which creates a new map similarly to ES6 maps, yet observable. Until properly documentation, see the [MDN docs](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map).
-
-# 1.0.1
-
-* Stricter argument checking for several apis.
-
-# 1.0
-
-## Renames
-
-* `isReactive` -> `isObservable`
-* `makeReactive` -> `observable`
-* `extendReactive` -> `extendObservable`
-* `observe` -> `autorun`
-* `observeUntil` -> `autorunUntil`
-* `observeAsync` -> `autorunAsync`
-* `reactiveComponent` -> `observer` (in `mobservable-react` package)
-
-## Breaking changes
-
-* dropped the `strict` and `logLevel` settings of mobservable. View functions are by default run in `strict` mode, `autorun` (formerly: `observe`) functions in `non-strict` mode (strict indicates that it is allowed to change other observable values during the computation of a view funtion).
-Use `extras.withStrict(boolean, block)` if you want to deviate from the default behavior.
-* `observable` (formerly `makeReactive`) no longer accepts an options object. The modifiers `asReference`, `asStructure` and `asFlat` can be used instead.
-* dropped the `default` export of observable
-* Removed all earlier deprecated functions
-
-## Bugfixes / improvements
-
-* `mobservable` now ships with TypeScript 1.6 compliant module typings, no external typings file is required anymore.
-* `mobservable-react` supports React Native as well through the import `"mobservable-react/native"`.
-* Improved debugger support
-* `for (var key in observablearray)` now lists the correct keys
-* `@observable` now works correct on classes that are transpiled by either TypeScript or Babel (Not all constructions where supported in Babel earlier)
-* Simplified error handling, mobservable will no longer catch errors in views, which makes the stack traces easier to debug.
-* Removed the initial 'welcom to mobservable' logline that was printed during start-up.
-
-# 0.7.1
-
-* Backported Babel support for the @observable decorator from the 1.0 branch. The decorator should now behave the same when compiled with either Typescript or Babeljs.
-
-# 0.7.0
-
-* Introduced `strict` mode (see issues [#30](), [#31]())
-* Renamed `sideEffect` to `observe`
-* Renamed `when` to `observeUntil`
-* Introduced `observeAsync`.
-* Fixed issue where changing the `logLevel` was not picked up.
-* Improved typings.
-* Introduces `asStructure` (see [#8]()) and `asFlat`.
-* Assigning a plain object to a reactive structure no longer clones the object, instead, the original object is decorated. (Arrays are still cloned due to Javascript limitations to extend arrays).
-* Reintroduced `expr(func)` as shorthand for `makeReactive(func)()`, which is useful to create temporarily views inside views
-* Deprecated the options object that could be passed to `makeReactive`.
-* Deprecated the options object that could be passed to `makeReactive`:
- * A `thisArg` can be passed as second param.
- * A name (for debugging) can be passed as second or third param
- * The `as` modifier is no longer needed, use `asReference` (instead of `as:'reference'`) or `asFlat` (instead of `recurse:false`).
-
-# 0.6.10
-
-* Fixed issue where @observable did not properly create a stand-alone view
-
-# 0.6.9
-
-* Fixed bug where views where sometimes not triggered again if the dependency tree changed to much.
-
-# 0.6.8
-
-* Introduced `when`, which, given a reactive predicate, observes it until it returns true.
-* Renamed `sideEffect -> observe`
-
-# 0.6.7:
-
-* Improved logging
-
-# 0.6.6:
-
-* Deprecated observable array `.values()` and `.clone()`
-* Deprecated observeUntilInvalid; use sideEffect instead
-* Renamed mobservable.toJson to mobservable.toJSON
-
-# 0.6.5:
-
-* It is no longer possible to create impure views; views that alter other reactive values.
-* Update links to the new documentation.
-
-# 0.6.4:
-
-* 2nd argument of sideEffect is now the scope, instead of an options object which hadn't any useful properties
-
-# 0.6.3
-
-* Deprecated: reactiveComponent, reactiveComponent from the separate package mobservable-react should be used instead
-* Store the trackingstack globally, so that multiple instances of mobservable can run together
-
-# 0.6.2
-
-* Deprecated: @observable on functions (use getter functions instead)
-* Introduced: `getDependencyTree`, `getObserverTree` and `trackTransitions`
-* Minor performance improvements
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000000..6267c67ae3
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,43 @@
+# MobX Contributor Guide
+
+Welcome to a community of developers just like you, striving to create the best experience around MobX. We welcome anyone who wants to contribute or provide constructive feedback, no matter the age or level of experience.
+
+Here are some ways to contribute to the project, from easiest to most difficult:
+
+- [Reporting bugs](#reporting-bugs)
+- [Improving the documentation](#improving-the-documentation)
+- [Responding to issues](#responding-to-issues)
+- [Small bug fixes](#small-bug-fixes)
+
+## Issues
+
+### Reporting bugs
+
+If you encounter a bug, please file an issue on GitHub via the repository of the sub-project you think contains the bug. If an issue you have is already reported, please add additional information or add a 👍 reaction to indicate your agreement.
+
+Include in the issue a link to your reproduction. A couple good options are a small Github repo or a [CodeSandbox](https://codesandbox.io/s/minimal-mobx-react-project-ppgml).
+
+If you have a more complicated issue where it is helpful to run it locally, you can download CodeSandbox template and work on it and then commit into your GitHub repo.
+
+### Improving the documentation
+
+Improving the documentation, examples, and other open source content can be the easiest way to contribute to the library. If you see a piece of content that can be better, open a PR with an improvement, no matter how small! If you would like to suggest a big change or major rewrite, we’d love to hear your ideas but please open an issue for discussion before writing the PR.
+
+### Responding to issues
+
+In addition to reporting issues, a great way to contribute to MobX is to respond to other peoples' issues and try to identify the problem or help them work around it. If you’re interested in taking a more active role in this process, please go ahead and respond to issues.
+
+### Small bug fixes
+
+For a small bug fix change (less than 20 lines of code changed), feel free to open a pull request. We’ll try to merge it as fast as possible and ideally publish a new release on the same day. The only requirement is, make sure you also add a test that verifies the bug you are trying to fix.
+
+#### Getting things running
+
+```
+git clone git@github.com:mobxjs/mobx.git
+cd mobx
+yarn install
+yarn lerna run build
+yarn test
+```
+
diff --git a/ISSUE_TEMPLATE.md b/ISSUE_TEMPLATE.md
deleted file mode 100644
index 0e0632d555..0000000000
--- a/ISSUE_TEMPLATE.md
+++ /dev/null
@@ -1,20 +0,0 @@
-Welcome to MobX. Please provide as much relevant information as possible!
-
-I have a:
-
-1. [ ] Question: Feel free to just state your question. For a quick answer, there are usually people online at our [Gitter](https://gitter.im/mobxjs/mobx) channel
-2. [ ] Documentation improvement. Please don't open an issue but create a PR instead!
-2. [ ] Issue:
- * [ ] **Provide error messages including stacktrace**
- * [ ] Provide a **minimal** sample reproduction. **Create a reproduction based on this [sandbox](https://codesandbox.io/s/v3v0my2370)**
- * [ ] Did you check this issue wasn't filed before?
- * [ ] Elaborate on your issue. What behavior did you expect?
- * [ ] State the versions of MobX and relevant libraries. Which browser / node / ... version?
-3. [ ] Idea:
- * [ ] What problem would it solve for you?
- * [ ] Do you think others will benefit from this change as well and it should in core package (see also mobx-utils)?
- * [ ] Are you willing to (attempt) a PR yourself?
-
-Please tick the appropriate boxes. Feel free to remove the _other_ sections.
-
-**Please be sure to close your issues promptly.**
diff --git a/PULL_REQUEST_TEMPLATE.md b/PULL_REQUEST_TEMPLATE.md
deleted file mode 100644
index b7bba38133..0000000000
--- a/PULL_REQUEST_TEMPLATE.md
+++ /dev/null
@@ -1,14 +0,0 @@
-Thanks for taking the effort to create a PR!
-
-If you are creating an extensive PR, you might want to open an issue with your idea first, so that you don't put a lot of effort in an PR that wouldn't be accepted. Please prepend pull requests with `WIP: ` if they are not yet finished
-PR checklist:
-
-* [ ] Added unit tests
-* [ ] Updated changelog
-* [ ] Updated docs (either in the description of this PR as markdown, or as separate PR on the `gh-pages` branch. Please refer to this PR). For new functionality, at least [API.md](https://github.com/mobxjs/mobx/blob/gh-pages/docs/refguide/api.md) should be updated
-* [ ] Added typescript typings
-* [ ] Verified that there is no significant performance drop (`npm run perf`)
-
-Feel free to ask help with any of these boxes!
-
-The above process doesn't apply to doc updates etc.
diff --git a/README.md b/README.md
index 35e3896e43..680138f06c 100644
--- a/README.md
+++ b/README.md
@@ -1,477 +1,186 @@
-
+
# MobX
-_Simple, scalable state management_
+_Simple, scalable state management._
-[](https://travis-ci.org/mobxjs/mobx)
-[](https://coveralls.io/github/mobxjs/mobx?branch=master)
-[](https://gitter.im/mobxjs/mobx?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
-[](https://hashnode.com/n/mobx)
-[](#backers)
-[](#sponsors)
-[](https://github.com/prettier/prettier)
-
-MobX is proudly sponsored by Mendix, Coinbase, Facebook Open Source, Canva, Algolia, Guilded and many [individual sponsors](#backers)
-
-
-
-
-
-
-# Installation
-
-* Installation: `npm install mobx --save`. React bindings: `npm install mobx-react --save`. To enable ESNext decorators (optional), see below.
-* CDN:
- * https://unpkg.com/mobx/lib/mobx.umd.js
- * https://cdnjs.com/libraries/mobx
-
-
-_Tip: Consider using the faster and smaller ES6 build if targetting a modern environment: `lib/mobx.es6.js`. For example by setting up a webpack alias: `resolve: { alias: { mobx: __dirname + "/node_modules/mobx/lib/mobx.es6.js" }}`_
-
-# Browser support
-
-| MobX version | Actively supported | Supported browsers | GitHub branch |
-| ----- | ----- | --- | --- |
-| 5.* | Yes | Any browser that supports [ES6 Proxies](https://kangax.github.io/compat-table/es6/#test-Proxy) (non polyfillable). _NOT:_ IE 11 and lower, Node 5 and lower | `master` |
-| 4.* | Yes (LTS) | Any ES5 compliant browser | `mobx4-master` |
-| 1-3.* | No | Any ES5 compliant browser | No active branch |
-
-* MobX >=5 runs on any browser with [ES6 proxy support](https://kangax.github.io/compat-table/es6/#test-Proxy). It will throw an error on startup on older environments such as IE11, Node.js <6 or React Native Android on old JavaScriptCore [how-to-upgrade](https://github.com/react-community/jsc-android-buildscripts#how-to-use-it-with-my-react-native-app). _Warning: since upgrading JSC is non-trivial, and decorators can be [troublesome](https://github.com/mobxjs/mobx/issues/1777) as well in React Native, for now it is recommended to stick to MobX 4.x for for React Native Android development_.
-* MobX 4 runs on any ES5 browser and will be actively maintained. The MobX 4 and 5 api's are the same and semantically can achieve the same, but MobX 4 has some [limitations](#mobx-4-vs-mobx-5).
+[](https://badge.fury.io/js/mobx)
+[](docs/backers-sponsors.md#backers)
+[](docs/backers-sponsors.md#sponsors)
+[](https://github.com/mobxjs/mobx/discussions)
+[](https://coveralls.io/github/mobxjs/mobx?branch=main)
+[](https://changelogs.xyz/mobx)
+---
-## Translations
+## Documentation
-* [中文](http://cn.mobx.js.org)
+Documentation can be found at **[mobx.js.org](https://mobx.js.org/)**.
-## Getting started
-
-* Egghead.io course
-* [Ten minute, interactive MobX + React tutorial](https://mobxjs.github.io/mobx/getting-started.html)
-* [The MobX book](https://books.google.nl/books?id=ALFmDwAAQBAJ&pg=PP1&lpg=PP1&dq=michel+weststrate+mobx+quick+start+guide:+supercharge+the+client+state+in+your+react+apps+with+mobx&source=bl&ots=D460fxti0F&sig=ivDGTxsPNwlOjLHrpKF1nweZFl8&hl=nl&sa=X&ved=2ahUKEwiwl8XO--ncAhWPmbQKHWOYBqIQ6AEwAnoECAkQAQ#v=onepage&q=michel%20weststrate%20mobx%20quick%20start%20guide%3A%20supercharge%20the%20client%20state%20in%20your%20react%20apps%20with%20mobx&f=false) by Pavan Podila and Michel Weststrate (which despite it's name is in-depth!)
-* [Official MobX 4 documentation and API overview](https://mobxjs.github.io/mobx/refguide/api.html) ([MobX 3](https://github.com/mobxjs/mobx/blob/54557dc319b04e92e31cb87427bef194ec1c549c/docs/refguide/api.md), [MobX 2](https://github.com/mobxjs/mobx/blob/7c9e7c86e0c6ead141bb0539d33143d0e1f576dd/docs/refguide/api.md))
-* [How to (not) use decorators](https://mobx.js.org/best/decorators.html)
-* Videos:
- * [ReactNext 2016: Real World MobX](https://www.youtube.com/watch?v=Aws40KOx90U) - 40m [slides](https://docs.google.com/presentation/d/1DrI6Hc2xIPTLBkfNH8YczOcPXQTOaCIcDESdyVfG_bE/edit?usp=sharing)
- * [Practical React with MobX](https://www.youtube.com/watch?v=XGwuM_u7UeQ). In depth introduction and explanation to MobX and React by Matt Ruby on OpenSourceNorth (ES5 only) - 42m.
- * LearnCode.academy MobX tutorial [Part I: MobX + React is AWESOME (7m)](https://www.youtube.com/watch?v=_q50BXqkAfI) [Part II: Computed Values and Nested/Referenced Observables (12m.)](https://www.youtube.com/watch?v=nYvNqKrl69s)
- * [Screencast: intro to MobX](https://www.youtube.com/watch?v=K8dr8BMU7-8) - 8m
- * [Talk: State Management Is Easy, React Amsterdam 2016 conf](https://www.youtube.com/watch?v=ApmSsu3qnf0&feature=youtu.be) ([slides](https://speakerdeck.com/mweststrate/state-management-is-easy-introduction-to-mobx))
-* [Boilerplates and related projects](http://mobxjs.github.io/mobx/faq/boilerplates.html)
-* More tutorials, blogs, videos, and other helpful resources can be found on the [MobX awesome list](https://github.com/mobxjs/awesome-mobx#awesome-mobx)
+---
+## Sponsors
+
+MobX is made possible by the generosity of the sponsors below, and many other [individual backers](https://github.com/mobxjs/mobx/blob/main/docs/backers-sponsors.md#backers). Sponsoring directly impacts the longevity of this project.
+
+**🥇🥇 Platinum sponsors (\$5000+ total contribution): 🥇🥇**
+
+
+
+
+
+
+**🥇 Gold sponsors (\$2500+ total contribution):**
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+**🥈 Silver sponsors (\$500+ total contributions):**
+
+
+
+
+
+
+
+
+
+
+
+
## Introduction
-MobX is a battle tested, simple and scalable state management library transparently applying functional reactive programming (TFRP). The Mobx design principle is very simple:
-
-_Anything that can be derived from the application state, should be derived. Automatically._
-
-This includes the UI, data serialization, server communication, etc.
-
-
-
-React and MobX together are a powerful combination. React renders the application state by providing mechanisms to translate it into a tree of renderable components. MobX provides the mechanism to store and update the application state that React then uses.
-
-Both React and MobX provide optimal and unique solutions to common problems in application development. React provides mechanisms to optimally render the UI by using a virtual DOM that reduces the number of costly DOM mutations. MobX provides mechanisms to optimally synchronize application state with React components by using a reactive virtual dependency state graph that is only updated when strictly needed and is never stale.
-
-## Core concepts
-
-MobX has only a few core concepts. The following snippets can be tried online using [codesandbox example](https://codesandbox.io/s/v3v0my2370).
-
-### Observable state
-
-Egghead.io lesson 1: observable & observer
-
-MobX adds observable capabilities to existing data structures like objects, arrays and class instances.
-This can simply be done by annotating your class properties with the [@observable](http://mobxjs.github.io/mobx/refguide/observable-decorator.html) decorator (ES.Next).
-
-```javascript
-import { observable } from "mobx"
-
-class Todo {
- id = Math.random();
- @observable title = "";
- @observable finished = false;
-}
-```
-
-Using `observable` is like turning a property of an object into a spreadsheet cell.
-But, unlike spreadsheets, these values can be not only primitive values, but also references, objects and arrays.
-
-If your environment doesn't support decorator syntax, don't worry.
-You can read [here](http://mobxjs.github.io/mobx/best/decorators.html) about how to set them up.
-Or you can skip them altoghether, as MobX can be used fine without decorator _syntax_, by leveraging the _decorate_ utility.
-Many MobX users prefer the slightly more concise decorator syntax, but the following snippet achieves the same:
-
-```javascript
-import { decorate, observable } from "mobx"
-
-class Todo {
- id = Math.random();
- title = "";
- finished = false;
-}
-decorate(Todo, {
- title: observable,
- finished: observable
-})
-```
-
-### Computed values
-
-Egghead.io lesson 3: computed values
-
-With MobX you can define values that will be derived automatically when relevant data is modified.
-By using the [`@computed`](http://mobxjs.github.io/mobx/refguide/computed-decorator.html) decorator or by using getter / setter functions when using `(extend)Observable` (Of course, you can use `decorate` here again as alternative to the `@` syntax).
-
-```javascript
-class TodoList {
- @observable todos = [];
- @computed get unfinishedTodoCount() {
- return this.todos.filter(todo => !todo.finished).length;
- }
-}
-```
-
-MobX will ensure that `unfinishedTodoCount` is updated automatically when a todo is added or when one of the `finished` properties is modified.
-Computations like these resemble formulas in spreadsheet programs like MS Excel. They update automatically and only when required.
-
-### Reactions
-
-Egghead.io lesson 9: custom reactions
-
-Reactions are similar to a computed value, but instead of producing a new value, a reaction produces a side effect for things like printing to the console, making network requests, incrementally updating the React component tree to patch the DOM, etc.
-In short, reactions bridge [reactive](https://en.wikipedia.org/wiki/Reactive_programming) and [imperative](https://en.wikipedia.org/wiki/Imperative_programming) programming.
-
-#### React components
-
-Egghead.io lesson 1: observable & observer
-
-If you are using React, you can turn your (stateless function) components into reactive components by simply adding the [`observer`](http://mobxjs.github.io/mobx/refguide/observer-component.html) function / decorator from the `mobx-react` package onto them.
-
-```javascript
-import React, {Component} from 'react';
-import ReactDOM from 'react-dom';
-import {observer} from 'mobx-react';
-
-@observer
-class TodoListView extends Component {
- render() {
- return
-
- {this.props.todoList.todos.map(todo =>
-
- )}
-
- Tasks left: {this.props.todoList.unfinishedTodoCount}
+_Anything that can be derived from the application state, should be. Automatically._
+
+MobX is a signal based, battle-tested library that makes state management simple and scalable by transparently applying functional reactive programming.
+The philosophy behind MobX is simple:
+
+
+
+
😙
+
+
Straightforward
+
Write minimalistic, boilerplate-free code that captures your intent.
+ Trying to update a record field? Simply use a normal JavaScript assignment —
+ the reactivity system will detect all your changes and propagate them out to where they are being used.
+ No special tools are required when updating data in an asynchronous process.
+
-)
-
-const store = new TodoList();
-ReactDOM.render(, document.getElementById('mount'));
-```
+
+
+
🚅
+
+
Effortless optimal rendering
+
+ All changes to and uses of your data are tracked at runtime, building a dependency tree that captures all relations between state and output.
+ This guarantees that computations that depend on your state, like React components, run only when strictly needed.
+ There is no need to manually optimize components with error-prone and sub-optimal techniques like memoization and selectors.
+
+
+
+
+
🤹🏻♂️
+
+
Architectural freedom
+
+ MobX is unopinionated and allows you to manage your application state outside of any UI framework.
+ This makes your code decoupled, portable, and above all, easily testable.
+
+
+
+
-`observer` turns React (function) components into derivations of the data they render.
-When using MobX there are no smart or dumb components.
-All components render smartly but are defined in a dumb manner. MobX will simply make sure the components are always re-rendered whenever needed, but also no more than that. So the `onClick` handler in the above example will force the proper `TodoView` to render, and it will cause the `TodoListView` to render if the number of unfinished tasks has changed.
-However, if you would remove the `Tasks left` line (or put it into a separate component), the `TodoListView` will no longer re-render when ticking a box. You can verify this yourself by changing the [JSFiddle](https://jsfiddle.net/mweststrate/wv3yopo0/).
+---
-#### Custom reactions
-Custom reactions can simply be created using the [`autorun`](http://mobxjs.github.io/mobx/refguide/autorun.html),
-[`reaction`](http://mobxjs.github.io/mobx/refguide/reaction.html) or [`when`](http://mobxjs.github.io/mobx/refguide/when.html) functions to fit your specific situations.
+## A quick example
-For example the following `autorun` prints a log message each time the amount of `unfinishedTodoCount` changes:
+So what does code that uses MobX look like?
```javascript
-autorun(() => {
- console.log("Tasks left: " + todos.unfinishedTodoCount)
-})
-```
-
-### What will MobX react to?
-
-Why does a new message get printed each time the `unfinishedTodoCount` is changed? The answer is this rule of thumb:
-
-_MobX reacts to any existing observable property that is read during the execution of a tracked function._
-
-For an in-depth explanation about how MobX determines to which observables needs to be reacted, check [understanding what MobX reacts to](https://github.com/mobxjs/mobx/blob/gh-pages/docs/best/react.md).
-
-### Actions
-
-Egghead.io lesson 5: actions
-
-Unlike many flux frameworks, MobX is unopinionated about how user events should be handled.
-
-* This can be done in a Flux like manner.
-* Or by processing events using RxJS.
-* Or by simply handling events in the most straightforward way possible, as demonstrated in the above `onClick` handler.
+import React from "react"
+import ReactDOM from "react-dom"
+import { makeAutoObservable } from "mobx"
+import { observer } from "mobx-react-lite"
+
+// Model the application state.
+function createTimer() {
+ return makeAutoObservable({
+ secondsPassed: 0,
+ increase() {
+ this.secondsPassed += 1
+ },
+ reset() {
+ this.secondsPassed = 0
+ }
+ })
+}
-In the end it all boils down to: somehow the state should be updated.
+const myTimer = createTimer()
-After updating the state `MobX` will take care of the rest in an efficient, glitch-free manner. So, simple statements, like the ones below, are enough to automatically update the user interface.
+// Build a "user interface" that uses the observable state.
+const TimerView = observer(({ timer }) => (
+
+))
-There is no technical need for firing events, calling a dispatcher, etc. A React component in the end is nothing more than a fancy representation of your state, i.e. a derivation that will be managed by MobX.
+ReactDOM.render(, document.body)
-```javascript
-store.todos.push(
- new Todo("Get Coffee"),
- new Todo("Write simpler code")
-);
-store.todos[0].finished = true;
+// Update the 'Seconds passed: X' text every second.
+setInterval(() => {
+ myTimer.increase()
+}, 1000)
```
-Nonetheless, MobX has an optional built-in concept of [`actions`](https://mobxjs.github.io/mobx/refguide/action.html).
-Read this section as well if you want to know more about writing asynchronous actions. It's easy!
-Use them to your advantage; they will help you to structure your code better and make wise decisions about when and where state should be modified.
-
-## MobX: Simple and scalable
-
-MobX is a simple, very scaleable and unobtrusive state management library.
-
-### Using classes and real references
-
-With MobX you don't need to normalize your data. This makes the library very suitable for very complex domain models. (At Mendix, for example, there are ~500 different domain classes in a single application.)
+The `observer` wrapper around the `TimerView` React component will automatically detect that rendering
+depends on the `timer.secondsPassed` observable, even though this relationship is not explicitly defined. The reactivity system will take care of re-rendering the component when _precisely that_ field is updated in the future.
-### Referential integrity is guaranteed
+Every event (`onClick` / `setInterval`) invokes an _action_ (`myTimer.increase` / `myTimer.reset`) that updates _observable state_ (`myTimer.secondsPassed`).
+Changes in the observable state are propagated precisely to all _computations_ and _side effects_ (`TimerView`) that depend on the changes being made.
-Since data doesn't need to be normalized and MobX automatically tracks the relations between state and derivations, you get referential integrity for free.
+
-Rendering something that is accessed through three levels of indirection? No problem. MobX will track them and re-render whenever one of the references changes. As a result, staleness bugs are eliminated. As a programmer, you might forget that changing some data might influence a seemingly unrelated component, but MobX won't forget.
+This conceptual picture can be applied to the above example, or any other application using MobX.
-### Simpler actions are easier to maintain
-
-As demonstrated above, modifying state when using MobX is very straightforward. You simply write down your intentions. MobX will take care of the rest.
-
-### Fine grained observability is efficient
-
-MobX builds a graph of all the derivations in your application to find the least number of re-computations that are needed to prevent staleness. "Derive everything" might sound expensive, but MobX builds a virtual derivation graph to minimize the number of recomputations needed to keep derivations in sync with the state.
-
-In fact, when testing MobX at Mendix we found out that using this library to track the relations in our code is often a lot more efficient than pushing changes through our application by using handwritten events or "smart" selector based container components.
-
-The simple reason is that MobX will establish far more fine grained 'listeners' on your data than you would do as a programmer.
+## Getting started
-Secondly, MobX sees the causality between derivations, so it can order them in such a way that no derivation has to run twice or introduce a glitch.
+To learn about the core concepts of MobX using a larger example, check out **[The gist of MobX](https://mobx.js.org/the-gist-of-mobx.html)** page, or take the **[10 minute interactive introduction to MobX and React](https://mobx.js.org/getting-started)**.
+The philosophy and benefits of the mental model provided by MobX are also described in great detail in the blog posts [UI as an afterthought](https://michel.codes/blogs/ui-as-an-afterthought) and [How to decouple state and UI (a.k.a. you don’t need componentWillMount)](https://hackernoon.com/how-to-decouple-state-and-ui-a-k-a-you-dont-need-componentwillmount-cc90b787aa37).
-How that works? See this [in-depth explanation of MobX](https://medium.com/@mweststrate/becoming-fully-reactive-an-in-depth-explanation-of-mobservable-55995262a254).
+## Further resources
-### Easy interoperability
+- The [MobX cheat sheet](https://gum.co/fSocU) (£5) is both useful and sponsors the project
+- [10 minute interactive introduction to MobX and React](https://mobx.js.org/getting-started)
+- [Egghead.io course, based on MobX 3](https://egghead.io/courses/manage-complex-state-in-react-apps-with-mobx)
+- The [MobX awesome list](https://github.com/mobxjs/awesome-mobx#awesome-mobx) – a long list of MobX resources and example projects
-MobX works with plain JavaScript structures. Due to its unobtrusiveness, it works with most JavaScript libraries out of the box without needing MobX specific library add-ons.
+### The MobX book
-So, you can simply keep using your existing router, data fetching, and utility libraries like `react-router`, `director`, `superagent`, `lodash`, etc.
+
-For the same reason, you can use it with both server and client side, isomorphic and react-native applications.
+The **[MobX Quick Start Guide](https://www.packtpub.com/product/mobx-quick-start-guide/9781789344837)** ($24.99) by [Pavan Podila](https://twitter.com/pavanpodila) and [Michel Weststrate](https://twitter.com/mweststrate) is available as an [ebook](https://www.packtpub.com/product/mobx-quick-start-guide/9781789344837), [paperback](https://www.amazon.com/MobX-Quick-Start-Guide-Supercharge/dp/1789344832), and on the [O'Reilly platform](https://www.oreilly.com/library/view/mobx-quick-start/9781789344837/) (see [preview](https://books.google.com/books?id=ALFmDwAAQBAJ&printsec=frontcover#v=onepage&q&f=false)).
-The result of this is that you often need to learn fewer new concepts when using MobX in comparison to other state management solutions.
+### Videos
----
+- [Introduction to MobX & React in 2020](https://www.youtube.com/watch?v=pnhIJA64ByY) by Leigh Halliday, _17 min_.
+- [ReactNext 2016: Real World MobX](https://www.youtube.com/watch?v=Aws40KOx90U) by Michel Weststrate, _40 min_, [slides](https://docs.google.com/presentation/d/1DrI6Hc2xIPTLBkfNH8YczOcPXQTOaCIcDESdyVfG_bE/edit?usp=sharing).
+- [CityJS 2020: MobX, from mutable to immutable, to observable data](https://youtu.be/sP7dtZm_Wx0?t=27050) by Michel Weststrate, _30 min_.
+- [OpenSourceNorth: Practical React with MobX (ES5)](https://www.youtube.com/watch?v=XGwuM_u7UeQ) by Matt Ruby, _42 min_.
+- [HolyJS 2019: MobX and the unique symbiosis of predictability and speed](https://www.youtube.com/watch?v=NBYbBbjZeX4&list=PL8sJahqnzh8JJD7xahG5zXkjfM5GOgcPA&index=21&t=0s) by Michel Weststrate, _59 min_.
+- [React Amsterdam 2016: State Management Is Easy](https://www.youtube.com/watch?v=ApmSsu3qnf0&feature=youtu.be) by Michel Weststrate, _20 min_, [slides](https://speakerdeck.com/mweststrate/state-management-is-easy-introduction-to-mobx).
+- {🚀} [React Live 2019: Reinventing MobX](https://www.youtube.com/watch?v=P_WqKZxpX8g) by Max Gallo, _27 min_.
## Credits
-MobX is inspired by reactive programming principles found in spreadsheets. It is inspired by MVVM frameworks such as MeteorJS tracker, Knockout and Vue.js. But, MobX brings Transparent Functional Reactive Programming to the next level and provides a stand alone implementation. It implements TFRP in a glitch-free, synchronous, predictable and efficient manner.
-
-A ton of credit goes to [Mendix](https://github.com/mendix) for providing the flexibility and support to maintain MobX and the chance to prove the philosophy of MobX in real, complex, performance critical applications.
-
-And finally, kudos to all the people that believed in, tried, validated and even [sponsored](https://github.com/mobxjs/mobx/blob/master/sponsors.md) MobX.
-
-## Further resources and documentation
+MobX is inspired by reactive programming principles, which are for example used in spreadsheets. It is inspired by model–view–viewmodel frameworks like [MeteorJS's Tracker](https://docs.meteor.com/api/tracker.html), [Knockout](https://knockoutjs.com/) and [Vue.js](https://vuejs.org/), but MobX brings _transparent functional reactive programming_ (TFRP, a concept which is further explained in the [MobX book](https://www.packtpub.com/product/mobx-quick-start-guide/9781789344837)) to the next level and provides a standalone implementation. It implements TFRP in a glitch-free, synchronous, predictable and efficient manner.
-* [The MobX book](https://books.google.nl/books?id=ALFmDwAAQBAJ&pg=PP1&lpg=PP1&dq=michel+weststrate+mobx+quick+start+guide:+supercharge+the+client+state+in+your+react+apps+with+mobx&source=bl&ots=D460fxti0F&sig=ivDGTxsPNwlOjLHrpKF1nweZFl8&hl=nl&sa=X&ved=2ahUKEwiwl8XO--ncAhWPmbQKHWOYBqIQ6AEwAnoECAkQAQ#v=onepage&q=michel%20weststrate%20mobx%20quick%20start%20guide%3A%20supercharge%20the%20client%20state%20in%20your%20react%20apps%20with%20mobx&f=false) by Pavan Podila and Michel Weststrate (which despite it's name is in-depth!)
-* [MobX homepage](http://mobxjs.github.io/mobx/faq/blogs.html)
-* [API overview](http://mobxjs.github.io/mobx/refguide/api.html)
-* [Tutorials, Blogs & Videos](http://mobxjs.github.io/mobx/faq/blogs.html)
-* [Boilerplates](http://mobxjs.github.io/mobx/faq/boilerplates.html)
-* [Related projects](http://mobxjs.github.io/mobx/faq/related.html)
-
-
-## What others are saying...
-
-> Guise, #mobx isn't pubsub, or your grandpa's observer pattern. Nay, it is a carefully orchestrated observable dimensional portal fueled by the power cosmic. It doesn't do change detection, it's actually a level 20 psionic with soul knife, slashing your viewmodel into submission.
-
-> After using #mobx for lone projects for a few weeks, it feels awesome to introduce it to the team. Time: 1/2, Fun: 2X
-
-> Working with #mobx is basically a continuous loop of me going “this is way too simple, it definitely won’t work” only to be proven wrong
-
-> Try react-mobx with es6 and you will love it so much that you will hug someone.
-
-> I have built big apps with MobX already and comparing to the one before that which was using Redux, it is simpler to read and much easier to reason about.
-
-> The #mobx is the way I always want things to be! It's really surprising simple and fast! Totally awesome! Don't miss it!
-
-> I've been using MobX for over 2 years now, and it *still* feels like cheating! 😎
-
-## Contributing
-
-* Feel free to send small pull requests. Please discuss new features or big changes in a GitHub issue first.
-* Use `npm test` to run the basic test suite, `npm run coverage` for the test suite with coverage and `npm run test:performance` for the performance tests.
-* Please note that if you want to backport a feature / fix to MobX 4 a second PR needs to be opened to the mobx4-master branch.
-
-# MobX 4 vs MobX 5
-
-The difference between MobX 4 and MobX 5 is that the latter uses Proxies to do property tracking. As a consequence, MobX 5 runs only on Proxy supporting browsers, in contrast to MobX 4 that runs on any ES 5 environment.
-
-The most noteable limitations of MobX 4:
- * Observable arrays are not real arrays, so they won't pass the `Array.isArray()` check. The practical consequence is that you often need to `.slice()` the array first (to get a real array shallow copy) before passing to third party libraries.
- * Adding properties to existing observable objects after creation is not automatically picked up. Instead, either use observable maps or use the the built-in [utility functions](https://mobx.js.org/refguide/object-api.html) to read / write / iterate objects that you want to dynamically add properties to.
-
-For more details see the [caveats page](https://mobx.js.org/best/pitfalls.html).
-
-## Flow support
-MobX ships with [flow typings](flow-typed/mobx.js). Flow will automatically include them when you import MobX modules. Although you **do not** need to import the types explicitly, you can still do it like this: `import type { ... } from 'mobx'`.
-
-To use the [flow typings](flow-typed/mobx.js) shipped with MobX:
-
-* In `.flowconfig`, you **cannot** ignore `node_modules`.
-* In `.flowconfig`, you **cannot** import it explicitly in the `[libs]` section.
-* You **do not** need to install library definition using [flow-typed](https://github.com/flowtype/flow-typed).
-
-## Donating
-
-Was MobX key in making your project a success?
-Join our [open collective](https://opencollective.com/mobx#) or use the [donate button](https://mobxjs.github.io/mobx/donate.html)!
-
-### Backers
-Support us with a monthly donation and help us continue our activities. [[Become a backer](https://opencollective.com/mobx#backer)]
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-One time donations through paypal are welcome as well and are recorded in the [sponsors](sponsors.md) list.
-
-[](https://mobxjs.github.io/mobx/donate.html)
-
-### Sponsors
-
-Become a sponsor and get your logo on our README on Github with a link to your site. [[Become a sponsor](https://opencollective.com/mobx#sponsor)]
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+A ton of credit goes to [Mendix](https://github.com/mendix), for providing the flexibility and support to maintain MobX and the chance to prove the philosophy of MobX in a real, complex, performance critical applications.
diff --git a/SECURITY.md b/SECURITY.md
new file mode 100644
index 0000000000..ffd3e2fcf1
--- /dev/null
+++ b/SECURITY.md
@@ -0,0 +1,12 @@
+# Security Policy
+
+All our packages are provided as-is without guarantees or SLAs.
+Security issues will be handled with appropriate urgency but without warranties.
+
+## Supported Versions
+
+Security issues must be reported against latest version of each package (as found on NPM) and will not be back-ported.
+
+## Reporting a Vulnerability
+
+Security issues can be reported at info@michel.codes. Since this software is provided as-is no follow up, remediation or time lines are guaranteed.
diff --git a/docs/LINKS.md b/docs/LINKS.md
new file mode 100644
index 0000000000..03c993e8a5
--- /dev/null
+++ b/docs/LINKS.md
@@ -0,0 +1,33 @@
+
+
+# Resources
+
+- [Ten minute interactive introduction to MobX and React](https://mobx.js.org/getting-started)
+- How MobX works: [In depth explanation of MobX](https://medium.com/@mweststrate/becoming-fully-reactive-an-in-depth-explanation-of-mobservable-55995262a254#.wnlo6bw8y)
+- Clone the boilerplate repository containing the above example from: https://github.com/mweststrate/react-mobservable-boilerplate.
+- Or fork this [JSFiddle](https://jsfiddle.net/mweststrate/wgbe4guu/).
+
+## Related projects
+
+- [mobx-connect](https://github.com/nightwolfz/mobx-connect) MobX @connect decorator for react components. Similar to redux's @connect.
+- [rfx-stack](https://github.com/foxhound87/rfx-stack) RFX Stack - Universal App featuring: React + Feathers + MobX
+- [mobx-reactor](https://github.com/amsb/mobx-reactor) Connect MobX data stores to functional stateless React components with async actions and unidirectional data flow.
+- [mobx-model](https://github.com/ikido/mobx-model) Simplify mobx data stores that mimic backend models
+- [rx-mobx](https://github.com/chicoxyzzy/rx-mobx) Convert MobX observables to RxJS and vice versa
+
+## More examples
+
+A nice list is WIP, but see this [github issue](https://github.com/mobxjs/mobx/issues/104) for a list of example projects, including routing, authorization, server side rendering etc.
+
+- [TodoMVC using MobX and React](https://github.com/mweststrate/mobx-todomvc)
+- The [ports of the _Notes_ and _Kanban_ examples](https://github.com/survivejs/mobservable-demo) from the book "SurviveJS - Webpack and React" to mobservable.
+- A simple webshop using [React + mobx](https://jsfiddle.net/mweststrate/46vL0phw) or [JQuery + mobx](http://jsfiddle.net/mweststrate/vxn7qgdw).
+- [Simple timer](https://jsfiddle.net/mweststrate/wgbe4guu/) application in JSFiddle.
+- [Simple ES5 MobX examples](https://github.com/mattruby/mobx-examples) Bite sized MobX examples all setup to run in jsFiddle.
+
+## Philosophy
+
+- [Making React reactive: the pursuit of high performing, easily maintainable React apps](https://www.mendix.com/tech-blog/making-react-reactive-pursuit-high-performing-easily-maintainable-react-apps/)
+- [SurviveJS interview on Mobservable, React and Flux](http://survivejs.com/blog/mobservable-interview/)
+- [Pure rendering in the light of time and state](https://medium.com/@mweststrate/pure-rendering-in-the-light-of-time-and-state-4b537d8d40b1)
+- [Official homepage](http://mobxjs.github.io/mobx/)
diff --git a/docs/README.md b/docs/README.md
new file mode 100644
index 0000000000..04e3529271
--- /dev/null
+++ b/docs/README.md
@@ -0,0 +1,186 @@
+---
+title: About MobX
+sidebar_label: About MobX
+hide_title: true
+---
+
+
+
+# MobX
+
+_Simple, scalable state management._
+
+[](https://github.com/mobxjs/mobx/discussions)
+[](https://badge.fury.io/js/mobx)
+[](backers-sponsors.md#backers)
+[](backers-sponsors.md#sponsors)
+[](https://changelogs.xyz/mobx)
+
+---
+
+MobX is made possible by the generosity of the sponsors below, and many other [individual backers](backers-sponsors.md#backers). Sponsoring directly impacts the longevity of this project.
+
+**🥇🥇 Platinum sponsors (\$5000+ total contribution): 🥇🥇**
+
+
+
+
+
+
+**🥇 Gold sponsors (\$2500+ total contribution):**
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+**🥈 Silver sponsors (\$500+ total contributions):**
+
+
+
+
+
+
+
+
+
+
+
+
+
+---
+
+## Introduction
+
+_Anything that can be derived from the application state, should be. Automatically._
+
+MobX is a signal based, battle-tested library that makes state management simple and scalable by transparently applying functional reactive programming.
+The philosophy behind MobX is simple:
+
+
+
+
😙
+
+
Straightforward
+
Write minimalistic, boilerplate-free code that captures your intent.
+ Trying to update a record field? Simply use a normal JavaScript assignment —
+ the reactivity system will detect all your changes and propagate them out to where they are being used.
+ No special tools are required when updating data in an asynchronous process.
+
+
+
+
+
🚅
+
+
Effortless optimal rendering
+
+ All changes to and uses of your data are tracked at runtime, building a dependency tree that captures all relations between state and output.
+ This guarantees that computations that depend on your state, like React components, run only when strictly needed.
+ There is no need to manually optimize components with error-prone and sub-optimal techniques like memoization and selectors.
+
+
+
+
+
🤹🏻♂️
+
+
Architectural freedom
+
+ MobX is unopinionated and allows you to manage your application state outside of any UI framework.
+ This makes your code decoupled, portable, and above all, easily testable.
+
+
+
+
+
+---
+
+## A quick example
+
+So what does code that uses MobX look like?
+
+```javascript
+import React from "react"
+import ReactDOM from "react-dom"
+import { makeAutoObservable } from "mobx"
+import { observer } from "mobx-react-lite"
+
+// Model the application state.
+function createTimer() {
+ return makeAutoObservable({
+ secondsPassed: 0,
+ increase() {
+ this.secondsPassed += 1
+ },
+ reset() {
+ this.secondsPassed = 0
+ }
+ })
+}
+
+const myTimer = createTimer()
+
+// Build a "user interface" that uses the observable state.
+const TimerView = observer(({ timer }) => (
+
+))
+
+ReactDOM.render(, document.body)
+
+// Update the 'Seconds passed: X' text every second.
+setInterval(() => {
+ myTimer.increase()
+}, 1000)
+```
+
+The `observer` wrapper around the `TimerView` React component will automatically detect that rendering
+depends on the `timer.secondsPassed` observable, even though this relationship is not explicitly defined. The reactivity system will take care of re-rendering the component when _precisely that_ field is updated in the future.
+
+Every event (`onClick` / `setInterval`) invokes an _action_ (`myTimer.increase` / `myTimer.reset`) that updates _observable state_ (`myTimer.secondsPassed`).
+Changes in the observable state are propagated precisely to all _computations_ and _side effects_ (`TimerView`) that depend on the changes being made.
+
+
+
+This conceptual picture can be applied to the above example, or any other application using MobX.
+
+## Getting started
+
+To learn about the core concepts of MobX using a larger example, check out **[The gist of MobX](the-gist-of-mobx.md)** page, or take the **[10 minute interactive introduction to MobX and React](https://mobx.js.org/getting-started)**.
+
+The philosophy and benefits of the mental model provided by MobX are also described in great detail in the blog posts [UI as an afterthought](https://michel.codes/blogs/ui-as-an-afterthought) and [How to decouple state and UI (a.k.a. you don’t need componentWillMount)](https://hackernoon.com/how-to-decouple-state-and-ui-a-k-a-you-dont-need-componentwillmount-cc90b787aa37).
+
+## Further resources
+
+- The [MobX cheat sheet](https://gum.co/fSocU) (£5) is both useful and sponsors the project
+- [10 minute interactive introduction to MobX and React](https://mobx.js.org/getting-started)
+- [Egghead.io course, based on MobX 3](https://egghead.io/courses/manage-complex-state-in-react-apps-with-mobx)
+- The [MobX awesome list](https://github.com/mobxjs/awesome-mobx#awesome-mobx) – a long list of MobX resources and example projects
+
+### The MobX book
+
+
+
+The **[MobX Quick Start Guide](https://www.packtpub.com/product/mobx-quick-start-guide/9781789344837)** ($24.99) by [Pavan Podila](https://twitter.com/pavanpodila) and [Michel Weststrate](https://twitter.com/mweststrate) is available as an [ebook](https://www.packtpub.com/product/mobx-quick-start-guide/9781789344837), [paperback](https://www.amazon.com/MobX-Quick-Start-Guide-Supercharge/dp/1789344832), and on the [O'Reilly platform](https://www.oreilly.com/library/view/mobx-quick-start/9781789344837/) (see [preview](https://books.google.com/books?id=ALFmDwAAQBAJ&printsec=frontcover#v=onepage&q&f=false)).
+
+### Videos
+
+- [Introduction to MobX & React in 2020](https://www.youtube.com/watch?v=pnhIJA64ByY) by Leigh Halliday, _17 min_.
+- [ReactNext 2016: Real World MobX](https://www.youtube.com/watch?v=Aws40KOx90U) by Michel Weststrate, _40 min_, [slides](https://docs.google.com/presentation/d/1DrI6Hc2xIPTLBkfNH8YczOcPXQTOaCIcDESdyVfG_bE/edit?usp=sharing).
+- [CityJS 2020: MobX, from mutable to immutable, to observable data](https://youtu.be/sP7dtZm_Wx0?t=27050) by Michel Weststrate, _30 min_.
+- [OpenSourceNorth: Practical React with MobX (ES5)](https://www.youtube.com/watch?v=XGwuM_u7UeQ) by Matt Ruby, _42 min_.
+- [HolyJS 2019: MobX and the unique symbiosis of predictability and speed](https://www.youtube.com/watch?v=NBYbBbjZeX4&list=PL8sJahqnzh8JJD7xahG5zXkjfM5GOgcPA&index=21&t=0s) by Michel Weststrate, _59 min_.
+- [React Amsterdam 2016: State Management Is Easy](https://www.youtube.com/watch?v=ApmSsu3qnf0&feature=youtu.be) by Michel Weststrate, _20 min_, [slides](https://speakerdeck.com/mweststrate/state-management-is-easy-introduction-to-mobx).
+- {🚀} [React Live 2019: Reinventing MobX](https://www.youtube.com/watch?v=P_WqKZxpX8g) by Max Gallo, _27 min_.
+
+## Credits
+
+MobX is inspired by reactive programming principles, which are for example used in spreadsheets. It is inspired by model–view–viewmodel frameworks like [MeteorJS's Tracker](https://docs.meteor.com/api/tracker.html), [Knockout](https://knockoutjs.com/) and [Vue.js](https://vuejs.org/), but MobX brings _transparent functional reactive programming_ (TFRP, a concept which is further explained in the [MobX book](https://www.packtpub.com/product/mobx-quick-start-guide/9781789344837)) to the next level and provides a standalone implementation. It implements TFRP in a glitch-free, synchronous, predictable and efficient manner.
+
+A ton of credit goes to [Mendix](https://github.com/mendix) for providing the flexibility and support to maintain MobX and the chance to prove the philosophy of MobX in a real, complex, performance critical applications.
diff --git a/docs/about-this-documentation.md b/docs/about-this-documentation.md
new file mode 100644
index 0000000000..b1df960d9a
--- /dev/null
+++ b/docs/about-this-documentation.md
@@ -0,0 +1,50 @@
+---
+title: About this documentation
+sidebar_label: About this documentation
+hide_title: true
+---
+
+
+
+# About this documentation
+
+It follows the principle that the most commonly used concepts are
+introduced before specialized information. This applies to the headings in the table
+of concepts as well as the pages under those headings.
+
+We've marked the sections and concepts that are more advanced with the {🚀} marker. You likely won't have to understand them until you will have a special use case, and can use MobX very effectively without knowing about them. Feel free to skip them and move on to the next section!
+
+The documentation has been rewritten for MobX 6. For older versions of MobX, it can be found [here](https://github.com/mobxjs/mobx/tree/mobx4and5/docs).
+All the principles are the same, and the API is largely the same. The main difference is that before MobX 6, [decorators](https://github.com/mobxjs/mobx/blob/mobx4and5/docs/best/decorators.md) were the recommended syntax to write MobX enhanced classes.
+
+A summary of the documentation can be downloaded as cheat sheet:
+
+
+
+## Guided tour
+
+To get an overall idea of how to use MobX with React, read through the current _Introduction_ heading, in particular [The gist of MobX](the-gist-of-mobx.md) section.
+It will introduce you to the most important principles, APIs and how they relate.
+You should be ready to use MobX once you read this!
+
+Here are a few suggestions about the next things to check out:
+
+- Try the [10 minute interactive introduction to MobX and React](https://mobx.js.org/getting-started)
+
+- [React integration](react-integration.md)
+
+- [`makeObservable` / `makeAutoObservable`](observable-state.md)
+
+- Learn about [actions](actions.md), which includes a discussion on asynchronous actions
+
+- The basics of [computeds](computeds.md)
+
+- Read about [`autorun`](reactions.md#autorun), if only because it's used in the examples
+
+- To get an idea on how to organize your application's data stores, check out [Defining data stores](defining-data-stores.md)
+
+- If the behavior of MobX confuses you, it's useful to check out [Understanding reactivity](understanding-reactivity.md)
+
+- Get a [quick overview of the API](api.md), also linked in the top navigation bar
+
+This should give you a good understanding of the day-to-day uses of MobX. There is plenty more available for you to read at your own leisure.
diff --git a/docs/actions.md b/docs/actions.md
new file mode 100644
index 0000000000..611c51a394
--- /dev/null
+++ b/docs/actions.md
@@ -0,0 +1,502 @@
+---
+title: Updating state using actions
+sidebar_label: Actions
+hide_title: true
+---
+
+
+
+# Updating state using actions
+
+Usage:
+
+- `action` _(annotation)_
+- `action(fn)`
+- `action(name, fn)`
+- `@action` _(method / field decorator)_
+
+All applications have actions. An action is any piece of code that modifies the state. In principle, actions always happen in response to an event. For example, a button was clicked, some input changed, a websocket message arrived, etc.
+
+MobX requires that you declare your actions, although [`makeAutoObservable`](observable-state.md#makeautoobservable) can automate much of this job. Actions help you structure your code better and offer the following performance benefits:
+
+1. They are run inside [transactions](api.md#transaction). No reactions will be run until the outer-most action has finished, guaranteeing that intermediate or incomplete values produced during an action are not visible to the rest of the application until the action has completed.
+
+2. By default, it is not allowed to change the state outside of actions. This helps to clearly identify in your code base where the state updates happen.
+
+The `action` annotation should only be used on functions that intend to _modify_ the state. Functions that derive information (performing lookups or filtering data) should _not_ be marked as actions, to allow MobX to track their invocations. `action` annotated members will be non-enumerable.
+
+## Examples
+
+
+
+
+```javascript
+import { makeObservable, observable, action } from "mobx"
+
+class Doubler {
+ value = 0
+
+ constructor() {
+ makeObservable(this, {
+ value: observable,
+ increment: action
+ })
+ }
+
+ increment() {
+ // Intermediate states will not become visible to observers.
+ this.value++
+ this.value++
+ }
+}
+```
+
+
+
+```javascript
+import { observable, action } from "mobx"
+
+class Doubler {
+ @observable accessor value = 0
+
+ @action
+ increment() {
+ // Intermediate states will not become visible to observers.
+ this.value++
+ this.value++
+ }
+}
+```
+
+
+
+```javascript
+import { makeAutoObservable } from "mobx"
+
+class Doubler {
+ value = 0
+
+ constructor() {
+ makeAutoObservable(this)
+ }
+
+ increment() {
+ this.value++
+ this.value++
+ }
+}
+```
+
+
+
+```javascript
+import { makeObservable, observable, action } from "mobx"
+
+class Doubler {
+ value = 0
+
+ constructor() {
+ makeObservable(this, {
+ value: observable,
+ increment: action.bound
+ })
+ }
+
+ increment() {
+ this.value++
+ this.value++
+ }
+}
+
+const doubler = new Doubler()
+
+// Calling increment this way is safe as it is already bound.
+setInterval(doubler.increment, 1000)
+```
+
+
+
+```javascript
+import { observable, action } from "mobx"
+
+const state = observable({ value: 0 })
+
+const increment = action(state => {
+ state.value++
+ state.value++
+})
+
+increment(state)
+```
+
+
+
+```javascript
+import { observable, runInAction } from "mobx"
+
+const state = observable({ value: 0 })
+
+runInAction(() => {
+ state.value++
+ state.value++
+})
+```
+
+
+
+## Wrapping functions using `action`
+
+To leverage the transactional nature of MobX as much as possible, actions should be passed as far outward as possible. It is good to mark a class method as an action if it modifies the state. It is even better to mark event handlers as actions, as it is the outer-most transaction that counts. A single unmarked event handler that calls two actions subsequently would still generate two transactions.
+
+To help create action based event handlers, `action` is not only an annotation, but also a higher order function. It can be called with a function as an argument, and in that case it will return an `action` wrapped function with the same signature.
+
+For example in React, an `onClick` handler can be wrapped as below.
+
+```javascript
+const ResetButton = ({ formState }) => (
+
+)
+```
+
+For debugging purposes, we recommend to either name the wrapped function, or pass a name as the first argument to `action`.
+
+**Note:** actions are untracked
+
+Another feature of actions is that they are [untracked](api.md#untracked). When an action is called from inside a side effect or a computed value (very rare!), observables read by the action won't be counted towards the dependencies of the derivation.
+
+`makeAutoObservable`, `extendObservable` and `observable` use a special flavour of `action` called [`autoAction`](observable-state.md#autoAction),
+that will determine at runtime if the function is a derivation or action.
+
+
+
+## `action.bound`
+
+Usage:
+
+- `action.bound` _(annotation)_
+
+The `action.bound` annotation can be used to automatically bind a method to the correct instance, so that `this` is always correctly bound inside the function.
+
+**Tip:** use `makeAutoObservable(o, {}, { autoBind: true })` to bind all actions and flows automatically
+
+```javascript
+import { makeAutoObservable } from "mobx"
+
+class Doubler {
+ value = 0
+
+ constructor() {
+ makeAutoObservable(this, {}, { autoBind: true })
+ }
+
+ increment() {
+ this.value++
+ this.value++
+ }
+
+ *flow() {
+ const response = yield fetch("http://example.com/value")
+ this.value = yield response.json()
+ }
+}
+```
+
+
+
+## `runInAction`
+
+Usage:
+
+- `runInAction(fn)`
+
+Use this utility to create a temporary action that is immediately invoked. Can be useful in asynchronous processes.
+Check out the [above code block](#examples) for an example.
+
+## Actions and inheritance
+
+Only actions defined **on prototype** can be **overridden** by subclass:
+
+```javascript
+class Parent {
+ // on instance
+ arrowAction = () => {}
+
+ // on prototype
+ action() {}
+ boundAction() {}
+
+ constructor() {
+ makeObservable(this, {
+ arrowAction: action,
+ action: action,
+ boundAction: action.bound,
+ })
+ }
+}
+class Child extends Parent {
+ // THROWS: TypeError: Cannot redefine property: arrowAction
+ arrowAction = () => {}
+
+ // OK
+ action() {}
+ boundAction() {}
+
+ constructor() {
+ super()
+ makeObservable(this, {
+ arrowAction: override,
+ action: override,
+ boundAction: override,
+ })
+ }
+}
+```
+
+To **bind** a single _action_ to `this`, `action.bound` can be used instead of _arrow functions_.
+See [**subclassing**](subclassing.md) for more information.
+
+## Asynchronous actions
+
+In essence, asynchronous processes don't need any special treatment in MobX, as all reactions will update automatically regardless of the moment in time they are caused.
+And since observable objects are mutable, it is generally safe to keep references to them for the duration of an action.
+However, every step (tick) that updates observables in an asynchronous process should be marked as `action`.
+This can be achieved in multiple ways by leveraging the above APIs, as shown below.
+
+For example, when handling promises, the handlers that update state should be actions or should be wrapped using `action`, as shown below.
+
+
+
+
+Promise resolution handlers are handled in-line, but run after the original action finished, so they need to be wrapped by `action`:
+
+```javascript
+import { action, makeAutoObservable } from "mobx"
+
+class Store {
+ githubProjects = []
+ state = "pending" // "pending", "done" or "error"
+
+ constructor() {
+ makeAutoObservable(this)
+ }
+
+ fetchProjects() {
+ this.githubProjects = []
+ this.state = "pending"
+ fetchGithubProjectsSomehow().then(
+ action("fetchSuccess", projects => {
+ const filteredProjects = somePreprocessing(projects)
+ this.githubProjects = filteredProjects
+ this.state = "done"
+ }),
+ action("fetchError", error => {
+ this.state = "error"
+ })
+ )
+ }
+}
+```
+
+
+
+If the promise handlers are class fields, they will automatically be wrapped in `action` by `makeAutoObservable`:
+
+```javascript
+import { makeAutoObservable } from "mobx"
+
+class Store {
+ githubProjects = []
+ state = "pending" // "pending", "done" or "error"
+
+ constructor() {
+ makeAutoObservable(this)
+ }
+
+ fetchProjects() {
+ this.githubProjects = []
+ this.state = "pending"
+ fetchGithubProjectsSomehow().then(this.projectsFetchSuccess, this.projectsFetchFailure)
+ }
+
+ projectsFetchSuccess = projects => {
+ const filteredProjects = somePreprocessing(projects)
+ this.githubProjects = filteredProjects
+ this.state = "done"
+ }
+
+ projectsFetchFailure = error => {
+ this.state = "error"
+ }
+}
+```
+
+
+
+Any steps after `await` aren't in the same tick, so they require action wrapping.
+Here, we can leverage `runInAction`:
+
+```javascript
+import { runInAction, makeAutoObservable } from "mobx"
+
+class Store {
+ githubProjects = []
+ state = "pending" // "pending", "done" or "error"
+
+ constructor() {
+ makeAutoObservable(this)
+ }
+
+ async fetchProjects() {
+ this.githubProjects = []
+ this.state = "pending"
+ try {
+ const projects = await fetchGithubProjectsSomehow()
+ const filteredProjects = somePreprocessing(projects)
+ runInAction(() => {
+ this.githubProjects = filteredProjects
+ this.state = "done"
+ })
+ } catch (e) {
+ runInAction(() => {
+ this.state = "error"
+ })
+ }
+ }
+}
+```
+
+
+
+```javascript
+import { flow, makeAutoObservable, flowResult } from "mobx"
+
+class Store {
+ githubProjects = []
+ state = "pending"
+
+ constructor() {
+ makeAutoObservable(this, {
+ fetchProjects: flow
+ })
+ }
+
+ // Note the star, this a generator function!
+ *fetchProjects() {
+ this.githubProjects = []
+ this.state = "pending"
+ try {
+ // Yield instead of await.
+ const projects = yield fetchGithubProjectsSomehow()
+ const filteredProjects = somePreprocessing(projects)
+ this.state = "done"
+ this.githubProjects = filteredProjects
+ return projects
+ } catch (error) {
+ this.state = "error"
+ }
+ }
+}
+
+const store = new Store()
+const projects = await flowResult(store.fetchProjects())
+```
+
+
+
+## Using flow instead of async / await {🚀}
+
+Usage:
+
+- `flow` _(annotation)_
+- `flow(function* (args) { })`
+- `@flow` _(method decorator)_
+
+The `flow` wrapper is an optional alternative to `async` / `await` that makes it easier to
+work with MobX actions.
+`flow` takes a [generator function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Generator) as its only input.
+Inside the generator, you can chain promises by yielding them (instead of `await somePromise` you write `yield somePromise`).
+The flow mechanism will then make sure the generator either continues or throws when a yielded promise resolves.
+
+So `flow` is an alternative to `async` / `await` that doesn't need any further `action` wrapping. It can be applied as follows:
+
+1. Wrap `flow` around your asynchronous function.
+2. Instead of `async` use `function *`.
+3. Instead of `await` use `yield`.
+
+The [`flow` + generator function](#asynchronous-actions) example above shows what this looks like in practice.
+
+Note that the `flowResult` function is only needed when using TypeScript.
+Since decorating a method with `flow`, it will wrap the returned generator in a promise.
+However, TypeScript isn't aware of that transformation, so `flowResult` will make sure that TypeScript is aware of that type change.
+
+`makeAutoObservable` and friends will automatically infer generators to be `flow`s. `flow` annotated members will be non-enumerable.
+
+{🚀} **Note:** using flow on object fields
+`flow`, like `action`, can be used to wrap functions directly. The above example could also have been written as follows:
+
+```typescript
+import { flow, makeObservable, observable } from "mobx"
+
+class Store {
+ githubProjects = []
+ state = "pending"
+
+ constructor() {
+ makeObservable(this, {
+ githubProjects: observable,
+ state: observable,
+ })
+ }
+
+ fetchProjects = flow(function* (this: Store) {
+ this.githubProjects = []
+ this.state = "pending"
+ try {
+ // yield instead of await.
+ const projects = yield fetchGithubProjectsSomehow()
+ const filteredProjects = somePreprocessing(projects)
+ this.state = "done"
+ this.githubProjects = filteredProjects
+ } catch (error) {
+ this.state = "error"
+ }
+ })
+}
+
+const store = new Store()
+const projects = await store.fetchProjects()
+```
+
+The upside is that we don't need `flowResult` anymore, the downside is that `this` needs to be typed to make sure its type is inferred correctly.
+
+
+
+## `flow.bound`
+
+Usage:
+
+- `flow.bound` _(annotation)_
+
+The `flow.bound` annotation can be used to automatically bind a method to the correct instance, so that `this` is always correctly bound inside the function.
+Similarly to actions, flows can be bound by default using [`autoBind` option](#auto-bind).
+
+## Cancelling flows {🚀}
+
+Another neat benefit of flows is that they are cancellable.
+The return value of `flow` is a promise that resolves with the value that is returned from the generator function in the end.
+The returned promise has an additional `cancel()` method that will interrupt the running generator and cancel it.
+Any `try` / `finally` clauses will still be run.
+
+## Disabling mandatory actions {🚀}
+
+By default, MobX 6 and later require that you use actions to make changes to the state.
+However, you can configure MobX to disable this behavior. Check out the [`enforceActions`](configuration.md#enforceactions) section.
+For example, this can be quite useful in unit test setup, where the warnings don't always have much value.
diff --git a/docs/analyzing-reactivity.md b/docs/analyzing-reactivity.md
new file mode 100644
index 0000000000..80ad31378a
--- /dev/null
+++ b/docs/analyzing-reactivity.md
@@ -0,0 +1,138 @@
+---
+title: Analyzing reactivity
+sidebar_label: Analyzing reactivity {🚀}
+hide_title: true
+---
+
+
+
+# Analyzing reactivity {🚀}
+
+# Using `trace` for debugging
+
+Trace is a small utility that helps you find out why your computed values, reactions or components are re-evaluating.
+
+It can be used by simply importing `import { trace } from "mobx"`, and then putting it inside a reaction or computed value.
+It will print why it is re-evaluating the current derivation.
+
+Optionally it is possible to automatically enter the debugger by passing `true` as the last argument.
+This way the exact mutation that causes the reaction to re-run will still be in stack, usually ~8 stack frames up. See the image below.
+
+In debugger mode, the debug information will also reveal the full derivation tree that is affecting the current computation / reaction.
+
+
+
+
+
+## Live examples
+
+Simple [CodeSandbox `trace` example](https://codesandbox.io/s/trace-dnhbz?file=/src/index.js:309-338).
+
+[Here's a deployed example](https://csb-nr58ylyn4m-hontnuliaa.now.sh/) for exploring the stack.
+Make sure to play with the chrome debugger's blackbox feature!
+
+## Usage examples
+
+There are different ways of calling `trace()`, some examples:
+
+```javascript
+import { observer } from "mobx-react"
+import { trace } from "mobx"
+
+const MyComponent = observer(() => {
+ trace(true) // Enter the debugger whenever an observable value causes this component to re-run.
+ return
{this.props.user.name}
+})
+```
+
+Enable trace by using the `reaction` argument of a reaction / autorun:
+
+```javascript
+mobx.autorun("logger", reaction => {
+ reaction.trace()
+ console.log(user.fullname)
+})
+```
+
+Pass in the property name of a computed property:
+
+```javascript
+trace(user, "fullname")
+```
+
+# Introspection APIs
+
+The following APIs might come in handy if you want to inspect the internal state of MobX while debugging, or want to build cool tools on top of MobX.
+Also relevant are the various [`isObservable*` APIs](api.md#isobservable).
+
+### `getDebugName`
+
+Usage:
+
+- `getDebugName(thing, property?)`
+
+Returns a (generated) friendly debug name of an observable object, property, reaction etc. Used for example by the [MobX developer tools](https://github.com/mobxjs/mobx-devtools).
+
+### `getDependencyTree`
+
+Usage:
+
+- `getDependencyTree(thing, property?)`.
+
+Returns a tree structure with all observables the given reaction / computation currently depends upon.
+
+### `getObserverTree`
+
+Usage:
+
+- `getObserverTree(thing, property?)`.
+
+Returns a tree structure with all reactions / computations that are observing the given observable.
+
+### `getAtom`
+
+Usage:
+
+- `getAtom(thing, property?)`.
+
+Returns the backing _Atom_ of a given observable object, property, reaction etc.
+
+# Spy
+
+Usage:
+
+- `spy(listener)`
+
+Registers a global spy listener that listens to all events that happen in MobX.
+It is similar to attaching an `observe` listener to _all_ observables at once, but also notifies about running (trans/re)actions and computations.
+Used for example by the [MobX developer tools](https://github.com/mobxjs/mobx-devtools).
+
+Example usage of spying all actions:
+
+```javascript
+spy(event => {
+ if (event.type === "action") {
+ console.log(`${event.name} with args: ${event.arguments}`)
+ }
+})
+```
+
+Spy listeners always receive one object, which usually has at least a `type` field. The following events are emitted by default by spy:
+
+| Type | observableKind | Other fields | Nested |
+| ------------------------------- | -------------- | -------------------------------------------------------------- | ------ |
+| action | | name, object (scope), arguments[] | yes |
+| scheduled-reaction | | name | no |
+| reaction | | name | yes |
+| error | | name, message, error | no |
+| add,update,remove,delete,splice | | Check out [Intercept & observe {🚀}](intercept-and-observe.md) | yes |
+| report-end | | spyReportEnd=true, time? (total execution time in ms) | no |
+
+The `report-end` events are part of an earlier fired event that had `spyReportStart: true`.
+This event indicates the end of an event and this way groups of events with sub-events are created.
+This event might report the total execution time as well.
+
+The spy events for observable values are identical to the events passed to `observe`.
+In production builds, the `spy` API is a no-op as it will be minimized away.
+
+Check out the [Intercept & observe {🚀}](intercept-and-observe.md#event-overview) section for an extensive overview.
diff --git a/docs/api.md b/docs/api.md
new file mode 100644
index 0000000000..4db83b102a
--- /dev/null
+++ b/docs/api.md
@@ -0,0 +1,582 @@
+---
+title: MobX API Reference
+sidebar_label: API
+hide_title: true
+---
+
+
+
+# MobX API Reference
+
+Functions marked with {🚀} are considered advanced, and should typically not be needed.
+Consider downloading our handy cheat sheet that explains all important APIs on a single page:
+
+
+ JavaScript mode supports several configuration options:
+
+
json which will set the mode to expect JSON
+ data rather than a JavaScript program.
+
jsonld which will set the mode to expect
+ JSON-LD linked data rather
+ than a JavaScript program (demo).
+
typescript which will activate additional
+ syntax highlighting and some other things for TypeScript code
+ (demo).
+
statementIndent which (given a number) will
+ determine the amount of indentation to use for statements
+ continued on a new line.
+
wordCharacters, a regexp that indicates which
+ characters should be considered part of an identifier.
+ Defaults to /[\w$]/, which does not handle
+ non-ASCII identifiers. Can be set to something more elaborate
+ to improve Unicode support.
+
diff --git a/docs/assets/getting-started-assets/javascripts/codemirror/lib/codemirror.css b/docs/assets/getting-started-assets/javascripts/codemirror/lib/codemirror.css
new file mode 100644
index 0000000000..a5d3ab913e
--- /dev/null
+++ b/docs/assets/getting-started-assets/javascripts/codemirror/lib/codemirror.css
@@ -0,0 +1,350 @@
+/* BASICS */
+
+.CodeMirror {
+ /* Set height, width, borders, and global font properties here */
+ font-family: monospace;
+ height: 300px;
+ color: black;
+ direction: ltr;
+ }
+
+ /* PADDING */
+
+ .CodeMirror-lines {
+ padding: 4px 0; /* Vertical padding around content */
+ }
+ .CodeMirror pre.CodeMirror-line,
+ .CodeMirror pre.CodeMirror-line-like {
+ padding: 0 4px; /* Horizontal padding of content */
+ }
+
+ .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
+ background-color: white; /* The little square between H and V scrollbars */
+ }
+
+ /* GUTTER */
+
+ .CodeMirror-gutters {
+ border-right: 1px solid #ddd;
+ background-color: #f7f7f7;
+ white-space: nowrap;
+ }
+ .CodeMirror-linenumbers {}
+ .CodeMirror-linenumber {
+ padding: 0 3px 0 5px;
+ min-width: 20px;
+ text-align: right;
+ color: #999;
+ white-space: nowrap;
+ }
+
+ .CodeMirror-guttermarker { color: black; }
+ .CodeMirror-guttermarker-subtle { color: #999; }
+
+ /* CURSOR */
+
+ .CodeMirror-cursor {
+ border-left: 1px solid black;
+ border-right: none;
+ width: 0;
+ }
+ /* Shown when moving in bi-directional text */
+ .CodeMirror div.CodeMirror-secondarycursor {
+ border-left: 1px solid silver;
+ }
+ .cm-fat-cursor .CodeMirror-cursor {
+ width: auto;
+ border: 0 !important;
+ background: #7e7;
+ }
+ .cm-fat-cursor div.CodeMirror-cursors {
+ z-index: 1;
+ }
+ .cm-fat-cursor-mark {
+ background-color: rgba(20, 255, 20, 0.5);
+ -webkit-animation: blink 1.06s steps(1) infinite;
+ -moz-animation: blink 1.06s steps(1) infinite;
+ animation: blink 1.06s steps(1) infinite;
+ }
+ .cm-animate-fat-cursor {
+ width: auto;
+ border: 0;
+ -webkit-animation: blink 1.06s steps(1) infinite;
+ -moz-animation: blink 1.06s steps(1) infinite;
+ animation: blink 1.06s steps(1) infinite;
+ background-color: #7e7;
+ }
+ @-moz-keyframes blink {
+ 0% {}
+ 50% { background-color: transparent; }
+ 100% {}
+ }
+ @-webkit-keyframes blink {
+ 0% {}
+ 50% { background-color: transparent; }
+ 100% {}
+ }
+ @keyframes blink {
+ 0% {}
+ 50% { background-color: transparent; }
+ 100% {}
+ }
+
+ /* Can style cursor different in overwrite (non-insert) mode */
+ .CodeMirror-overwrite .CodeMirror-cursor {}
+
+ .cm-tab { display: inline-block; text-decoration: inherit; }
+
+ .CodeMirror-rulers {
+ position: absolute;
+ left: 0; right: 0; top: -50px; bottom: 0;
+ overflow: hidden;
+ }
+ .CodeMirror-ruler {
+ border-left: 1px solid #ccc;
+ top: 0; bottom: 0;
+ position: absolute;
+ }
+
+ /* DEFAULT THEME */
+
+ .cm-s-default .cm-header {color: blue;}
+ .cm-s-default .cm-quote {color: #090;}
+ .cm-negative {color: #d44;}
+ .cm-positive {color: #292;}
+ .cm-header, .cm-strong {font-weight: bold;}
+ .cm-em {font-style: italic;}
+ .cm-link {text-decoration: underline;}
+ .cm-strikethrough {text-decoration: line-through;}
+
+ .cm-s-default .cm-keyword {color: #708;}
+ .cm-s-default .cm-atom {color: #219;}
+ .cm-s-default .cm-number {color: #164;}
+ .cm-s-default .cm-def {color: #00f;}
+ .cm-s-default .cm-variable,
+ .cm-s-default .cm-punctuation,
+ .cm-s-default .cm-property,
+ .cm-s-default .cm-operator {}
+ .cm-s-default .cm-variable-2 {color: #05a;}
+ .cm-s-default .cm-variable-3, .cm-s-default .cm-type {color: #085;}
+ .cm-s-default .cm-comment {color: #a50;}
+ .cm-s-default .cm-string {color: #a11;}
+ .cm-s-default .cm-string-2 {color: #f50;}
+ .cm-s-default .cm-meta {color: #555;}
+ .cm-s-default .cm-qualifier {color: #555;}
+ .cm-s-default .cm-builtin {color: #30a;}
+ .cm-s-default .cm-bracket {color: #997;}
+ .cm-s-default .cm-tag {color: #170;}
+ .cm-s-default .cm-attribute {color: #00c;}
+ .cm-s-default .cm-hr {color: #999;}
+ .cm-s-default .cm-link {color: #00c;}
+
+ .cm-s-default .cm-error {color: #f00;}
+ .cm-invalidchar {color: #f00;}
+
+ .CodeMirror-composing { border-bottom: 2px solid; }
+
+ /* Default styles for common addons */
+
+ div.CodeMirror span.CodeMirror-matchingbracket {color: #0b0;}
+ div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #a22;}
+ .CodeMirror-matchingtag { background: rgba(255, 150, 0, .3); }
+ .CodeMirror-activeline-background {background: #e8f2ff;}
+
+ /* STOP */
+
+ /* The rest of this file contains styles related to the mechanics of
+ the editor. You probably shouldn't touch them. */
+
+ .CodeMirror {
+ position: relative;
+ overflow: hidden;
+ background: white;
+ }
+
+ .CodeMirror-scroll {
+ overflow: scroll !important; /* Things will break if this is overridden */
+ /* 30px is the magic margin used to hide the element's real scrollbars */
+ /* See overflow: hidden in .CodeMirror */
+ margin-bottom: -30px; margin-right: -30px;
+ padding-bottom: 30px;
+ height: 100%;
+ outline: none; /* Prevent dragging from highlighting the element */
+ position: relative;
+ }
+ .CodeMirror-sizer {
+ position: relative;
+ border-right: 30px solid transparent;
+ }
+
+ /* The fake, visible scrollbars. Used to force redraw during scrolling
+ before actual scrolling happens, thus preventing shaking and
+ flickering artifacts. */
+ .CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
+ position: absolute;
+ z-index: 6;
+ display: none;
+ }
+ .CodeMirror-vscrollbar {
+ right: 0; top: 0;
+ overflow-x: hidden;
+ overflow-y: scroll;
+ }
+ .CodeMirror-hscrollbar {
+ bottom: 0; left: 0;
+ overflow-y: hidden;
+ overflow-x: scroll;
+ }
+ .CodeMirror-scrollbar-filler {
+ right: 0; bottom: 0;
+ }
+ .CodeMirror-gutter-filler {
+ left: 0; bottom: 0;
+ }
+
+ .CodeMirror-gutters {
+ position: absolute; left: 0; top: 0;
+ min-height: 100%;
+ z-index: 3;
+ }
+ .CodeMirror-gutter {
+ white-space: normal;
+ height: 100%;
+ display: inline-block;
+ vertical-align: top;
+ margin-bottom: -30px;
+ }
+ .CodeMirror-gutter-wrapper {
+ position: absolute;
+ z-index: 4;
+ background: none !important;
+ border: none !important;
+ }
+ .CodeMirror-gutter-background {
+ position: absolute;
+ top: 0; bottom: 0;
+ z-index: 4;
+ }
+ .CodeMirror-gutter-elt {
+ position: absolute;
+ cursor: default;
+ z-index: 4;
+ }
+ .CodeMirror-gutter-wrapper ::selection { background-color: transparent }
+ .CodeMirror-gutter-wrapper ::-moz-selection { background-color: transparent }
+
+ .CodeMirror-lines {
+ cursor: text;
+ min-height: 1px; /* prevents collapsing before first draw */
+ }
+ .CodeMirror pre.CodeMirror-line,
+ .CodeMirror pre.CodeMirror-line-like {
+ /* Reset some styles that the rest of the page might have set */
+ -moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0;
+ border-width: 0;
+ background: transparent;
+ font-family: inherit;
+ font-size: inherit;
+ margin: 0;
+ white-space: pre;
+ word-wrap: normal;
+ line-height: inherit;
+ color: inherit;
+ z-index: 2;
+ position: relative;
+ overflow: visible;
+ -webkit-tap-highlight-color: transparent;
+ -webkit-font-variant-ligatures: contextual;
+ font-variant-ligatures: contextual;
+ }
+ .CodeMirror-wrap pre.CodeMirror-line,
+ .CodeMirror-wrap pre.CodeMirror-line-like {
+ word-wrap: break-word;
+ white-space: pre-wrap;
+ word-break: normal;
+ }
+
+ .CodeMirror-linebackground {
+ position: absolute;
+ left: 0; right: 0; top: 0; bottom: 0;
+ z-index: 0;
+ }
+
+ .CodeMirror-linewidget {
+ position: relative;
+ z-index: 2;
+ padding: 0.1px; /* Force widget margins to stay inside of the container */
+ }
+
+ .CodeMirror-widget {}
+
+ .CodeMirror-rtl pre { direction: rtl; }
+
+ .CodeMirror-code {
+ outline: none;
+ }
+
+ /* Force content-box sizing for the elements where we expect it */
+ .CodeMirror-scroll,
+ .CodeMirror-sizer,
+ .CodeMirror-gutter,
+ .CodeMirror-gutters,
+ .CodeMirror-linenumber {
+ -moz-box-sizing: content-box;
+ box-sizing: content-box;
+ }
+
+ .CodeMirror-measure {
+ position: absolute;
+ width: 100%;
+ height: 0;
+ overflow: hidden;
+ visibility: hidden;
+ }
+
+ .CodeMirror-cursor {
+ position: absolute;
+ pointer-events: none;
+ }
+ .CodeMirror-measure pre { position: static; }
+
+ div.CodeMirror-cursors {
+ visibility: hidden;
+ position: relative;
+ z-index: 3;
+ }
+ div.CodeMirror-dragcursors {
+ visibility: visible;
+ }
+
+ .CodeMirror-focused div.CodeMirror-cursors {
+ visibility: visible;
+ }
+
+ .CodeMirror-selected { background: #d9d9d9; }
+ .CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; }
+ .CodeMirror-crosshair { cursor: crosshair; }
+ .CodeMirror-line::selection, .CodeMirror-line > span::selection, .CodeMirror-line > span > span::selection { background: #d7d4f0; }
+ .CodeMirror-line::-moz-selection, .CodeMirror-line > span::-moz-selection, .CodeMirror-line > span > span::-moz-selection { background: #d7d4f0; }
+
+ .cm-searching {
+ background-color: #ffa;
+ background-color: rgba(255, 255, 0, .4);
+ }
+
+ /* Used to force a border model for a node */
+ .cm-force-border { padding-right: .1px; }
+
+ @media print {
+ /* Hide the cursor when printing */
+ .CodeMirror div.CodeMirror-cursors {
+ visibility: hidden;
+ }
+ }
+
+ /* See issue #2901 */
+ .cm-tab-wrap-hack:after { content: ''; }
+
+ /* Help users use markselection to safely style text background */
+ span.CodeMirror-selectedtext { background: none; }
+
\ No newline at end of file
diff --git a/docs/assets/getting-started-assets/javascripts/codemirror/lib/codemirror.js b/docs/assets/getting-started-assets/javascripts/codemirror/lib/codemirror.js
new file mode 100644
index 0000000000..32db983dd0
--- /dev/null
+++ b/docs/assets/getting-started-assets/javascripts/codemirror/lib/codemirror.js
@@ -0,0 +1,13920 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+// This is CodeMirror (https://codemirror.net), a code editor
+// implemented in JavaScript on top of the browser's DOM.
+//
+// You can find some technical background for some of the code below
+// at http://marijnhaverbeke.nl/blog/#cm-internals .
+
+;(function(global, factory) {
+ typeof exports === "object" && typeof module !== "undefined"
+ ? (module.exports = factory())
+ : typeof define === "function" && define.amd
+ ? define(factory)
+ : ((global = global || self), (global.CodeMirror = factory()))
+})(this, function() {
+ "use strict"
+
+ // Kludges for bugs and behavior differences that can't be feature
+ // detected are enabled based on userAgent etc sniffing.
+ var userAgent = navigator.userAgent
+ var platform = navigator.platform
+
+ var gecko = /gecko\/\d/i.test(userAgent)
+ var ie_upto10 = /MSIE \d/.test(userAgent)
+ var ie_11up = /Trident\/(?:[7-9]|\d{2,})\..*rv:(\d+)/.exec(userAgent)
+ var edge = /Edge\/(\d+)/.exec(userAgent)
+ var ie = ie_upto10 || ie_11up || edge
+ var ie_version = ie && (ie_upto10 ? document.documentMode || 6 : +(edge || ie_11up)[1])
+ var webkit = !edge && /WebKit\//.test(userAgent)
+ var qtwebkit = webkit && /Qt\/\d+\.\d+/.test(userAgent)
+ var chrome = !edge && /Chrome\//.test(userAgent)
+ var presto = /Opera\//.test(userAgent)
+ var safari = /Apple Computer/.test(navigator.vendor)
+ var mac_geMountainLion = /Mac OS X 1\d\D([8-9]|\d\d)\D/.test(userAgent)
+ var phantom = /PhantomJS/.test(userAgent)
+
+ var ios = !edge && /AppleWebKit/.test(userAgent) && /Mobile\/\w+/.test(userAgent)
+ var android = /Android/.test(userAgent)
+ // This is woefully incomplete. Suggestions for alternative methods welcome.
+ var mobile =
+ ios || android || /webOS|BlackBerry|Opera Mini|Opera Mobi|IEMobile/i.test(userAgent)
+ var mac = ios || /Mac/.test(platform)
+ var chromeOS = /\bCrOS\b/.test(userAgent)
+ var windows = /win/i.test(platform)
+
+ var presto_version = presto && userAgent.match(/Version\/(\d*\.\d*)/)
+ if (presto_version) {
+ presto_version = Number(presto_version[1])
+ }
+ if (presto_version && presto_version >= 15) {
+ presto = false
+ webkit = true
+ }
+ // Some browsers use the wrong event properties to signal cmd/ctrl on OS X
+ var flipCtrlCmd =
+ mac && (qtwebkit || (presto && (presto_version == null || presto_version < 12.11)))
+ var captureRightClick = gecko || (ie && ie_version >= 9)
+
+ function classTest(cls) {
+ return new RegExp("(^|\\s)" + cls + "(?:$|\\s)\\s*")
+ }
+
+ var rmClass = function(node, cls) {
+ var current = node.className
+ var match = classTest(cls).exec(current)
+ if (match) {
+ var after = current.slice(match.index + match[0].length)
+ node.className = current.slice(0, match.index) + (after ? match[1] + after : "")
+ }
+ }
+
+ function removeChildren(e) {
+ for (var count = e.childNodes.length; count > 0; --count) {
+ e.removeChild(e.firstChild)
+ }
+ return e
+ }
+
+ function removeChildrenAndAdd(parent, e) {
+ return removeChildren(parent).appendChild(e)
+ }
+
+ function elt(tag, content, className, style) {
+ var e = document.createElement(tag)
+ if (className) {
+ e.className = className
+ }
+ if (style) {
+ e.style.cssText = style
+ }
+ if (typeof content == "string") {
+ e.appendChild(document.createTextNode(content))
+ } else if (content) {
+ for (var i = 0; i < content.length; ++i) {
+ e.appendChild(content[i])
+ }
+ }
+ return e
+ }
+ // wrapper for elt, which removes the elt from the accessibility tree
+ function eltP(tag, content, className, style) {
+ var e = elt(tag, content, className, style)
+ e.setAttribute("role", "presentation")
+ return e
+ }
+
+ var range
+ if (document.createRange) {
+ range = function(node, start, end, endNode) {
+ var r = document.createRange()
+ r.setEnd(endNode || node, end)
+ r.setStart(node, start)
+ return r
+ }
+ } else {
+ range = function(node, start, end) {
+ var r = document.body.createTextRange()
+ try {
+ r.moveToElementText(node.parentNode)
+ } catch (e) {
+ return r
+ }
+ r.collapse(true)
+ r.moveEnd("character", end)
+ r.moveStart("character", start)
+ return r
+ }
+ }
+
+ function contains(parent, child) {
+ if (child.nodeType == 3) {
+ // Android browser always returns false when child is a textnode
+ child = child.parentNode
+ }
+ if (parent.contains) {
+ return parent.contains(child)
+ }
+ do {
+ if (child.nodeType == 11) {
+ child = child.host
+ }
+ if (child == parent) {
+ return true
+ }
+ } while ((child = child.parentNode))
+ }
+
+ function activeElt() {
+ // IE and Edge may throw an "Unspecified Error" when accessing document.activeElement.
+ // IE < 10 will throw when accessed while the page is loading or in an iframe.
+ // IE > 9 and Edge will throw when accessed in an iframe if document.body is unavailable.
+ var activeElement
+ try {
+ activeElement = document.activeElement
+ } catch (e) {
+ activeElement = document.body || null
+ }
+ while (
+ activeElement &&
+ activeElement.shadowRoot &&
+ activeElement.shadowRoot.activeElement
+ ) {
+ activeElement = activeElement.shadowRoot.activeElement
+ }
+ return activeElement
+ }
+
+ function addClass(node, cls) {
+ var current = node.className
+ if (!classTest(cls).test(current)) {
+ node.className += (current ? " " : "") + cls
+ }
+ }
+ function joinClasses(a, b) {
+ var as = a.split(" ")
+ for (var i = 0; i < as.length; i++) {
+ if (as[i] && !classTest(as[i]).test(b)) {
+ b += " " + as[i]
+ }
+ }
+ return b
+ }
+
+ var selectInput = function(node) {
+ node.select()
+ }
+ if (ios) {
+ // Mobile Safari apparently has a bug where select() is broken.
+ selectInput = function(node) {
+ node.selectionStart = 0
+ node.selectionEnd = node.value.length
+ }
+ } else if (ie) {
+ // Suppress mysterious IE10 errors
+ selectInput = function(node) {
+ try {
+ node.select()
+ } catch (_e) {}
+ }
+ }
+
+ function bind(f) {
+ var args = Array.prototype.slice.call(arguments, 1)
+ return function() {
+ return f.apply(null, args)
+ }
+ }
+
+ function copyObj(obj, target, overwrite) {
+ if (!target) {
+ target = {}
+ }
+ for (var prop in obj) {
+ if (obj.hasOwnProperty(prop) && (overwrite !== false || !target.hasOwnProperty(prop))) {
+ target[prop] = obj[prop]
+ }
+ }
+ return target
+ }
+
+ // Counts the column offset in a string, taking tabs into account.
+ // Used mostly to find indentation.
+ function countColumn(string, end, tabSize, startIndex, startValue) {
+ if (end == null) {
+ end = string.search(/[^\s\u00a0]/)
+ if (end == -1) {
+ end = string.length
+ }
+ }
+ for (var i = startIndex || 0, n = startValue || 0; ; ) {
+ var nextTab = string.indexOf("\t", i)
+ if (nextTab < 0 || nextTab >= end) {
+ return n + (end - i)
+ }
+ n += nextTab - i
+ n += tabSize - (n % tabSize)
+ i = nextTab + 1
+ }
+ }
+
+ var Delayed = function() {
+ this.id = null
+ this.f = null
+ this.time = 0
+ this.handler = bind(this.onTimeout, this)
+ }
+ Delayed.prototype.onTimeout = function(self) {
+ self.id = 0
+ if (self.time <= +new Date()) {
+ self.f()
+ } else {
+ setTimeout(self.handler, self.time - +new Date())
+ }
+ }
+ Delayed.prototype.set = function(ms, f) {
+ this.f = f
+ var time = +new Date() + ms
+ if (!this.id || time < this.time) {
+ clearTimeout(this.id)
+ this.id = setTimeout(this.handler, ms)
+ this.time = time
+ }
+ }
+
+ function indexOf(array, elt) {
+ for (var i = 0; i < array.length; ++i) {
+ if (array[i] == elt) {
+ return i
+ }
+ }
+ return -1
+ }
+
+ // Number of pixels added to scroller and sizer to hide scrollbar
+ var scrollerGap = 30
+
+ // Returned or thrown by various protocols to signal 'I'm not
+ // handling this'.
+ var Pass = {
+ toString: function() {
+ return "CodeMirror.Pass"
+ }
+ }
+
+ // Reused option objects for setSelection & friends
+ var sel_dontScroll = { scroll: false },
+ sel_mouse = { origin: "*mouse" },
+ sel_move = { origin: "+move" }
+
+ // The inverse of countColumn -- find the offset that corresponds to
+ // a particular column.
+ function findColumn(string, goal, tabSize) {
+ for (var pos = 0, col = 0; ; ) {
+ var nextTab = string.indexOf("\t", pos)
+ if (nextTab == -1) {
+ nextTab = string.length
+ }
+ var skipped = nextTab - pos
+ if (nextTab == string.length || col + skipped >= goal) {
+ return pos + Math.min(skipped, goal - col)
+ }
+ col += nextTab - pos
+ col += tabSize - (col % tabSize)
+ pos = nextTab + 1
+ if (col >= goal) {
+ return pos
+ }
+ }
+ }
+
+ var spaceStrs = [""]
+ function spaceStr(n) {
+ while (spaceStrs.length <= n) {
+ spaceStrs.push(lst(spaceStrs) + " ")
+ }
+ return spaceStrs[n]
+ }
+
+ function lst(arr) {
+ return arr[arr.length - 1]
+ }
+
+ function map(array, f) {
+ var out = []
+ for (var i = 0; i < array.length; i++) {
+ out[i] = f(array[i], i)
+ }
+ return out
+ }
+
+ function insertSorted(array, value, score) {
+ var pos = 0,
+ priority = score(value)
+ while (pos < array.length && score(array[pos]) <= priority) {
+ pos++
+ }
+ array.splice(pos, 0, value)
+ }
+
+ function nothing() {}
+
+ function createObj(base, props) {
+ var inst
+ if (Object.create) {
+ inst = Object.create(base)
+ } else {
+ nothing.prototype = base
+ inst = new nothing()
+ }
+ if (props) {
+ copyObj(props, inst)
+ }
+ return inst
+ }
+
+ var nonASCIISingleCaseWordChar = /[\u00df\u0587\u0590-\u05f4\u0600-\u06ff\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc\uac00-\ud7af]/
+ function isWordCharBasic(ch) {
+ return (
+ /\w/.test(ch) ||
+ (ch > "\x80" &&
+ (ch.toUpperCase() != ch.toLowerCase() || nonASCIISingleCaseWordChar.test(ch)))
+ )
+ }
+ function isWordChar(ch, helper) {
+ if (!helper) {
+ return isWordCharBasic(ch)
+ }
+ if (helper.source.indexOf("\\w") > -1 && isWordCharBasic(ch)) {
+ return true
+ }
+ return helper.test(ch)
+ }
+
+ function isEmpty(obj) {
+ for (var n in obj) {
+ if (obj.hasOwnProperty(n) && obj[n]) {
+ return false
+ }
+ }
+ return true
+ }
+
+ // Extending unicode characters. A series of a non-extending char +
+ // any number of extending chars is treated as a single unit as far
+ // as editing and measuring is concerned. This is not fully correct,
+ // since some scripts/fonts/browsers also treat other configurations
+ // of code points as a group.
+ var extendingChars = /[\u0300-\u036f\u0483-\u0489\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u0610-\u061a\u064b-\u065e\u0670\u06d6-\u06dc\u06de-\u06e4\u06e7\u06e8\u06ea-\u06ed\u0711\u0730-\u074a\u07a6-\u07b0\u07eb-\u07f3\u0816-\u0819\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0900-\u0902\u093c\u0941-\u0948\u094d\u0951-\u0955\u0962\u0963\u0981\u09bc\u09be\u09c1-\u09c4\u09cd\u09d7\u09e2\u09e3\u0a01\u0a02\u0a3c\u0a41\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a70\u0a71\u0a75\u0a81\u0a82\u0abc\u0ac1-\u0ac5\u0ac7\u0ac8\u0acd\u0ae2\u0ae3\u0b01\u0b3c\u0b3e\u0b3f\u0b41-\u0b44\u0b4d\u0b56\u0b57\u0b62\u0b63\u0b82\u0bbe\u0bc0\u0bcd\u0bd7\u0c3e-\u0c40\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c62\u0c63\u0cbc\u0cbf\u0cc2\u0cc6\u0ccc\u0ccd\u0cd5\u0cd6\u0ce2\u0ce3\u0d3e\u0d41-\u0d44\u0d4d\u0d57\u0d62\u0d63\u0dca\u0dcf\u0dd2-\u0dd4\u0dd6\u0ddf\u0e31\u0e34-\u0e3a\u0e47-\u0e4e\u0eb1\u0eb4-\u0eb9\u0ebb\u0ebc\u0ec8-\u0ecd\u0f18\u0f19\u0f35\u0f37\u0f39\u0f71-\u0f7e\u0f80-\u0f84\u0f86\u0f87\u0f90-\u0f97\u0f99-\u0fbc\u0fc6\u102d-\u1030\u1032-\u1037\u1039\u103a\u103d\u103e\u1058\u1059\u105e-\u1060\u1071-\u1074\u1082\u1085\u1086\u108d\u109d\u135f\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u17b7-\u17bd\u17c6\u17c9-\u17d3\u17dd\u180b-\u180d\u18a9\u1920-\u1922\u1927\u1928\u1932\u1939-\u193b\u1a17\u1a18\u1a56\u1a58-\u1a5e\u1a60\u1a62\u1a65-\u1a6c\u1a73-\u1a7c\u1a7f\u1b00-\u1b03\u1b34\u1b36-\u1b3a\u1b3c\u1b42\u1b6b-\u1b73\u1b80\u1b81\u1ba2-\u1ba5\u1ba8\u1ba9\u1c2c-\u1c33\u1c36\u1c37\u1cd0-\u1cd2\u1cd4-\u1ce0\u1ce2-\u1ce8\u1ced\u1dc0-\u1de6\u1dfd-\u1dff\u200c\u200d\u20d0-\u20f0\u2cef-\u2cf1\u2de0-\u2dff\u302a-\u302f\u3099\u309a\ua66f-\ua672\ua67c\ua67d\ua6f0\ua6f1\ua802\ua806\ua80b\ua825\ua826\ua8c4\ua8e0-\ua8f1\ua926-\ua92d\ua947-\ua951\ua980-\ua982\ua9b3\ua9b6-\ua9b9\ua9bc\uaa29-\uaa2e\uaa31\uaa32\uaa35\uaa36\uaa43\uaa4c\uaab0\uaab2-\uaab4\uaab7\uaab8\uaabe\uaabf\uaac1\uabe5\uabe8\uabed\udc00-\udfff\ufb1e\ufe00-\ufe0f\ufe20-\ufe26\uff9e\uff9f]/
+ function isExtendingChar(ch) {
+ return ch.charCodeAt(0) >= 768 && extendingChars.test(ch)
+ }
+
+ // Returns a number from the range [`0`; `str.length`] unless `pos` is outside that range.
+ function skipExtendingChars(str, pos, dir) {
+ while ((dir < 0 ? pos > 0 : pos < str.length) && isExtendingChar(str.charAt(pos))) {
+ pos += dir
+ }
+ return pos
+ }
+
+ // Returns the value from the range [`from`; `to`] that satisfies
+ // `pred` and is closest to `from`. Assumes that at least `to`
+ // satisfies `pred`. Supports `from` being greater than `to`.
+ function findFirst(pred, from, to) {
+ // At any point we are certain `to` satisfies `pred`, don't know
+ // whether `from` does.
+ var dir = from > to ? -1 : 1
+ for (;;) {
+ if (from == to) {
+ return from
+ }
+ var midF = (from + to) / 2,
+ mid = dir < 0 ? Math.ceil(midF) : Math.floor(midF)
+ if (mid == from) {
+ return pred(mid) ? from : to
+ }
+ if (pred(mid)) {
+ to = mid
+ } else {
+ from = mid + dir
+ }
+ }
+ }
+
+ // BIDI HELPERS
+
+ function iterateBidiSections(order, from, to, f) {
+ if (!order) {
+ return f(from, to, "ltr", 0)
+ }
+ var found = false
+ for (var i = 0; i < order.length; ++i) {
+ var part = order[i]
+ if ((part.from < to && part.to > from) || (from == to && part.to == from)) {
+ f(
+ Math.max(part.from, from),
+ Math.min(part.to, to),
+ part.level == 1 ? "rtl" : "ltr",
+ i
+ )
+ found = true
+ }
+ }
+ if (!found) {
+ f(from, to, "ltr")
+ }
+ }
+
+ var bidiOther = null
+ function getBidiPartAt(order, ch, sticky) {
+ var found
+ bidiOther = null
+ for (var i = 0; i < order.length; ++i) {
+ var cur = order[i]
+ if (cur.from < ch && cur.to > ch) {
+ return i
+ }
+ if (cur.to == ch) {
+ if (cur.from != cur.to && sticky == "before") {
+ found = i
+ } else {
+ bidiOther = i
+ }
+ }
+ if (cur.from == ch) {
+ if (cur.from != cur.to && sticky != "before") {
+ found = i
+ } else {
+ bidiOther = i
+ }
+ }
+ }
+ return found != null ? found : bidiOther
+ }
+
+ // Bidirectional ordering algorithm
+ // See http://unicode.org/reports/tr9/tr9-13.html for the algorithm
+ // that this (partially) implements.
+
+ // One-char codes used for character types:
+ // L (L): Left-to-Right
+ // R (R): Right-to-Left
+ // r (AL): Right-to-Left Arabic
+ // 1 (EN): European Number
+ // + (ES): European Number Separator
+ // % (ET): European Number Terminator
+ // n (AN): Arabic Number
+ // , (CS): Common Number Separator
+ // m (NSM): Non-Spacing Mark
+ // b (BN): Boundary Neutral
+ // s (B): Paragraph Separator
+ // t (S): Segment Separator
+ // w (WS): Whitespace
+ // N (ON): Other Neutrals
+
+ // Returns null if characters are ordered as they appear
+ // (left-to-right), or an array of sections ({from, to, level}
+ // objects) in the order in which they occur visually.
+ var bidiOrdering = (function() {
+ // Character types for codepoints 0 to 0xff
+ var lowTypes =
+ "bbbbbbbbbtstwsbbbbbbbbbbbbbbssstwNN%%%NNNNNN,N,N1111111111NNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNbbbbbbsbbbbbbbbbbbbbbbbbbbbbbbbbb,N%%%%NNNNLNNNNN%%11NLNNN1LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLN"
+ // Character types for codepoints 0x600 to 0x6f9
+ var arabicTypes =
+ "nnnnnnNNr%%r,rNNmmmmmmmmmmmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmmmmmmmmnnnnnnnnnn%nnrrrmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmnNmmmmmmrrmmNmmmmrr1111111111"
+ function charType(code) {
+ if (code <= 0xf7) {
+ return lowTypes.charAt(code)
+ } else if (0x590 <= code && code <= 0x5f4) {
+ return "R"
+ } else if (0x600 <= code && code <= 0x6f9) {
+ return arabicTypes.charAt(code - 0x600)
+ } else if (0x6ee <= code && code <= 0x8ac) {
+ return "r"
+ } else if (0x2000 <= code && code <= 0x200b) {
+ return "w"
+ } else if (code == 0x200c) {
+ return "b"
+ } else {
+ return "L"
+ }
+ }
+
+ var bidiRE = /[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac]/
+ var isNeutral = /[stwN]/,
+ isStrong = /[LRr]/,
+ countsAsLeft = /[Lb1n]/,
+ countsAsNum = /[1n]/
+
+ function BidiSpan(level, from, to) {
+ this.level = level
+ this.from = from
+ this.to = to
+ }
+
+ return function(str, direction) {
+ var outerType = direction == "ltr" ? "L" : "R"
+
+ if (str.length == 0 || (direction == "ltr" && !bidiRE.test(str))) {
+ return false
+ }
+ var len = str.length,
+ types = []
+ for (var i = 0; i < len; ++i) {
+ types.push(charType(str.charCodeAt(i)))
+ }
+
+ // W1. Examine each non-spacing mark (NSM) in the level run, and
+ // change the type of the NSM to the type of the previous
+ // character. If the NSM is at the start of the level run, it will
+ // get the type of sor.
+ for (var i$1 = 0, prev = outerType; i$1 < len; ++i$1) {
+ var type = types[i$1]
+ if (type == "m") {
+ types[i$1] = prev
+ } else {
+ prev = type
+ }
+ }
+
+ // W2. Search backwards from each instance of a European number
+ // until the first strong type (R, L, AL, or sor) is found. If an
+ // AL is found, change the type of the European number to Arabic
+ // number.
+ // W3. Change all ALs to R.
+ for (var i$2 = 0, cur = outerType; i$2 < len; ++i$2) {
+ var type$1 = types[i$2]
+ if (type$1 == "1" && cur == "r") {
+ types[i$2] = "n"
+ } else if (isStrong.test(type$1)) {
+ cur = type$1
+ if (type$1 == "r") {
+ types[i$2] = "R"
+ }
+ }
+ }
+
+ // W4. A single European separator between two European numbers
+ // changes to a European number. A single common separator between
+ // two numbers of the same type changes to that type.
+ for (var i$3 = 1, prev$1 = types[0]; i$3 < len - 1; ++i$3) {
+ var type$2 = types[i$3]
+ if (type$2 == "+" && prev$1 == "1" && types[i$3 + 1] == "1") {
+ types[i$3] = "1"
+ } else if (
+ type$2 == "," &&
+ prev$1 == types[i$3 + 1] &&
+ (prev$1 == "1" || prev$1 == "n")
+ ) {
+ types[i$3] = prev$1
+ }
+ prev$1 = type$2
+ }
+
+ // W5. A sequence of European terminators adjacent to European
+ // numbers changes to all European numbers.
+ // W6. Otherwise, separators and terminators change to Other
+ // Neutral.
+ for (var i$4 = 0; i$4 < len; ++i$4) {
+ var type$3 = types[i$4]
+ if (type$3 == ",") {
+ types[i$4] = "N"
+ } else if (type$3 == "%") {
+ var end = void 0
+ for (end = i$4 + 1; end < len && types[end] == "%"; ++end) {}
+ var replace =
+ (i$4 && types[i$4 - 1] == "!") || (end < len && types[end] == "1")
+ ? "1"
+ : "N"
+ for (var j = i$4; j < end; ++j) {
+ types[j] = replace
+ }
+ i$4 = end - 1
+ }
+ }
+
+ // W7. Search backwards from each instance of a European number
+ // until the first strong type (R, L, or sor) is found. If an L is
+ // found, then change the type of the European number to L.
+ for (var i$5 = 0, cur$1 = outerType; i$5 < len; ++i$5) {
+ var type$4 = types[i$5]
+ if (cur$1 == "L" && type$4 == "1") {
+ types[i$5] = "L"
+ } else if (isStrong.test(type$4)) {
+ cur$1 = type$4
+ }
+ }
+
+ // N1. A sequence of neutrals takes the direction of the
+ // surrounding strong text if the text on both sides has the same
+ // direction. European and Arabic numbers act as if they were R in
+ // terms of their influence on neutrals. Start-of-level-run (sor)
+ // and end-of-level-run (eor) are used at level run boundaries.
+ // N2. Any remaining neutrals take the embedding direction.
+ for (var i$6 = 0; i$6 < len; ++i$6) {
+ if (isNeutral.test(types[i$6])) {
+ var end$1 = void 0
+ for (end$1 = i$6 + 1; end$1 < len && isNeutral.test(types[end$1]); ++end$1) {}
+ var before = (i$6 ? types[i$6 - 1] : outerType) == "L"
+ var after = (end$1 < len ? types[end$1] : outerType) == "L"
+ var replace$1 = before == after ? (before ? "L" : "R") : outerType
+ for (var j$1 = i$6; j$1 < end$1; ++j$1) {
+ types[j$1] = replace$1
+ }
+ i$6 = end$1 - 1
+ }
+ }
+
+ // Here we depart from the documented algorithm, in order to avoid
+ // building up an actual levels array. Since there are only three
+ // levels (0, 1, 2) in an implementation that doesn't take
+ // explicit embedding into account, we can build up the order on
+ // the fly, without following the level-based algorithm.
+ var order = [],
+ m
+ for (var i$7 = 0; i$7 < len; ) {
+ if (countsAsLeft.test(types[i$7])) {
+ var start = i$7
+ for (++i$7; i$7 < len && countsAsLeft.test(types[i$7]); ++i$7) {}
+ order.push(new BidiSpan(0, start, i$7))
+ } else {
+ var pos = i$7,
+ at = order.length,
+ isRTL = direction == "rtl" ? 1 : 0
+ for (++i$7; i$7 < len && types[i$7] != "L"; ++i$7) {}
+ for (var j$2 = pos; j$2 < i$7; ) {
+ if (countsAsNum.test(types[j$2])) {
+ if (pos < j$2) {
+ order.splice(at, 0, new BidiSpan(1, pos, j$2))
+ at += isRTL
+ }
+ var nstart = j$2
+ for (++j$2; j$2 < i$7 && countsAsNum.test(types[j$2]); ++j$2) {}
+ order.splice(at, 0, new BidiSpan(2, nstart, j$2))
+ at += isRTL
+ pos = j$2
+ } else {
+ ++j$2
+ }
+ }
+ if (pos < i$7) {
+ order.splice(at, 0, new BidiSpan(1, pos, i$7))
+ }
+ }
+ }
+ if (direction == "ltr") {
+ if (order[0].level == 1 && (m = str.match(/^\s+/))) {
+ order[0].from = m[0].length
+ order.unshift(new BidiSpan(0, 0, m[0].length))
+ }
+ if (lst(order).level == 1 && (m = str.match(/\s+$/))) {
+ lst(order).to -= m[0].length
+ order.push(new BidiSpan(0, len - m[0].length, len))
+ }
+ }
+
+ return direction == "rtl" ? order.reverse() : order
+ }
+ })()
+
+ // Get the bidi ordering for the given line (and cache it). Returns
+ // false for lines that are fully left-to-right, and an array of
+ // BidiSpan objects otherwise.
+ function getOrder(line, direction) {
+ var order = line.order
+ if (order == null) {
+ order = line.order = bidiOrdering(line.text, direction)
+ }
+ return order
+ }
+
+ // EVENT HANDLING
+
+ // Lightweight event framework. on/off also work on DOM nodes,
+ // registering native DOM handlers.
+
+ var noHandlers = []
+
+ var on = function(emitter, type, f) {
+ if (emitter.addEventListener) {
+ emitter.addEventListener(type, f, false)
+ } else if (emitter.attachEvent) {
+ emitter.attachEvent("on" + type, f)
+ } else {
+ var map = emitter._handlers || (emitter._handlers = {})
+ map[type] = (map[type] || noHandlers).concat(f)
+ }
+ }
+
+ function getHandlers(emitter, type) {
+ return (emitter._handlers && emitter._handlers[type]) || noHandlers
+ }
+
+ function off(emitter, type, f) {
+ if (emitter.removeEventListener) {
+ emitter.removeEventListener(type, f, false)
+ } else if (emitter.detachEvent) {
+ emitter.detachEvent("on" + type, f)
+ } else {
+ var map = emitter._handlers,
+ arr = map && map[type]
+ if (arr) {
+ var index = indexOf(arr, f)
+ if (index > -1) {
+ map[type] = arr.slice(0, index).concat(arr.slice(index + 1))
+ }
+ }
+ }
+ }
+
+ function signal(emitter, type /*, values...*/) {
+ var handlers = getHandlers(emitter, type)
+ if (!handlers.length) {
+ return
+ }
+ var args = Array.prototype.slice.call(arguments, 2)
+ for (var i = 0; i < handlers.length; ++i) {
+ handlers[i].apply(null, args)
+ }
+ }
+
+ // The DOM events that CodeMirror handles can be overridden by
+ // registering a (non-DOM) handler on the editor for the event name,
+ // and preventDefault-ing the event in that handler.
+ function signalDOMEvent(cm, e, override) {
+ if (typeof e == "string") {
+ e = {
+ type: e,
+ preventDefault: function() {
+ this.defaultPrevented = true
+ }
+ }
+ }
+ signal(cm, override || e.type, cm, e)
+ return e_defaultPrevented(e) || e.codemirrorIgnore
+ }
+
+ function signalCursorActivity(cm) {
+ var arr = cm._handlers && cm._handlers.cursorActivity
+ if (!arr) {
+ return
+ }
+ var set = cm.curOp.cursorActivityHandlers || (cm.curOp.cursorActivityHandlers = [])
+ for (var i = 0; i < arr.length; ++i) {
+ if (indexOf(set, arr[i]) == -1) {
+ set.push(arr[i])
+ }
+ }
+ }
+
+ function hasHandler(emitter, type) {
+ return getHandlers(emitter, type).length > 0
+ }
+
+ // Add on and off methods to a constructor's prototype, to make
+ // registering events on such objects more convenient.
+ function eventMixin(ctor) {
+ ctor.prototype.on = function(type, f) {
+ on(this, type, f)
+ }
+ ctor.prototype.off = function(type, f) {
+ off(this, type, f)
+ }
+ }
+
+ // Due to the fact that we still support jurassic IE versions, some
+ // compatibility wrappers are needed.
+
+ function e_preventDefault(e) {
+ if (e.preventDefault) {
+ e.preventDefault()
+ } else {
+ e.returnValue = false
+ }
+ }
+ function e_stopPropagation(e) {
+ if (e.stopPropagation) {
+ e.stopPropagation()
+ } else {
+ e.cancelBubble = true
+ }
+ }
+ function e_defaultPrevented(e) {
+ return e.defaultPrevented != null ? e.defaultPrevented : e.returnValue == false
+ }
+ function e_stop(e) {
+ e_preventDefault(e)
+ e_stopPropagation(e)
+ }
+
+ function e_target(e) {
+ return e.target || e.srcElement
+ }
+ function e_button(e) {
+ var b = e.which
+ if (b == null) {
+ if (e.button & 1) {
+ b = 1
+ } else if (e.button & 2) {
+ b = 3
+ } else if (e.button & 4) {
+ b = 2
+ }
+ }
+ if (mac && e.ctrlKey && b == 1) {
+ b = 3
+ }
+ return b
+ }
+
+ // Detect drag-and-drop
+ var dragAndDrop = (function() {
+ // There is *some* kind of drag-and-drop support in IE6-8, but I
+ // couldn't get it to work yet.
+ if (ie && ie_version < 9) {
+ return false
+ }
+ var div = elt("div")
+ return "draggable" in div || "dragDrop" in div
+ })()
+
+ var zwspSupported
+ function zeroWidthElement(measure) {
+ if (zwspSupported == null) {
+ var test = elt("span", "\u200b")
+ removeChildrenAndAdd(measure, elt("span", [test, document.createTextNode("x")]))
+ if (measure.firstChild.offsetHeight != 0) {
+ zwspSupported =
+ test.offsetWidth <= 1 && test.offsetHeight > 2 && !(ie && ie_version < 8)
+ }
+ }
+ var node = zwspSupported
+ ? elt("span", "\u200b")
+ : elt("span", "\u00a0", null, "display: inline-block; width: 1px; margin-right: -1px")
+ node.setAttribute("cm-text", "")
+ return node
+ }
+
+ // Feature-detect IE's crummy client rect reporting for bidi text
+ var badBidiRects
+ function hasBadBidiRects(measure) {
+ if (badBidiRects != null) {
+ return badBidiRects
+ }
+ var txt = removeChildrenAndAdd(measure, document.createTextNode("A\u062eA"))
+ var r0 = range(txt, 0, 1).getBoundingClientRect()
+ var r1 = range(txt, 1, 2).getBoundingClientRect()
+ removeChildren(measure)
+ if (!r0 || r0.left == r0.right) {
+ return false
+ } // Safari returns null in some cases (#2780)
+ return (badBidiRects = r1.right - r0.right < 3)
+ }
+
+ // See if "".split is the broken IE version, if so, provide an
+ // alternative way to split lines.
+ var splitLinesAuto =
+ "\n\nb".split(/\n/).length != 3
+ ? function(string) {
+ var pos = 0,
+ result = [],
+ l = string.length
+ while (pos <= l) {
+ var nl = string.indexOf("\n", pos)
+ if (nl == -1) {
+ nl = string.length
+ }
+ var line = string.slice(pos, string.charAt(nl - 1) == "\r" ? nl - 1 : nl)
+ var rt = line.indexOf("\r")
+ if (rt != -1) {
+ result.push(line.slice(0, rt))
+ pos += rt + 1
+ } else {
+ result.push(line)
+ pos = nl + 1
+ }
+ }
+ return result
+ }
+ : function(string) {
+ return string.split(/\r\n?|\n/)
+ }
+
+ var hasSelection = window.getSelection
+ ? function(te) {
+ try {
+ return te.selectionStart != te.selectionEnd
+ } catch (e) {
+ return false
+ }
+ }
+ : function(te) {
+ var range
+ try {
+ range = te.ownerDocument.selection.createRange()
+ } catch (e) {}
+ if (!range || range.parentElement() != te) {
+ return false
+ }
+ return range.compareEndPoints("StartToEnd", range) != 0
+ }
+
+ var hasCopyEvent = (function() {
+ var e = elt("div")
+ if ("oncopy" in e) {
+ return true
+ }
+ e.setAttribute("oncopy", "return;")
+ return typeof e.oncopy == "function"
+ })()
+
+ var badZoomedRects = null
+ function hasBadZoomedRects(measure) {
+ if (badZoomedRects != null) {
+ return badZoomedRects
+ }
+ var node = removeChildrenAndAdd(measure, elt("span", "x"))
+ var normal = node.getBoundingClientRect()
+ var fromRange = range(node, 0, 1).getBoundingClientRect()
+ return (badZoomedRects = Math.abs(normal.left - fromRange.left) > 1)
+ }
+
+ // Known modes, by name and by MIME
+ var modes = {},
+ mimeModes = {}
+
+ // Extra arguments are stored as the mode's dependencies, which is
+ // used by (legacy) mechanisms like loadmode.js to automatically
+ // load a mode. (Preferred mechanism is the require/define calls.)
+ function defineMode(name, mode) {
+ if (arguments.length > 2) {
+ mode.dependencies = Array.prototype.slice.call(arguments, 2)
+ }
+ modes[name] = mode
+ }
+
+ function defineMIME(mime, spec) {
+ mimeModes[mime] = spec
+ }
+
+ // Given a MIME type, a {name, ...options} config object, or a name
+ // string, return a mode config object.
+ function resolveMode(spec) {
+ if (typeof spec == "string" && mimeModes.hasOwnProperty(spec)) {
+ spec = mimeModes[spec]
+ } else if (spec && typeof spec.name == "string" && mimeModes.hasOwnProperty(spec.name)) {
+ var found = mimeModes[spec.name]
+ if (typeof found == "string") {
+ found = { name: found }
+ }
+ spec = createObj(found, spec)
+ spec.name = found.name
+ } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+xml$/.test(spec)) {
+ return resolveMode("application/xml")
+ } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+json$/.test(spec)) {
+ return resolveMode("application/json")
+ }
+ if (typeof spec == "string") {
+ return { name: spec }
+ } else {
+ return spec || { name: "null" }
+ }
+ }
+
+ // Given a mode spec (anything that resolveMode accepts), find and
+ // initialize an actual mode object.
+ function getMode(options, spec) {
+ spec = resolveMode(spec)
+ var mfactory = modes[spec.name]
+ if (!mfactory) {
+ return getMode(options, "text/plain")
+ }
+ var modeObj = mfactory(options, spec)
+ if (modeExtensions.hasOwnProperty(spec.name)) {
+ var exts = modeExtensions[spec.name]
+ for (var prop in exts) {
+ if (!exts.hasOwnProperty(prop)) {
+ continue
+ }
+ if (modeObj.hasOwnProperty(prop)) {
+ modeObj["_" + prop] = modeObj[prop]
+ }
+ modeObj[prop] = exts[prop]
+ }
+ }
+ modeObj.name = spec.name
+ if (spec.helperType) {
+ modeObj.helperType = spec.helperType
+ }
+ if (spec.modeProps) {
+ for (var prop$1 in spec.modeProps) {
+ modeObj[prop$1] = spec.modeProps[prop$1]
+ }
+ }
+
+ return modeObj
+ }
+
+ // This can be used to attach properties to mode objects from
+ // outside the actual mode definition.
+ var modeExtensions = {}
+ function extendMode(mode, properties) {
+ var exts = modeExtensions.hasOwnProperty(mode)
+ ? modeExtensions[mode]
+ : (modeExtensions[mode] = {})
+ copyObj(properties, exts)
+ }
+
+ function copyState(mode, state) {
+ if (state === true) {
+ return state
+ }
+ if (mode.copyState) {
+ return mode.copyState(state)
+ }
+ var nstate = {}
+ for (var n in state) {
+ var val = state[n]
+ if (val instanceof Array) {
+ val = val.concat([])
+ }
+ nstate[n] = val
+ }
+ return nstate
+ }
+
+ // Given a mode and a state (for that mode), find the inner mode and
+ // state at the position that the state refers to.
+ function innerMode(mode, state) {
+ var info
+ while (mode.innerMode) {
+ info = mode.innerMode(state)
+ if (!info || info.mode == mode) {
+ break
+ }
+ state = info.state
+ mode = info.mode
+ }
+ return info || { mode: mode, state: state }
+ }
+
+ function startState(mode, a1, a2) {
+ return mode.startState ? mode.startState(a1, a2) : true
+ }
+
+ // STRING STREAM
+
+ // Fed to the mode parsers, provides helper functions to make
+ // parsers more succinct.
+
+ var StringStream = function(string, tabSize, lineOracle) {
+ this.pos = this.start = 0
+ this.string = string
+ this.tabSize = tabSize || 8
+ this.lastColumnPos = this.lastColumnValue = 0
+ this.lineStart = 0
+ this.lineOracle = lineOracle
+ }
+
+ StringStream.prototype.eol = function() {
+ return this.pos >= this.string.length
+ }
+ StringStream.prototype.sol = function() {
+ return this.pos == this.lineStart
+ }
+ StringStream.prototype.peek = function() {
+ return this.string.charAt(this.pos) || undefined
+ }
+ StringStream.prototype.next = function() {
+ if (this.pos < this.string.length) {
+ return this.string.charAt(this.pos++)
+ }
+ }
+ StringStream.prototype.eat = function(match) {
+ var ch = this.string.charAt(this.pos)
+ var ok
+ if (typeof match == "string") {
+ ok = ch == match
+ } else {
+ ok = ch && (match.test ? match.test(ch) : match(ch))
+ }
+ if (ok) {
+ ++this.pos
+ return ch
+ }
+ }
+ StringStream.prototype.eatWhile = function(match) {
+ var start = this.pos
+ while (this.eat(match)) {}
+ return this.pos > start
+ }
+ StringStream.prototype.eatSpace = function() {
+ var start = this.pos
+ while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) {
+ ++this.pos
+ }
+ return this.pos > start
+ }
+ StringStream.prototype.skipToEnd = function() {
+ this.pos = this.string.length
+ }
+ StringStream.prototype.skipTo = function(ch) {
+ var found = this.string.indexOf(ch, this.pos)
+ if (found > -1) {
+ this.pos = found
+ return true
+ }
+ }
+ StringStream.prototype.backUp = function(n) {
+ this.pos -= n
+ }
+ StringStream.prototype.column = function() {
+ if (this.lastColumnPos < this.start) {
+ this.lastColumnValue = countColumn(
+ this.string,
+ this.start,
+ this.tabSize,
+ this.lastColumnPos,
+ this.lastColumnValue
+ )
+ this.lastColumnPos = this.start
+ }
+ return (
+ this.lastColumnValue -
+ (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0)
+ )
+ }
+ StringStream.prototype.indentation = function() {
+ return (
+ countColumn(this.string, null, this.tabSize) -
+ (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0)
+ )
+ }
+ StringStream.prototype.match = function(pattern, consume, caseInsensitive) {
+ if (typeof pattern == "string") {
+ var cased = function(str) {
+ return caseInsensitive ? str.toLowerCase() : str
+ }
+ var substr = this.string.substr(this.pos, pattern.length)
+ if (cased(substr) == cased(pattern)) {
+ if (consume !== false) {
+ this.pos += pattern.length
+ }
+ return true
+ }
+ } else {
+ var match = this.string.slice(this.pos).match(pattern)
+ if (match && match.index > 0) {
+ return null
+ }
+ if (match && consume !== false) {
+ this.pos += match[0].length
+ }
+ return match
+ }
+ }
+ StringStream.prototype.current = function() {
+ return this.string.slice(this.start, this.pos)
+ }
+ StringStream.prototype.hideFirstChars = function(n, inner) {
+ this.lineStart += n
+ try {
+ return inner()
+ } finally {
+ this.lineStart -= n
+ }
+ }
+ StringStream.prototype.lookAhead = function(n) {
+ var oracle = this.lineOracle
+ return oracle && oracle.lookAhead(n)
+ }
+ StringStream.prototype.baseToken = function() {
+ var oracle = this.lineOracle
+ return oracle && oracle.baseToken(this.pos)
+ }
+
+ // Find the line object corresponding to the given line number.
+ function getLine(doc, n) {
+ n -= doc.first
+ if (n < 0 || n >= doc.size) {
+ throw new Error("There is no line " + (n + doc.first) + " in the document.")
+ }
+ var chunk = doc
+ while (!chunk.lines) {
+ for (var i = 0; ; ++i) {
+ var child = chunk.children[i],
+ sz = child.chunkSize()
+ if (n < sz) {
+ chunk = child
+ break
+ }
+ n -= sz
+ }
+ }
+ return chunk.lines[n]
+ }
+
+ // Get the part of a document between two positions, as an array of
+ // strings.
+ function getBetween(doc, start, end) {
+ var out = [],
+ n = start.line
+ doc.iter(start.line, end.line + 1, function(line) {
+ var text = line.text
+ if (n == end.line) {
+ text = text.slice(0, end.ch)
+ }
+ if (n == start.line) {
+ text = text.slice(start.ch)
+ }
+ out.push(text)
+ ++n
+ })
+ return out
+ }
+ // Get the lines between from and to, as array of strings.
+ function getLines(doc, from, to) {
+ var out = []
+ doc.iter(from, to, function(line) {
+ out.push(line.text)
+ }) // iter aborts when callback returns truthy value
+ return out
+ }
+
+ // Update the height of a line, propagating the height change
+ // upwards to parent nodes.
+ function updateLineHeight(line, height) {
+ var diff = height - line.height
+ if (diff) {
+ for (var n = line; n; n = n.parent) {
+ n.height += diff
+ }
+ }
+ }
+
+ // Given a line object, find its line number by walking up through
+ // its parent links.
+ function lineNo(line) {
+ if (line.parent == null) {
+ return null
+ }
+ var cur = line.parent,
+ no = indexOf(cur.lines, line)
+ for (var chunk = cur.parent; chunk; cur = chunk, chunk = chunk.parent) {
+ for (var i = 0; ; ++i) {
+ if (chunk.children[i] == cur) {
+ break
+ }
+ no += chunk.children[i].chunkSize()
+ }
+ }
+ return no + cur.first
+ }
+
+ // Find the line at the given vertical position, using the height
+ // information in the document tree.
+ function lineAtHeight(chunk, h) {
+ var n = chunk.first
+ outer: do {
+ for (var i$1 = 0; i$1 < chunk.children.length; ++i$1) {
+ var child = chunk.children[i$1],
+ ch = child.height
+ if (h < ch) {
+ chunk = child
+ continue outer
+ }
+ h -= ch
+ n += child.chunkSize()
+ }
+ return n
+ } while (!chunk.lines)
+ var i = 0
+ for (; i < chunk.lines.length; ++i) {
+ var line = chunk.lines[i],
+ lh = line.height
+ if (h < lh) {
+ break
+ }
+ h -= lh
+ }
+ return n + i
+ }
+
+ function isLine(doc, l) {
+ return l >= doc.first && l < doc.first + doc.size
+ }
+
+ function lineNumberFor(options, i) {
+ return String(options.lineNumberFormatter(i + options.firstLineNumber))
+ }
+
+ // A Pos instance represents a position within the text.
+ function Pos(line, ch, sticky) {
+ if (sticky === void 0) sticky = null
+
+ if (!(this instanceof Pos)) {
+ return new Pos(line, ch, sticky)
+ }
+ this.line = line
+ this.ch = ch
+ this.sticky = sticky
+ }
+
+ // Compare two positions, return 0 if they are the same, a negative
+ // number when a is less, and a positive number otherwise.
+ function cmp(a, b) {
+ return a.line - b.line || a.ch - b.ch
+ }
+
+ function equalCursorPos(a, b) {
+ return a.sticky == b.sticky && cmp(a, b) == 0
+ }
+
+ function copyPos(x) {
+ return Pos(x.line, x.ch)
+ }
+ function maxPos(a, b) {
+ return cmp(a, b) < 0 ? b : a
+ }
+ function minPos(a, b) {
+ return cmp(a, b) < 0 ? a : b
+ }
+
+ // Most of the external API clips given positions to make sure they
+ // actually exist within the document.
+ function clipLine(doc, n) {
+ return Math.max(doc.first, Math.min(n, doc.first + doc.size - 1))
+ }
+ function clipPos(doc, pos) {
+ if (pos.line < doc.first) {
+ return Pos(doc.first, 0)
+ }
+ var last = doc.first + doc.size - 1
+ if (pos.line > last) {
+ return Pos(last, getLine(doc, last).text.length)
+ }
+ return clipToLen(pos, getLine(doc, pos.line).text.length)
+ }
+ function clipToLen(pos, linelen) {
+ var ch = pos.ch
+ if (ch == null || ch > linelen) {
+ return Pos(pos.line, linelen)
+ } else if (ch < 0) {
+ return Pos(pos.line, 0)
+ } else {
+ return pos
+ }
+ }
+ function clipPosArray(doc, array) {
+ var out = []
+ for (var i = 0; i < array.length; i++) {
+ out[i] = clipPos(doc, array[i])
+ }
+ return out
+ }
+
+ var SavedContext = function(state, lookAhead) {
+ this.state = state
+ this.lookAhead = lookAhead
+ }
+
+ var Context = function(doc, state, line, lookAhead) {
+ this.state = state
+ this.doc = doc
+ this.line = line
+ this.maxLookAhead = lookAhead || 0
+ this.baseTokens = null
+ this.baseTokenPos = 1
+ }
+
+ Context.prototype.lookAhead = function(n) {
+ var line = this.doc.getLine(this.line + n)
+ if (line != null && n > this.maxLookAhead) {
+ this.maxLookAhead = n
+ }
+ return line
+ }
+
+ Context.prototype.baseToken = function(n) {
+ if (!this.baseTokens) {
+ return null
+ }
+ while (this.baseTokens[this.baseTokenPos] <= n) {
+ this.baseTokenPos += 2
+ }
+ var type = this.baseTokens[this.baseTokenPos + 1]
+ return {
+ type: type && type.replace(/( |^)overlay .*/, ""),
+ size: this.baseTokens[this.baseTokenPos] - n
+ }
+ }
+
+ Context.prototype.nextLine = function() {
+ this.line++
+ if (this.maxLookAhead > 0) {
+ this.maxLookAhead--
+ }
+ }
+
+ Context.fromSaved = function(doc, saved, line) {
+ if (saved instanceof SavedContext) {
+ return new Context(doc, copyState(doc.mode, saved.state), line, saved.lookAhead)
+ } else {
+ return new Context(doc, copyState(doc.mode, saved), line)
+ }
+ }
+
+ Context.prototype.save = function(copy) {
+ var state = copy !== false ? copyState(this.doc.mode, this.state) : this.state
+ return this.maxLookAhead > 0 ? new SavedContext(state, this.maxLookAhead) : state
+ }
+
+ // Compute a style array (an array starting with a mode generation
+ // -- for invalidation -- followed by pairs of end positions and
+ // style strings), which is used to highlight the tokens on the
+ // line.
+ function highlightLine(cm, line, context, forceToEnd) {
+ // A styles array always starts with a number identifying the
+ // mode/overlays that it is based on (for easy invalidation).
+ var st = [cm.state.modeGen],
+ lineClasses = {}
+ // Compute the base array of styles
+ runMode(
+ cm,
+ line.text,
+ cm.doc.mode,
+ context,
+ function(end, style) {
+ return st.push(end, style)
+ },
+ lineClasses,
+ forceToEnd
+ )
+ var state = context.state
+
+ // Run overlays, adjust style array.
+ var loop = function(o) {
+ context.baseTokens = st
+ var overlay = cm.state.overlays[o],
+ i = 1,
+ at = 0
+ context.state = true
+ runMode(
+ cm,
+ line.text,
+ overlay.mode,
+ context,
+ function(end, style) {
+ var start = i
+ // Ensure there's a token end at the current position, and that i points at it
+ while (at < end) {
+ var i_end = st[i]
+ if (i_end > end) {
+ st.splice(i, 1, end, st[i + 1], i_end)
+ }
+ i += 2
+ at = Math.min(end, i_end)
+ }
+ if (!style) {
+ return
+ }
+ if (overlay.opaque) {
+ st.splice(start, i - start, end, "overlay " + style)
+ i = start + 2
+ } else {
+ for (; start < i; start += 2) {
+ var cur = st[start + 1]
+ st[start + 1] = (cur ? cur + " " : "") + "overlay " + style
+ }
+ }
+ },
+ lineClasses
+ )
+ context.state = state
+ context.baseTokens = null
+ context.baseTokenPos = 1
+ }
+
+ for (var o = 0; o < cm.state.overlays.length; ++o) loop(o)
+
+ return {
+ styles: st,
+ classes: lineClasses.bgClass || lineClasses.textClass ? lineClasses : null
+ }
+ }
+
+ function getLineStyles(cm, line, updateFrontier) {
+ if (!line.styles || line.styles[0] != cm.state.modeGen) {
+ var context = getContextBefore(cm, lineNo(line))
+ var resetState =
+ line.text.length > cm.options.maxHighlightLength &&
+ copyState(cm.doc.mode, context.state)
+ var result = highlightLine(cm, line, context)
+ if (resetState) {
+ context.state = resetState
+ }
+ line.stateAfter = context.save(!resetState)
+ line.styles = result.styles
+ if (result.classes) {
+ line.styleClasses = result.classes
+ } else if (line.styleClasses) {
+ line.styleClasses = null
+ }
+ if (updateFrontier === cm.doc.highlightFrontier) {
+ cm.doc.modeFrontier = Math.max(cm.doc.modeFrontier, ++cm.doc.highlightFrontier)
+ }
+ }
+ return line.styles
+ }
+
+ function getContextBefore(cm, n, precise) {
+ var doc = cm.doc,
+ display = cm.display
+ if (!doc.mode.startState) {
+ return new Context(doc, true, n)
+ }
+ var start = findStartLine(cm, n, precise)
+ var saved = start > doc.first && getLine(doc, start - 1).stateAfter
+ var context = saved
+ ? Context.fromSaved(doc, saved, start)
+ : new Context(doc, startState(doc.mode), start)
+
+ doc.iter(start, n, function(line) {
+ processLine(cm, line.text, context)
+ var pos = context.line
+ line.stateAfter =
+ pos == n - 1 || pos % 5 == 0 || (pos >= display.viewFrom && pos < display.viewTo)
+ ? context.save()
+ : null
+ context.nextLine()
+ })
+ if (precise) {
+ doc.modeFrontier = context.line
+ }
+ return context
+ }
+
+ // Lightweight form of highlight -- proceed over this line and
+ // update state, but don't save a style array. Used for lines that
+ // aren't currently visible.
+ function processLine(cm, text, context, startAt) {
+ var mode = cm.doc.mode
+ var stream = new StringStream(text, cm.options.tabSize, context)
+ stream.start = stream.pos = startAt || 0
+ if (text == "") {
+ callBlankLine(mode, context.state)
+ }
+ while (!stream.eol()) {
+ readToken(mode, stream, context.state)
+ stream.start = stream.pos
+ }
+ }
+
+ function callBlankLine(mode, state) {
+ if (mode.blankLine) {
+ return mode.blankLine(state)
+ }
+ if (!mode.innerMode) {
+ return
+ }
+ var inner = innerMode(mode, state)
+ if (inner.mode.blankLine) {
+ return inner.mode.blankLine(inner.state)
+ }
+ }
+
+ function readToken(mode, stream, state, inner) {
+ for (var i = 0; i < 10; i++) {
+ if (inner) {
+ inner[0] = innerMode(mode, state).mode
+ }
+ var style = mode.token(stream, state)
+ if (stream.pos > stream.start) {
+ return style
+ }
+ }
+ throw new Error("Mode " + mode.name + " failed to advance stream.")
+ }
+
+ var Token = function(stream, type, state) {
+ this.start = stream.start
+ this.end = stream.pos
+ this.string = stream.current()
+ this.type = type || null
+ this.state = state
+ }
+
+ // Utility for getTokenAt and getLineTokens
+ function takeToken(cm, pos, precise, asArray) {
+ var doc = cm.doc,
+ mode = doc.mode,
+ style
+ pos = clipPos(doc, pos)
+ var line = getLine(doc, pos.line),
+ context = getContextBefore(cm, pos.line, precise)
+ var stream = new StringStream(line.text, cm.options.tabSize, context),
+ tokens
+ if (asArray) {
+ tokens = []
+ }
+ while ((asArray || stream.pos < pos.ch) && !stream.eol()) {
+ stream.start = stream.pos
+ style = readToken(mode, stream, context.state)
+ if (asArray) {
+ tokens.push(new Token(stream, style, copyState(doc.mode, context.state)))
+ }
+ }
+ return asArray ? tokens : new Token(stream, style, context.state)
+ }
+
+ function extractLineClasses(type, output) {
+ if (type) {
+ for (;;) {
+ var lineClass = type.match(/(?:^|\s+)line-(background-)?(\S+)/)
+ if (!lineClass) {
+ break
+ }
+ type =
+ type.slice(0, lineClass.index) +
+ type.slice(lineClass.index + lineClass[0].length)
+ var prop = lineClass[1] ? "bgClass" : "textClass"
+ if (output[prop] == null) {
+ output[prop] = lineClass[2]
+ } else if (!new RegExp("(?:^|s)" + lineClass[2] + "(?:$|s)").test(output[prop])) {
+ output[prop] += " " + lineClass[2]
+ }
+ }
+ }
+ return type
+ }
+
+ // Run the given mode's parser over a line, calling f for each token.
+ function runMode(cm, text, mode, context, f, lineClasses, forceToEnd) {
+ var flattenSpans = mode.flattenSpans
+ if (flattenSpans == null) {
+ flattenSpans = cm.options.flattenSpans
+ }
+ var curStart = 0,
+ curStyle = null
+ var stream = new StringStream(text, cm.options.tabSize, context),
+ style
+ var inner = cm.options.addModeClass && [null]
+ if (text == "") {
+ extractLineClasses(callBlankLine(mode, context.state), lineClasses)
+ }
+ while (!stream.eol()) {
+ if (stream.pos > cm.options.maxHighlightLength) {
+ flattenSpans = false
+ if (forceToEnd) {
+ processLine(cm, text, context, stream.pos)
+ }
+ stream.pos = text.length
+ style = null
+ } else {
+ style = extractLineClasses(
+ readToken(mode, stream, context.state, inner),
+ lineClasses
+ )
+ }
+ if (inner) {
+ var mName = inner[0].name
+ if (mName) {
+ style = "m-" + (style ? mName + " " + style : mName)
+ }
+ }
+ if (!flattenSpans || curStyle != style) {
+ while (curStart < stream.start) {
+ curStart = Math.min(stream.start, curStart + 5000)
+ f(curStart, curStyle)
+ }
+ curStyle = style
+ }
+ stream.start = stream.pos
+ }
+ while (curStart < stream.pos) {
+ // Webkit seems to refuse to render text nodes longer than 57444
+ // characters, and returns inaccurate measurements in nodes
+ // starting around 5000 chars.
+ var pos = Math.min(stream.pos, curStart + 5000)
+ f(pos, curStyle)
+ curStart = pos
+ }
+ }
+
+ // Finds the line to start with when starting a parse. Tries to
+ // find a line with a stateAfter, so that it can start with a
+ // valid state. If that fails, it returns the line with the
+ // smallest indentation, which tends to need the least context to
+ // parse correctly.
+ function findStartLine(cm, n, precise) {
+ var minindent,
+ minline,
+ doc = cm.doc
+ var lim = precise ? -1 : n - (cm.doc.mode.innerMode ? 1000 : 100)
+ for (var search = n; search > lim; --search) {
+ if (search <= doc.first) {
+ return doc.first
+ }
+ var line = getLine(doc, search - 1),
+ after = line.stateAfter
+ if (
+ after &&
+ (!precise ||
+ search + (after instanceof SavedContext ? after.lookAhead : 0) <=
+ doc.modeFrontier)
+ ) {
+ return search
+ }
+ var indented = countColumn(line.text, null, cm.options.tabSize)
+ if (minline == null || minindent > indented) {
+ minline = search - 1
+ minindent = indented
+ }
+ }
+ return minline
+ }
+
+ function retreatFrontier(doc, n) {
+ doc.modeFrontier = Math.min(doc.modeFrontier, n)
+ if (doc.highlightFrontier < n - 10) {
+ return
+ }
+ var start = doc.first
+ for (var line = n - 1; line > start; line--) {
+ var saved = getLine(doc, line).stateAfter
+ // change is on 3
+ // state on line 1 looked ahead 2 -- so saw 3
+ // test 1 + 2 < 3 should cover this
+ if (saved && (!(saved instanceof SavedContext) || line + saved.lookAhead < n)) {
+ start = line + 1
+ break
+ }
+ }
+ doc.highlightFrontier = Math.min(doc.highlightFrontier, start)
+ }
+
+ // Optimize some code when these features are not used.
+ var sawReadOnlySpans = false,
+ sawCollapsedSpans = false
+
+ function seeReadOnlySpans() {
+ sawReadOnlySpans = true
+ }
+
+ function seeCollapsedSpans() {
+ sawCollapsedSpans = true
+ }
+
+ // TEXTMARKER SPANS
+
+ function MarkedSpan(marker, from, to) {
+ this.marker = marker
+ this.from = from
+ this.to = to
+ }
+
+ // Search an array of spans for a span matching the given marker.
+ function getMarkedSpanFor(spans, marker) {
+ if (spans) {
+ for (var i = 0; i < spans.length; ++i) {
+ var span = spans[i]
+ if (span.marker == marker) {
+ return span
+ }
+ }
+ }
+ }
+ // Remove a span from an array, returning undefined if no spans are
+ // left (we don't store arrays for lines without spans).
+ function removeMarkedSpan(spans, span) {
+ var r
+ for (var i = 0; i < spans.length; ++i) {
+ if (spans[i] != span) {
+ ;(r || (r = [])).push(spans[i])
+ }
+ }
+ return r
+ }
+ // Add a span to a line.
+ function addMarkedSpan(line, span) {
+ line.markedSpans = line.markedSpans ? line.markedSpans.concat([span]) : [span]
+ span.marker.attachLine(line)
+ }
+
+ // Used for the algorithm that adjusts markers for a change in the
+ // document. These functions cut an array of spans at a given
+ // character position, returning an array of remaining chunks (or
+ // undefined if nothing remains).
+ function markedSpansBefore(old, startCh, isInsert) {
+ var nw
+ if (old) {
+ for (var i = 0; i < old.length; ++i) {
+ var span = old[i],
+ marker = span.marker
+ var startsBefore =
+ span.from == null ||
+ (marker.inclusiveLeft ? span.from <= startCh : span.from < startCh)
+ if (
+ startsBefore ||
+ (span.from == startCh &&
+ marker.type == "bookmark" &&
+ (!isInsert || !span.marker.insertLeft))
+ ) {
+ var endsAfter =
+ span.to == null ||
+ (marker.inclusiveRight ? span.to >= startCh : span.to > startCh)
+ ;(nw || (nw = [])).push(
+ new MarkedSpan(marker, span.from, endsAfter ? null : span.to)
+ )
+ }
+ }
+ }
+ return nw
+ }
+ function markedSpansAfter(old, endCh, isInsert) {
+ var nw
+ if (old) {
+ for (var i = 0; i < old.length; ++i) {
+ var span = old[i],
+ marker = span.marker
+ var endsAfter =
+ span.to == null || (marker.inclusiveRight ? span.to >= endCh : span.to > endCh)
+ if (
+ endsAfter ||
+ (span.from == endCh &&
+ marker.type == "bookmark" &&
+ (!isInsert || span.marker.insertLeft))
+ ) {
+ var startsBefore =
+ span.from == null ||
+ (marker.inclusiveLeft ? span.from <= endCh : span.from < endCh)
+ ;(nw || (nw = [])).push(
+ new MarkedSpan(
+ marker,
+ startsBefore ? null : span.from - endCh,
+ span.to == null ? null : span.to - endCh
+ )
+ )
+ }
+ }
+ }
+ return nw
+ }
+
+ // Given a change object, compute the new set of marker spans that
+ // cover the line in which the change took place. Removes spans
+ // entirely within the change, reconnects spans belonging to the
+ // same marker that appear on both sides of the change, and cuts off
+ // spans partially within the change. Returns an array of span
+ // arrays with one element for each line in (after) the change.
+ function stretchSpansOverChange(doc, change) {
+ if (change.full) {
+ return null
+ }
+ var oldFirst = isLine(doc, change.from.line) && getLine(doc, change.from.line).markedSpans
+ var oldLast = isLine(doc, change.to.line) && getLine(doc, change.to.line).markedSpans
+ if (!oldFirst && !oldLast) {
+ return null
+ }
+
+ var startCh = change.from.ch,
+ endCh = change.to.ch,
+ isInsert = cmp(change.from, change.to) == 0
+ // Get the spans that 'stick out' on both sides
+ var first = markedSpansBefore(oldFirst, startCh, isInsert)
+ var last = markedSpansAfter(oldLast, endCh, isInsert)
+
+ // Next, merge those two ends
+ var sameLine = change.text.length == 1,
+ offset = lst(change.text).length + (sameLine ? startCh : 0)
+ if (first) {
+ // Fix up .to properties of first
+ for (var i = 0; i < first.length; ++i) {
+ var span = first[i]
+ if (span.to == null) {
+ var found = getMarkedSpanFor(last, span.marker)
+ if (!found) {
+ span.to = startCh
+ } else if (sameLine) {
+ span.to = found.to == null ? null : found.to + offset
+ }
+ }
+ }
+ }
+ if (last) {
+ // Fix up .from in last (or move them into first in case of sameLine)
+ for (var i$1 = 0; i$1 < last.length; ++i$1) {
+ var span$1 = last[i$1]
+ if (span$1.to != null) {
+ span$1.to += offset
+ }
+ if (span$1.from == null) {
+ var found$1 = getMarkedSpanFor(first, span$1.marker)
+ if (!found$1) {
+ span$1.from = offset
+ if (sameLine) {
+ ;(first || (first = [])).push(span$1)
+ }
+ }
+ } else {
+ span$1.from += offset
+ if (sameLine) {
+ ;(first || (first = [])).push(span$1)
+ }
+ }
+ }
+ }
+ // Make sure we didn't create any zero-length spans
+ if (first) {
+ first = clearEmptySpans(first)
+ }
+ if (last && last != first) {
+ last = clearEmptySpans(last)
+ }
+
+ var newMarkers = [first]
+ if (!sameLine) {
+ // Fill gap with whole-line-spans
+ var gap = change.text.length - 2,
+ gapMarkers
+ if (gap > 0 && first) {
+ for (var i$2 = 0; i$2 < first.length; ++i$2) {
+ if (first[i$2].to == null) {
+ ;(gapMarkers || (gapMarkers = [])).push(
+ new MarkedSpan(first[i$2].marker, null, null)
+ )
+ }
+ }
+ }
+ for (var i$3 = 0; i$3 < gap; ++i$3) {
+ newMarkers.push(gapMarkers)
+ }
+ newMarkers.push(last)
+ }
+ return newMarkers
+ }
+
+ // Remove spans that are empty and don't have a clearWhenEmpty
+ // option of false.
+ function clearEmptySpans(spans) {
+ for (var i = 0; i < spans.length; ++i) {
+ var span = spans[i]
+ if (span.from != null && span.from == span.to && span.marker.clearWhenEmpty !== false) {
+ spans.splice(i--, 1)
+ }
+ }
+ if (!spans.length) {
+ return null
+ }
+ return spans
+ }
+
+ // Used to 'clip' out readOnly ranges when making a change.
+ function removeReadOnlyRanges(doc, from, to) {
+ var markers = null
+ doc.iter(from.line, to.line + 1, function(line) {
+ if (line.markedSpans) {
+ for (var i = 0; i < line.markedSpans.length; ++i) {
+ var mark = line.markedSpans[i].marker
+ if (mark.readOnly && (!markers || indexOf(markers, mark) == -1)) {
+ ;(markers || (markers = [])).push(mark)
+ }
+ }
+ }
+ })
+ if (!markers) {
+ return null
+ }
+ var parts = [{ from: from, to: to }]
+ for (var i = 0; i < markers.length; ++i) {
+ var mk = markers[i],
+ m = mk.find(0)
+ for (var j = 0; j < parts.length; ++j) {
+ var p = parts[j]
+ if (cmp(p.to, m.from) < 0 || cmp(p.from, m.to) > 0) {
+ continue
+ }
+ var newParts = [j, 1],
+ dfrom = cmp(p.from, m.from),
+ dto = cmp(p.to, m.to)
+ if (dfrom < 0 || (!mk.inclusiveLeft && !dfrom)) {
+ newParts.push({ from: p.from, to: m.from })
+ }
+ if (dto > 0 || (!mk.inclusiveRight && !dto)) {
+ newParts.push({ from: m.to, to: p.to })
+ }
+ parts.splice.apply(parts, newParts)
+ j += newParts.length - 3
+ }
+ }
+ return parts
+ }
+
+ // Connect or disconnect spans from a line.
+ function detachMarkedSpans(line) {
+ var spans = line.markedSpans
+ if (!spans) {
+ return
+ }
+ for (var i = 0; i < spans.length; ++i) {
+ spans[i].marker.detachLine(line)
+ }
+ line.markedSpans = null
+ }
+ function attachMarkedSpans(line, spans) {
+ if (!spans) {
+ return
+ }
+ for (var i = 0; i < spans.length; ++i) {
+ spans[i].marker.attachLine(line)
+ }
+ line.markedSpans = spans
+ }
+
+ // Helpers used when computing which overlapping collapsed span
+ // counts as the larger one.
+ function extraLeft(marker) {
+ return marker.inclusiveLeft ? -1 : 0
+ }
+ function extraRight(marker) {
+ return marker.inclusiveRight ? 1 : 0
+ }
+
+ // Returns a number indicating which of two overlapping collapsed
+ // spans is larger (and thus includes the other). Falls back to
+ // comparing ids when the spans cover exactly the same range.
+ function compareCollapsedMarkers(a, b) {
+ var lenDiff = a.lines.length - b.lines.length
+ if (lenDiff != 0) {
+ return lenDiff
+ }
+ var aPos = a.find(),
+ bPos = b.find()
+ var fromCmp = cmp(aPos.from, bPos.from) || extraLeft(a) - extraLeft(b)
+ if (fromCmp) {
+ return -fromCmp
+ }
+ var toCmp = cmp(aPos.to, bPos.to) || extraRight(a) - extraRight(b)
+ if (toCmp) {
+ return toCmp
+ }
+ return b.id - a.id
+ }
+
+ // Find out whether a line ends or starts in a collapsed span. If
+ // so, return the marker for that span.
+ function collapsedSpanAtSide(line, start) {
+ var sps = sawCollapsedSpans && line.markedSpans,
+ found
+ if (sps) {
+ for (var sp = void 0, i = 0; i < sps.length; ++i) {
+ sp = sps[i]
+ if (
+ sp.marker.collapsed &&
+ (start ? sp.from : sp.to) == null &&
+ (!found || compareCollapsedMarkers(found, sp.marker) < 0)
+ ) {
+ found = sp.marker
+ }
+ }
+ }
+ return found
+ }
+ function collapsedSpanAtStart(line) {
+ return collapsedSpanAtSide(line, true)
+ }
+ function collapsedSpanAtEnd(line) {
+ return collapsedSpanAtSide(line, false)
+ }
+
+ function collapsedSpanAround(line, ch) {
+ var sps = sawCollapsedSpans && line.markedSpans,
+ found
+ if (sps) {
+ for (var i = 0; i < sps.length; ++i) {
+ var sp = sps[i]
+ if (
+ sp.marker.collapsed &&
+ (sp.from == null || sp.from < ch) &&
+ (sp.to == null || sp.to > ch) &&
+ (!found || compareCollapsedMarkers(found, sp.marker) < 0)
+ ) {
+ found = sp.marker
+ }
+ }
+ }
+ return found
+ }
+
+ // Test whether there exists a collapsed span that partially
+ // overlaps (covers the start or end, but not both) of a new span.
+ // Such overlap is not allowed.
+ function conflictingCollapsedRange(doc, lineNo, from, to, marker) {
+ var line = getLine(doc, lineNo)
+ var sps = sawCollapsedSpans && line.markedSpans
+ if (sps) {
+ for (var i = 0; i < sps.length; ++i) {
+ var sp = sps[i]
+ if (!sp.marker.collapsed) {
+ continue
+ }
+ var found = sp.marker.find(0)
+ var fromCmp = cmp(found.from, from) || extraLeft(sp.marker) - extraLeft(marker)
+ var toCmp = cmp(found.to, to) || extraRight(sp.marker) - extraRight(marker)
+ if ((fromCmp >= 0 && toCmp <= 0) || (fromCmp <= 0 && toCmp >= 0)) {
+ continue
+ }
+ if (
+ (fromCmp <= 0 &&
+ (sp.marker.inclusiveRight && marker.inclusiveLeft
+ ? cmp(found.to, from) >= 0
+ : cmp(found.to, from) > 0)) ||
+ (fromCmp >= 0 &&
+ (sp.marker.inclusiveRight && marker.inclusiveLeft
+ ? cmp(found.from, to) <= 0
+ : cmp(found.from, to) < 0))
+ ) {
+ return true
+ }
+ }
+ }
+ }
+
+ // A visual line is a line as drawn on the screen. Folding, for
+ // example, can cause multiple logical lines to appear on the same
+ // visual line. This finds the start of the visual line that the
+ // given line is part of (usually that is the line itself).
+ function visualLine(line) {
+ var merged
+ while ((merged = collapsedSpanAtStart(line))) {
+ line = merged.find(-1, true).line
+ }
+ return line
+ }
+
+ function visualLineEnd(line) {
+ var merged
+ while ((merged = collapsedSpanAtEnd(line))) {
+ line = merged.find(1, true).line
+ }
+ return line
+ }
+
+ // Returns an array of logical lines that continue the visual line
+ // started by the argument, or undefined if there are no such lines.
+ function visualLineContinued(line) {
+ var merged, lines
+ while ((merged = collapsedSpanAtEnd(line))) {
+ line = merged.find(1, true).line
+ ;(lines || (lines = [])).push(line)
+ }
+ return lines
+ }
+
+ // Get the line number of the start of the visual line that the
+ // given line number is part of.
+ function visualLineNo(doc, lineN) {
+ var line = getLine(doc, lineN),
+ vis = visualLine(line)
+ if (line == vis) {
+ return lineN
+ }
+ return lineNo(vis)
+ }
+
+ // Get the line number of the start of the next visual line after
+ // the given line.
+ function visualLineEndNo(doc, lineN) {
+ if (lineN > doc.lastLine()) {
+ return lineN
+ }
+ var line = getLine(doc, lineN),
+ merged
+ if (!lineIsHidden(doc, line)) {
+ return lineN
+ }
+ while ((merged = collapsedSpanAtEnd(line))) {
+ line = merged.find(1, true).line
+ }
+ return lineNo(line) + 1
+ }
+
+ // Compute whether a line is hidden. Lines count as hidden when they
+ // are part of a visual line that starts with another line, or when
+ // they are entirely covered by collapsed, non-widget span.
+ function lineIsHidden(doc, line) {
+ var sps = sawCollapsedSpans && line.markedSpans
+ if (sps) {
+ for (var sp = void 0, i = 0; i < sps.length; ++i) {
+ sp = sps[i]
+ if (!sp.marker.collapsed) {
+ continue
+ }
+ if (sp.from == null) {
+ return true
+ }
+ if (sp.marker.widgetNode) {
+ continue
+ }
+ if (sp.from == 0 && sp.marker.inclusiveLeft && lineIsHiddenInner(doc, line, sp)) {
+ return true
+ }
+ }
+ }
+ }
+ function lineIsHiddenInner(doc, line, span) {
+ if (span.to == null) {
+ var end = span.marker.find(1, true)
+ return lineIsHiddenInner(
+ doc,
+ end.line,
+ getMarkedSpanFor(end.line.markedSpans, span.marker)
+ )
+ }
+ if (span.marker.inclusiveRight && span.to == line.text.length) {
+ return true
+ }
+ for (var sp = void 0, i = 0; i < line.markedSpans.length; ++i) {
+ sp = line.markedSpans[i]
+ if (
+ sp.marker.collapsed &&
+ !sp.marker.widgetNode &&
+ sp.from == span.to &&
+ (sp.to == null || sp.to != span.from) &&
+ (sp.marker.inclusiveLeft || span.marker.inclusiveRight) &&
+ lineIsHiddenInner(doc, line, sp)
+ ) {
+ return true
+ }
+ }
+ }
+
+ // Find the height above the given line.
+ function heightAtLine(lineObj) {
+ lineObj = visualLine(lineObj)
+
+ var h = 0,
+ chunk = lineObj.parent
+ for (var i = 0; i < chunk.lines.length; ++i) {
+ var line = chunk.lines[i]
+ if (line == lineObj) {
+ break
+ } else {
+ h += line.height
+ }
+ }
+ for (var p = chunk.parent; p; chunk = p, p = chunk.parent) {
+ for (var i$1 = 0; i$1 < p.children.length; ++i$1) {
+ var cur = p.children[i$1]
+ if (cur == chunk) {
+ break
+ } else {
+ h += cur.height
+ }
+ }
+ }
+ return h
+ }
+
+ // Compute the character length of a line, taking into account
+ // collapsed ranges (see markText) that might hide parts, and join
+ // other lines onto it.
+ function lineLength(line) {
+ if (line.height == 0) {
+ return 0
+ }
+ var len = line.text.length,
+ merged,
+ cur = line
+ while ((merged = collapsedSpanAtStart(cur))) {
+ var found = merged.find(0, true)
+ cur = found.from.line
+ len += found.from.ch - found.to.ch
+ }
+ cur = line
+ while ((merged = collapsedSpanAtEnd(cur))) {
+ var found$1 = merged.find(0, true)
+ len -= cur.text.length - found$1.from.ch
+ cur = found$1.to.line
+ len += cur.text.length - found$1.to.ch
+ }
+ return len
+ }
+
+ // Find the longest line in the document.
+ function findMaxLine(cm) {
+ var d = cm.display,
+ doc = cm.doc
+ d.maxLine = getLine(doc, doc.first)
+ d.maxLineLength = lineLength(d.maxLine)
+ d.maxLineChanged = true
+ doc.iter(function(line) {
+ var len = lineLength(line)
+ if (len > d.maxLineLength) {
+ d.maxLineLength = len
+ d.maxLine = line
+ }
+ })
+ }
+
+ // LINE DATA STRUCTURE
+
+ // Line objects. These hold state related to a line, including
+ // highlighting info (the styles array).
+ var Line = function(text, markedSpans, estimateHeight) {
+ this.text = text
+ attachMarkedSpans(this, markedSpans)
+ this.height = estimateHeight ? estimateHeight(this) : 1
+ }
+
+ Line.prototype.lineNo = function() {
+ return lineNo(this)
+ }
+ eventMixin(Line)
+
+ // Change the content (text, markers) of a line. Automatically
+ // invalidates cached information and tries to re-estimate the
+ // line's height.
+ function updateLine(line, text, markedSpans, estimateHeight) {
+ line.text = text
+ if (line.stateAfter) {
+ line.stateAfter = null
+ }
+ if (line.styles) {
+ line.styles = null
+ }
+ if (line.order != null) {
+ line.order = null
+ }
+ detachMarkedSpans(line)
+ attachMarkedSpans(line, markedSpans)
+ var estHeight = estimateHeight ? estimateHeight(line) : 1
+ if (estHeight != line.height) {
+ updateLineHeight(line, estHeight)
+ }
+ }
+
+ // Detach a line from the document tree and its markers.
+ function cleanUpLine(line) {
+ line.parent = null
+ detachMarkedSpans(line)
+ }
+
+ // Convert a style as returned by a mode (either null, or a string
+ // containing one or more styles) to a CSS style. This is cached,
+ // and also looks for line-wide styles.
+ var styleToClassCache = {},
+ styleToClassCacheWithMode = {}
+ function interpretTokenStyle(style, options) {
+ if (!style || /^\s*$/.test(style)) {
+ return null
+ }
+ var cache = options.addModeClass ? styleToClassCacheWithMode : styleToClassCache
+ return cache[style] || (cache[style] = style.replace(/\S+/g, "cm-$&"))
+ }
+
+ // Render the DOM representation of the text of a line. Also builds
+ // up a 'line map', which points at the DOM nodes that represent
+ // specific stretches of text, and is used by the measuring code.
+ // The returned object contains the DOM node, this map, and
+ // information about line-wide styles that were set by the mode.
+ function buildLineContent(cm, lineView) {
+ // The padding-right forces the element to have a 'border', which
+ // is needed on Webkit to be able to get line-level bounding
+ // rectangles for it (in measureChar).
+ var content = eltP("span", null, null, webkit ? "padding-right: .1px" : null)
+ var builder = {
+ pre: eltP("pre", [content], "CodeMirror-line"),
+ content: content,
+ col: 0,
+ pos: 0,
+ cm: cm,
+ trailingSpace: false,
+ splitSpaces: cm.getOption("lineWrapping")
+ }
+ lineView.measure = {}
+
+ // Iterate over the logical lines that make up this visual line.
+ for (var i = 0; i <= (lineView.rest ? lineView.rest.length : 0); i++) {
+ var line = i ? lineView.rest[i - 1] : lineView.line,
+ order = void 0
+ builder.pos = 0
+ builder.addToken = buildToken
+ // Optionally wire in some hacks into the token-rendering
+ // algorithm, to deal with browser quirks.
+ if (hasBadBidiRects(cm.display.measure) && (order = getOrder(line, cm.doc.direction))) {
+ builder.addToken = buildTokenBadBidi(builder.addToken, order)
+ }
+ builder.map = []
+ var allowFrontierUpdate = lineView != cm.display.externalMeasured && lineNo(line)
+ insertLineContent(line, builder, getLineStyles(cm, line, allowFrontierUpdate))
+ if (line.styleClasses) {
+ if (line.styleClasses.bgClass) {
+ builder.bgClass = joinClasses(line.styleClasses.bgClass, builder.bgClass || "")
+ }
+ if (line.styleClasses.textClass) {
+ builder.textClass = joinClasses(
+ line.styleClasses.textClass,
+ builder.textClass || ""
+ )
+ }
+ }
+
+ // Ensure at least a single node is present, for measuring.
+ if (builder.map.length == 0) {
+ builder.map.push(
+ 0,
+ 0,
+ builder.content.appendChild(zeroWidthElement(cm.display.measure))
+ )
+ }
+
+ // Store the map and a cache object for the current logical line
+ if (i == 0) {
+ lineView.measure.map = builder.map
+ lineView.measure.cache = {}
+ } else {
+ ;(lineView.measure.maps || (lineView.measure.maps = [])).push(builder.map)
+ ;(lineView.measure.caches || (lineView.measure.caches = [])).push({})
+ }
+ }
+
+ // See issue #2901
+ if (webkit) {
+ var last = builder.content.lastChild
+ if (
+ /\bcm-tab\b/.test(last.className) ||
+ (last.querySelector && last.querySelector(".cm-tab"))
+ ) {
+ builder.content.className = "cm-tab-wrap-hack"
+ }
+ }
+
+ signal(cm, "renderLine", cm, lineView.line, builder.pre)
+ if (builder.pre.className) {
+ builder.textClass = joinClasses(builder.pre.className, builder.textClass || "")
+ }
+
+ return builder
+ }
+
+ function defaultSpecialCharPlaceholder(ch) {
+ var token = elt("span", "\u2022", "cm-invalidchar")
+ token.title = "\\u" + ch.charCodeAt(0).toString(16)
+ token.setAttribute("aria-label", token.title)
+ return token
+ }
+
+ // Build up the DOM representation for a single token, and add it to
+ // the line map. Takes care to render special characters separately.
+ function buildToken(builder, text, style, startStyle, endStyle, css, attributes) {
+ if (!text) {
+ return
+ }
+ var displayText = builder.splitSpaces ? splitSpaces(text, builder.trailingSpace) : text
+ var special = builder.cm.state.specialChars,
+ mustWrap = false
+ var content
+ if (!special.test(text)) {
+ builder.col += text.length
+ content = document.createTextNode(displayText)
+ builder.map.push(builder.pos, builder.pos + text.length, content)
+ if (ie && ie_version < 9) {
+ mustWrap = true
+ }
+ builder.pos += text.length
+ } else {
+ content = document.createDocumentFragment()
+ var pos = 0
+ while (true) {
+ special.lastIndex = pos
+ var m = special.exec(text)
+ var skipped = m ? m.index - pos : text.length - pos
+ if (skipped) {
+ var txt = document.createTextNode(displayText.slice(pos, pos + skipped))
+ if (ie && ie_version < 9) {
+ content.appendChild(elt("span", [txt]))
+ } else {
+ content.appendChild(txt)
+ }
+ builder.map.push(builder.pos, builder.pos + skipped, txt)
+ builder.col += skipped
+ builder.pos += skipped
+ }
+ if (!m) {
+ break
+ }
+ pos += skipped + 1
+ var txt$1 = void 0
+ if (m[0] == "\t") {
+ var tabSize = builder.cm.options.tabSize,
+ tabWidth = tabSize - (builder.col % tabSize)
+ txt$1 = content.appendChild(elt("span", spaceStr(tabWidth), "cm-tab"))
+ txt$1.setAttribute("role", "presentation")
+ txt$1.setAttribute("cm-text", "\t")
+ builder.col += tabWidth
+ } else if (m[0] == "\r" || m[0] == "\n") {
+ txt$1 = content.appendChild(
+ elt("span", m[0] == "\r" ? "\u240d" : "\u2424", "cm-invalidchar")
+ )
+ txt$1.setAttribute("cm-text", m[0])
+ builder.col += 1
+ } else {
+ txt$1 = builder.cm.options.specialCharPlaceholder(m[0])
+ txt$1.setAttribute("cm-text", m[0])
+ if (ie && ie_version < 9) {
+ content.appendChild(elt("span", [txt$1]))
+ } else {
+ content.appendChild(txt$1)
+ }
+ builder.col += 1
+ }
+ builder.map.push(builder.pos, builder.pos + 1, txt$1)
+ builder.pos++
+ }
+ }
+ builder.trailingSpace = displayText.charCodeAt(text.length - 1) == 32
+ if (style || startStyle || endStyle || mustWrap || css) {
+ var fullStyle = style || ""
+ if (startStyle) {
+ fullStyle += startStyle
+ }
+ if (endStyle) {
+ fullStyle += endStyle
+ }
+ var token = elt("span", [content], fullStyle, css)
+ if (attributes) {
+ for (var attr in attributes) {
+ if (attributes.hasOwnProperty(attr) && attr != "style" && attr != "class") {
+ token.setAttribute(attr, attributes[attr])
+ }
+ }
+ }
+ return builder.content.appendChild(token)
+ }
+ builder.content.appendChild(content)
+ }
+
+ // Change some spaces to NBSP to prevent the browser from collapsing
+ // trailing spaces at the end of a line when rendering text (issue #1362).
+ function splitSpaces(text, trailingBefore) {
+ if (text.length > 1 && !/ /.test(text)) {
+ return text
+ }
+ var spaceBefore = trailingBefore,
+ result = ""
+ for (var i = 0; i < text.length; i++) {
+ var ch = text.charAt(i)
+ if (
+ ch == " " &&
+ spaceBefore &&
+ (i == text.length - 1 || text.charCodeAt(i + 1) == 32)
+ ) {
+ ch = "\u00a0"
+ }
+ result += ch
+ spaceBefore = ch == " "
+ }
+ return result
+ }
+
+ // Work around nonsense dimensions being reported for stretches of
+ // right-to-left text.
+ function buildTokenBadBidi(inner, order) {
+ return function(builder, text, style, startStyle, endStyle, css, attributes) {
+ style = style ? style + " cm-force-border" : "cm-force-border"
+ var start = builder.pos,
+ end = start + text.length
+ for (;;) {
+ // Find the part that overlaps with the start of this text
+ var part = void 0
+ for (var i = 0; i < order.length; i++) {
+ part = order[i]
+ if (part.to > start && part.from <= start) {
+ break
+ }
+ }
+ if (part.to >= end) {
+ return inner(builder, text, style, startStyle, endStyle, css, attributes)
+ }
+ inner(
+ builder,
+ text.slice(0, part.to - start),
+ style,
+ startStyle,
+ null,
+ css,
+ attributes
+ )
+ startStyle = null
+ text = text.slice(part.to - start)
+ start = part.to
+ }
+ }
+ }
+
+ function buildCollapsedSpan(builder, size, marker, ignoreWidget) {
+ var widget = !ignoreWidget && marker.widgetNode
+ if (widget) {
+ builder.map.push(builder.pos, builder.pos + size, widget)
+ }
+ if (!ignoreWidget && builder.cm.display.input.needsContentAttribute) {
+ if (!widget) {
+ widget = builder.content.appendChild(document.createElement("span"))
+ }
+ widget.setAttribute("cm-marker", marker.id)
+ }
+ if (widget) {
+ builder.cm.display.input.setUneditable(widget)
+ builder.content.appendChild(widget)
+ }
+ builder.pos += size
+ builder.trailingSpace = false
+ }
+
+ // Outputs a number of spans to make up a line, taking highlighting
+ // and marked text into account.
+ function insertLineContent(line, builder, styles) {
+ var spans = line.markedSpans,
+ allText = line.text,
+ at = 0
+ if (!spans) {
+ for (var i$1 = 1; i$1 < styles.length; i$1 += 2) {
+ builder.addToken(
+ builder,
+ allText.slice(at, (at = styles[i$1])),
+ interpretTokenStyle(styles[i$1 + 1], builder.cm.options)
+ )
+ }
+ return
+ }
+
+ var len = allText.length,
+ pos = 0,
+ i = 1,
+ text = "",
+ style,
+ css
+ var nextChange = 0,
+ spanStyle,
+ spanEndStyle,
+ spanStartStyle,
+ collapsed,
+ attributes
+ for (;;) {
+ if (nextChange == pos) {
+ // Update current marker set
+ spanStyle = spanEndStyle = spanStartStyle = css = ""
+ attributes = null
+ collapsed = null
+ nextChange = Infinity
+ var foundBookmarks = [],
+ endStyles = void 0
+ for (var j = 0; j < spans.length; ++j) {
+ var sp = spans[j],
+ m = sp.marker
+ if (m.type == "bookmark" && sp.from == pos && m.widgetNode) {
+ foundBookmarks.push(m)
+ } else if (
+ sp.from <= pos &&
+ (sp.to == null ||
+ sp.to > pos ||
+ (m.collapsed && sp.to == pos && sp.from == pos))
+ ) {
+ if (sp.to != null && sp.to != pos && nextChange > sp.to) {
+ nextChange = sp.to
+ spanEndStyle = ""
+ }
+ if (m.className) {
+ spanStyle += " " + m.className
+ }
+ if (m.css) {
+ css = (css ? css + ";" : "") + m.css
+ }
+ if (m.startStyle && sp.from == pos) {
+ spanStartStyle += " " + m.startStyle
+ }
+ if (m.endStyle && sp.to == nextChange) {
+ ;(endStyles || (endStyles = [])).push(m.endStyle, sp.to)
+ }
+ // support for the old title property
+ // https://github.com/codemirror/CodeMirror/pull/5673
+ if (m.title) {
+ ;(attributes || (attributes = {})).title = m.title
+ }
+ if (m.attributes) {
+ for (var attr in m.attributes) {
+ ;(attributes || (attributes = {}))[attr] = m.attributes[attr]
+ }
+ }
+ if (
+ m.collapsed &&
+ (!collapsed || compareCollapsedMarkers(collapsed.marker, m) < 0)
+ ) {
+ collapsed = sp
+ }
+ } else if (sp.from > pos && nextChange > sp.from) {
+ nextChange = sp.from
+ }
+ }
+ if (endStyles) {
+ for (var j$1 = 0; j$1 < endStyles.length; j$1 += 2) {
+ if (endStyles[j$1 + 1] == nextChange) {
+ spanEndStyle += " " + endStyles[j$1]
+ }
+ }
+ }
+
+ if (!collapsed || collapsed.from == pos) {
+ for (var j$2 = 0; j$2 < foundBookmarks.length; ++j$2) {
+ buildCollapsedSpan(builder, 0, foundBookmarks[j$2])
+ }
+ }
+ if (collapsed && (collapsed.from || 0) == pos) {
+ buildCollapsedSpan(
+ builder,
+ (collapsed.to == null ? len + 1 : collapsed.to) - pos,
+ collapsed.marker,
+ collapsed.from == null
+ )
+ if (collapsed.to == null) {
+ return
+ }
+ if (collapsed.to == pos) {
+ collapsed = false
+ }
+ }
+ }
+ if (pos >= len) {
+ break
+ }
+
+ var upto = Math.min(len, nextChange)
+ while (true) {
+ if (text) {
+ var end = pos + text.length
+ if (!collapsed) {
+ var tokenText = end > upto ? text.slice(0, upto - pos) : text
+ builder.addToken(
+ builder,
+ tokenText,
+ style ? style + spanStyle : spanStyle,
+ spanStartStyle,
+ pos + tokenText.length == nextChange ? spanEndStyle : "",
+ css,
+ attributes
+ )
+ }
+ if (end >= upto) {
+ text = text.slice(upto - pos)
+ pos = upto
+ break
+ }
+ pos = end
+ spanStartStyle = ""
+ }
+ text = allText.slice(at, (at = styles[i++]))
+ style = interpretTokenStyle(styles[i++], builder.cm.options)
+ }
+ }
+ }
+
+ // These objects are used to represent the visible (currently drawn)
+ // part of the document. A LineView may correspond to multiple
+ // logical lines, if those are connected by collapsed ranges.
+ function LineView(doc, line, lineN) {
+ // The starting line
+ this.line = line
+ // Continuing lines, if any
+ this.rest = visualLineContinued(line)
+ // Number of logical lines in this visual line
+ this.size = this.rest ? lineNo(lst(this.rest)) - lineN + 1 : 1
+ this.node = this.text = null
+ this.hidden = lineIsHidden(doc, line)
+ }
+
+ // Create a range of LineView objects for the given lines.
+ function buildViewArray(cm, from, to) {
+ var array = [],
+ nextPos
+ for (var pos = from; pos < to; pos = nextPos) {
+ var view = new LineView(cm.doc, getLine(cm.doc, pos), pos)
+ nextPos = pos + view.size
+ array.push(view)
+ }
+ return array
+ }
+
+ var operationGroup = null
+
+ function pushOperation(op) {
+ if (operationGroup) {
+ operationGroup.ops.push(op)
+ } else {
+ op.ownsGroup = operationGroup = {
+ ops: [op],
+ delayedCallbacks: []
+ }
+ }
+ }
+
+ function fireCallbacksForOps(group) {
+ // Calls delayed callbacks and cursorActivity handlers until no
+ // new ones appear
+ var callbacks = group.delayedCallbacks,
+ i = 0
+ do {
+ for (; i < callbacks.length; i++) {
+ callbacks[i].call(null)
+ }
+ for (var j = 0; j < group.ops.length; j++) {
+ var op = group.ops[j]
+ if (op.cursorActivityHandlers) {
+ while (op.cursorActivityCalled < op.cursorActivityHandlers.length) {
+ op.cursorActivityHandlers[op.cursorActivityCalled++].call(null, op.cm)
+ }
+ }
+ }
+ } while (i < callbacks.length)
+ }
+
+ function finishOperation(op, endCb) {
+ var group = op.ownsGroup
+ if (!group) {
+ return
+ }
+
+ try {
+ fireCallbacksForOps(group)
+ } finally {
+ operationGroup = null
+ endCb(group)
+ }
+ }
+
+ var orphanDelayedCallbacks = null
+
+ // Often, we want to signal events at a point where we are in the
+ // middle of some work, but don't want the handler to start calling
+ // other methods on the editor, which might be in an inconsistent
+ // state or simply not expect any other events to happen.
+ // signalLater looks whether there are any handlers, and schedules
+ // them to be executed when the last operation ends, or, if no
+ // operation is active, when a timeout fires.
+ function signalLater(emitter, type /*, values...*/) {
+ var arr = getHandlers(emitter, type)
+ if (!arr.length) {
+ return
+ }
+ var args = Array.prototype.slice.call(arguments, 2),
+ list
+ if (operationGroup) {
+ list = operationGroup.delayedCallbacks
+ } else if (orphanDelayedCallbacks) {
+ list = orphanDelayedCallbacks
+ } else {
+ list = orphanDelayedCallbacks = []
+ setTimeout(fireOrphanDelayed, 0)
+ }
+ var loop = function(i) {
+ list.push(function() {
+ return arr[i].apply(null, args)
+ })
+ }
+
+ for (var i = 0; i < arr.length; ++i) loop(i)
+ }
+
+ function fireOrphanDelayed() {
+ var delayed = orphanDelayedCallbacks
+ orphanDelayedCallbacks = null
+ for (var i = 0; i < delayed.length; ++i) {
+ delayed[i]()
+ }
+ }
+
+ // When an aspect of a line changes, a string is added to
+ // lineView.changes. This updates the relevant part of the line's
+ // DOM structure.
+ function updateLineForChanges(cm, lineView, lineN, dims) {
+ for (var j = 0; j < lineView.changes.length; j++) {
+ var type = lineView.changes[j]
+ if (type == "text") {
+ updateLineText(cm, lineView)
+ } else if (type == "gutter") {
+ updateLineGutter(cm, lineView, lineN, dims)
+ } else if (type == "class") {
+ updateLineClasses(cm, lineView)
+ } else if (type == "widget") {
+ updateLineWidgets(cm, lineView, dims)
+ }
+ }
+ lineView.changes = null
+ }
+
+ // Lines with gutter elements, widgets or a background class need to
+ // be wrapped, and have the extra elements added to the wrapper div
+ function ensureLineWrapped(lineView) {
+ if (lineView.node == lineView.text) {
+ lineView.node = elt("div", null, null, "position: relative")
+ if (lineView.text.parentNode) {
+ lineView.text.parentNode.replaceChild(lineView.node, lineView.text)
+ }
+ lineView.node.appendChild(lineView.text)
+ if (ie && ie_version < 8) {
+ lineView.node.style.zIndex = 2
+ }
+ }
+ return lineView.node
+ }
+
+ function updateLineBackground(cm, lineView) {
+ var cls = lineView.bgClass
+ ? lineView.bgClass + " " + (lineView.line.bgClass || "")
+ : lineView.line.bgClass
+ if (cls) {
+ cls += " CodeMirror-linebackground"
+ }
+ if (lineView.background) {
+ if (cls) {
+ lineView.background.className = cls
+ } else {
+ lineView.background.parentNode.removeChild(lineView.background)
+ lineView.background = null
+ }
+ } else if (cls) {
+ var wrap = ensureLineWrapped(lineView)
+ lineView.background = wrap.insertBefore(elt("div", null, cls), wrap.firstChild)
+ cm.display.input.setUneditable(lineView.background)
+ }
+ }
+
+ // Wrapper around buildLineContent which will reuse the structure
+ // in display.externalMeasured when possible.
+ function getLineContent(cm, lineView) {
+ var ext = cm.display.externalMeasured
+ if (ext && ext.line == lineView.line) {
+ cm.display.externalMeasured = null
+ lineView.measure = ext.measure
+ return ext.built
+ }
+ return buildLineContent(cm, lineView)
+ }
+
+ // Redraw the line's text. Interacts with the background and text
+ // classes because the mode may output tokens that influence these
+ // classes.
+ function updateLineText(cm, lineView) {
+ var cls = lineView.text.className
+ var built = getLineContent(cm, lineView)
+ if (lineView.text == lineView.node) {
+ lineView.node = built.pre
+ }
+ lineView.text.parentNode.replaceChild(built.pre, lineView.text)
+ lineView.text = built.pre
+ if (built.bgClass != lineView.bgClass || built.textClass != lineView.textClass) {
+ lineView.bgClass = built.bgClass
+ lineView.textClass = built.textClass
+ updateLineClasses(cm, lineView)
+ } else if (cls) {
+ lineView.text.className = cls
+ }
+ }
+
+ function updateLineClasses(cm, lineView) {
+ updateLineBackground(cm, lineView)
+ if (lineView.line.wrapClass) {
+ ensureLineWrapped(lineView).className = lineView.line.wrapClass
+ } else if (lineView.node != lineView.text) {
+ lineView.node.className = ""
+ }
+ var textClass = lineView.textClass
+ ? lineView.textClass + " " + (lineView.line.textClass || "")
+ : lineView.line.textClass
+ lineView.text.className = textClass || ""
+ }
+
+ function updateLineGutter(cm, lineView, lineN, dims) {
+ if (lineView.gutter) {
+ lineView.node.removeChild(lineView.gutter)
+ lineView.gutter = null
+ }
+ if (lineView.gutterBackground) {
+ lineView.node.removeChild(lineView.gutterBackground)
+ lineView.gutterBackground = null
+ }
+ if (lineView.line.gutterClass) {
+ var wrap = ensureLineWrapped(lineView)
+ lineView.gutterBackground = elt(
+ "div",
+ null,
+ "CodeMirror-gutter-background " + lineView.line.gutterClass,
+ "left: " +
+ (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) +
+ "px; width: " +
+ dims.gutterTotalWidth +
+ "px"
+ )
+ cm.display.input.setUneditable(lineView.gutterBackground)
+ wrap.insertBefore(lineView.gutterBackground, lineView.text)
+ }
+ var markers = lineView.line.gutterMarkers
+ if (cm.options.lineNumbers || markers) {
+ var wrap$1 = ensureLineWrapped(lineView)
+ var gutterWrap = (lineView.gutter = elt(
+ "div",
+ null,
+ "CodeMirror-gutter-wrapper",
+ "left: " + (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px"
+ ))
+ cm.display.input.setUneditable(gutterWrap)
+ wrap$1.insertBefore(gutterWrap, lineView.text)
+ if (lineView.line.gutterClass) {
+ gutterWrap.className += " " + lineView.line.gutterClass
+ }
+ if (cm.options.lineNumbers && (!markers || !markers["CodeMirror-linenumbers"])) {
+ lineView.lineNumber = gutterWrap.appendChild(
+ elt(
+ "div",
+ lineNumberFor(cm.options, lineN),
+ "CodeMirror-linenumber CodeMirror-gutter-elt",
+ "left: " +
+ dims.gutterLeft["CodeMirror-linenumbers"] +
+ "px; width: " +
+ cm.display.lineNumInnerWidth +
+ "px"
+ )
+ )
+ }
+ if (markers) {
+ for (var k = 0; k < cm.display.gutterSpecs.length; ++k) {
+ var id = cm.display.gutterSpecs[k].className,
+ found = markers.hasOwnProperty(id) && markers[id]
+ if (found) {
+ gutterWrap.appendChild(
+ elt(
+ "div",
+ [found],
+ "CodeMirror-gutter-elt",
+ "left: " +
+ dims.gutterLeft[id] +
+ "px; width: " +
+ dims.gutterWidth[id] +
+ "px"
+ )
+ )
+ }
+ }
+ }
+ }
+ }
+
+ function updateLineWidgets(cm, lineView, dims) {
+ if (lineView.alignable) {
+ lineView.alignable = null
+ }
+ var isWidget = classTest("CodeMirror-linewidget")
+ for (var node = lineView.node.firstChild, next = void 0; node; node = next) {
+ next = node.nextSibling
+ if (isWidget.test(node.className)) {
+ lineView.node.removeChild(node)
+ }
+ }
+ insertLineWidgets(cm, lineView, dims)
+ }
+
+ // Build a line's DOM representation from scratch
+ function buildLineElement(cm, lineView, lineN, dims) {
+ var built = getLineContent(cm, lineView)
+ lineView.text = lineView.node = built.pre
+ if (built.bgClass) {
+ lineView.bgClass = built.bgClass
+ }
+ if (built.textClass) {
+ lineView.textClass = built.textClass
+ }
+
+ updateLineClasses(cm, lineView)
+ updateLineGutter(cm, lineView, lineN, dims)
+ insertLineWidgets(cm, lineView, dims)
+ return lineView.node
+ }
+
+ // A lineView may contain multiple logical lines (when merged by
+ // collapsed spans). The widgets for all of them need to be drawn.
+ function insertLineWidgets(cm, lineView, dims) {
+ insertLineWidgetsFor(cm, lineView.line, lineView, dims, true)
+ if (lineView.rest) {
+ for (var i = 0; i < lineView.rest.length; i++) {
+ insertLineWidgetsFor(cm, lineView.rest[i], lineView, dims, false)
+ }
+ }
+ }
+
+ function insertLineWidgetsFor(cm, line, lineView, dims, allowAbove) {
+ if (!line.widgets) {
+ return
+ }
+ var wrap = ensureLineWrapped(lineView)
+ for (var i = 0, ws = line.widgets; i < ws.length; ++i) {
+ var widget = ws[i],
+ node = elt(
+ "div",
+ [widget.node],
+ "CodeMirror-linewidget" + (widget.className ? " " + widget.className : "")
+ )
+ if (!widget.handleMouseEvents) {
+ node.setAttribute("cm-ignore-events", "true")
+ }
+ positionLineWidget(widget, node, lineView, dims)
+ cm.display.input.setUneditable(node)
+ if (allowAbove && widget.above) {
+ wrap.insertBefore(node, lineView.gutter || lineView.text)
+ } else {
+ wrap.appendChild(node)
+ }
+ signalLater(widget, "redraw")
+ }
+ }
+
+ function positionLineWidget(widget, node, lineView, dims) {
+ if (widget.noHScroll) {
+ ;(lineView.alignable || (lineView.alignable = [])).push(node)
+ var width = dims.wrapperWidth
+ node.style.left = dims.fixedPos + "px"
+ if (!widget.coverGutter) {
+ width -= dims.gutterTotalWidth
+ node.style.paddingLeft = dims.gutterTotalWidth + "px"
+ }
+ node.style.width = width + "px"
+ }
+ if (widget.coverGutter) {
+ node.style.zIndex = 5
+ node.style.position = "relative"
+ if (!widget.noHScroll) {
+ node.style.marginLeft = -dims.gutterTotalWidth + "px"
+ }
+ }
+ }
+
+ function widgetHeight(widget) {
+ if (widget.height != null) {
+ return widget.height
+ }
+ var cm = widget.doc.cm
+ if (!cm) {
+ return 0
+ }
+ if (!contains(document.body, widget.node)) {
+ var parentStyle = "position: relative;"
+ if (widget.coverGutter) {
+ parentStyle += "margin-left: -" + cm.display.gutters.offsetWidth + "px;"
+ }
+ if (widget.noHScroll) {
+ parentStyle += "width: " + cm.display.wrapper.clientWidth + "px;"
+ }
+ removeChildrenAndAdd(cm.display.measure, elt("div", [widget.node], null, parentStyle))
+ }
+ return (widget.height = widget.node.parentNode.offsetHeight)
+ }
+
+ // Return true when the given mouse event happened in a widget
+ function eventInWidget(display, e) {
+ for (var n = e_target(e); n != display.wrapper; n = n.parentNode) {
+ if (
+ !n ||
+ (n.nodeType == 1 && n.getAttribute("cm-ignore-events") == "true") ||
+ (n.parentNode == display.sizer && n != display.mover)
+ ) {
+ return true
+ }
+ }
+ }
+
+ // POSITION MEASUREMENT
+
+ function paddingTop(display) {
+ return display.lineSpace.offsetTop
+ }
+ function paddingVert(display) {
+ return display.mover.offsetHeight - display.lineSpace.offsetHeight
+ }
+ function paddingH(display) {
+ if (display.cachedPaddingH) {
+ return display.cachedPaddingH
+ }
+ var e = removeChildrenAndAdd(display.measure, elt("pre", "x", "CodeMirror-line-like"))
+ var style = window.getComputedStyle ? window.getComputedStyle(e) : e.currentStyle
+ var data = { left: parseInt(style.paddingLeft), right: parseInt(style.paddingRight) }
+ if (!isNaN(data.left) && !isNaN(data.right)) {
+ display.cachedPaddingH = data
+ }
+ return data
+ }
+
+ function scrollGap(cm) {
+ return scrollerGap - cm.display.nativeBarWidth
+ }
+ function displayWidth(cm) {
+ return cm.display.scroller.clientWidth - scrollGap(cm) - cm.display.barWidth
+ }
+ function displayHeight(cm) {
+ return cm.display.scroller.clientHeight - scrollGap(cm) - cm.display.barHeight
+ }
+
+ // Ensure the lineView.wrapping.heights array is populated. This is
+ // an array of bottom offsets for the lines that make up a drawn
+ // line. When lineWrapping is on, there might be more than one
+ // height.
+ function ensureLineHeights(cm, lineView, rect) {
+ var wrapping = cm.options.lineWrapping
+ var curWidth = wrapping && displayWidth(cm)
+ if (!lineView.measure.heights || (wrapping && lineView.measure.width != curWidth)) {
+ var heights = (lineView.measure.heights = [])
+ if (wrapping) {
+ lineView.measure.width = curWidth
+ var rects = lineView.text.firstChild.getClientRects()
+ for (var i = 0; i < rects.length - 1; i++) {
+ var cur = rects[i],
+ next = rects[i + 1]
+ if (Math.abs(cur.bottom - next.bottom) > 2) {
+ heights.push((cur.bottom + next.top) / 2 - rect.top)
+ }
+ }
+ }
+ heights.push(rect.bottom - rect.top)
+ }
+ }
+
+ // Find a line map (mapping character offsets to text nodes) and a
+ // measurement cache for the given line number. (A line view might
+ // contain multiple lines when collapsed ranges are present.)
+ function mapFromLineView(lineView, line, lineN) {
+ if (lineView.line == line) {
+ return { map: lineView.measure.map, cache: lineView.measure.cache }
+ }
+ for (var i = 0; i < lineView.rest.length; i++) {
+ if (lineView.rest[i] == line) {
+ return { map: lineView.measure.maps[i], cache: lineView.measure.caches[i] }
+ }
+ }
+ for (var i$1 = 0; i$1 < lineView.rest.length; i$1++) {
+ if (lineNo(lineView.rest[i$1]) > lineN) {
+ return {
+ map: lineView.measure.maps[i$1],
+ cache: lineView.measure.caches[i$1],
+ before: true
+ }
+ }
+ }
+ }
+
+ // Render a line into the hidden node display.externalMeasured. Used
+ // when measurement is needed for a line that's not in the viewport.
+ function updateExternalMeasurement(cm, line) {
+ line = visualLine(line)
+ var lineN = lineNo(line)
+ var view = (cm.display.externalMeasured = new LineView(cm.doc, line, lineN))
+ view.lineN = lineN
+ var built = (view.built = buildLineContent(cm, view))
+ view.text = built.pre
+ removeChildrenAndAdd(cm.display.lineMeasure, built.pre)
+ return view
+ }
+
+ // Get a {top, bottom, left, right} box (in line-local coordinates)
+ // for a given character.
+ function measureChar(cm, line, ch, bias) {
+ return measureCharPrepared(cm, prepareMeasureForLine(cm, line), ch, bias)
+ }
+
+ // Find a line view that corresponds to the given line number.
+ function findViewForLine(cm, lineN) {
+ if (lineN >= cm.display.viewFrom && lineN < cm.display.viewTo) {
+ return cm.display.view[findViewIndex(cm, lineN)]
+ }
+ var ext = cm.display.externalMeasured
+ if (ext && lineN >= ext.lineN && lineN < ext.lineN + ext.size) {
+ return ext
+ }
+ }
+
+ // Measurement can be split in two steps, the set-up work that
+ // applies to the whole line, and the measurement of the actual
+ // character. Functions like coordsChar, that need to do a lot of
+ // measurements in a row, can thus ensure that the set-up work is
+ // only done once.
+ function prepareMeasureForLine(cm, line) {
+ var lineN = lineNo(line)
+ var view = findViewForLine(cm, lineN)
+ if (view && !view.text) {
+ view = null
+ } else if (view && view.changes) {
+ updateLineForChanges(cm, view, lineN, getDimensions(cm))
+ cm.curOp.forceUpdate = true
+ }
+ if (!view) {
+ view = updateExternalMeasurement(cm, line)
+ }
+
+ var info = mapFromLineView(view, line, lineN)
+ return {
+ line: line,
+ view: view,
+ rect: null,
+ map: info.map,
+ cache: info.cache,
+ before: info.before,
+ hasHeights: false
+ }
+ }
+
+ // Given a prepared measurement object, measures the position of an
+ // actual character (or fetches it from the cache).
+ function measureCharPrepared(cm, prepared, ch, bias, varHeight) {
+ if (prepared.before) {
+ ch = -1
+ }
+ var key = ch + (bias || ""),
+ found
+ if (prepared.cache.hasOwnProperty(key)) {
+ found = prepared.cache[key]
+ } else {
+ if (!prepared.rect) {
+ prepared.rect = prepared.view.text.getBoundingClientRect()
+ }
+ if (!prepared.hasHeights) {
+ ensureLineHeights(cm, prepared.view, prepared.rect)
+ prepared.hasHeights = true
+ }
+ found = measureCharInner(cm, prepared, ch, bias)
+ if (!found.bogus) {
+ prepared.cache[key] = found
+ }
+ }
+ return {
+ left: found.left,
+ right: found.right,
+ top: varHeight ? found.rtop : found.top,
+ bottom: varHeight ? found.rbottom : found.bottom
+ }
+ }
+
+ var nullRect = { left: 0, right: 0, top: 0, bottom: 0 }
+
+ function nodeAndOffsetInLineMap(map, ch, bias) {
+ var node, start, end, collapse, mStart, mEnd
+ // First, search the line map for the text node corresponding to,
+ // or closest to, the target character.
+ for (var i = 0; i < map.length; i += 3) {
+ mStart = map[i]
+ mEnd = map[i + 1]
+ if (ch < mStart) {
+ start = 0
+ end = 1
+ collapse = "left"
+ } else if (ch < mEnd) {
+ start = ch - mStart
+ end = start + 1
+ } else if (i == map.length - 3 || (ch == mEnd && map[i + 3] > ch)) {
+ end = mEnd - mStart
+ start = end - 1
+ if (ch >= mEnd) {
+ collapse = "right"
+ }
+ }
+ if (start != null) {
+ node = map[i + 2]
+ if (mStart == mEnd && bias == (node.insertLeft ? "left" : "right")) {
+ collapse = bias
+ }
+ if (bias == "left" && start == 0) {
+ while (i && map[i - 2] == map[i - 3] && map[i - 1].insertLeft) {
+ node = map[(i -= 3) + 2]
+ collapse = "left"
+ }
+ }
+ if (bias == "right" && start == mEnd - mStart) {
+ while (
+ i < map.length - 3 &&
+ map[i + 3] == map[i + 4] &&
+ !map[i + 5].insertLeft
+ ) {
+ node = map[(i += 3) + 2]
+ collapse = "right"
+ }
+ }
+ break
+ }
+ }
+ return {
+ node: node,
+ start: start,
+ end: end,
+ collapse: collapse,
+ coverStart: mStart,
+ coverEnd: mEnd
+ }
+ }
+
+ function getUsefulRect(rects, bias) {
+ var rect = nullRect
+ if (bias == "left") {
+ for (var i = 0; i < rects.length; i++) {
+ if ((rect = rects[i]).left != rect.right) {
+ break
+ }
+ }
+ } else {
+ for (var i$1 = rects.length - 1; i$1 >= 0; i$1--) {
+ if ((rect = rects[i$1]).left != rect.right) {
+ break
+ }
+ }
+ }
+ return rect
+ }
+
+ function measureCharInner(cm, prepared, ch, bias) {
+ var place = nodeAndOffsetInLineMap(prepared.map, ch, bias)
+ var node = place.node,
+ start = place.start,
+ end = place.end,
+ collapse = place.collapse
+
+ var rect
+ if (node.nodeType == 3) {
+ // If it is a text node, use a range to retrieve the coordinates.
+ for (var i$1 = 0; i$1 < 4; i$1++) {
+ // Retry a maximum of 4 times when nonsense rectangles are returned
+ while (
+ start &&
+ isExtendingChar(prepared.line.text.charAt(place.coverStart + start))
+ ) {
+ --start
+ }
+ while (
+ place.coverStart + end < place.coverEnd &&
+ isExtendingChar(prepared.line.text.charAt(place.coverStart + end))
+ ) {
+ ++end
+ }
+ if (
+ ie &&
+ ie_version < 9 &&
+ start == 0 &&
+ end == place.coverEnd - place.coverStart
+ ) {
+ rect = node.parentNode.getBoundingClientRect()
+ } else {
+ rect = getUsefulRect(range(node, start, end).getClientRects(), bias)
+ }
+ if (rect.left || rect.right || start == 0) {
+ break
+ }
+ end = start
+ start = start - 1
+ collapse = "right"
+ }
+ if (ie && ie_version < 11) {
+ rect = maybeUpdateRectForZooming(cm.display.measure, rect)
+ }
+ } else {
+ // If it is a widget, simply get the box for the whole widget.
+ if (start > 0) {
+ collapse = bias = "right"
+ }
+ var rects
+ if (cm.options.lineWrapping && (rects = node.getClientRects()).length > 1) {
+ rect = rects[bias == "right" ? rects.length - 1 : 0]
+ } else {
+ rect = node.getBoundingClientRect()
+ }
+ }
+ if (ie && ie_version < 9 && !start && (!rect || (!rect.left && !rect.right))) {
+ var rSpan = node.parentNode.getClientRects()[0]
+ if (rSpan) {
+ rect = {
+ left: rSpan.left,
+ right: rSpan.left + charWidth(cm.display),
+ top: rSpan.top,
+ bottom: rSpan.bottom
+ }
+ } else {
+ rect = nullRect
+ }
+ }
+
+ var rtop = rect.top - prepared.rect.top,
+ rbot = rect.bottom - prepared.rect.top
+ var mid = (rtop + rbot) / 2
+ var heights = prepared.view.measure.heights
+ var i = 0
+ for (; i < heights.length - 1; i++) {
+ if (mid < heights[i]) {
+ break
+ }
+ }
+ var top = i ? heights[i - 1] : 0,
+ bot = heights[i]
+ var result = {
+ left: (collapse == "right" ? rect.right : rect.left) - prepared.rect.left,
+ right: (collapse == "left" ? rect.left : rect.right) - prepared.rect.left,
+ top: top,
+ bottom: bot
+ }
+ if (!rect.left && !rect.right) {
+ result.bogus = true
+ }
+ if (!cm.options.singleCursorHeightPerLine) {
+ result.rtop = rtop
+ result.rbottom = rbot
+ }
+
+ return result
+ }
+
+ // Work around problem with bounding client rects on ranges being
+ // returned incorrectly when zoomed on IE10 and below.
+ function maybeUpdateRectForZooming(measure, rect) {
+ if (
+ !window.screen ||
+ screen.logicalXDPI == null ||
+ screen.logicalXDPI == screen.deviceXDPI ||
+ !hasBadZoomedRects(measure)
+ ) {
+ return rect
+ }
+ var scaleX = screen.logicalXDPI / screen.deviceXDPI
+ var scaleY = screen.logicalYDPI / screen.deviceYDPI
+ return {
+ left: rect.left * scaleX,
+ right: rect.right * scaleX,
+ top: rect.top * scaleY,
+ bottom: rect.bottom * scaleY
+ }
+ }
+
+ function clearLineMeasurementCacheFor(lineView) {
+ if (lineView.measure) {
+ lineView.measure.cache = {}
+ lineView.measure.heights = null
+ if (lineView.rest) {
+ for (var i = 0; i < lineView.rest.length; i++) {
+ lineView.measure.caches[i] = {}
+ }
+ }
+ }
+ }
+
+ function clearLineMeasurementCache(cm) {
+ cm.display.externalMeasure = null
+ removeChildren(cm.display.lineMeasure)
+ for (var i = 0; i < cm.display.view.length; i++) {
+ clearLineMeasurementCacheFor(cm.display.view[i])
+ }
+ }
+
+ function clearCaches(cm) {
+ clearLineMeasurementCache(cm)
+ cm.display.cachedCharWidth = cm.display.cachedTextHeight = cm.display.cachedPaddingH = null
+ if (!cm.options.lineWrapping) {
+ cm.display.maxLineChanged = true
+ }
+ cm.display.lineNumChars = null
+ }
+
+ function pageScrollX() {
+ // Work around https://bugs.chromium.org/p/chromium/issues/detail?id=489206
+ // which causes page_Offset and bounding client rects to use
+ // different reference viewports and invalidate our calculations.
+ if (chrome && android) {
+ return -(
+ document.body.getBoundingClientRect().left -
+ parseInt(getComputedStyle(document.body).marginLeft)
+ )
+ }
+ return window.pageXOffset || (document.documentElement || document.body).scrollLeft
+ }
+ function pageScrollY() {
+ if (chrome && android) {
+ return -(
+ document.body.getBoundingClientRect().top -
+ parseInt(getComputedStyle(document.body).marginTop)
+ )
+ }
+ return window.pageYOffset || (document.documentElement || document.body).scrollTop
+ }
+
+ function widgetTopHeight(lineObj) {
+ var height = 0
+ if (lineObj.widgets) {
+ for (var i = 0; i < lineObj.widgets.length; ++i) {
+ if (lineObj.widgets[i].above) {
+ height += widgetHeight(lineObj.widgets[i])
+ }
+ }
+ }
+ return height
+ }
+
+ // Converts a {top, bottom, left, right} box from line-local
+ // coordinates into another coordinate system. Context may be one of
+ // "line", "div" (display.lineDiv), "local"./null (editor), "window",
+ // or "page".
+ function intoCoordSystem(cm, lineObj, rect, context, includeWidgets) {
+ if (!includeWidgets) {
+ var height = widgetTopHeight(lineObj)
+ rect.top += height
+ rect.bottom += height
+ }
+ if (context == "line") {
+ return rect
+ }
+ if (!context) {
+ context = "local"
+ }
+ var yOff = heightAtLine(lineObj)
+ if (context == "local") {
+ yOff += paddingTop(cm.display)
+ } else {
+ yOff -= cm.display.viewOffset
+ }
+ if (context == "page" || context == "window") {
+ var lOff = cm.display.lineSpace.getBoundingClientRect()
+ yOff += lOff.top + (context == "window" ? 0 : pageScrollY())
+ var xOff = lOff.left + (context == "window" ? 0 : pageScrollX())
+ rect.left += xOff
+ rect.right += xOff
+ }
+ rect.top += yOff
+ rect.bottom += yOff
+ return rect
+ }
+
+ // Coverts a box from "div" coords to another coordinate system.
+ // Context may be "window", "page", "div", or "local"./null.
+ function fromCoordSystem(cm, coords, context) {
+ if (context == "div") {
+ return coords
+ }
+ var left = coords.left,
+ top = coords.top
+ // First move into "page" coordinate system
+ if (context == "page") {
+ left -= pageScrollX()
+ top -= pageScrollY()
+ } else if (context == "local" || !context) {
+ var localBox = cm.display.sizer.getBoundingClientRect()
+ left += localBox.left
+ top += localBox.top
+ }
+
+ var lineSpaceBox = cm.display.lineSpace.getBoundingClientRect()
+ return { left: left - lineSpaceBox.left, top: top - lineSpaceBox.top }
+ }
+
+ function charCoords(cm, pos, context, lineObj, bias) {
+ if (!lineObj) {
+ lineObj = getLine(cm.doc, pos.line)
+ }
+ return intoCoordSystem(cm, lineObj, measureChar(cm, lineObj, pos.ch, bias), context)
+ }
+
+ // Returns a box for a given cursor position, which may have an
+ // 'other' property containing the position of the secondary cursor
+ // on a bidi boundary.
+ // A cursor Pos(line, char, "before") is on the same visual line as `char - 1`
+ // and after `char - 1` in writing order of `char - 1`
+ // A cursor Pos(line, char, "after") is on the same visual line as `char`
+ // and before `char` in writing order of `char`
+ // Examples (upper-case letters are RTL, lower-case are LTR):
+ // Pos(0, 1, ...)
+ // before after
+ // ab a|b a|b
+ // aB a|B aB|
+ // Ab |Ab A|b
+ // AB B|A B|A
+ // Every position after the last character on a line is considered to stick
+ // to the last character on the line.
+ function cursorCoords(cm, pos, context, lineObj, preparedMeasure, varHeight) {
+ lineObj = lineObj || getLine(cm.doc, pos.line)
+ if (!preparedMeasure) {
+ preparedMeasure = prepareMeasureForLine(cm, lineObj)
+ }
+ function get(ch, right) {
+ var m = measureCharPrepared(
+ cm,
+ preparedMeasure,
+ ch,
+ right ? "right" : "left",
+ varHeight
+ )
+ if (right) {
+ m.left = m.right
+ } else {
+ m.right = m.left
+ }
+ return intoCoordSystem(cm, lineObj, m, context)
+ }
+ var order = getOrder(lineObj, cm.doc.direction),
+ ch = pos.ch,
+ sticky = pos.sticky
+ if (ch >= lineObj.text.length) {
+ ch = lineObj.text.length
+ sticky = "before"
+ } else if (ch <= 0) {
+ ch = 0
+ sticky = "after"
+ }
+ if (!order) {
+ return get(sticky == "before" ? ch - 1 : ch, sticky == "before")
+ }
+
+ function getBidi(ch, partPos, invert) {
+ var part = order[partPos],
+ right = part.level == 1
+ return get(invert ? ch - 1 : ch, right != invert)
+ }
+ var partPos = getBidiPartAt(order, ch, sticky)
+ var other = bidiOther
+ var val = getBidi(ch, partPos, sticky == "before")
+ if (other != null) {
+ val.other = getBidi(ch, other, sticky != "before")
+ }
+ return val
+ }
+
+ // Used to cheaply estimate the coordinates for a position. Used for
+ // intermediate scroll updates.
+ function estimateCoords(cm, pos) {
+ var left = 0
+ pos = clipPos(cm.doc, pos)
+ if (!cm.options.lineWrapping) {
+ left = charWidth(cm.display) * pos.ch
+ }
+ var lineObj = getLine(cm.doc, pos.line)
+ var top = heightAtLine(lineObj) + paddingTop(cm.display)
+ return { left: left, right: left, top: top, bottom: top + lineObj.height }
+ }
+
+ // Positions returned by coordsChar contain some extra information.
+ // xRel is the relative x position of the input coordinates compared
+ // to the found position (so xRel > 0 means the coordinates are to
+ // the right of the character position, for example). When outside
+ // is true, that means the coordinates lie outside the line's
+ // vertical range.
+ function PosWithInfo(line, ch, sticky, outside, xRel) {
+ var pos = Pos(line, ch, sticky)
+ pos.xRel = xRel
+ if (outside) {
+ pos.outside = outside
+ }
+ return pos
+ }
+
+ // Compute the character position closest to the given coordinates.
+ // Input must be lineSpace-local ("div" coordinate system).
+ function coordsChar(cm, x, y) {
+ var doc = cm.doc
+ y += cm.display.viewOffset
+ if (y < 0) {
+ return PosWithInfo(doc.first, 0, null, -1, -1)
+ }
+ var lineN = lineAtHeight(doc, y),
+ last = doc.first + doc.size - 1
+ if (lineN > last) {
+ return PosWithInfo(doc.first + doc.size - 1, getLine(doc, last).text.length, null, 1, 1)
+ }
+ if (x < 0) {
+ x = 0
+ }
+
+ var lineObj = getLine(doc, lineN)
+ for (;;) {
+ var found = coordsCharInner(cm, lineObj, lineN, x, y)
+ var collapsed = collapsedSpanAround(
+ lineObj,
+ found.ch + (found.xRel > 0 || found.outside > 0 ? 1 : 0)
+ )
+ if (!collapsed) {
+ return found
+ }
+ var rangeEnd = collapsed.find(1)
+ if (rangeEnd.line == lineN) {
+ return rangeEnd
+ }
+ lineObj = getLine(doc, (lineN = rangeEnd.line))
+ }
+ }
+
+ function wrappedLineExtent(cm, lineObj, preparedMeasure, y) {
+ y -= widgetTopHeight(lineObj)
+ var end = lineObj.text.length
+ var begin = findFirst(
+ function(ch) {
+ return measureCharPrepared(cm, preparedMeasure, ch - 1).bottom <= y
+ },
+ end,
+ 0
+ )
+ end = findFirst(
+ function(ch) {
+ return measureCharPrepared(cm, preparedMeasure, ch).top > y
+ },
+ begin,
+ end
+ )
+ return { begin: begin, end: end }
+ }
+
+ function wrappedLineExtentChar(cm, lineObj, preparedMeasure, target) {
+ if (!preparedMeasure) {
+ preparedMeasure = prepareMeasureForLine(cm, lineObj)
+ }
+ var targetTop = intoCoordSystem(
+ cm,
+ lineObj,
+ measureCharPrepared(cm, preparedMeasure, target),
+ "line"
+ ).top
+ return wrappedLineExtent(cm, lineObj, preparedMeasure, targetTop)
+ }
+
+ // Returns true if the given side of a box is after the given
+ // coordinates, in top-to-bottom, left-to-right order.
+ function boxIsAfter(box, x, y, left) {
+ return box.bottom <= y ? false : box.top > y ? true : (left ? box.left : box.right) > x
+ }
+
+ function coordsCharInner(cm, lineObj, lineNo, x, y) {
+ // Move y into line-local coordinate space
+ y -= heightAtLine(lineObj)
+ var preparedMeasure = prepareMeasureForLine(cm, lineObj)
+ // When directly calling `measureCharPrepared`, we have to adjust
+ // for the widgets at this line.
+ var widgetHeight = widgetTopHeight(lineObj)
+ var begin = 0,
+ end = lineObj.text.length,
+ ltr = true
+
+ var order = getOrder(lineObj, cm.doc.direction)
+ // If the line isn't plain left-to-right text, first figure out
+ // which bidi section the coordinates fall into.
+ if (order) {
+ var part = (cm.options.lineWrapping ? coordsBidiPartWrapped : coordsBidiPart)(
+ cm,
+ lineObj,
+ lineNo,
+ preparedMeasure,
+ order,
+ x,
+ y
+ )
+ ltr = part.level != 1
+ // The awkward -1 offsets are needed because findFirst (called
+ // on these below) will treat its first bound as inclusive,
+ // second as exclusive, but we want to actually address the
+ // characters in the part's range
+ begin = ltr ? part.from : part.to - 1
+ end = ltr ? part.to : part.from - 1
+ }
+
+ // A binary search to find the first character whose bounding box
+ // starts after the coordinates. If we run across any whose box wrap
+ // the coordinates, store that.
+ var chAround = null,
+ boxAround = null
+ var ch = findFirst(
+ function(ch) {
+ var box = measureCharPrepared(cm, preparedMeasure, ch)
+ box.top += widgetHeight
+ box.bottom += widgetHeight
+ if (!boxIsAfter(box, x, y, false)) {
+ return false
+ }
+ if (box.top <= y && box.left <= x) {
+ chAround = ch
+ boxAround = box
+ }
+ return true
+ },
+ begin,
+ end
+ )
+
+ var baseX,
+ sticky,
+ outside = false
+ // If a box around the coordinates was found, use that
+ if (boxAround) {
+ // Distinguish coordinates nearer to the left or right side of the box
+ var atLeft = x - boxAround.left < boxAround.right - x,
+ atStart = atLeft == ltr
+ ch = chAround + (atStart ? 0 : 1)
+ sticky = atStart ? "after" : "before"
+ baseX = atLeft ? boxAround.left : boxAround.right
+ } else {
+ // (Adjust for extended bound, if necessary.)
+ if (!ltr && (ch == end || ch == begin)) {
+ ch++
+ }
+ // To determine which side to associate with, get the box to the
+ // left of the character and compare it's vertical position to the
+ // coordinates
+ sticky =
+ ch == 0
+ ? "after"
+ : ch == lineObj.text.length
+ ? "before"
+ : measureCharPrepared(cm, preparedMeasure, ch - (ltr ? 1 : 0)).bottom +
+ widgetHeight <=
+ y ==
+ ltr
+ ? "after"
+ : "before"
+ // Now get accurate coordinates for this place, in order to get a
+ // base X position
+ var coords = cursorCoords(cm, Pos(lineNo, ch, sticky), "line", lineObj, preparedMeasure)
+ baseX = coords.left
+ outside = y < coords.top ? -1 : y >= coords.bottom ? 1 : 0
+ }
+
+ ch = skipExtendingChars(lineObj.text, ch, 1)
+ return PosWithInfo(lineNo, ch, sticky, outside, x - baseX)
+ }
+
+ function coordsBidiPart(cm, lineObj, lineNo, preparedMeasure, order, x, y) {
+ // Bidi parts are sorted left-to-right, and in a non-line-wrapping
+ // situation, we can take this ordering to correspond to the visual
+ // ordering. This finds the first part whose end is after the given
+ // coordinates.
+ var index = findFirst(
+ function(i) {
+ var part = order[i],
+ ltr = part.level != 1
+ return boxIsAfter(
+ cursorCoords(
+ cm,
+ Pos(lineNo, ltr ? part.to : part.from, ltr ? "before" : "after"),
+ "line",
+ lineObj,
+ preparedMeasure
+ ),
+ x,
+ y,
+ true
+ )
+ },
+ 0,
+ order.length - 1
+ )
+ var part = order[index]
+ // If this isn't the first part, the part's start is also after
+ // the coordinates, and the coordinates aren't on the same line as
+ // that start, move one part back.
+ if (index > 0) {
+ var ltr = part.level != 1
+ var start = cursorCoords(
+ cm,
+ Pos(lineNo, ltr ? part.from : part.to, ltr ? "after" : "before"),
+ "line",
+ lineObj,
+ preparedMeasure
+ )
+ if (boxIsAfter(start, x, y, true) && start.top > y) {
+ part = order[index - 1]
+ }
+ }
+ return part
+ }
+
+ function coordsBidiPartWrapped(cm, lineObj, _lineNo, preparedMeasure, order, x, y) {
+ // In a wrapped line, rtl text on wrapping boundaries can do things
+ // that don't correspond to the ordering in our `order` array at
+ // all, so a binary search doesn't work, and we want to return a
+ // part that only spans one line so that the binary search in
+ // coordsCharInner is safe. As such, we first find the extent of the
+ // wrapped line, and then do a flat search in which we discard any
+ // spans that aren't on the line.
+ var ref = wrappedLineExtent(cm, lineObj, preparedMeasure, y)
+ var begin = ref.begin
+ var end = ref.end
+ if (/\s/.test(lineObj.text.charAt(end - 1))) {
+ end--
+ }
+ var part = null,
+ closestDist = null
+ for (var i = 0; i < order.length; i++) {
+ var p = order[i]
+ if (p.from >= end || p.to <= begin) {
+ continue
+ }
+ var ltr = p.level != 1
+ var endX = measureCharPrepared(
+ cm,
+ preparedMeasure,
+ ltr ? Math.min(end, p.to) - 1 : Math.max(begin, p.from)
+ ).right
+ // Weigh against spans ending before this, so that they are only
+ // picked if nothing ends after
+ var dist = endX < x ? x - endX + 1e9 : endX - x
+ if (!part || closestDist > dist) {
+ part = p
+ closestDist = dist
+ }
+ }
+ if (!part) {
+ part = order[order.length - 1]
+ }
+ // Clip the part to the wrapped line.
+ if (part.from < begin) {
+ part = { from: begin, to: part.to, level: part.level }
+ }
+ if (part.to > end) {
+ part = { from: part.from, to: end, level: part.level }
+ }
+ return part
+ }
+
+ var measureText
+ // Compute the default text height.
+ function textHeight(display) {
+ if (display.cachedTextHeight != null) {
+ return display.cachedTextHeight
+ }
+ if (measureText == null) {
+ measureText = elt("pre", null, "CodeMirror-line-like")
+ // Measure a bunch of lines, for browsers that compute
+ // fractional heights.
+ for (var i = 0; i < 49; ++i) {
+ measureText.appendChild(document.createTextNode("x"))
+ measureText.appendChild(elt("br"))
+ }
+ measureText.appendChild(document.createTextNode("x"))
+ }
+ removeChildrenAndAdd(display.measure, measureText)
+ var height = measureText.offsetHeight / 50
+ if (height > 3) {
+ display.cachedTextHeight = height
+ }
+ removeChildren(display.measure)
+ return height || 1
+ }
+
+ // Compute the default character width.
+ function charWidth(display) {
+ if (display.cachedCharWidth != null) {
+ return display.cachedCharWidth
+ }
+ var anchor = elt("span", "xxxxxxxxxx")
+ var pre = elt("pre", [anchor], "CodeMirror-line-like")
+ removeChildrenAndAdd(display.measure, pre)
+ var rect = anchor.getBoundingClientRect(),
+ width = (rect.right - rect.left) / 10
+ if (width > 2) {
+ display.cachedCharWidth = width
+ }
+ return width || 10
+ }
+
+ // Do a bulk-read of the DOM positions and sizes needed to draw the
+ // view, so that we don't interleave reading and writing to the DOM.
+ function getDimensions(cm) {
+ var d = cm.display,
+ left = {},
+ width = {}
+ var gutterLeft = d.gutters.clientLeft
+ for (var n = d.gutters.firstChild, i = 0; n; n = n.nextSibling, ++i) {
+ var id = cm.display.gutterSpecs[i].className
+ left[id] = n.offsetLeft + n.clientLeft + gutterLeft
+ width[id] = n.clientWidth
+ }
+ return {
+ fixedPos: compensateForHScroll(d),
+ gutterTotalWidth: d.gutters.offsetWidth,
+ gutterLeft: left,
+ gutterWidth: width,
+ wrapperWidth: d.wrapper.clientWidth
+ }
+ }
+
+ // Computes display.scroller.scrollLeft + display.gutters.offsetWidth,
+ // but using getBoundingClientRect to get a sub-pixel-accurate
+ // result.
+ function compensateForHScroll(display) {
+ return (
+ display.scroller.getBoundingClientRect().left -
+ display.sizer.getBoundingClientRect().left
+ )
+ }
+
+ // Returns a function that estimates the height of a line, to use as
+ // first approximation until the line becomes visible (and is thus
+ // properly measurable).
+ function estimateHeight(cm) {
+ var th = textHeight(cm.display),
+ wrapping = cm.options.lineWrapping
+ var perLine =
+ wrapping && Math.max(5, cm.display.scroller.clientWidth / charWidth(cm.display) - 3)
+ return function(line) {
+ if (lineIsHidden(cm.doc, line)) {
+ return 0
+ }
+
+ var widgetsHeight = 0
+ if (line.widgets) {
+ for (var i = 0; i < line.widgets.length; i++) {
+ if (line.widgets[i].height) {
+ widgetsHeight += line.widgets[i].height
+ }
+ }
+ }
+
+ if (wrapping) {
+ return widgetsHeight + (Math.ceil(line.text.length / perLine) || 1) * th
+ } else {
+ return widgetsHeight + th
+ }
+ }
+ }
+
+ function estimateLineHeights(cm) {
+ var doc = cm.doc,
+ est = estimateHeight(cm)
+ doc.iter(function(line) {
+ var estHeight = est(line)
+ if (estHeight != line.height) {
+ updateLineHeight(line, estHeight)
+ }
+ })
+ }
+
+ // Given a mouse event, find the corresponding position. If liberal
+ // is false, it checks whether a gutter or scrollbar was clicked,
+ // and returns null if it was. forRect is used by rectangular
+ // selections, and tries to estimate a character position even for
+ // coordinates beyond the right of the text.
+ function posFromMouse(cm, e, liberal, forRect) {
+ var display = cm.display
+ if (!liberal && e_target(e).getAttribute("cm-not-content") == "true") {
+ return null
+ }
+
+ var x,
+ y,
+ space = display.lineSpace.getBoundingClientRect()
+ // Fails unpredictably on IE[67] when mouse is dragged around quickly.
+ try {
+ x = e.clientX - space.left
+ y = e.clientY - space.top
+ } catch (e) {
+ return null
+ }
+ var coords = coordsChar(cm, x, y),
+ line
+ if (
+ forRect &&
+ coords.xRel > 0 &&
+ (line = getLine(cm.doc, coords.line).text).length == coords.ch
+ ) {
+ var colDiff = countColumn(line, line.length, cm.options.tabSize) - line.length
+ coords = Pos(
+ coords.line,
+ Math.max(
+ 0,
+ Math.round((x - paddingH(cm.display).left) / charWidth(cm.display)) - colDiff
+ )
+ )
+ }
+ return coords
+ }
+
+ // Find the view element corresponding to a given line. Return null
+ // when the line isn't visible.
+ function findViewIndex(cm, n) {
+ if (n >= cm.display.viewTo) {
+ return null
+ }
+ n -= cm.display.viewFrom
+ if (n < 0) {
+ return null
+ }
+ var view = cm.display.view
+ for (var i = 0; i < view.length; i++) {
+ n -= view[i].size
+ if (n < 0) {
+ return i
+ }
+ }
+ }
+
+ // Updates the display.view data structure for a given change to the
+ // document. From and to are in pre-change coordinates. Lendiff is
+ // the amount of lines added or subtracted by the change. This is
+ // used for changes that span multiple lines, or change the way
+ // lines are divided into visual lines. regLineChange (below)
+ // registers single-line changes.
+ function regChange(cm, from, to, lendiff) {
+ if (from == null) {
+ from = cm.doc.first
+ }
+ if (to == null) {
+ to = cm.doc.first + cm.doc.size
+ }
+ if (!lendiff) {
+ lendiff = 0
+ }
+
+ var display = cm.display
+ if (
+ lendiff &&
+ to < display.viewTo &&
+ (display.updateLineNumbers == null || display.updateLineNumbers > from)
+ ) {
+ display.updateLineNumbers = from
+ }
+
+ cm.curOp.viewChanged = true
+
+ if (from >= display.viewTo) {
+ // Change after
+ if (sawCollapsedSpans && visualLineNo(cm.doc, from) < display.viewTo) {
+ resetView(cm)
+ }
+ } else if (to <= display.viewFrom) {
+ // Change before
+ if (sawCollapsedSpans && visualLineEndNo(cm.doc, to + lendiff) > display.viewFrom) {
+ resetView(cm)
+ } else {
+ display.viewFrom += lendiff
+ display.viewTo += lendiff
+ }
+ } else if (from <= display.viewFrom && to >= display.viewTo) {
+ // Full overlap
+ resetView(cm)
+ } else if (from <= display.viewFrom) {
+ // Top overlap
+ var cut = viewCuttingPoint(cm, to, to + lendiff, 1)
+ if (cut) {
+ display.view = display.view.slice(cut.index)
+ display.viewFrom = cut.lineN
+ display.viewTo += lendiff
+ } else {
+ resetView(cm)
+ }
+ } else if (to >= display.viewTo) {
+ // Bottom overlap
+ var cut$1 = viewCuttingPoint(cm, from, from, -1)
+ if (cut$1) {
+ display.view = display.view.slice(0, cut$1.index)
+ display.viewTo = cut$1.lineN
+ } else {
+ resetView(cm)
+ }
+ } else {
+ // Gap in the middle
+ var cutTop = viewCuttingPoint(cm, from, from, -1)
+ var cutBot = viewCuttingPoint(cm, to, to + lendiff, 1)
+ if (cutTop && cutBot) {
+ display.view = display.view
+ .slice(0, cutTop.index)
+ .concat(buildViewArray(cm, cutTop.lineN, cutBot.lineN))
+ .concat(display.view.slice(cutBot.index))
+ display.viewTo += lendiff
+ } else {
+ resetView(cm)
+ }
+ }
+
+ var ext = display.externalMeasured
+ if (ext) {
+ if (to < ext.lineN) {
+ ext.lineN += lendiff
+ } else if (from < ext.lineN + ext.size) {
+ display.externalMeasured = null
+ }
+ }
+ }
+
+ // Register a change to a single line. Type must be one of "text",
+ // "gutter", "class", "widget"
+ function regLineChange(cm, line, type) {
+ cm.curOp.viewChanged = true
+ var display = cm.display,
+ ext = cm.display.externalMeasured
+ if (ext && line >= ext.lineN && line < ext.lineN + ext.size) {
+ display.externalMeasured = null
+ }
+
+ if (line < display.viewFrom || line >= display.viewTo) {
+ return
+ }
+ var lineView = display.view[findViewIndex(cm, line)]
+ if (lineView.node == null) {
+ return
+ }
+ var arr = lineView.changes || (lineView.changes = [])
+ if (indexOf(arr, type) == -1) {
+ arr.push(type)
+ }
+ }
+
+ // Clear the view.
+ function resetView(cm) {
+ cm.display.viewFrom = cm.display.viewTo = cm.doc.first
+ cm.display.view = []
+ cm.display.viewOffset = 0
+ }
+
+ function viewCuttingPoint(cm, oldN, newN, dir) {
+ var index = findViewIndex(cm, oldN),
+ diff,
+ view = cm.display.view
+ if (!sawCollapsedSpans || newN == cm.doc.first + cm.doc.size) {
+ return { index: index, lineN: newN }
+ }
+ var n = cm.display.viewFrom
+ for (var i = 0; i < index; i++) {
+ n += view[i].size
+ }
+ if (n != oldN) {
+ if (dir > 0) {
+ if (index == view.length - 1) {
+ return null
+ }
+ diff = n + view[index].size - oldN
+ index++
+ } else {
+ diff = n - oldN
+ }
+ oldN += diff
+ newN += diff
+ }
+ while (visualLineNo(cm.doc, newN) != newN) {
+ if (index == (dir < 0 ? 0 : view.length - 1)) {
+ return null
+ }
+ newN += dir * view[index - (dir < 0 ? 1 : 0)].size
+ index += dir
+ }
+ return { index: index, lineN: newN }
+ }
+
+ // Force the view to cover a given range, adding empty view element
+ // or clipping off existing ones as needed.
+ function adjustView(cm, from, to) {
+ var display = cm.display,
+ view = display.view
+ if (view.length == 0 || from >= display.viewTo || to <= display.viewFrom) {
+ display.view = buildViewArray(cm, from, to)
+ display.viewFrom = from
+ } else {
+ if (display.viewFrom > from) {
+ display.view = buildViewArray(cm, from, display.viewFrom).concat(display.view)
+ } else if (display.viewFrom < from) {
+ display.view = display.view.slice(findViewIndex(cm, from))
+ }
+ display.viewFrom = from
+ if (display.viewTo < to) {
+ display.view = display.view.concat(buildViewArray(cm, display.viewTo, to))
+ } else if (display.viewTo > to) {
+ display.view = display.view.slice(0, findViewIndex(cm, to))
+ }
+ }
+ display.viewTo = to
+ }
+
+ // Count the number of lines in the view whose DOM representation is
+ // out of date (or nonexistent).
+ function countDirtyView(cm) {
+ var view = cm.display.view,
+ dirty = 0
+ for (var i = 0; i < view.length; i++) {
+ var lineView = view[i]
+ if (!lineView.hidden && (!lineView.node || lineView.changes)) {
+ ++dirty
+ }
+ }
+ return dirty
+ }
+
+ function updateSelection(cm) {
+ cm.display.input.showSelection(cm.display.input.prepareSelection())
+ }
+
+ function prepareSelection(cm, primary) {
+ if (primary === void 0) primary = true
+
+ var doc = cm.doc,
+ result = {}
+ var curFragment = (result.cursors = document.createDocumentFragment())
+ var selFragment = (result.selection = document.createDocumentFragment())
+
+ for (var i = 0; i < doc.sel.ranges.length; i++) {
+ if (!primary && i == doc.sel.primIndex) {
+ continue
+ }
+ var range = doc.sel.ranges[i]
+ if (range.from().line >= cm.display.viewTo || range.to().line < cm.display.viewFrom) {
+ continue
+ }
+ var collapsed = range.empty()
+ if (collapsed || cm.options.showCursorWhenSelecting) {
+ drawSelectionCursor(cm, range.head, curFragment)
+ }
+ if (!collapsed) {
+ drawSelectionRange(cm, range, selFragment)
+ }
+ }
+ return result
+ }
+
+ // Draws a cursor for the given range
+ function drawSelectionCursor(cm, head, output) {
+ var pos = cursorCoords(cm, head, "div", null, null, !cm.options.singleCursorHeightPerLine)
+
+ var cursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor"))
+ cursor.style.left = pos.left + "px"
+ cursor.style.top = pos.top + "px"
+ cursor.style.height = Math.max(0, pos.bottom - pos.top) * cm.options.cursorHeight + "px"
+
+ if (pos.other) {
+ // Secondary cursor, shown when on a 'jump' in bi-directional text
+ var otherCursor = output.appendChild(
+ elt("div", "\u00a0", "CodeMirror-cursor CodeMirror-secondarycursor")
+ )
+ otherCursor.style.display = ""
+ otherCursor.style.left = pos.other.left + "px"
+ otherCursor.style.top = pos.other.top + "px"
+ otherCursor.style.height = (pos.other.bottom - pos.other.top) * 0.85 + "px"
+ }
+ }
+
+ function cmpCoords(a, b) {
+ return a.top - b.top || a.left - b.left
+ }
+
+ // Draws the given range as a highlighted selection
+ function drawSelectionRange(cm, range, output) {
+ var display = cm.display,
+ doc = cm.doc
+ var fragment = document.createDocumentFragment()
+ var padding = paddingH(cm.display),
+ leftSide = padding.left
+ var rightSide =
+ Math.max(display.sizerWidth, displayWidth(cm) - display.sizer.offsetLeft) -
+ padding.right
+ var docLTR = doc.direction == "ltr"
+
+ function add(left, top, width, bottom) {
+ if (top < 0) {
+ top = 0
+ }
+ top = Math.round(top)
+ bottom = Math.round(bottom)
+ fragment.appendChild(
+ elt(
+ "div",
+ null,
+ "CodeMirror-selected",
+ "position: absolute; left: " +
+ left +
+ "px;\n top: " +
+ top +
+ "px; width: " +
+ (width == null ? rightSide - left : width) +
+ "px;\n height: " +
+ (bottom - top) +
+ "px"
+ )
+ )
+ }
+
+ function drawForLine(line, fromArg, toArg) {
+ var lineObj = getLine(doc, line)
+ var lineLen = lineObj.text.length
+ var start, end
+ function coords(ch, bias) {
+ return charCoords(cm, Pos(line, ch), "div", lineObj, bias)
+ }
+
+ function wrapX(pos, dir, side) {
+ var extent = wrappedLineExtentChar(cm, lineObj, null, pos)
+ var prop = (dir == "ltr") == (side == "after") ? "left" : "right"
+ var ch =
+ side == "after"
+ ? extent.begin
+ : extent.end - (/\s/.test(lineObj.text.charAt(extent.end - 1)) ? 2 : 1)
+ return coords(ch, prop)[prop]
+ }
+
+ var order = getOrder(lineObj, doc.direction)
+ iterateBidiSections(order, fromArg || 0, toArg == null ? lineLen : toArg, function(
+ from,
+ to,
+ dir,
+ i
+ ) {
+ var ltr = dir == "ltr"
+ var fromPos = coords(from, ltr ? "left" : "right")
+ var toPos = coords(to - 1, ltr ? "right" : "left")
+
+ var openStart = fromArg == null && from == 0,
+ openEnd = toArg == null && to == lineLen
+ var first = i == 0,
+ last = !order || i == order.length - 1
+ if (toPos.top - fromPos.top <= 3) {
+ // Single line
+ var openLeft = (docLTR ? openStart : openEnd) && first
+ var openRight = (docLTR ? openEnd : openStart) && last
+ var left = openLeft ? leftSide : (ltr ? fromPos : toPos).left
+ var right = openRight ? rightSide : (ltr ? toPos : fromPos).right
+ add(left, fromPos.top, right - left, fromPos.bottom)
+ } else {
+ // Multiple lines
+ var topLeft, topRight, botLeft, botRight
+ if (ltr) {
+ topLeft = docLTR && openStart && first ? leftSide : fromPos.left
+ topRight = docLTR ? rightSide : wrapX(from, dir, "before")
+ botLeft = docLTR ? leftSide : wrapX(to, dir, "after")
+ botRight = docLTR && openEnd && last ? rightSide : toPos.right
+ } else {
+ topLeft = !docLTR ? leftSide : wrapX(from, dir, "before")
+ topRight = !docLTR && openStart && first ? rightSide : fromPos.right
+ botLeft = !docLTR && openEnd && last ? leftSide : toPos.left
+ botRight = !docLTR ? rightSide : wrapX(to, dir, "after")
+ }
+ add(topLeft, fromPos.top, topRight - topLeft, fromPos.bottom)
+ if (fromPos.bottom < toPos.top) {
+ add(leftSide, fromPos.bottom, null, toPos.top)
+ }
+ add(botLeft, toPos.top, botRight - botLeft, toPos.bottom)
+ }
+
+ if (!start || cmpCoords(fromPos, start) < 0) {
+ start = fromPos
+ }
+ if (cmpCoords(toPos, start) < 0) {
+ start = toPos
+ }
+ if (!end || cmpCoords(fromPos, end) < 0) {
+ end = fromPos
+ }
+ if (cmpCoords(toPos, end) < 0) {
+ end = toPos
+ }
+ })
+ return { start: start, end: end }
+ }
+
+ var sFrom = range.from(),
+ sTo = range.to()
+ if (sFrom.line == sTo.line) {
+ drawForLine(sFrom.line, sFrom.ch, sTo.ch)
+ } else {
+ var fromLine = getLine(doc, sFrom.line),
+ toLine = getLine(doc, sTo.line)
+ var singleVLine = visualLine(fromLine) == visualLine(toLine)
+ var leftEnd = drawForLine(
+ sFrom.line,
+ sFrom.ch,
+ singleVLine ? fromLine.text.length + 1 : null
+ ).end
+ var rightStart = drawForLine(sTo.line, singleVLine ? 0 : null, sTo.ch).start
+ if (singleVLine) {
+ if (leftEnd.top < rightStart.top - 2) {
+ add(leftEnd.right, leftEnd.top, null, leftEnd.bottom)
+ add(leftSide, rightStart.top, rightStart.left, rightStart.bottom)
+ } else {
+ add(leftEnd.right, leftEnd.top, rightStart.left - leftEnd.right, leftEnd.bottom)
+ }
+ }
+ if (leftEnd.bottom < rightStart.top) {
+ add(leftSide, leftEnd.bottom, null, rightStart.top)
+ }
+ }
+
+ output.appendChild(fragment)
+ }
+
+ // Cursor-blinking
+ function restartBlink(cm) {
+ if (!cm.state.focused) {
+ return
+ }
+ var display = cm.display
+ clearInterval(display.blinker)
+ var on = true
+ display.cursorDiv.style.visibility = ""
+ if (cm.options.cursorBlinkRate > 0) {
+ display.blinker = setInterval(function() {
+ return (display.cursorDiv.style.visibility = (on = !on) ? "" : "hidden")
+ }, cm.options.cursorBlinkRate)
+ } else if (cm.options.cursorBlinkRate < 0) {
+ display.cursorDiv.style.visibility = "hidden"
+ }
+ }
+
+ function ensureFocus(cm) {
+ if (!cm.state.focused) {
+ cm.display.input.focus()
+ onFocus(cm)
+ }
+ }
+
+ function delayBlurEvent(cm) {
+ cm.state.delayingBlurEvent = true
+ setTimeout(function() {
+ if (cm.state.delayingBlurEvent) {
+ cm.state.delayingBlurEvent = false
+ onBlur(cm)
+ }
+ }, 100)
+ }
+
+ function onFocus(cm, e) {
+ if (cm.state.delayingBlurEvent) {
+ cm.state.delayingBlurEvent = false
+ }
+
+ if (cm.options.readOnly == "nocursor") {
+ return
+ }
+ if (!cm.state.focused) {
+ signal(cm, "focus", cm, e)
+ cm.state.focused = true
+ addClass(cm.display.wrapper, "CodeMirror-focused")
+ // This test prevents this from firing when a context
+ // menu is closed (since the input reset would kill the
+ // select-all detection hack)
+ if (!cm.curOp && cm.display.selForContextMenu != cm.doc.sel) {
+ cm.display.input.reset()
+ if (webkit) {
+ setTimeout(function() {
+ return cm.display.input.reset(true)
+ }, 20)
+ } // Issue #1730
+ }
+ cm.display.input.receivedFocus()
+ }
+ restartBlink(cm)
+ }
+ function onBlur(cm, e) {
+ if (cm.state.delayingBlurEvent) {
+ return
+ }
+
+ if (cm.state.focused) {
+ signal(cm, "blur", cm, e)
+ cm.state.focused = false
+ rmClass(cm.display.wrapper, "CodeMirror-focused")
+ }
+ clearInterval(cm.display.blinker)
+ setTimeout(function() {
+ if (!cm.state.focused) {
+ cm.display.shift = false
+ }
+ }, 150)
+ }
+
+ // Read the actual heights of the rendered lines, and update their
+ // stored heights to match.
+ function updateHeightsInViewport(cm) {
+ var display = cm.display
+ var prevBottom = display.lineDiv.offsetTop
+ for (var i = 0; i < display.view.length; i++) {
+ var cur = display.view[i],
+ wrapping = cm.options.lineWrapping
+ var height = void 0,
+ width = 0
+ if (cur.hidden) {
+ continue
+ }
+ if (ie && ie_version < 8) {
+ var bot = cur.node.offsetTop + cur.node.offsetHeight
+ height = bot - prevBottom
+ prevBottom = bot
+ } else {
+ var box = cur.node.getBoundingClientRect()
+ height = box.bottom - box.top
+ // Check that lines don't extend past the right of the current
+ // editor width
+ if (!wrapping && cur.text.firstChild) {
+ width = cur.text.firstChild.getBoundingClientRect().right - box.left - 1
+ }
+ }
+ var diff = cur.line.height - height
+ if (diff > 0.005 || diff < -0.005) {
+ updateLineHeight(cur.line, height)
+ updateWidgetHeight(cur.line)
+ if (cur.rest) {
+ for (var j = 0; j < cur.rest.length; j++) {
+ updateWidgetHeight(cur.rest[j])
+ }
+ }
+ }
+ if (width > cm.display.sizerWidth) {
+ var chWidth = Math.ceil(width / charWidth(cm.display))
+ if (chWidth > cm.display.maxLineLength) {
+ cm.display.maxLineLength = chWidth
+ cm.display.maxLine = cur.line
+ cm.display.maxLineChanged = true
+ }
+ }
+ }
+ }
+
+ // Read and store the height of line widgets associated with the
+ // given line.
+ function updateWidgetHeight(line) {
+ if (line.widgets) {
+ for (var i = 0; i < line.widgets.length; ++i) {
+ var w = line.widgets[i],
+ parent = w.node.parentNode
+ if (parent) {
+ w.height = parent.offsetHeight
+ }
+ }
+ }
+ }
+
+ // Compute the lines that are visible in a given viewport (defaults
+ // the the current scroll position). viewport may contain top,
+ // height, and ensure (see op.scrollToPos) properties.
+ function visibleLines(display, doc, viewport) {
+ var top =
+ viewport && viewport.top != null
+ ? Math.max(0, viewport.top)
+ : display.scroller.scrollTop
+ top = Math.floor(top - paddingTop(display))
+ var bottom =
+ viewport && viewport.bottom != null
+ ? viewport.bottom
+ : top + display.wrapper.clientHeight
+
+ var from = lineAtHeight(doc, top),
+ to = lineAtHeight(doc, bottom)
+ // Ensure is a {from: {line, ch}, to: {line, ch}} object, and
+ // forces those lines into the viewport (if possible).
+ if (viewport && viewport.ensure) {
+ var ensureFrom = viewport.ensure.from.line,
+ ensureTo = viewport.ensure.to.line
+ if (ensureFrom < from) {
+ from = ensureFrom
+ to = lineAtHeight(
+ doc,
+ heightAtLine(getLine(doc, ensureFrom)) + display.wrapper.clientHeight
+ )
+ } else if (Math.min(ensureTo, doc.lastLine()) >= to) {
+ from = lineAtHeight(
+ doc,
+ heightAtLine(getLine(doc, ensureTo)) - display.wrapper.clientHeight
+ )
+ to = ensureTo
+ }
+ }
+ return { from: from, to: Math.max(to, from + 1) }
+ }
+
+ // SCROLLING THINGS INTO VIEW
+
+ // If an editor sits on the top or bottom of the window, partially
+ // scrolled out of view, this ensures that the cursor is visible.
+ function maybeScrollWindow(cm, rect) {
+ if (signalDOMEvent(cm, "scrollCursorIntoView")) {
+ return
+ }
+
+ var display = cm.display,
+ box = display.sizer.getBoundingClientRect(),
+ doScroll = null
+ if (rect.top + box.top < 0) {
+ doScroll = true
+ } else if (
+ rect.bottom + box.top >
+ (window.innerHeight || document.documentElement.clientHeight)
+ ) {
+ doScroll = false
+ }
+ if (doScroll != null && !phantom) {
+ var scrollNode = elt(
+ "div",
+ "\u200b",
+ null,
+ "position: absolute;\n top: " +
+ (rect.top - display.viewOffset - paddingTop(cm.display)) +
+ "px;\n height: " +
+ (rect.bottom - rect.top + scrollGap(cm) + display.barHeight) +
+ "px;\n left: " +
+ rect.left +
+ "px; width: " +
+ Math.max(2, rect.right - rect.left) +
+ "px;"
+ )
+ cm.display.lineSpace.appendChild(scrollNode)
+ scrollNode.scrollIntoView(doScroll)
+ cm.display.lineSpace.removeChild(scrollNode)
+ }
+ }
+
+ // Scroll a given position into view (immediately), verifying that
+ // it actually became visible (as line heights are accurately
+ // measured, the position of something may 'drift' during drawing).
+ function scrollPosIntoView(cm, pos, end, margin) {
+ if (margin == null) {
+ margin = 0
+ }
+ var rect
+ if (!cm.options.lineWrapping && pos == end) {
+ // Set pos and end to the cursor positions around the character pos sticks to
+ // If pos.sticky == "before", that is around pos.ch - 1, otherwise around pos.ch
+ // If pos == Pos(_, 0, "before"), pos and end are unchanged
+ pos = pos.ch
+ ? Pos(pos.line, pos.sticky == "before" ? pos.ch - 1 : pos.ch, "after")
+ : pos
+ end = pos.sticky == "before" ? Pos(pos.line, pos.ch + 1, "before") : pos
+ }
+ for (var limit = 0; limit < 5; limit++) {
+ var changed = false
+ var coords = cursorCoords(cm, pos)
+ var endCoords = !end || end == pos ? coords : cursorCoords(cm, end)
+ rect = {
+ left: Math.min(coords.left, endCoords.left),
+ top: Math.min(coords.top, endCoords.top) - margin,
+ right: Math.max(coords.left, endCoords.left),
+ bottom: Math.max(coords.bottom, endCoords.bottom) + margin
+ }
+ var scrollPos = calculateScrollPos(cm, rect)
+ var startTop = cm.doc.scrollTop,
+ startLeft = cm.doc.scrollLeft
+ if (scrollPos.scrollTop != null) {
+ updateScrollTop(cm, scrollPos.scrollTop)
+ if (Math.abs(cm.doc.scrollTop - startTop) > 1) {
+ changed = true
+ }
+ }
+ if (scrollPos.scrollLeft != null) {
+ setScrollLeft(cm, scrollPos.scrollLeft)
+ if (Math.abs(cm.doc.scrollLeft - startLeft) > 1) {
+ changed = true
+ }
+ }
+ if (!changed) {
+ break
+ }
+ }
+ return rect
+ }
+
+ // Scroll a given set of coordinates into view (immediately).
+ function scrollIntoView(cm, rect) {
+ var scrollPos = calculateScrollPos(cm, rect)
+ if (scrollPos.scrollTop != null) {
+ updateScrollTop(cm, scrollPos.scrollTop)
+ }
+ if (scrollPos.scrollLeft != null) {
+ setScrollLeft(cm, scrollPos.scrollLeft)
+ }
+ }
+
+ // Calculate a new scroll position needed to scroll the given
+ // rectangle into view. Returns an object with scrollTop and
+ // scrollLeft properties. When these are undefined, the
+ // vertical/horizontal position does not need to be adjusted.
+ function calculateScrollPos(cm, rect) {
+ var display = cm.display,
+ snapMargin = textHeight(cm.display)
+ if (rect.top < 0) {
+ rect.top = 0
+ }
+ var screentop =
+ cm.curOp && cm.curOp.scrollTop != null ? cm.curOp.scrollTop : display.scroller.scrollTop
+ var screen = displayHeight(cm),
+ result = {}
+ if (rect.bottom - rect.top > screen) {
+ rect.bottom = rect.top + screen
+ }
+ var docBottom = cm.doc.height + paddingVert(display)
+ var atTop = rect.top < snapMargin,
+ atBottom = rect.bottom > docBottom - snapMargin
+ if (rect.top < screentop) {
+ result.scrollTop = atTop ? 0 : rect.top
+ } else if (rect.bottom > screentop + screen) {
+ var newTop = Math.min(rect.top, (atBottom ? docBottom : rect.bottom) - screen)
+ if (newTop != screentop) {
+ result.scrollTop = newTop
+ }
+ }
+
+ var screenleft =
+ cm.curOp && cm.curOp.scrollLeft != null
+ ? cm.curOp.scrollLeft
+ : display.scroller.scrollLeft
+ var screenw = displayWidth(cm) - (cm.options.fixedGutter ? display.gutters.offsetWidth : 0)
+ var tooWide = rect.right - rect.left > screenw
+ if (tooWide) {
+ rect.right = rect.left + screenw
+ }
+ if (rect.left < 10) {
+ result.scrollLeft = 0
+ } else if (rect.left < screenleft) {
+ result.scrollLeft = Math.max(0, rect.left - (tooWide ? 0 : 10))
+ } else if (rect.right > screenw + screenleft - 3) {
+ result.scrollLeft = rect.right + (tooWide ? 0 : 10) - screenw
+ }
+ return result
+ }
+
+ // Store a relative adjustment to the scroll position in the current
+ // operation (to be applied when the operation finishes).
+ function addToScrollTop(cm, top) {
+ if (top == null) {
+ return
+ }
+ resolveScrollToPos(cm)
+ cm.curOp.scrollTop =
+ (cm.curOp.scrollTop == null ? cm.doc.scrollTop : cm.curOp.scrollTop) + top
+ }
+
+ // Make sure that at the end of the operation the current cursor is
+ // shown.
+ function ensureCursorVisible(cm) {
+ resolveScrollToPos(cm)
+ var cur = cm.getCursor()
+ cm.curOp.scrollToPos = { from: cur, to: cur, margin: cm.options.cursorScrollMargin }
+ }
+
+ function scrollToCoords(cm, x, y) {
+ if (x != null || y != null) {
+ resolveScrollToPos(cm)
+ }
+ if (x != null) {
+ cm.curOp.scrollLeft = x
+ }
+ if (y != null) {
+ cm.curOp.scrollTop = y
+ }
+ }
+
+ function scrollToRange(cm, range) {
+ resolveScrollToPos(cm)
+ cm.curOp.scrollToPos = range
+ }
+
+ // When an operation has its scrollToPos property set, and another
+ // scroll action is applied before the end of the operation, this
+ // 'simulates' scrolling that position into view in a cheap way, so
+ // that the effect of intermediate scroll commands is not ignored.
+ function resolveScrollToPos(cm) {
+ var range = cm.curOp.scrollToPos
+ if (range) {
+ cm.curOp.scrollToPos = null
+ var from = estimateCoords(cm, range.from),
+ to = estimateCoords(cm, range.to)
+ scrollToCoordsRange(cm, from, to, range.margin)
+ }
+ }
+
+ function scrollToCoordsRange(cm, from, to, margin) {
+ var sPos = calculateScrollPos(cm, {
+ left: Math.min(from.left, to.left),
+ top: Math.min(from.top, to.top) - margin,
+ right: Math.max(from.right, to.right),
+ bottom: Math.max(from.bottom, to.bottom) + margin
+ })
+ scrollToCoords(cm, sPos.scrollLeft, sPos.scrollTop)
+ }
+
+ // Sync the scrollable area and scrollbars, ensure the viewport
+ // covers the visible area.
+ function updateScrollTop(cm, val) {
+ if (Math.abs(cm.doc.scrollTop - val) < 2) {
+ return
+ }
+ if (!gecko) {
+ updateDisplaySimple(cm, { top: val })
+ }
+ setScrollTop(cm, val, true)
+ if (gecko) {
+ updateDisplaySimple(cm)
+ }
+ startWorker(cm, 100)
+ }
+
+ function setScrollTop(cm, val, forceScroll) {
+ val = Math.max(
+ 0,
+ Math.min(cm.display.scroller.scrollHeight - cm.display.scroller.clientHeight, val)
+ )
+ if (cm.display.scroller.scrollTop == val && !forceScroll) {
+ return
+ }
+ cm.doc.scrollTop = val
+ cm.display.scrollbars.setScrollTop(val)
+ if (cm.display.scroller.scrollTop != val) {
+ cm.display.scroller.scrollTop = val
+ }
+ }
+
+ // Sync scroller and scrollbar, ensure the gutter elements are
+ // aligned.
+ function setScrollLeft(cm, val, isScroller, forceScroll) {
+ val = Math.max(
+ 0,
+ Math.min(val, cm.display.scroller.scrollWidth - cm.display.scroller.clientWidth)
+ )
+ if (
+ (isScroller ? val == cm.doc.scrollLeft : Math.abs(cm.doc.scrollLeft - val) < 2) &&
+ !forceScroll
+ ) {
+ return
+ }
+ cm.doc.scrollLeft = val
+ alignHorizontally(cm)
+ if (cm.display.scroller.scrollLeft != val) {
+ cm.display.scroller.scrollLeft = val
+ }
+ cm.display.scrollbars.setScrollLeft(val)
+ }
+
+ // SCROLLBARS
+
+ // Prepare DOM reads needed to update the scrollbars. Done in one
+ // shot to minimize update/measure roundtrips.
+ function measureForScrollbars(cm) {
+ var d = cm.display,
+ gutterW = d.gutters.offsetWidth
+ var docH = Math.round(cm.doc.height + paddingVert(cm.display))
+ return {
+ clientHeight: d.scroller.clientHeight,
+ viewHeight: d.wrapper.clientHeight,
+ scrollWidth: d.scroller.scrollWidth,
+ clientWidth: d.scroller.clientWidth,
+ viewWidth: d.wrapper.clientWidth,
+ barLeft: cm.options.fixedGutter ? gutterW : 0,
+ docHeight: docH,
+ scrollHeight: docH + scrollGap(cm) + d.barHeight,
+ nativeBarWidth: d.nativeBarWidth,
+ gutterWidth: gutterW
+ }
+ }
+
+ var NativeScrollbars = function(place, scroll, cm) {
+ this.cm = cm
+ var vert = (this.vert = elt(
+ "div",
+ [elt("div", null, null, "min-width: 1px")],
+ "CodeMirror-vscrollbar"
+ ))
+ var horiz = (this.horiz = elt(
+ "div",
+ [elt("div", null, null, "height: 100%; min-height: 1px")],
+ "CodeMirror-hscrollbar"
+ ))
+ vert.tabIndex = horiz.tabIndex = -1
+ place(vert)
+ place(horiz)
+
+ on(vert, "scroll", function() {
+ if (vert.clientHeight) {
+ scroll(vert.scrollTop, "vertical")
+ }
+ })
+ on(horiz, "scroll", function() {
+ if (horiz.clientWidth) {
+ scroll(horiz.scrollLeft, "horizontal")
+ }
+ })
+
+ this.checkedZeroWidth = false
+ // Need to set a minimum width to see the scrollbar on IE7 (but must not set it on IE8).
+ if (ie && ie_version < 8) {
+ this.horiz.style.minHeight = this.vert.style.minWidth = "18px"
+ }
+ }
+
+ NativeScrollbars.prototype.update = function(measure) {
+ var needsH = measure.scrollWidth > measure.clientWidth + 1
+ var needsV = measure.scrollHeight > measure.clientHeight + 1
+ var sWidth = measure.nativeBarWidth
+
+ if (needsV) {
+ this.vert.style.display = "block"
+ this.vert.style.bottom = needsH ? sWidth + "px" : "0"
+ var totalHeight = measure.viewHeight - (needsH ? sWidth : 0)
+ // A bug in IE8 can cause this value to be negative, so guard it.
+ this.vert.firstChild.style.height =
+ Math.max(0, measure.scrollHeight - measure.clientHeight + totalHeight) + "px"
+ } else {
+ this.vert.style.display = ""
+ this.vert.firstChild.style.height = "0"
+ }
+
+ if (needsH) {
+ this.horiz.style.display = "block"
+ this.horiz.style.right = needsV ? sWidth + "px" : "0"
+ this.horiz.style.left = measure.barLeft + "px"
+ var totalWidth = measure.viewWidth - measure.barLeft - (needsV ? sWidth : 0)
+ this.horiz.firstChild.style.width =
+ Math.max(0, measure.scrollWidth - measure.clientWidth + totalWidth) + "px"
+ } else {
+ this.horiz.style.display = ""
+ this.horiz.firstChild.style.width = "0"
+ }
+
+ if (!this.checkedZeroWidth && measure.clientHeight > 0) {
+ if (sWidth == 0) {
+ this.zeroWidthHack()
+ }
+ this.checkedZeroWidth = true
+ }
+
+ return { right: needsV ? sWidth : 0, bottom: needsH ? sWidth : 0 }
+ }
+
+ NativeScrollbars.prototype.setScrollLeft = function(pos) {
+ if (this.horiz.scrollLeft != pos) {
+ this.horiz.scrollLeft = pos
+ }
+ if (this.disableHoriz) {
+ this.enableZeroWidthBar(this.horiz, this.disableHoriz, "horiz")
+ }
+ }
+
+ NativeScrollbars.prototype.setScrollTop = function(pos) {
+ if (this.vert.scrollTop != pos) {
+ this.vert.scrollTop = pos
+ }
+ if (this.disableVert) {
+ this.enableZeroWidthBar(this.vert, this.disableVert, "vert")
+ }
+ }
+
+ NativeScrollbars.prototype.zeroWidthHack = function() {
+ var w = mac && !mac_geMountainLion ? "12px" : "18px"
+ this.horiz.style.height = this.vert.style.width = w
+ this.horiz.style.pointerEvents = this.vert.style.pointerEvents = "none"
+ this.disableHoriz = new Delayed()
+ this.disableVert = new Delayed()
+ }
+
+ NativeScrollbars.prototype.enableZeroWidthBar = function(bar, delay, type) {
+ bar.style.pointerEvents = "auto"
+ function maybeDisable() {
+ // To find out whether the scrollbar is still visible, we
+ // check whether the element under the pixel in the bottom
+ // right corner of the scrollbar box is the scrollbar box
+ // itself (when the bar is still visible) or its filler child
+ // (when the bar is hidden). If it is still visible, we keep
+ // it enabled, if it's hidden, we disable pointer events.
+ var box = bar.getBoundingClientRect()
+ var elt =
+ type == "vert"
+ ? document.elementFromPoint(box.right - 1, (box.top + box.bottom) / 2)
+ : document.elementFromPoint((box.right + box.left) / 2, box.bottom - 1)
+ if (elt != bar) {
+ bar.style.pointerEvents = "none"
+ } else {
+ delay.set(1000, maybeDisable)
+ }
+ }
+ delay.set(1000, maybeDisable)
+ }
+
+ NativeScrollbars.prototype.clear = function() {
+ var parent = this.horiz.parentNode
+ parent.removeChild(this.horiz)
+ parent.removeChild(this.vert)
+ }
+
+ var NullScrollbars = function() {}
+
+ NullScrollbars.prototype.update = function() {
+ return { bottom: 0, right: 0 }
+ }
+ NullScrollbars.prototype.setScrollLeft = function() {}
+ NullScrollbars.prototype.setScrollTop = function() {}
+ NullScrollbars.prototype.clear = function() {}
+
+ function updateScrollbars(cm, measure) {
+ if (!measure) {
+ measure = measureForScrollbars(cm)
+ }
+ var startWidth = cm.display.barWidth,
+ startHeight = cm.display.barHeight
+ updateScrollbarsInner(cm, measure)
+ for (
+ var i = 0;
+ (i < 4 && startWidth != cm.display.barWidth) || startHeight != cm.display.barHeight;
+ i++
+ ) {
+ if (startWidth != cm.display.barWidth && cm.options.lineWrapping) {
+ updateHeightsInViewport(cm)
+ }
+ updateScrollbarsInner(cm, measureForScrollbars(cm))
+ startWidth = cm.display.barWidth
+ startHeight = cm.display.barHeight
+ }
+ }
+
+ // Re-synchronize the fake scrollbars with the actual size of the
+ // content.
+ function updateScrollbarsInner(cm, measure) {
+ var d = cm.display
+ var sizes = d.scrollbars.update(measure)
+
+ d.sizer.style.paddingRight = (d.barWidth = sizes.right) + "px"
+ d.sizer.style.paddingBottom = (d.barHeight = sizes.bottom) + "px"
+ d.heightForcer.style.borderBottom = sizes.bottom + "px solid transparent"
+
+ if (sizes.right && sizes.bottom) {
+ d.scrollbarFiller.style.display = "block"
+ d.scrollbarFiller.style.height = sizes.bottom + "px"
+ d.scrollbarFiller.style.width = sizes.right + "px"
+ } else {
+ d.scrollbarFiller.style.display = ""
+ }
+ if (sizes.bottom && cm.options.coverGutterNextToScrollbar && cm.options.fixedGutter) {
+ d.gutterFiller.style.display = "block"
+ d.gutterFiller.style.height = sizes.bottom + "px"
+ d.gutterFiller.style.width = measure.gutterWidth + "px"
+ } else {
+ d.gutterFiller.style.display = ""
+ }
+ }
+
+ var scrollbarModel = { native: NativeScrollbars, null: NullScrollbars }
+
+ function initScrollbars(cm) {
+ if (cm.display.scrollbars) {
+ cm.display.scrollbars.clear()
+ if (cm.display.scrollbars.addClass) {
+ rmClass(cm.display.wrapper, cm.display.scrollbars.addClass)
+ }
+ }
+
+ cm.display.scrollbars = new scrollbarModel[cm.options.scrollbarStyle](
+ function(node) {
+ cm.display.wrapper.insertBefore(node, cm.display.scrollbarFiller)
+ // Prevent clicks in the scrollbars from killing focus
+ on(node, "mousedown", function() {
+ if (cm.state.focused) {
+ setTimeout(function() {
+ return cm.display.input.focus()
+ }, 0)
+ }
+ })
+ node.setAttribute("cm-not-content", "true")
+ },
+ function(pos, axis) {
+ if (axis == "horizontal") {
+ setScrollLeft(cm, pos)
+ } else {
+ updateScrollTop(cm, pos)
+ }
+ },
+ cm
+ )
+ if (cm.display.scrollbars.addClass) {
+ addClass(cm.display.wrapper, cm.display.scrollbars.addClass)
+ }
+ }
+
+ // Operations are used to wrap a series of changes to the editor
+ // state in such a way that each change won't have to update the
+ // cursor and display (which would be awkward, slow, and
+ // error-prone). Instead, display updates are batched and then all
+ // combined and executed at once.
+
+ var nextOpId = 0
+ // Start a new operation.
+ function startOperation(cm) {
+ cm.curOp = {
+ cm: cm,
+ viewChanged: false, // Flag that indicates that lines might need to be redrawn
+ startHeight: cm.doc.height, // Used to detect need to update scrollbar
+ forceUpdate: false, // Used to force a redraw
+ updateInput: 0, // Whether to reset the input textarea
+ typing: false, // Whether this reset should be careful to leave existing text (for compositing)
+ changeObjs: null, // Accumulated changes, for firing change events
+ cursorActivityHandlers: null, // Set of handlers to fire cursorActivity on
+ cursorActivityCalled: 0, // Tracks which cursorActivity handlers have been called already
+ selectionChanged: false, // Whether the selection needs to be redrawn
+ updateMaxLine: false, // Set when the widest line needs to be determined anew
+ scrollLeft: null,
+ scrollTop: null, // Intermediate scroll position, not pushed to DOM yet
+ scrollToPos: null, // Used to scroll to a specific position
+ focus: false,
+ id: ++nextOpId // Unique ID
+ }
+ pushOperation(cm.curOp)
+ }
+
+ // Finish an operation, updating the display and signalling delayed events
+ function endOperation(cm) {
+ var op = cm.curOp
+ if (op) {
+ finishOperation(op, function(group) {
+ for (var i = 0; i < group.ops.length; i++) {
+ group.ops[i].cm.curOp = null
+ }
+ endOperations(group)
+ })
+ }
+ }
+
+ // The DOM updates done when an operation finishes are batched so
+ // that the minimum number of relayouts are required.
+ function endOperations(group) {
+ var ops = group.ops
+ for (
+ var i = 0;
+ i < ops.length;
+ i++ // Read DOM
+ ) {
+ endOperation_R1(ops[i])
+ }
+ for (
+ var i$1 = 0;
+ i$1 < ops.length;
+ i$1++ // Write DOM (maybe)
+ ) {
+ endOperation_W1(ops[i$1])
+ }
+ for (
+ var i$2 = 0;
+ i$2 < ops.length;
+ i$2++ // Read DOM
+ ) {
+ endOperation_R2(ops[i$2])
+ }
+ for (
+ var i$3 = 0;
+ i$3 < ops.length;
+ i$3++ // Write DOM (maybe)
+ ) {
+ endOperation_W2(ops[i$3])
+ }
+ for (
+ var i$4 = 0;
+ i$4 < ops.length;
+ i$4++ // Read DOM
+ ) {
+ endOperation_finish(ops[i$4])
+ }
+ }
+
+ function endOperation_R1(op) {
+ var cm = op.cm,
+ display = cm.display
+ maybeClipScrollbars(cm)
+ if (op.updateMaxLine) {
+ findMaxLine(cm)
+ }
+
+ op.mustUpdate =
+ op.viewChanged ||
+ op.forceUpdate ||
+ op.scrollTop != null ||
+ (op.scrollToPos &&
+ (op.scrollToPos.from.line < display.viewFrom ||
+ op.scrollToPos.to.line >= display.viewTo)) ||
+ (display.maxLineChanged && cm.options.lineWrapping)
+ op.update =
+ op.mustUpdate &&
+ new DisplayUpdate(
+ cm,
+ op.mustUpdate && { top: op.scrollTop, ensure: op.scrollToPos },
+ op.forceUpdate
+ )
+ }
+
+ function endOperation_W1(op) {
+ op.updatedDisplay = op.mustUpdate && updateDisplayIfNeeded(op.cm, op.update)
+ }
+
+ function endOperation_R2(op) {
+ var cm = op.cm,
+ display = cm.display
+ if (op.updatedDisplay) {
+ updateHeightsInViewport(cm)
+ }
+
+ op.barMeasure = measureForScrollbars(cm)
+
+ // If the max line changed since it was last measured, measure it,
+ // and ensure the document's width matches it.
+ // updateDisplay_W2 will use these properties to do the actual resizing
+ if (display.maxLineChanged && !cm.options.lineWrapping) {
+ op.adjustWidthTo =
+ measureChar(cm, display.maxLine, display.maxLine.text.length).left + 3
+ cm.display.sizerWidth = op.adjustWidthTo
+ op.barMeasure.scrollWidth = Math.max(
+ display.scroller.clientWidth,
+ display.sizer.offsetLeft + op.adjustWidthTo + scrollGap(cm) + cm.display.barWidth
+ )
+ op.maxScrollLeft = Math.max(
+ 0,
+ display.sizer.offsetLeft + op.adjustWidthTo - displayWidth(cm)
+ )
+ }
+
+ if (op.updatedDisplay || op.selectionChanged) {
+ op.preparedSelection = display.input.prepareSelection()
+ }
+ }
+
+ function endOperation_W2(op) {
+ var cm = op.cm
+
+ if (op.adjustWidthTo != null) {
+ cm.display.sizer.style.minWidth = op.adjustWidthTo + "px"
+ if (op.maxScrollLeft < cm.doc.scrollLeft) {
+ setScrollLeft(cm, Math.min(cm.display.scroller.scrollLeft, op.maxScrollLeft), true)
+ }
+ cm.display.maxLineChanged = false
+ }
+
+ var takeFocus = op.focus && op.focus == activeElt()
+ if (op.preparedSelection) {
+ cm.display.input.showSelection(op.preparedSelection, takeFocus)
+ }
+ if (op.updatedDisplay || op.startHeight != cm.doc.height) {
+ updateScrollbars(cm, op.barMeasure)
+ }
+ if (op.updatedDisplay) {
+ setDocumentHeight(cm, op.barMeasure)
+ }
+
+ if (op.selectionChanged) {
+ restartBlink(cm)
+ }
+
+ if (cm.state.focused && op.updateInput) {
+ cm.display.input.reset(op.typing)
+ }
+ if (takeFocus) {
+ ensureFocus(op.cm)
+ }
+ }
+
+ function endOperation_finish(op) {
+ var cm = op.cm,
+ display = cm.display,
+ doc = cm.doc
+
+ if (op.updatedDisplay) {
+ postUpdateDisplay(cm, op.update)
+ }
+
+ // Abort mouse wheel delta measurement, when scrolling explicitly
+ if (
+ display.wheelStartX != null &&
+ (op.scrollTop != null || op.scrollLeft != null || op.scrollToPos)
+ ) {
+ display.wheelStartX = display.wheelStartY = null
+ }
+
+ // Propagate the scroll position to the actual DOM scroller
+ if (op.scrollTop != null) {
+ setScrollTop(cm, op.scrollTop, op.forceScroll)
+ }
+
+ if (op.scrollLeft != null) {
+ setScrollLeft(cm, op.scrollLeft, true, true)
+ }
+ // If we need to scroll a specific position into view, do so.
+ if (op.scrollToPos) {
+ var rect = scrollPosIntoView(
+ cm,
+ clipPos(doc, op.scrollToPos.from),
+ clipPos(doc, op.scrollToPos.to),
+ op.scrollToPos.margin
+ )
+ maybeScrollWindow(cm, rect)
+ }
+
+ // Fire events for markers that are hidden/unidden by editing or
+ // undoing
+ var hidden = op.maybeHiddenMarkers,
+ unhidden = op.maybeUnhiddenMarkers
+ if (hidden) {
+ for (var i = 0; i < hidden.length; ++i) {
+ if (!hidden[i].lines.length) {
+ signal(hidden[i], "hide")
+ }
+ }
+ }
+ if (unhidden) {
+ for (var i$1 = 0; i$1 < unhidden.length; ++i$1) {
+ if (unhidden[i$1].lines.length) {
+ signal(unhidden[i$1], "unhide")
+ }
+ }
+ }
+
+ if (display.wrapper.offsetHeight) {
+ doc.scrollTop = cm.display.scroller.scrollTop
+ }
+
+ // Fire change events, and delayed event handlers
+ if (op.changeObjs) {
+ signal(cm, "changes", cm, op.changeObjs)
+ }
+ if (op.update) {
+ op.update.finish()
+ }
+ }
+
+ // Run the given function in an operation
+ function runInOp(cm, f) {
+ if (cm.curOp) {
+ return f()
+ }
+ startOperation(cm)
+ try {
+ return f()
+ } finally {
+ endOperation(cm)
+ }
+ }
+ // Wraps a function in an operation. Returns the wrapped function.
+ function operation(cm, f) {
+ return function() {
+ if (cm.curOp) {
+ return f.apply(cm, arguments)
+ }
+ startOperation(cm)
+ try {
+ return f.apply(cm, arguments)
+ } finally {
+ endOperation(cm)
+ }
+ }
+ }
+ // Used to add methods to editor and doc instances, wrapping them in
+ // operations.
+ function methodOp(f) {
+ return function() {
+ if (this.curOp) {
+ return f.apply(this, arguments)
+ }
+ startOperation(this)
+ try {
+ return f.apply(this, arguments)
+ } finally {
+ endOperation(this)
+ }
+ }
+ }
+ function docMethodOp(f) {
+ return function() {
+ var cm = this.cm
+ if (!cm || cm.curOp) {
+ return f.apply(this, arguments)
+ }
+ startOperation(cm)
+ try {
+ return f.apply(this, arguments)
+ } finally {
+ endOperation(cm)
+ }
+ }
+ }
+
+ // HIGHLIGHT WORKER
+
+ function startWorker(cm, time) {
+ if (cm.doc.highlightFrontier < cm.display.viewTo) {
+ cm.state.highlight.set(time, bind(highlightWorker, cm))
+ }
+ }
+
+ function highlightWorker(cm) {
+ var doc = cm.doc
+ if (doc.highlightFrontier >= cm.display.viewTo) {
+ return
+ }
+ var end = +new Date() + cm.options.workTime
+ var context = getContextBefore(cm, doc.highlightFrontier)
+ var changedLines = []
+
+ doc.iter(context.line, Math.min(doc.first + doc.size, cm.display.viewTo + 500), function(
+ line
+ ) {
+ if (context.line >= cm.display.viewFrom) {
+ // Visible
+ var oldStyles = line.styles
+ var resetState =
+ line.text.length > cm.options.maxHighlightLength
+ ? copyState(doc.mode, context.state)
+ : null
+ var highlighted = highlightLine(cm, line, context, true)
+ if (resetState) {
+ context.state = resetState
+ }
+ line.styles = highlighted.styles
+ var oldCls = line.styleClasses,
+ newCls = highlighted.classes
+ if (newCls) {
+ line.styleClasses = newCls
+ } else if (oldCls) {
+ line.styleClasses = null
+ }
+ var ischange =
+ !oldStyles ||
+ oldStyles.length != line.styles.length ||
+ (oldCls != newCls &&
+ (!oldCls ||
+ !newCls ||
+ oldCls.bgClass != newCls.bgClass ||
+ oldCls.textClass != newCls.textClass))
+ for (var i = 0; !ischange && i < oldStyles.length; ++i) {
+ ischange = oldStyles[i] != line.styles[i]
+ }
+ if (ischange) {
+ changedLines.push(context.line)
+ }
+ line.stateAfter = context.save()
+ context.nextLine()
+ } else {
+ if (line.text.length <= cm.options.maxHighlightLength) {
+ processLine(cm, line.text, context)
+ }
+ line.stateAfter = context.line % 5 == 0 ? context.save() : null
+ context.nextLine()
+ }
+ if (+new Date() > end) {
+ startWorker(cm, cm.options.workDelay)
+ return true
+ }
+ })
+ doc.highlightFrontier = context.line
+ doc.modeFrontier = Math.max(doc.modeFrontier, context.line)
+ if (changedLines.length) {
+ runInOp(cm, function() {
+ for (var i = 0; i < changedLines.length; i++) {
+ regLineChange(cm, changedLines[i], "text")
+ }
+ })
+ }
+ }
+
+ // DISPLAY DRAWING
+
+ var DisplayUpdate = function(cm, viewport, force) {
+ var display = cm.display
+
+ this.viewport = viewport
+ // Store some values that we'll need later (but don't want to force a relayout for)
+ this.visible = visibleLines(display, cm.doc, viewport)
+ this.editorIsHidden = !display.wrapper.offsetWidth
+ this.wrapperHeight = display.wrapper.clientHeight
+ this.wrapperWidth = display.wrapper.clientWidth
+ this.oldDisplayWidth = displayWidth(cm)
+ this.force = force
+ this.dims = getDimensions(cm)
+ this.events = []
+ }
+
+ DisplayUpdate.prototype.signal = function(emitter, type) {
+ if (hasHandler(emitter, type)) {
+ this.events.push(arguments)
+ }
+ }
+ DisplayUpdate.prototype.finish = function() {
+ for (var i = 0; i < this.events.length; i++) {
+ signal.apply(null, this.events[i])
+ }
+ }
+
+ function maybeClipScrollbars(cm) {
+ var display = cm.display
+ if (!display.scrollbarsClipped && display.scroller.offsetWidth) {
+ display.nativeBarWidth = display.scroller.offsetWidth - display.scroller.clientWidth
+ display.heightForcer.style.height = scrollGap(cm) + "px"
+ display.sizer.style.marginBottom = -display.nativeBarWidth + "px"
+ display.sizer.style.borderRightWidth = scrollGap(cm) + "px"
+ display.scrollbarsClipped = true
+ }
+ }
+
+ function selectionSnapshot(cm) {
+ if (cm.hasFocus()) {
+ return null
+ }
+ var active = activeElt()
+ if (!active || !contains(cm.display.lineDiv, active)) {
+ return null
+ }
+ var result = { activeElt: active }
+ if (window.getSelection) {
+ var sel = window.getSelection()
+ if (sel.anchorNode && sel.extend && contains(cm.display.lineDiv, sel.anchorNode)) {
+ result.anchorNode = sel.anchorNode
+ result.anchorOffset = sel.anchorOffset
+ result.focusNode = sel.focusNode
+ result.focusOffset = sel.focusOffset
+ }
+ }
+ return result
+ }
+
+ function restoreSelection(snapshot) {
+ if (!snapshot || !snapshot.activeElt || snapshot.activeElt == activeElt()) {
+ return
+ }
+ snapshot.activeElt.focus()
+ if (
+ snapshot.anchorNode &&
+ contains(document.body, snapshot.anchorNode) &&
+ contains(document.body, snapshot.focusNode)
+ ) {
+ var sel = window.getSelection(),
+ range = document.createRange()
+ range.setEnd(snapshot.anchorNode, snapshot.anchorOffset)
+ range.collapse(false)
+ sel.removeAllRanges()
+ sel.addRange(range)
+ sel.extend(snapshot.focusNode, snapshot.focusOffset)
+ }
+ }
+
+ // Does the actual updating of the line display. Bails out
+ // (returning false) when there is nothing to be done and forced is
+ // false.
+ function updateDisplayIfNeeded(cm, update) {
+ var display = cm.display,
+ doc = cm.doc
+
+ if (update.editorIsHidden) {
+ resetView(cm)
+ return false
+ }
+
+ // Bail out if the visible area is already rendered and nothing changed.
+ if (
+ !update.force &&
+ update.visible.from >= display.viewFrom &&
+ update.visible.to <= display.viewTo &&
+ (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo) &&
+ display.renderedView == display.view &&
+ countDirtyView(cm) == 0
+ ) {
+ return false
+ }
+
+ if (maybeUpdateLineNumberWidth(cm)) {
+ resetView(cm)
+ update.dims = getDimensions(cm)
+ }
+
+ // Compute a suitable new viewport (from & to)
+ var end = doc.first + doc.size
+ var from = Math.max(update.visible.from - cm.options.viewportMargin, doc.first)
+ var to = Math.min(end, update.visible.to + cm.options.viewportMargin)
+ if (display.viewFrom < from && from - display.viewFrom < 20) {
+ from = Math.max(doc.first, display.viewFrom)
+ }
+ if (display.viewTo > to && display.viewTo - to < 20) {
+ to = Math.min(end, display.viewTo)
+ }
+ if (sawCollapsedSpans) {
+ from = visualLineNo(cm.doc, from)
+ to = visualLineEndNo(cm.doc, to)
+ }
+
+ var different =
+ from != display.viewFrom ||
+ to != display.viewTo ||
+ display.lastWrapHeight != update.wrapperHeight ||
+ display.lastWrapWidth != update.wrapperWidth
+ adjustView(cm, from, to)
+
+ display.viewOffset = heightAtLine(getLine(cm.doc, display.viewFrom))
+ // Position the mover div to align with the current scroll position
+ cm.display.mover.style.top = display.viewOffset + "px"
+
+ var toUpdate = countDirtyView(cm)
+ if (
+ !different &&
+ toUpdate == 0 &&
+ !update.force &&
+ display.renderedView == display.view &&
+ (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo)
+ ) {
+ return false
+ }
+
+ // For big changes, we hide the enclosing element during the
+ // update, since that speeds up the operations on most browsers.
+ var selSnapshot = selectionSnapshot(cm)
+ if (toUpdate > 4) {
+ display.lineDiv.style.display = "none"
+ }
+ patchDisplay(cm, display.updateLineNumbers, update.dims)
+ if (toUpdate > 4) {
+ display.lineDiv.style.display = ""
+ }
+ display.renderedView = display.view
+ // There might have been a widget with a focused element that got
+ // hidden or updated, if so re-focus it.
+ restoreSelection(selSnapshot)
+
+ // Prevent selection and cursors from interfering with the scroll
+ // width and height.
+ removeChildren(display.cursorDiv)
+ removeChildren(display.selectionDiv)
+ display.gutters.style.height = display.sizer.style.minHeight = 0
+
+ if (different) {
+ display.lastWrapHeight = update.wrapperHeight
+ display.lastWrapWidth = update.wrapperWidth
+ startWorker(cm, 400)
+ }
+
+ display.updateLineNumbers = null
+
+ return true
+ }
+
+ function postUpdateDisplay(cm, update) {
+ var viewport = update.viewport
+
+ for (var first = true; ; first = false) {
+ if (!first || !cm.options.lineWrapping || update.oldDisplayWidth == displayWidth(cm)) {
+ // Clip forced viewport to actual scrollable area.
+ if (viewport && viewport.top != null) {
+ viewport = {
+ top: Math.min(
+ cm.doc.height + paddingVert(cm.display) - displayHeight(cm),
+ viewport.top
+ )
+ }
+ }
+ // Updated line heights might result in the drawn area not
+ // actually covering the viewport. Keep looping until it does.
+ update.visible = visibleLines(cm.display, cm.doc, viewport)
+ if (
+ update.visible.from >= cm.display.viewFrom &&
+ update.visible.to <= cm.display.viewTo
+ ) {
+ break
+ }
+ }
+ if (!updateDisplayIfNeeded(cm, update)) {
+ break
+ }
+ updateHeightsInViewport(cm)
+ var barMeasure = measureForScrollbars(cm)
+ updateSelection(cm)
+ updateScrollbars(cm, barMeasure)
+ setDocumentHeight(cm, barMeasure)
+ update.force = false
+ }
+
+ update.signal(cm, "update", cm)
+ if (
+ cm.display.viewFrom != cm.display.reportedViewFrom ||
+ cm.display.viewTo != cm.display.reportedViewTo
+ ) {
+ update.signal(cm, "viewportChange", cm, cm.display.viewFrom, cm.display.viewTo)
+ cm.display.reportedViewFrom = cm.display.viewFrom
+ cm.display.reportedViewTo = cm.display.viewTo
+ }
+ }
+
+ function updateDisplaySimple(cm, viewport) {
+ var update = new DisplayUpdate(cm, viewport)
+ if (updateDisplayIfNeeded(cm, update)) {
+ updateHeightsInViewport(cm)
+ postUpdateDisplay(cm, update)
+ var barMeasure = measureForScrollbars(cm)
+ updateSelection(cm)
+ updateScrollbars(cm, barMeasure)
+ setDocumentHeight(cm, barMeasure)
+ update.finish()
+ }
+ }
+
+ // Sync the actual display DOM structure with display.view, removing
+ // nodes for lines that are no longer in view, and creating the ones
+ // that are not there yet, and updating the ones that are out of
+ // date.
+ function patchDisplay(cm, updateNumbersFrom, dims) {
+ var display = cm.display,
+ lineNumbers = cm.options.lineNumbers
+ var container = display.lineDiv,
+ cur = container.firstChild
+
+ function rm(node) {
+ var next = node.nextSibling
+ // Works around a throw-scroll bug in OS X Webkit
+ if (webkit && mac && cm.display.currentWheelTarget == node) {
+ node.style.display = "none"
+ } else {
+ node.parentNode.removeChild(node)
+ }
+ return next
+ }
+
+ var view = display.view,
+ lineN = display.viewFrom
+ // Loop over the elements in the view, syncing cur (the DOM nodes
+ // in display.lineDiv) with the view as we go.
+ for (var i = 0; i < view.length; i++) {
+ var lineView = view[i]
+ if (lineView.hidden);
+ else if (!lineView.node || lineView.node.parentNode != container) {
+ // Not drawn yet
+ var node = buildLineElement(cm, lineView, lineN, dims)
+ container.insertBefore(node, cur)
+ } else {
+ // Already drawn
+ while (cur != lineView.node) {
+ cur = rm(cur)
+ }
+ var updateNumber =
+ lineNumbers &&
+ updateNumbersFrom != null &&
+ updateNumbersFrom <= lineN &&
+ lineView.lineNumber
+ if (lineView.changes) {
+ if (indexOf(lineView.changes, "gutter") > -1) {
+ updateNumber = false
+ }
+ updateLineForChanges(cm, lineView, lineN, dims)
+ }
+ if (updateNumber) {
+ removeChildren(lineView.lineNumber)
+ lineView.lineNumber.appendChild(
+ document.createTextNode(lineNumberFor(cm.options, lineN))
+ )
+ }
+ cur = lineView.node.nextSibling
+ }
+ lineN += lineView.size
+ }
+ while (cur) {
+ cur = rm(cur)
+ }
+ }
+
+ function updateGutterSpace(display) {
+ var width = display.gutters.offsetWidth
+ display.sizer.style.marginLeft = width + "px"
+ }
+
+ function setDocumentHeight(cm, measure) {
+ cm.display.sizer.style.minHeight = measure.docHeight + "px"
+ cm.display.heightForcer.style.top = measure.docHeight + "px"
+ cm.display.gutters.style.height =
+ measure.docHeight + cm.display.barHeight + scrollGap(cm) + "px"
+ }
+
+ // Re-align line numbers and gutter marks to compensate for
+ // horizontal scrolling.
+ function alignHorizontally(cm) {
+ var display = cm.display,
+ view = display.view
+ if (!display.alignWidgets && (!display.gutters.firstChild || !cm.options.fixedGutter)) {
+ return
+ }
+ var comp = compensateForHScroll(display) - display.scroller.scrollLeft + cm.doc.scrollLeft
+ var gutterW = display.gutters.offsetWidth,
+ left = comp + "px"
+ for (var i = 0; i < view.length; i++) {
+ if (!view[i].hidden) {
+ if (cm.options.fixedGutter) {
+ if (view[i].gutter) {
+ view[i].gutter.style.left = left
+ }
+ if (view[i].gutterBackground) {
+ view[i].gutterBackground.style.left = left
+ }
+ }
+ var align = view[i].alignable
+ if (align) {
+ for (var j = 0; j < align.length; j++) {
+ align[j].style.left = left
+ }
+ }
+ }
+ }
+ if (cm.options.fixedGutter) {
+ display.gutters.style.left = comp + gutterW + "px"
+ }
+ }
+
+ // Used to ensure that the line number gutter is still the right
+ // size for the current document size. Returns true when an update
+ // is needed.
+ function maybeUpdateLineNumberWidth(cm) {
+ if (!cm.options.lineNumbers) {
+ return false
+ }
+ var doc = cm.doc,
+ last = lineNumberFor(cm.options, doc.first + doc.size - 1),
+ display = cm.display
+ if (last.length != display.lineNumChars) {
+ var test = display.measure.appendChild(
+ elt("div", [elt("div", last)], "CodeMirror-linenumber CodeMirror-gutter-elt")
+ )
+ var innerW = test.firstChild.offsetWidth,
+ padding = test.offsetWidth - innerW
+ display.lineGutter.style.width = ""
+ display.lineNumInnerWidth =
+ Math.max(innerW, display.lineGutter.offsetWidth - padding) + 1
+ display.lineNumWidth = display.lineNumInnerWidth + padding
+ display.lineNumChars = display.lineNumInnerWidth ? last.length : -1
+ display.lineGutter.style.width = display.lineNumWidth + "px"
+ updateGutterSpace(cm.display)
+ return true
+ }
+ return false
+ }
+
+ function getGutters(gutters, lineNumbers) {
+ var result = [],
+ sawLineNumbers = false
+ for (var i = 0; i < gutters.length; i++) {
+ var name = gutters[i],
+ style = null
+ if (typeof name != "string") {
+ style = name.style
+ name = name.className
+ }
+ if (name == "CodeMirror-linenumbers") {
+ if (!lineNumbers) {
+ continue
+ } else {
+ sawLineNumbers = true
+ }
+ }
+ result.push({ className: name, style: style })
+ }
+ if (lineNumbers && !sawLineNumbers) {
+ result.push({ className: "CodeMirror-linenumbers", style: null })
+ }
+ return result
+ }
+
+ // Rebuild the gutter elements, ensure the margin to the left of the
+ // code matches their width.
+ function renderGutters(display) {
+ var gutters = display.gutters,
+ specs = display.gutterSpecs
+ removeChildren(gutters)
+ display.lineGutter = null
+ for (var i = 0; i < specs.length; ++i) {
+ var ref = specs[i]
+ var className = ref.className
+ var style = ref.style
+ var gElt = gutters.appendChild(elt("div", null, "CodeMirror-gutter " + className))
+ if (style) {
+ gElt.style.cssText = style
+ }
+ if (className == "CodeMirror-linenumbers") {
+ display.lineGutter = gElt
+ gElt.style.width = (display.lineNumWidth || 1) + "px"
+ }
+ }
+ gutters.style.display = specs.length ? "" : "none"
+ updateGutterSpace(display)
+ }
+
+ function updateGutters(cm) {
+ renderGutters(cm.display)
+ regChange(cm)
+ alignHorizontally(cm)
+ }
+
+ // The display handles the DOM integration, both for input reading
+ // and content drawing. It holds references to DOM nodes and
+ // display-related state.
+
+ function Display(place, doc, input, options) {
+ var d = this
+ this.input = input
+
+ // Covers bottom-right square when both scrollbars are present.
+ d.scrollbarFiller = elt("div", null, "CodeMirror-scrollbar-filler")
+ d.scrollbarFiller.setAttribute("cm-not-content", "true")
+ // Covers bottom of gutter when coverGutterNextToScrollbar is on
+ // and h scrollbar is present.
+ d.gutterFiller = elt("div", null, "CodeMirror-gutter-filler")
+ d.gutterFiller.setAttribute("cm-not-content", "true")
+ // Will contain the actual code, positioned to cover the viewport.
+ d.lineDiv = eltP("div", null, "CodeMirror-code")
+ // Elements are added to these to represent selection and cursors.
+ d.selectionDiv = elt("div", null, null, "position: relative; z-index: 1")
+ d.cursorDiv = elt("div", null, "CodeMirror-cursors")
+ // A visibility: hidden element used to find the size of things.
+ d.measure = elt("div", null, "CodeMirror-measure")
+ // When lines outside of the viewport are measured, they are drawn in this.
+ d.lineMeasure = elt("div", null, "CodeMirror-measure")
+ // Wraps everything that needs to exist inside the vertically-padded coordinate system
+ d.lineSpace = eltP(
+ "div",
+ [d.measure, d.lineMeasure, d.selectionDiv, d.cursorDiv, d.lineDiv],
+ null,
+ "position: relative; outline: none"
+ )
+ var lines = eltP("div", [d.lineSpace], "CodeMirror-lines")
+ // Moved around its parent to cover visible view.
+ d.mover = elt("div", [lines], null, "position: relative")
+ // Set to the height of the document, allowing scrolling.
+ d.sizer = elt("div", [d.mover], "CodeMirror-sizer")
+ d.sizerWidth = null
+ // Behavior of elts with overflow: auto and padding is
+ // inconsistent across browsers. This is used to ensure the
+ // scrollable area is big enough.
+ d.heightForcer = elt(
+ "div",
+ null,
+ null,
+ "position: absolute; height: " + scrollerGap + "px; width: 1px;"
+ )
+ // Will contain the gutters, if any.
+ d.gutters = elt("div", null, "CodeMirror-gutters")
+ d.lineGutter = null
+ // Actual scrollable element.
+ d.scroller = elt("div", [d.sizer, d.heightForcer, d.gutters], "CodeMirror-scroll")
+ d.scroller.setAttribute("tabIndex", "-1")
+ // The element in which the editor lives.
+ d.wrapper = elt("div", [d.scrollbarFiller, d.gutterFiller, d.scroller], "CodeMirror")
+
+ // Work around IE7 z-index bug (not perfect, hence IE7 not really being supported)
+ if (ie && ie_version < 8) {
+ d.gutters.style.zIndex = -1
+ d.scroller.style.paddingRight = 0
+ }
+ if (!webkit && !(gecko && mobile)) {
+ d.scroller.draggable = true
+ }
+
+ if (place) {
+ if (place.appendChild) {
+ place.appendChild(d.wrapper)
+ } else {
+ place(d.wrapper)
+ }
+ }
+
+ // Current rendered range (may be bigger than the view window).
+ d.viewFrom = d.viewTo = doc.first
+ d.reportedViewFrom = d.reportedViewTo = doc.first
+ // Information about the rendered lines.
+ d.view = []
+ d.renderedView = null
+ // Holds info about a single rendered line when it was rendered
+ // for measurement, while not in view.
+ d.externalMeasured = null
+ // Empty space (in pixels) above the view
+ d.viewOffset = 0
+ d.lastWrapHeight = d.lastWrapWidth = 0
+ d.updateLineNumbers = null
+
+ d.nativeBarWidth = d.barHeight = d.barWidth = 0
+ d.scrollbarsClipped = false
+
+ // Used to only resize the line number gutter when necessary (when
+ // the amount of lines crosses a boundary that makes its width change)
+ d.lineNumWidth = d.lineNumInnerWidth = d.lineNumChars = null
+ // Set to true when a non-horizontal-scrolling line widget is
+ // added. As an optimization, line widget aligning is skipped when
+ // this is false.
+ d.alignWidgets = false
+
+ d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null
+
+ // Tracks the maximum line length so that the horizontal scrollbar
+ // can be kept static when scrolling.
+ d.maxLine = null
+ d.maxLineLength = 0
+ d.maxLineChanged = false
+
+ // Used for measuring wheel scrolling granularity
+ d.wheelDX = d.wheelDY = d.wheelStartX = d.wheelStartY = null
+
+ // True when shift is held down.
+ d.shift = false
+
+ // Used to track whether anything happened since the context menu
+ // was opened.
+ d.selForContextMenu = null
+
+ d.activeTouch = null
+
+ d.gutterSpecs = getGutters(options.gutters, options.lineNumbers)
+ renderGutters(d)
+
+ input.init(d)
+ }
+
+ // Since the delta values reported on mouse wheel events are
+ // unstandardized between browsers and even browser versions, and
+ // generally horribly unpredictable, this code starts by measuring
+ // the scroll effect that the first few mouse wheel events have,
+ // and, from that, detects the way it can convert deltas to pixel
+ // offsets afterwards.
+ //
+ // The reason we want to know the amount a wheel event will scroll
+ // is that it gives us a chance to update the display before the
+ // actual scrolling happens, reducing flickering.
+
+ var wheelSamples = 0,
+ wheelPixelsPerUnit = null
+ // Fill in a browser-detected starting value on browsers where we
+ // know one. These don't have to be accurate -- the result of them
+ // being wrong would just be a slight flicker on the first wheel
+ // scroll (if it is large enough).
+ if (ie) {
+ wheelPixelsPerUnit = -0.53
+ } else if (gecko) {
+ wheelPixelsPerUnit = 15
+ } else if (chrome) {
+ wheelPixelsPerUnit = -0.7
+ } else if (safari) {
+ wheelPixelsPerUnit = -1 / 3
+ }
+
+ function wheelEventDelta(e) {
+ var dx = e.wheelDeltaX,
+ dy = e.wheelDeltaY
+ if (dx == null && e.detail && e.axis == e.HORIZONTAL_AXIS) {
+ dx = e.detail
+ }
+ if (dy == null && e.detail && e.axis == e.VERTICAL_AXIS) {
+ dy = e.detail
+ } else if (dy == null) {
+ dy = e.wheelDelta
+ }
+ return { x: dx, y: dy }
+ }
+ function wheelEventPixels(e) {
+ var delta = wheelEventDelta(e)
+ delta.x *= wheelPixelsPerUnit
+ delta.y *= wheelPixelsPerUnit
+ return delta
+ }
+
+ function onScrollWheel(cm, e) {
+ var delta = wheelEventDelta(e),
+ dx = delta.x,
+ dy = delta.y
+
+ var display = cm.display,
+ scroll = display.scroller
+ // Quit if there's nothing to scroll here
+ var canScrollX = scroll.scrollWidth > scroll.clientWidth
+ var canScrollY = scroll.scrollHeight > scroll.clientHeight
+ if (!((dx && canScrollX) || (dy && canScrollY))) {
+ return
+ }
+
+ // Webkit browsers on OS X abort momentum scrolls when the target
+ // of the scroll event is removed from the scrollable element.
+ // This hack (see related code in patchDisplay) makes sure the
+ // element is kept around.
+ if (dy && mac && webkit) {
+ outer: for (
+ var cur = e.target, view = display.view;
+ cur != scroll;
+ cur = cur.parentNode
+ ) {
+ for (var i = 0; i < view.length; i++) {
+ if (view[i].node == cur) {
+ cm.display.currentWheelTarget = cur
+ break outer
+ }
+ }
+ }
+ }
+
+ // On some browsers, horizontal scrolling will cause redraws to
+ // happen before the gutter has been realigned, causing it to
+ // wriggle around in a most unseemly way. When we have an
+ // estimated pixels/delta value, we just handle horizontal
+ // scrolling entirely here. It'll be slightly off from native, but
+ // better than glitching out.
+ if (dx && !gecko && !presto && wheelPixelsPerUnit != null) {
+ if (dy && canScrollY) {
+ updateScrollTop(cm, Math.max(0, scroll.scrollTop + dy * wheelPixelsPerUnit))
+ }
+ setScrollLeft(cm, Math.max(0, scroll.scrollLeft + dx * wheelPixelsPerUnit))
+ // Only prevent default scrolling if vertical scrolling is
+ // actually possible. Otherwise, it causes vertical scroll
+ // jitter on OSX trackpads when deltaX is small and deltaY
+ // is large (issue #3579)
+ if (!dy || (dy && canScrollY)) {
+ e_preventDefault(e)
+ }
+ display.wheelStartX = null // Abort measurement, if in progress
+ return
+ }
+
+ // 'Project' the visible viewport to cover the area that is being
+ // scrolled into view (if we know enough to estimate it).
+ if (dy && wheelPixelsPerUnit != null) {
+ var pixels = dy * wheelPixelsPerUnit
+ var top = cm.doc.scrollTop,
+ bot = top + display.wrapper.clientHeight
+ if (pixels < 0) {
+ top = Math.max(0, top + pixels - 50)
+ } else {
+ bot = Math.min(cm.doc.height, bot + pixels + 50)
+ }
+ updateDisplaySimple(cm, { top: top, bottom: bot })
+ }
+
+ if (wheelSamples < 20) {
+ if (display.wheelStartX == null) {
+ display.wheelStartX = scroll.scrollLeft
+ display.wheelStartY = scroll.scrollTop
+ display.wheelDX = dx
+ display.wheelDY = dy
+ setTimeout(function() {
+ if (display.wheelStartX == null) {
+ return
+ }
+ var movedX = scroll.scrollLeft - display.wheelStartX
+ var movedY = scroll.scrollTop - display.wheelStartY
+ var sample =
+ (movedY && display.wheelDY && movedY / display.wheelDY) ||
+ (movedX && display.wheelDX && movedX / display.wheelDX)
+ display.wheelStartX = display.wheelStartY = null
+ if (!sample) {
+ return
+ }
+ wheelPixelsPerUnit =
+ (wheelPixelsPerUnit * wheelSamples + sample) / (wheelSamples + 1)
+ ++wheelSamples
+ }, 200)
+ } else {
+ display.wheelDX += dx
+ display.wheelDY += dy
+ }
+ }
+ }
+
+ // Selection objects are immutable. A new one is created every time
+ // the selection changes. A selection is one or more non-overlapping
+ // (and non-touching) ranges, sorted, and an integer that indicates
+ // which one is the primary selection (the one that's scrolled into
+ // view, that getCursor returns, etc).
+ var Selection = function(ranges, primIndex) {
+ this.ranges = ranges
+ this.primIndex = primIndex
+ }
+
+ Selection.prototype.primary = function() {
+ return this.ranges[this.primIndex]
+ }
+
+ Selection.prototype.equals = function(other) {
+ if (other == this) {
+ return true
+ }
+ if (other.primIndex != this.primIndex || other.ranges.length != this.ranges.length) {
+ return false
+ }
+ for (var i = 0; i < this.ranges.length; i++) {
+ var here = this.ranges[i],
+ there = other.ranges[i]
+ if (
+ !equalCursorPos(here.anchor, there.anchor) ||
+ !equalCursorPos(here.head, there.head)
+ ) {
+ return false
+ }
+ }
+ return true
+ }
+
+ Selection.prototype.deepCopy = function() {
+ var out = []
+ for (var i = 0; i < this.ranges.length; i++) {
+ out[i] = new Range(copyPos(this.ranges[i].anchor), copyPos(this.ranges[i].head))
+ }
+ return new Selection(out, this.primIndex)
+ }
+
+ Selection.prototype.somethingSelected = function() {
+ for (var i = 0; i < this.ranges.length; i++) {
+ if (!this.ranges[i].empty()) {
+ return true
+ }
+ }
+ return false
+ }
+
+ Selection.prototype.contains = function(pos, end) {
+ if (!end) {
+ end = pos
+ }
+ for (var i = 0; i < this.ranges.length; i++) {
+ var range = this.ranges[i]
+ if (cmp(end, range.from()) >= 0 && cmp(pos, range.to()) <= 0) {
+ return i
+ }
+ }
+ return -1
+ }
+
+ var Range = function(anchor, head) {
+ this.anchor = anchor
+ this.head = head
+ }
+
+ Range.prototype.from = function() {
+ return minPos(this.anchor, this.head)
+ }
+ Range.prototype.to = function() {
+ return maxPos(this.anchor, this.head)
+ }
+ Range.prototype.empty = function() {
+ return this.head.line == this.anchor.line && this.head.ch == this.anchor.ch
+ }
+
+ // Take an unsorted, potentially overlapping set of ranges, and
+ // build a selection out of it. 'Consumes' ranges array (modifying
+ // it).
+ function normalizeSelection(cm, ranges, primIndex) {
+ var mayTouch = cm && cm.options.selectionsMayTouch
+ var prim = ranges[primIndex]
+ ranges.sort(function(a, b) {
+ return cmp(a.from(), b.from())
+ })
+ primIndex = indexOf(ranges, prim)
+ for (var i = 1; i < ranges.length; i++) {
+ var cur = ranges[i],
+ prev = ranges[i - 1]
+ var diff = cmp(prev.to(), cur.from())
+ if (mayTouch && !cur.empty() ? diff > 0 : diff >= 0) {
+ var from = minPos(prev.from(), cur.from()),
+ to = maxPos(prev.to(), cur.to())
+ var inv = prev.empty() ? cur.from() == cur.head : prev.from() == prev.head
+ if (i <= primIndex) {
+ --primIndex
+ }
+ ranges.splice(--i, 2, new Range(inv ? to : from, inv ? from : to))
+ }
+ }
+ return new Selection(ranges, primIndex)
+ }
+
+ function simpleSelection(anchor, head) {
+ return new Selection([new Range(anchor, head || anchor)], 0)
+ }
+
+ // Compute the position of the end of a change (its 'to' property
+ // refers to the pre-change end).
+ function changeEnd(change) {
+ if (!change.text) {
+ return change.to
+ }
+ return Pos(
+ change.from.line + change.text.length - 1,
+ lst(change.text).length + (change.text.length == 1 ? change.from.ch : 0)
+ )
+ }
+
+ // Adjust a position to refer to the post-change position of the
+ // same text, or the end of the change if the change covers it.
+ function adjustForChange(pos, change) {
+ if (cmp(pos, change.from) < 0) {
+ return pos
+ }
+ if (cmp(pos, change.to) <= 0) {
+ return changeEnd(change)
+ }
+
+ var line = pos.line + change.text.length - (change.to.line - change.from.line) - 1,
+ ch = pos.ch
+ if (pos.line == change.to.line) {
+ ch += changeEnd(change).ch - change.to.ch
+ }
+ return Pos(line, ch)
+ }
+
+ function computeSelAfterChange(doc, change) {
+ var out = []
+ for (var i = 0; i < doc.sel.ranges.length; i++) {
+ var range = doc.sel.ranges[i]
+ out.push(
+ new Range(
+ adjustForChange(range.anchor, change),
+ adjustForChange(range.head, change)
+ )
+ )
+ }
+ return normalizeSelection(doc.cm, out, doc.sel.primIndex)
+ }
+
+ function offsetPos(pos, old, nw) {
+ if (pos.line == old.line) {
+ return Pos(nw.line, pos.ch - old.ch + nw.ch)
+ } else {
+ return Pos(nw.line + (pos.line - old.line), pos.ch)
+ }
+ }
+
+ // Used by replaceSelections to allow moving the selection to the
+ // start or around the replaced test. Hint may be "start" or "around".
+ function computeReplacedSel(doc, changes, hint) {
+ var out = []
+ var oldPrev = Pos(doc.first, 0),
+ newPrev = oldPrev
+ for (var i = 0; i < changes.length; i++) {
+ var change = changes[i]
+ var from = offsetPos(change.from, oldPrev, newPrev)
+ var to = offsetPos(changeEnd(change), oldPrev, newPrev)
+ oldPrev = change.to
+ newPrev = to
+ if (hint == "around") {
+ var range = doc.sel.ranges[i],
+ inv = cmp(range.head, range.anchor) < 0
+ out[i] = new Range(inv ? to : from, inv ? from : to)
+ } else {
+ out[i] = new Range(from, from)
+ }
+ }
+ return new Selection(out, doc.sel.primIndex)
+ }
+
+ // Used to get the editor into a consistent state again when options change.
+
+ function loadMode(cm) {
+ cm.doc.mode = getMode(cm.options, cm.doc.modeOption)
+ resetModeState(cm)
+ }
+
+ function resetModeState(cm) {
+ cm.doc.iter(function(line) {
+ if (line.stateAfter) {
+ line.stateAfter = null
+ }
+ if (line.styles) {
+ line.styles = null
+ }
+ })
+ cm.doc.modeFrontier = cm.doc.highlightFrontier = cm.doc.first
+ startWorker(cm, 100)
+ cm.state.modeGen++
+ if (cm.curOp) {
+ regChange(cm)
+ }
+ }
+
+ // DOCUMENT DATA STRUCTURE
+
+ // By default, updates that start and end at the beginning of a line
+ // are treated specially, in order to make the association of line
+ // widgets and marker elements with the text behave more intuitive.
+ function isWholeLineUpdate(doc, change) {
+ return (
+ change.from.ch == 0 &&
+ change.to.ch == 0 &&
+ lst(change.text) == "" &&
+ (!doc.cm || doc.cm.options.wholeLineUpdateBefore)
+ )
+ }
+
+ // Perform a change on the document data structure.
+ function updateDoc(doc, change, markedSpans, estimateHeight) {
+ function spansFor(n) {
+ return markedSpans ? markedSpans[n] : null
+ }
+ function update(line, text, spans) {
+ updateLine(line, text, spans, estimateHeight)
+ signalLater(line, "change", line, change)
+ }
+ function linesFor(start, end) {
+ var result = []
+ for (var i = start; i < end; ++i) {
+ result.push(new Line(text[i], spansFor(i), estimateHeight))
+ }
+ return result
+ }
+
+ var from = change.from,
+ to = change.to,
+ text = change.text
+ var firstLine = getLine(doc, from.line),
+ lastLine = getLine(doc, to.line)
+ var lastText = lst(text),
+ lastSpans = spansFor(text.length - 1),
+ nlines = to.line - from.line
+
+ // Adjust the line structure
+ if (change.full) {
+ doc.insert(0, linesFor(0, text.length))
+ doc.remove(text.length, doc.size - text.length)
+ } else if (isWholeLineUpdate(doc, change)) {
+ // This is a whole-line replace. Treated specially to make
+ // sure line objects move the way they are supposed to.
+ var added = linesFor(0, text.length - 1)
+ update(lastLine, lastLine.text, lastSpans)
+ if (nlines) {
+ doc.remove(from.line, nlines)
+ }
+ if (added.length) {
+ doc.insert(from.line, added)
+ }
+ } else if (firstLine == lastLine) {
+ if (text.length == 1) {
+ update(
+ firstLine,
+ firstLine.text.slice(0, from.ch) + lastText + firstLine.text.slice(to.ch),
+ lastSpans
+ )
+ } else {
+ var added$1 = linesFor(1, text.length - 1)
+ added$1.push(
+ new Line(lastText + firstLine.text.slice(to.ch), lastSpans, estimateHeight)
+ )
+ update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0))
+ doc.insert(from.line + 1, added$1)
+ }
+ } else if (text.length == 1) {
+ update(
+ firstLine,
+ firstLine.text.slice(0, from.ch) + text[0] + lastLine.text.slice(to.ch),
+ spansFor(0)
+ )
+ doc.remove(from.line + 1, nlines)
+ } else {
+ update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0))
+ update(lastLine, lastText + lastLine.text.slice(to.ch), lastSpans)
+ var added$2 = linesFor(1, text.length - 1)
+ if (nlines > 1) {
+ doc.remove(from.line + 1, nlines - 1)
+ }
+ doc.insert(from.line + 1, added$2)
+ }
+
+ signalLater(doc, "change", doc, change)
+ }
+
+ // Call f for all linked documents.
+ function linkedDocs(doc, f, sharedHistOnly) {
+ function propagate(doc, skip, sharedHist) {
+ if (doc.linked) {
+ for (var i = 0; i < doc.linked.length; ++i) {
+ var rel = doc.linked[i]
+ if (rel.doc == skip) {
+ continue
+ }
+ var shared = sharedHist && rel.sharedHist
+ if (sharedHistOnly && !shared) {
+ continue
+ }
+ f(rel.doc, shared)
+ propagate(rel.doc, doc, shared)
+ }
+ }
+ }
+ propagate(doc, null, true)
+ }
+
+ // Attach a document to an editor.
+ function attachDoc(cm, doc) {
+ if (doc.cm) {
+ throw new Error("This document is already in use.")
+ }
+ cm.doc = doc
+ doc.cm = cm
+ estimateLineHeights(cm)
+ loadMode(cm)
+ setDirectionClass(cm)
+ if (!cm.options.lineWrapping) {
+ findMaxLine(cm)
+ }
+ cm.options.mode = doc.modeOption
+ regChange(cm)
+ }
+
+ function setDirectionClass(cm) {
+ ;(cm.doc.direction == "rtl" ? addClass : rmClass)(cm.display.lineDiv, "CodeMirror-rtl")
+ }
+
+ function directionChanged(cm) {
+ runInOp(cm, function() {
+ setDirectionClass(cm)
+ regChange(cm)
+ })
+ }
+
+ function History(startGen) {
+ // Arrays of change events and selections. Doing something adds an
+ // event to done and clears undo. Undoing moves events from done
+ // to undone, redoing moves them in the other direction.
+ this.done = []
+ this.undone = []
+ this.undoDepth = Infinity
+ // Used to track when changes can be merged into a single undo
+ // event
+ this.lastModTime = this.lastSelTime = 0
+ this.lastOp = this.lastSelOp = null
+ this.lastOrigin = this.lastSelOrigin = null
+ // Used by the isClean() method
+ this.generation = this.maxGeneration = startGen || 1
+ }
+
+ // Create a history change event from an updateDoc-style change
+ // object.
+ function historyChangeFromChange(doc, change) {
+ var histChange = {
+ from: copyPos(change.from),
+ to: changeEnd(change),
+ text: getBetween(doc, change.from, change.to)
+ }
+ attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1)
+ linkedDocs(
+ doc,
+ function(doc) {
+ return attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1)
+ },
+ true
+ )
+ return histChange
+ }
+
+ // Pop all selection events off the end of a history array. Stop at
+ // a change event.
+ function clearSelectionEvents(array) {
+ while (array.length) {
+ var last = lst(array)
+ if (last.ranges) {
+ array.pop()
+ } else {
+ break
+ }
+ }
+ }
+
+ // Find the top change event in the history. Pop off selection
+ // events that are in the way.
+ function lastChangeEvent(hist, force) {
+ if (force) {
+ clearSelectionEvents(hist.done)
+ return lst(hist.done)
+ } else if (hist.done.length && !lst(hist.done).ranges) {
+ return lst(hist.done)
+ } else if (hist.done.length > 1 && !hist.done[hist.done.length - 2].ranges) {
+ hist.done.pop()
+ return lst(hist.done)
+ }
+ }
+
+ // Register a change in the history. Merges changes that are within
+ // a single operation, or are close together with an origin that
+ // allows merging (starting with "+") into a single event.
+ function addChangeToHistory(doc, change, selAfter, opId) {
+ var hist = doc.history
+ hist.undone.length = 0
+ var time = +new Date(),
+ cur
+ var last
+
+ if (
+ (hist.lastOp == opId ||
+ (hist.lastOrigin == change.origin &&
+ change.origin &&
+ ((change.origin.charAt(0) == "+" &&
+ hist.lastModTime >
+ time - (doc.cm ? doc.cm.options.historyEventDelay : 500)) ||
+ change.origin.charAt(0) == "*"))) &&
+ (cur = lastChangeEvent(hist, hist.lastOp == opId))
+ ) {
+ // Merge this change into the last event
+ last = lst(cur.changes)
+ if (cmp(change.from, change.to) == 0 && cmp(change.from, last.to) == 0) {
+ // Optimized case for simple insertion -- don't want to add
+ // new changesets for every character typed
+ last.to = changeEnd(change)
+ } else {
+ // Add new sub-event
+ cur.changes.push(historyChangeFromChange(doc, change))
+ }
+ } else {
+ // Can not be merged, start a new event.
+ var before = lst(hist.done)
+ if (!before || !before.ranges) {
+ pushSelectionToHistory(doc.sel, hist.done)
+ }
+ cur = { changes: [historyChangeFromChange(doc, change)], generation: hist.generation }
+ hist.done.push(cur)
+ while (hist.done.length > hist.undoDepth) {
+ hist.done.shift()
+ if (!hist.done[0].ranges) {
+ hist.done.shift()
+ }
+ }
+ }
+ hist.done.push(selAfter)
+ hist.generation = ++hist.maxGeneration
+ hist.lastModTime = hist.lastSelTime = time
+ hist.lastOp = hist.lastSelOp = opId
+ hist.lastOrigin = hist.lastSelOrigin = change.origin
+
+ if (!last) {
+ signal(doc, "historyAdded")
+ }
+ }
+
+ function selectionEventCanBeMerged(doc, origin, prev, sel) {
+ var ch = origin.charAt(0)
+ return (
+ ch == "*" ||
+ (ch == "+" &&
+ prev.ranges.length == sel.ranges.length &&
+ prev.somethingSelected() == sel.somethingSelected() &&
+ new Date() - doc.history.lastSelTime <=
+ (doc.cm ? doc.cm.options.historyEventDelay : 500))
+ )
+ }
+
+ // Called whenever the selection changes, sets the new selection as
+ // the pending selection in the history, and pushes the old pending
+ // selection into the 'done' array when it was significantly
+ // different (in number of selected ranges, emptiness, or time).
+ function addSelectionToHistory(doc, sel, opId, options) {
+ var hist = doc.history,
+ origin = options && options.origin
+
+ // A new event is started when the previous origin does not match
+ // the current, or the origins don't allow matching. Origins
+ // starting with * are always merged, those starting with + are
+ // merged when similar and close together in time.
+ if (
+ opId == hist.lastSelOp ||
+ (origin &&
+ hist.lastSelOrigin == origin &&
+ ((hist.lastModTime == hist.lastSelTime && hist.lastOrigin == origin) ||
+ selectionEventCanBeMerged(doc, origin, lst(hist.done), sel)))
+ ) {
+ hist.done[hist.done.length - 1] = sel
+ } else {
+ pushSelectionToHistory(sel, hist.done)
+ }
+
+ hist.lastSelTime = +new Date()
+ hist.lastSelOrigin = origin
+ hist.lastSelOp = opId
+ if (options && options.clearRedo !== false) {
+ clearSelectionEvents(hist.undone)
+ }
+ }
+
+ function pushSelectionToHistory(sel, dest) {
+ var top = lst(dest)
+ if (!(top && top.ranges && top.equals(sel))) {
+ dest.push(sel)
+ }
+ }
+
+ // Used to store marked span information in the history.
+ function attachLocalSpans(doc, change, from, to) {
+ var existing = change["spans_" + doc.id],
+ n = 0
+ doc.iter(Math.max(doc.first, from), Math.min(doc.first + doc.size, to), function(line) {
+ if (line.markedSpans) {
+ ;(existing || (existing = change["spans_" + doc.id] = {}))[n] = line.markedSpans
+ }
+ ++n
+ })
+ }
+
+ // When un/re-doing restores text containing marked spans, those
+ // that have been explicitly cleared should not be restored.
+ function removeClearedSpans(spans) {
+ if (!spans) {
+ return null
+ }
+ var out
+ for (var i = 0; i < spans.length; ++i) {
+ if (spans[i].marker.explicitlyCleared) {
+ if (!out) {
+ out = spans.slice(0, i)
+ }
+ } else if (out) {
+ out.push(spans[i])
+ }
+ }
+ return !out ? spans : out.length ? out : null
+ }
+
+ // Retrieve and filter the old marked spans stored in a change event.
+ function getOldSpans(doc, change) {
+ var found = change["spans_" + doc.id]
+ if (!found) {
+ return null
+ }
+ var nw = []
+ for (var i = 0; i < change.text.length; ++i) {
+ nw.push(removeClearedSpans(found[i]))
+ }
+ return nw
+ }
+
+ // Used for un/re-doing changes from the history. Combines the
+ // result of computing the existing spans with the set of spans that
+ // existed in the history (so that deleting around a span and then
+ // undoing brings back the span).
+ function mergeOldSpans(doc, change) {
+ var old = getOldSpans(doc, change)
+ var stretched = stretchSpansOverChange(doc, change)
+ if (!old) {
+ return stretched
+ }
+ if (!stretched) {
+ return old
+ }
+
+ for (var i = 0; i < old.length; ++i) {
+ var oldCur = old[i],
+ stretchCur = stretched[i]
+ if (oldCur && stretchCur) {
+ spans: for (var j = 0; j < stretchCur.length; ++j) {
+ var span = stretchCur[j]
+ for (var k = 0; k < oldCur.length; ++k) {
+ if (oldCur[k].marker == span.marker) {
+ continue spans
+ }
+ }
+ oldCur.push(span)
+ }
+ } else if (stretchCur) {
+ old[i] = stretchCur
+ }
+ }
+ return old
+ }
+
+ // Used both to provide a JSON-safe object in .getHistory, and, when
+ // detaching a document, to split the history in two
+ function copyHistoryArray(events, newGroup, instantiateSel) {
+ var copy = []
+ for (var i = 0; i < events.length; ++i) {
+ var event = events[i]
+ if (event.ranges) {
+ copy.push(instantiateSel ? Selection.prototype.deepCopy.call(event) : event)
+ continue
+ }
+ var changes = event.changes,
+ newChanges = []
+ copy.push({ changes: newChanges })
+ for (var j = 0; j < changes.length; ++j) {
+ var change = changes[j],
+ m = void 0
+ newChanges.push({ from: change.from, to: change.to, text: change.text })
+ if (newGroup) {
+ for (var prop in change) {
+ if ((m = prop.match(/^spans_(\d+)$/))) {
+ if (indexOf(newGroup, Number(m[1])) > -1) {
+ lst(newChanges)[prop] = change[prop]
+ delete change[prop]
+ }
+ }
+ }
+ }
+ }
+ }
+ return copy
+ }
+
+ // The 'scroll' parameter given to many of these indicated whether
+ // the new cursor position should be scrolled into view after
+ // modifying the selection.
+
+ // If shift is held or the extend flag is set, extends a range to
+ // include a given position (and optionally a second position).
+ // Otherwise, simply returns the range between the given positions.
+ // Used for cursor motion and such.
+ function extendRange(range, head, other, extend) {
+ if (extend) {
+ var anchor = range.anchor
+ if (other) {
+ var posBefore = cmp(head, anchor) < 0
+ if (posBefore != cmp(other, anchor) < 0) {
+ anchor = head
+ head = other
+ } else if (posBefore != cmp(head, other) < 0) {
+ head = other
+ }
+ }
+ return new Range(anchor, head)
+ } else {
+ return new Range(other || head, head)
+ }
+ }
+
+ // Extend the primary selection range, discard the rest.
+ function extendSelection(doc, head, other, options, extend) {
+ if (extend == null) {
+ extend = doc.cm && (doc.cm.display.shift || doc.extend)
+ }
+ setSelection(
+ doc,
+ new Selection([extendRange(doc.sel.primary(), head, other, extend)], 0),
+ options
+ )
+ }
+
+ // Extend all selections (pos is an array of selections with length
+ // equal the number of selections)
+ function extendSelections(doc, heads, options) {
+ var out = []
+ var extend = doc.cm && (doc.cm.display.shift || doc.extend)
+ for (var i = 0; i < doc.sel.ranges.length; i++) {
+ out[i] = extendRange(doc.sel.ranges[i], heads[i], null, extend)
+ }
+ var newSel = normalizeSelection(doc.cm, out, doc.sel.primIndex)
+ setSelection(doc, newSel, options)
+ }
+
+ // Updates a single range in the selection.
+ function replaceOneSelection(doc, i, range, options) {
+ var ranges = doc.sel.ranges.slice(0)
+ ranges[i] = range
+ setSelection(doc, normalizeSelection(doc.cm, ranges, doc.sel.primIndex), options)
+ }
+
+ // Reset the selection to a single range.
+ function setSimpleSelection(doc, anchor, head, options) {
+ setSelection(doc, simpleSelection(anchor, head), options)
+ }
+
+ // Give beforeSelectionChange handlers a change to influence a
+ // selection update.
+ function filterSelectionChange(doc, sel, options) {
+ var obj = {
+ ranges: sel.ranges,
+ update: function(ranges) {
+ this.ranges = []
+ for (var i = 0; i < ranges.length; i++) {
+ this.ranges[i] = new Range(
+ clipPos(doc, ranges[i].anchor),
+ clipPos(doc, ranges[i].head)
+ )
+ }
+ },
+ origin: options && options.origin
+ }
+ signal(doc, "beforeSelectionChange", doc, obj)
+ if (doc.cm) {
+ signal(doc.cm, "beforeSelectionChange", doc.cm, obj)
+ }
+ if (obj.ranges != sel.ranges) {
+ return normalizeSelection(doc.cm, obj.ranges, obj.ranges.length - 1)
+ } else {
+ return sel
+ }
+ }
+
+ function setSelectionReplaceHistory(doc, sel, options) {
+ var done = doc.history.done,
+ last = lst(done)
+ if (last && last.ranges) {
+ done[done.length - 1] = sel
+ setSelectionNoUndo(doc, sel, options)
+ } else {
+ setSelection(doc, sel, options)
+ }
+ }
+
+ // Set a new selection.
+ function setSelection(doc, sel, options) {
+ setSelectionNoUndo(doc, sel, options)
+ addSelectionToHistory(doc, doc.sel, doc.cm ? doc.cm.curOp.id : NaN, options)
+ }
+
+ function setSelectionNoUndo(doc, sel, options) {
+ if (
+ hasHandler(doc, "beforeSelectionChange") ||
+ (doc.cm && hasHandler(doc.cm, "beforeSelectionChange"))
+ ) {
+ sel = filterSelectionChange(doc, sel, options)
+ }
+
+ var bias =
+ (options && options.bias) ||
+ (cmp(sel.primary().head, doc.sel.primary().head) < 0 ? -1 : 1)
+ setSelectionInner(doc, skipAtomicInSelection(doc, sel, bias, true))
+
+ if (!(options && options.scroll === false) && doc.cm) {
+ ensureCursorVisible(doc.cm)
+ }
+ }
+
+ function setSelectionInner(doc, sel) {
+ if (sel.equals(doc.sel)) {
+ return
+ }
+
+ doc.sel = sel
+
+ if (doc.cm) {
+ doc.cm.curOp.updateInput = 1
+ doc.cm.curOp.selectionChanged = true
+ signalCursorActivity(doc.cm)
+ }
+ signalLater(doc, "cursorActivity", doc)
+ }
+
+ // Verify that the selection does not partially select any atomic
+ // marked ranges.
+ function reCheckSelection(doc) {
+ setSelectionInner(doc, skipAtomicInSelection(doc, doc.sel, null, false))
+ }
+
+ // Return a selection that does not partially select any atomic
+ // ranges.
+ function skipAtomicInSelection(doc, sel, bias, mayClear) {
+ var out
+ for (var i = 0; i < sel.ranges.length; i++) {
+ var range = sel.ranges[i]
+ var old = sel.ranges.length == doc.sel.ranges.length && doc.sel.ranges[i]
+ var newAnchor = skipAtomic(doc, range.anchor, old && old.anchor, bias, mayClear)
+ var newHead = skipAtomic(doc, range.head, old && old.head, bias, mayClear)
+ if (out || newAnchor != range.anchor || newHead != range.head) {
+ if (!out) {
+ out = sel.ranges.slice(0, i)
+ }
+ out[i] = new Range(newAnchor, newHead)
+ }
+ }
+ return out ? normalizeSelection(doc.cm, out, sel.primIndex) : sel
+ }
+
+ function skipAtomicInner(doc, pos, oldPos, dir, mayClear) {
+ var line = getLine(doc, pos.line)
+ if (line.markedSpans) {
+ for (var i = 0; i < line.markedSpans.length; ++i) {
+ var sp = line.markedSpans[i],
+ m = sp.marker
+
+ // Determine if we should prevent the cursor being placed to the left/right of an atomic marker
+ // Historically this was determined using the inclusiveLeft/Right option, but the new way to control it
+ // is with selectLeft/Right
+ var preventCursorLeft = "selectLeft" in m ? !m.selectLeft : m.inclusiveLeft
+ var preventCursorRight = "selectRight" in m ? !m.selectRight : m.inclusiveRight
+
+ if (
+ (sp.from == null ||
+ (preventCursorLeft ? sp.from <= pos.ch : sp.from < pos.ch)) &&
+ (sp.to == null || (preventCursorRight ? sp.to >= pos.ch : sp.to > pos.ch))
+ ) {
+ if (mayClear) {
+ signal(m, "beforeCursorEnter")
+ if (m.explicitlyCleared) {
+ if (!line.markedSpans) {
+ break
+ } else {
+ --i
+ continue
+ }
+ }
+ }
+ if (!m.atomic) {
+ continue
+ }
+
+ if (oldPos) {
+ var near = m.find(dir < 0 ? 1 : -1),
+ diff = void 0
+ if (dir < 0 ? preventCursorRight : preventCursorLeft) {
+ near = movePos(
+ doc,
+ near,
+ -dir,
+ near && near.line == pos.line ? line : null
+ )
+ }
+ if (
+ near &&
+ near.line == pos.line &&
+ (diff = cmp(near, oldPos)) &&
+ (dir < 0 ? diff < 0 : diff > 0)
+ ) {
+ return skipAtomicInner(doc, near, pos, dir, mayClear)
+ }
+ }
+
+ var far = m.find(dir < 0 ? -1 : 1)
+ if (dir < 0 ? preventCursorLeft : preventCursorRight) {
+ far = movePos(doc, far, dir, far.line == pos.line ? line : null)
+ }
+ return far ? skipAtomicInner(doc, far, pos, dir, mayClear) : null
+ }
+ }
+ }
+ return pos
+ }
+
+ // Ensure a given position is not inside an atomic range.
+ function skipAtomic(doc, pos, oldPos, bias, mayClear) {
+ var dir = bias || 1
+ var found =
+ skipAtomicInner(doc, pos, oldPos, dir, mayClear) ||
+ (!mayClear && skipAtomicInner(doc, pos, oldPos, dir, true)) ||
+ skipAtomicInner(doc, pos, oldPos, -dir, mayClear) ||
+ (!mayClear && skipAtomicInner(doc, pos, oldPos, -dir, true))
+ if (!found) {
+ doc.cantEdit = true
+ return Pos(doc.first, 0)
+ }
+ return found
+ }
+
+ function movePos(doc, pos, dir, line) {
+ if (dir < 0 && pos.ch == 0) {
+ if (pos.line > doc.first) {
+ return clipPos(doc, Pos(pos.line - 1))
+ } else {
+ return null
+ }
+ } else if (dir > 0 && pos.ch == (line || getLine(doc, pos.line)).text.length) {
+ if (pos.line < doc.first + doc.size - 1) {
+ return Pos(pos.line + 1, 0)
+ } else {
+ return null
+ }
+ } else {
+ return new Pos(pos.line, pos.ch + dir)
+ }
+ }
+
+ function selectAll(cm) {
+ cm.setSelection(Pos(cm.firstLine(), 0), Pos(cm.lastLine()), sel_dontScroll)
+ }
+
+ // UPDATING
+
+ // Allow "beforeChange" event handlers to influence a change
+ function filterChange(doc, change, update) {
+ var obj = {
+ canceled: false,
+ from: change.from,
+ to: change.to,
+ text: change.text,
+ origin: change.origin,
+ cancel: function() {
+ return (obj.canceled = true)
+ }
+ }
+ if (update) {
+ obj.update = function(from, to, text, origin) {
+ if (from) {
+ obj.from = clipPos(doc, from)
+ }
+ if (to) {
+ obj.to = clipPos(doc, to)
+ }
+ if (text) {
+ obj.text = text
+ }
+ if (origin !== undefined) {
+ obj.origin = origin
+ }
+ }
+ }
+ signal(doc, "beforeChange", doc, obj)
+ if (doc.cm) {
+ signal(doc.cm, "beforeChange", doc.cm, obj)
+ }
+
+ if (obj.canceled) {
+ if (doc.cm) {
+ doc.cm.curOp.updateInput = 2
+ }
+ return null
+ }
+ return { from: obj.from, to: obj.to, text: obj.text, origin: obj.origin }
+ }
+
+ // Apply a change to a document, and add it to the document's
+ // history, and propagating it to all linked documents.
+ function makeChange(doc, change, ignoreReadOnly) {
+ if (doc.cm) {
+ if (!doc.cm.curOp) {
+ return operation(doc.cm, makeChange)(doc, change, ignoreReadOnly)
+ }
+ if (doc.cm.state.suppressEdits) {
+ return
+ }
+ }
+
+ if (hasHandler(doc, "beforeChange") || (doc.cm && hasHandler(doc.cm, "beforeChange"))) {
+ change = filterChange(doc, change, true)
+ if (!change) {
+ return
+ }
+ }
+
+ // Possibly split or suppress the update based on the presence
+ // of read-only spans in its range.
+ var split =
+ sawReadOnlySpans && !ignoreReadOnly && removeReadOnlyRanges(doc, change.from, change.to)
+ if (split) {
+ for (var i = split.length - 1; i >= 0; --i) {
+ makeChangeInner(doc, {
+ from: split[i].from,
+ to: split[i].to,
+ text: i ? [""] : change.text,
+ origin: change.origin
+ })
+ }
+ } else {
+ makeChangeInner(doc, change)
+ }
+ }
+
+ function makeChangeInner(doc, change) {
+ if (change.text.length == 1 && change.text[0] == "" && cmp(change.from, change.to) == 0) {
+ return
+ }
+ var selAfter = computeSelAfterChange(doc, change)
+ addChangeToHistory(doc, change, selAfter, doc.cm ? doc.cm.curOp.id : NaN)
+
+ makeChangeSingleDoc(doc, change, selAfter, stretchSpansOverChange(doc, change))
+ var rebased = []
+
+ linkedDocs(doc, function(doc, sharedHist) {
+ if (!sharedHist && indexOf(rebased, doc.history) == -1) {
+ rebaseHist(doc.history, change)
+ rebased.push(doc.history)
+ }
+ makeChangeSingleDoc(doc, change, null, stretchSpansOverChange(doc, change))
+ })
+ }
+
+ // Revert a change stored in a document's history.
+ function makeChangeFromHistory(doc, type, allowSelectionOnly) {
+ var suppress = doc.cm && doc.cm.state.suppressEdits
+ if (suppress && !allowSelectionOnly) {
+ return
+ }
+
+ var hist = doc.history,
+ event,
+ selAfter = doc.sel
+ var source = type == "undo" ? hist.done : hist.undone,
+ dest = type == "undo" ? hist.undone : hist.done
+
+ // Verify that there is a useable event (so that ctrl-z won't
+ // needlessly clear selection events)
+ var i = 0
+ for (; i < source.length; i++) {
+ event = source[i]
+ if (allowSelectionOnly ? event.ranges && !event.equals(doc.sel) : !event.ranges) {
+ break
+ }
+ }
+ if (i == source.length) {
+ return
+ }
+ hist.lastOrigin = hist.lastSelOrigin = null
+
+ for (;;) {
+ event = source.pop()
+ if (event.ranges) {
+ pushSelectionToHistory(event, dest)
+ if (allowSelectionOnly && !event.equals(doc.sel)) {
+ setSelection(doc, event, { clearRedo: false })
+ return
+ }
+ selAfter = event
+ } else if (suppress) {
+ source.push(event)
+ return
+ } else {
+ break
+ }
+ }
+
+ // Build up a reverse change object to add to the opposite history
+ // stack (redo when undoing, and vice versa).
+ var antiChanges = []
+ pushSelectionToHistory(selAfter, dest)
+ dest.push({ changes: antiChanges, generation: hist.generation })
+ hist.generation = event.generation || ++hist.maxGeneration
+
+ var filter =
+ hasHandler(doc, "beforeChange") || (doc.cm && hasHandler(doc.cm, "beforeChange"))
+
+ var loop = function(i) {
+ var change = event.changes[i]
+ change.origin = type
+ if (filter && !filterChange(doc, change, false)) {
+ source.length = 0
+ return {}
+ }
+
+ antiChanges.push(historyChangeFromChange(doc, change))
+
+ var after = i ? computeSelAfterChange(doc, change) : lst(source)
+ makeChangeSingleDoc(doc, change, after, mergeOldSpans(doc, change))
+ if (!i && doc.cm) {
+ doc.cm.scrollIntoView({ from: change.from, to: changeEnd(change) })
+ }
+ var rebased = []
+
+ // Propagate to the linked documents
+ linkedDocs(doc, function(doc, sharedHist) {
+ if (!sharedHist && indexOf(rebased, doc.history) == -1) {
+ rebaseHist(doc.history, change)
+ rebased.push(doc.history)
+ }
+ makeChangeSingleDoc(doc, change, null, mergeOldSpans(doc, change))
+ })
+ }
+
+ for (var i$1 = event.changes.length - 1; i$1 >= 0; --i$1) {
+ var returned = loop(i$1)
+
+ if (returned) return returned.v
+ }
+ }
+
+ // Sub-views need their line numbers shifted when text is added
+ // above or below them in the parent document.
+ function shiftDoc(doc, distance) {
+ if (distance == 0) {
+ return
+ }
+ doc.first += distance
+ doc.sel = new Selection(
+ map(doc.sel.ranges, function(range) {
+ return new Range(
+ Pos(range.anchor.line + distance, range.anchor.ch),
+ Pos(range.head.line + distance, range.head.ch)
+ )
+ }),
+ doc.sel.primIndex
+ )
+ if (doc.cm) {
+ regChange(doc.cm, doc.first, doc.first - distance, distance)
+ for (var d = doc.cm.display, l = d.viewFrom; l < d.viewTo; l++) {
+ regLineChange(doc.cm, l, "gutter")
+ }
+ }
+ }
+
+ // More lower-level change function, handling only a single document
+ // (not linked ones).
+ function makeChangeSingleDoc(doc, change, selAfter, spans) {
+ if (doc.cm && !doc.cm.curOp) {
+ return operation(doc.cm, makeChangeSingleDoc)(doc, change, selAfter, spans)
+ }
+
+ if (change.to.line < doc.first) {
+ shiftDoc(doc, change.text.length - 1 - (change.to.line - change.from.line))
+ return
+ }
+ if (change.from.line > doc.lastLine()) {
+ return
+ }
+
+ // Clip the change to the size of this doc
+ if (change.from.line < doc.first) {
+ var shift = change.text.length - 1 - (doc.first - change.from.line)
+ shiftDoc(doc, shift)
+ change = {
+ from: Pos(doc.first, 0),
+ to: Pos(change.to.line + shift, change.to.ch),
+ text: [lst(change.text)],
+ origin: change.origin
+ }
+ }
+ var last = doc.lastLine()
+ if (change.to.line > last) {
+ change = {
+ from: change.from,
+ to: Pos(last, getLine(doc, last).text.length),
+ text: [change.text[0]],
+ origin: change.origin
+ }
+ }
+
+ change.removed = getBetween(doc, change.from, change.to)
+
+ if (!selAfter) {
+ selAfter = computeSelAfterChange(doc, change)
+ }
+ if (doc.cm) {
+ makeChangeSingleDocInEditor(doc.cm, change, spans)
+ } else {
+ updateDoc(doc, change, spans)
+ }
+ setSelectionNoUndo(doc, selAfter, sel_dontScroll)
+
+ if (doc.cantEdit && skipAtomic(doc, Pos(doc.firstLine(), 0))) {
+ doc.cantEdit = false
+ }
+ }
+
+ // Handle the interaction of a change to a document with the editor
+ // that this document is part of.
+ function makeChangeSingleDocInEditor(cm, change, spans) {
+ var doc = cm.doc,
+ display = cm.display,
+ from = change.from,
+ to = change.to
+
+ var recomputeMaxLength = false,
+ checkWidthStart = from.line
+ if (!cm.options.lineWrapping) {
+ checkWidthStart = lineNo(visualLine(getLine(doc, from.line)))
+ doc.iter(checkWidthStart, to.line + 1, function(line) {
+ if (line == display.maxLine) {
+ recomputeMaxLength = true
+ return true
+ }
+ })
+ }
+
+ if (doc.sel.contains(change.from, change.to) > -1) {
+ signalCursorActivity(cm)
+ }
+
+ updateDoc(doc, change, spans, estimateHeight(cm))
+
+ if (!cm.options.lineWrapping) {
+ doc.iter(checkWidthStart, from.line + change.text.length, function(line) {
+ var len = lineLength(line)
+ if (len > display.maxLineLength) {
+ display.maxLine = line
+ display.maxLineLength = len
+ display.maxLineChanged = true
+ recomputeMaxLength = false
+ }
+ })
+ if (recomputeMaxLength) {
+ cm.curOp.updateMaxLine = true
+ }
+ }
+
+ retreatFrontier(doc, from.line)
+ startWorker(cm, 400)
+
+ var lendiff = change.text.length - (to.line - from.line) - 1
+ // Remember that these lines changed, for updating the display
+ if (change.full) {
+ regChange(cm)
+ } else if (
+ from.line == to.line &&
+ change.text.length == 1 &&
+ !isWholeLineUpdate(cm.doc, change)
+ ) {
+ regLineChange(cm, from.line, "text")
+ } else {
+ regChange(cm, from.line, to.line + 1, lendiff)
+ }
+
+ var changesHandler = hasHandler(cm, "changes"),
+ changeHandler = hasHandler(cm, "change")
+ if (changeHandler || changesHandler) {
+ var obj = {
+ from: from,
+ to: to,
+ text: change.text,
+ removed: change.removed,
+ origin: change.origin
+ }
+ if (changeHandler) {
+ signalLater(cm, "change", cm, obj)
+ }
+ if (changesHandler) {
+ ;(cm.curOp.changeObjs || (cm.curOp.changeObjs = [])).push(obj)
+ }
+ }
+ cm.display.selForContextMenu = null
+ }
+
+ function replaceRange(doc, code, from, to, origin) {
+ var assign
+
+ if (!to) {
+ to = from
+ }
+ if (cmp(to, from) < 0) {
+ ;(assign = [to, from]), (from = assign[0]), (to = assign[1])
+ }
+ if (typeof code == "string") {
+ code = doc.splitLines(code)
+ }
+ makeChange(doc, { from: from, to: to, text: code, origin: origin })
+ }
+
+ // Rebasing/resetting history to deal with externally-sourced changes
+
+ function rebaseHistSelSingle(pos, from, to, diff) {
+ if (to < pos.line) {
+ pos.line += diff
+ } else if (from < pos.line) {
+ pos.line = from
+ pos.ch = 0
+ }
+ }
+
+ // Tries to rebase an array of history events given a change in the
+ // document. If the change touches the same lines as the event, the
+ // event, and everything 'behind' it, is discarded. If the change is
+ // before the event, the event's positions are updated. Uses a
+ // copy-on-write scheme for the positions, to avoid having to
+ // reallocate them all on every rebase, but also avoid problems with
+ // shared position objects being unsafely updated.
+ function rebaseHistArray(array, from, to, diff) {
+ for (var i = 0; i < array.length; ++i) {
+ var sub = array[i],
+ ok = true
+ if (sub.ranges) {
+ if (!sub.copied) {
+ sub = array[i] = sub.deepCopy()
+ sub.copied = true
+ }
+ for (var j = 0; j < sub.ranges.length; j++) {
+ rebaseHistSelSingle(sub.ranges[j].anchor, from, to, diff)
+ rebaseHistSelSingle(sub.ranges[j].head, from, to, diff)
+ }
+ continue
+ }
+ for (var j$1 = 0; j$1 < sub.changes.length; ++j$1) {
+ var cur = sub.changes[j$1]
+ if (to < cur.from.line) {
+ cur.from = Pos(cur.from.line + diff, cur.from.ch)
+ cur.to = Pos(cur.to.line + diff, cur.to.ch)
+ } else if (from <= cur.to.line) {
+ ok = false
+ break
+ }
+ }
+ if (!ok) {
+ array.splice(0, i + 1)
+ i = 0
+ }
+ }
+ }
+
+ function rebaseHist(hist, change) {
+ var from = change.from.line,
+ to = change.to.line,
+ diff = change.text.length - (to - from) - 1
+ rebaseHistArray(hist.done, from, to, diff)
+ rebaseHistArray(hist.undone, from, to, diff)
+ }
+
+ // Utility for applying a change to a line by handle or number,
+ // returning the number and optionally registering the line as
+ // changed.
+ function changeLine(doc, handle, changeType, op) {
+ var no = handle,
+ line = handle
+ if (typeof handle == "number") {
+ line = getLine(doc, clipLine(doc, handle))
+ } else {
+ no = lineNo(handle)
+ }
+ if (no == null) {
+ return null
+ }
+ if (op(line, no) && doc.cm) {
+ regLineChange(doc.cm, no, changeType)
+ }
+ return line
+ }
+
+ // The document is represented as a BTree consisting of leaves, with
+ // chunk of lines in them, and branches, with up to ten leaves or
+ // other branch nodes below them. The top node is always a branch
+ // node, and is the document object itself (meaning it has
+ // additional methods and properties).
+ //
+ // All nodes have parent links. The tree is used both to go from
+ // line numbers to line objects, and to go from objects to numbers.
+ // It also indexes by height, and is used to convert between height
+ // and line object, and to find the total height of the document.
+ //
+ // See also http://marijnhaverbeke.nl/blog/codemirror-line-tree.html
+
+ function LeafChunk(lines) {
+ this.lines = lines
+ this.parent = null
+ var height = 0
+ for (var i = 0; i < lines.length; ++i) {
+ lines[i].parent = this
+ height += lines[i].height
+ }
+ this.height = height
+ }
+
+ LeafChunk.prototype = {
+ chunkSize: function() {
+ return this.lines.length
+ },
+
+ // Remove the n lines at offset 'at'.
+ removeInner: function(at, n) {
+ for (var i = at, e = at + n; i < e; ++i) {
+ var line = this.lines[i]
+ this.height -= line.height
+ cleanUpLine(line)
+ signalLater(line, "delete")
+ }
+ this.lines.splice(at, n)
+ },
+
+ // Helper used to collapse a small branch into a single leaf.
+ collapse: function(lines) {
+ lines.push.apply(lines, this.lines)
+ },
+
+ // Insert the given array of lines at offset 'at', count them as
+ // having the given height.
+ insertInner: function(at, lines, height) {
+ this.height += height
+ this.lines = this.lines
+ .slice(0, at)
+ .concat(lines)
+ .concat(this.lines.slice(at))
+ for (var i = 0; i < lines.length; ++i) {
+ lines[i].parent = this
+ }
+ },
+
+ // Used to iterate over a part of the tree.
+ iterN: function(at, n, op) {
+ for (var e = at + n; at < e; ++at) {
+ if (op(this.lines[at])) {
+ return true
+ }
+ }
+ }
+ }
+
+ function BranchChunk(children) {
+ this.children = children
+ var size = 0,
+ height = 0
+ for (var i = 0; i < children.length; ++i) {
+ var ch = children[i]
+ size += ch.chunkSize()
+ height += ch.height
+ ch.parent = this
+ }
+ this.size = size
+ this.height = height
+ this.parent = null
+ }
+
+ BranchChunk.prototype = {
+ chunkSize: function() {
+ return this.size
+ },
+
+ removeInner: function(at, n) {
+ this.size -= n
+ for (var i = 0; i < this.children.length; ++i) {
+ var child = this.children[i],
+ sz = child.chunkSize()
+ if (at < sz) {
+ var rm = Math.min(n, sz - at),
+ oldHeight = child.height
+ child.removeInner(at, rm)
+ this.height -= oldHeight - child.height
+ if (sz == rm) {
+ this.children.splice(i--, 1)
+ child.parent = null
+ }
+ if ((n -= rm) == 0) {
+ break
+ }
+ at = 0
+ } else {
+ at -= sz
+ }
+ }
+ // If the result is smaller than 25 lines, ensure that it is a
+ // single leaf node.
+ if (
+ this.size - n < 25 &&
+ (this.children.length > 1 || !(this.children[0] instanceof LeafChunk))
+ ) {
+ var lines = []
+ this.collapse(lines)
+ this.children = [new LeafChunk(lines)]
+ this.children[0].parent = this
+ }
+ },
+
+ collapse: function(lines) {
+ for (var i = 0; i < this.children.length; ++i) {
+ this.children[i].collapse(lines)
+ }
+ },
+
+ insertInner: function(at, lines, height) {
+ this.size += lines.length
+ this.height += height
+ for (var i = 0; i < this.children.length; ++i) {
+ var child = this.children[i],
+ sz = child.chunkSize()
+ if (at <= sz) {
+ child.insertInner(at, lines, height)
+ if (child.lines && child.lines.length > 50) {
+ // To avoid memory thrashing when child.lines is huge (e.g. first view of a large file), it's never spliced.
+ // Instead, small slices are taken. They're taken in order because sequential memory accesses are fastest.
+ var remaining = (child.lines.length % 25) + 25
+ for (var pos = remaining; pos < child.lines.length; ) {
+ var leaf = new LeafChunk(child.lines.slice(pos, (pos += 25)))
+ child.height -= leaf.height
+ this.children.splice(++i, 0, leaf)
+ leaf.parent = this
+ }
+ child.lines = child.lines.slice(0, remaining)
+ this.maybeSpill()
+ }
+ break
+ }
+ at -= sz
+ }
+ },
+
+ // When a node has grown, check whether it should be split.
+ maybeSpill: function() {
+ if (this.children.length <= 10) {
+ return
+ }
+ var me = this
+ do {
+ var spilled = me.children.splice(me.children.length - 5, 5)
+ var sibling = new BranchChunk(spilled)
+ if (!me.parent) {
+ // Become the parent node
+ var copy = new BranchChunk(me.children)
+ copy.parent = me
+ me.children = [copy, sibling]
+ me = copy
+ } else {
+ me.size -= sibling.size
+ me.height -= sibling.height
+ var myIndex = indexOf(me.parent.children, me)
+ me.parent.children.splice(myIndex + 1, 0, sibling)
+ }
+ sibling.parent = me.parent
+ } while (me.children.length > 10)
+ me.parent.maybeSpill()
+ },
+
+ iterN: function(at, n, op) {
+ for (var i = 0; i < this.children.length; ++i) {
+ var child = this.children[i],
+ sz = child.chunkSize()
+ if (at < sz) {
+ var used = Math.min(n, sz - at)
+ if (child.iterN(at, used, op)) {
+ return true
+ }
+ if ((n -= used) == 0) {
+ break
+ }
+ at = 0
+ } else {
+ at -= sz
+ }
+ }
+ }
+ }
+
+ // Line widgets are block elements displayed above or below a line.
+
+ var LineWidget = function(doc, node, options) {
+ if (options) {
+ for (var opt in options) {
+ if (options.hasOwnProperty(opt)) {
+ this[opt] = options[opt]
+ }
+ }
+ }
+ this.doc = doc
+ this.node = node
+ }
+
+ LineWidget.prototype.clear = function() {
+ var cm = this.doc.cm,
+ ws = this.line.widgets,
+ line = this.line,
+ no = lineNo(line)
+ if (no == null || !ws) {
+ return
+ }
+ for (var i = 0; i < ws.length; ++i) {
+ if (ws[i] == this) {
+ ws.splice(i--, 1)
+ }
+ }
+ if (!ws.length) {
+ line.widgets = null
+ }
+ var height = widgetHeight(this)
+ updateLineHeight(line, Math.max(0, line.height - height))
+ if (cm) {
+ runInOp(cm, function() {
+ adjustScrollWhenAboveVisible(cm, line, -height)
+ regLineChange(cm, no, "widget")
+ })
+ signalLater(cm, "lineWidgetCleared", cm, this, no)
+ }
+ }
+
+ LineWidget.prototype.changed = function() {
+ var this$1 = this
+
+ var oldH = this.height,
+ cm = this.doc.cm,
+ line = this.line
+ this.height = null
+ var diff = widgetHeight(this) - oldH
+ if (!diff) {
+ return
+ }
+ if (!lineIsHidden(this.doc, line)) {
+ updateLineHeight(line, line.height + diff)
+ }
+ if (cm) {
+ runInOp(cm, function() {
+ cm.curOp.forceUpdate = true
+ adjustScrollWhenAboveVisible(cm, line, diff)
+ signalLater(cm, "lineWidgetChanged", cm, this$1, lineNo(line))
+ })
+ }
+ }
+ eventMixin(LineWidget)
+
+ function adjustScrollWhenAboveVisible(cm, line, diff) {
+ if (heightAtLine(line) < ((cm.curOp && cm.curOp.scrollTop) || cm.doc.scrollTop)) {
+ addToScrollTop(cm, diff)
+ }
+ }
+
+ function addLineWidget(doc, handle, node, options) {
+ var widget = new LineWidget(doc, node, options)
+ var cm = doc.cm
+ if (cm && widget.noHScroll) {
+ cm.display.alignWidgets = true
+ }
+ changeLine(doc, handle, "widget", function(line) {
+ var widgets = line.widgets || (line.widgets = [])
+ if (widget.insertAt == null) {
+ widgets.push(widget)
+ } else {
+ widgets.splice(
+ Math.min(widgets.length - 1, Math.max(0, widget.insertAt)),
+ 0,
+ widget
+ )
+ }
+ widget.line = line
+ if (cm && !lineIsHidden(doc, line)) {
+ var aboveVisible = heightAtLine(line) < doc.scrollTop
+ updateLineHeight(line, line.height + widgetHeight(widget))
+ if (aboveVisible) {
+ addToScrollTop(cm, widget.height)
+ }
+ cm.curOp.forceUpdate = true
+ }
+ return true
+ })
+ if (cm) {
+ signalLater(
+ cm,
+ "lineWidgetAdded",
+ cm,
+ widget,
+ typeof handle == "number" ? handle : lineNo(handle)
+ )
+ }
+ return widget
+ }
+
+ // TEXTMARKERS
+
+ // Created with markText and setBookmark methods. A TextMarker is a
+ // handle that can be used to clear or find a marked position in the
+ // document. Line objects hold arrays (markedSpans) containing
+ // {from, to, marker} object pointing to such marker objects, and
+ // indicating that such a marker is present on that line. Multiple
+ // lines may point to the same marker when it spans across lines.
+ // The spans will have null for their from/to properties when the
+ // marker continues beyond the start/end of the line. Markers have
+ // links back to the lines they currently touch.
+
+ // Collapsed markers have unique ids, in order to be able to order
+ // them, which is needed for uniquely determining an outer marker
+ // when they overlap (they may nest, but not partially overlap).
+ var nextMarkerId = 0
+
+ var TextMarker = function(doc, type) {
+ this.lines = []
+ this.type = type
+ this.doc = doc
+ this.id = ++nextMarkerId
+ }
+
+ // Clear the marker.
+ TextMarker.prototype.clear = function() {
+ if (this.explicitlyCleared) {
+ return
+ }
+ var cm = this.doc.cm,
+ withOp = cm && !cm.curOp
+ if (withOp) {
+ startOperation(cm)
+ }
+ if (hasHandler(this, "clear")) {
+ var found = this.find()
+ if (found) {
+ signalLater(this, "clear", found.from, found.to)
+ }
+ }
+ var min = null,
+ max = null
+ for (var i = 0; i < this.lines.length; ++i) {
+ var line = this.lines[i]
+ var span = getMarkedSpanFor(line.markedSpans, this)
+ if (cm && !this.collapsed) {
+ regLineChange(cm, lineNo(line), "text")
+ } else if (cm) {
+ if (span.to != null) {
+ max = lineNo(line)
+ }
+ if (span.from != null) {
+ min = lineNo(line)
+ }
+ }
+ line.markedSpans = removeMarkedSpan(line.markedSpans, span)
+ if (span.from == null && this.collapsed && !lineIsHidden(this.doc, line) && cm) {
+ updateLineHeight(line, textHeight(cm.display))
+ }
+ }
+ if (cm && this.collapsed && !cm.options.lineWrapping) {
+ for (var i$1 = 0; i$1 < this.lines.length; ++i$1) {
+ var visual = visualLine(this.lines[i$1]),
+ len = lineLength(visual)
+ if (len > cm.display.maxLineLength) {
+ cm.display.maxLine = visual
+ cm.display.maxLineLength = len
+ cm.display.maxLineChanged = true
+ }
+ }
+ }
+
+ if (min != null && cm && this.collapsed) {
+ regChange(cm, min, max + 1)
+ }
+ this.lines.length = 0
+ this.explicitlyCleared = true
+ if (this.atomic && this.doc.cantEdit) {
+ this.doc.cantEdit = false
+ if (cm) {
+ reCheckSelection(cm.doc)
+ }
+ }
+ if (cm) {
+ signalLater(cm, "markerCleared", cm, this, min, max)
+ }
+ if (withOp) {
+ endOperation(cm)
+ }
+ if (this.parent) {
+ this.parent.clear()
+ }
+ }
+
+ // Find the position of the marker in the document. Returns a {from,
+ // to} object by default. Side can be passed to get a specific side
+ // -- 0 (both), -1 (left), or 1 (right). When lineObj is true, the
+ // Pos objects returned contain a line object, rather than a line
+ // number (used to prevent looking up the same line twice).
+ TextMarker.prototype.find = function(side, lineObj) {
+ if (side == null && this.type == "bookmark") {
+ side = 1
+ }
+ var from, to
+ for (var i = 0; i < this.lines.length; ++i) {
+ var line = this.lines[i]
+ var span = getMarkedSpanFor(line.markedSpans, this)
+ if (span.from != null) {
+ from = Pos(lineObj ? line : lineNo(line), span.from)
+ if (side == -1) {
+ return from
+ }
+ }
+ if (span.to != null) {
+ to = Pos(lineObj ? line : lineNo(line), span.to)
+ if (side == 1) {
+ return to
+ }
+ }
+ }
+ return from && { from: from, to: to }
+ }
+
+ // Signals that the marker's widget changed, and surrounding layout
+ // should be recomputed.
+ TextMarker.prototype.changed = function() {
+ var this$1 = this
+
+ var pos = this.find(-1, true),
+ widget = this,
+ cm = this.doc.cm
+ if (!pos || !cm) {
+ return
+ }
+ runInOp(cm, function() {
+ var line = pos.line,
+ lineN = lineNo(pos.line)
+ var view = findViewForLine(cm, lineN)
+ if (view) {
+ clearLineMeasurementCacheFor(view)
+ cm.curOp.selectionChanged = cm.curOp.forceUpdate = true
+ }
+ cm.curOp.updateMaxLine = true
+ if (!lineIsHidden(widget.doc, line) && widget.height != null) {
+ var oldHeight = widget.height
+ widget.height = null
+ var dHeight = widgetHeight(widget) - oldHeight
+ if (dHeight) {
+ updateLineHeight(line, line.height + dHeight)
+ }
+ }
+ signalLater(cm, "markerChanged", cm, this$1)
+ })
+ }
+
+ TextMarker.prototype.attachLine = function(line) {
+ if (!this.lines.length && this.doc.cm) {
+ var op = this.doc.cm.curOp
+ if (!op.maybeHiddenMarkers || indexOf(op.maybeHiddenMarkers, this) == -1) {
+ ;(op.maybeUnhiddenMarkers || (op.maybeUnhiddenMarkers = [])).push(this)
+ }
+ }
+ this.lines.push(line)
+ }
+
+ TextMarker.prototype.detachLine = function(line) {
+ this.lines.splice(indexOf(this.lines, line), 1)
+ if (!this.lines.length && this.doc.cm) {
+ var op = this.doc.cm.curOp
+ ;(op.maybeHiddenMarkers || (op.maybeHiddenMarkers = [])).push(this)
+ }
+ }
+ eventMixin(TextMarker)
+
+ // Create a marker, wire it up to the right lines, and
+ function markText(doc, from, to, options, type) {
+ // Shared markers (across linked documents) are handled separately
+ // (markTextShared will call out to this again, once per
+ // document).
+ if (options && options.shared) {
+ return markTextShared(doc, from, to, options, type)
+ }
+ // Ensure we are in an operation.
+ if (doc.cm && !doc.cm.curOp) {
+ return operation(doc.cm, markText)(doc, from, to, options, type)
+ }
+
+ var marker = new TextMarker(doc, type),
+ diff = cmp(from, to)
+ if (options) {
+ copyObj(options, marker, false)
+ }
+ // Don't connect empty markers unless clearWhenEmpty is false
+ if (diff > 0 || (diff == 0 && marker.clearWhenEmpty !== false)) {
+ return marker
+ }
+ if (marker.replacedWith) {
+ // Showing up as a widget implies collapsed (widget replaces text)
+ marker.collapsed = true
+ marker.widgetNode = eltP("span", [marker.replacedWith], "CodeMirror-widget")
+ if (!options.handleMouseEvents) {
+ marker.widgetNode.setAttribute("cm-ignore-events", "true")
+ }
+ if (options.insertLeft) {
+ marker.widgetNode.insertLeft = true
+ }
+ }
+ if (marker.collapsed) {
+ if (
+ conflictingCollapsedRange(doc, from.line, from, to, marker) ||
+ (from.line != to.line && conflictingCollapsedRange(doc, to.line, from, to, marker))
+ ) {
+ throw new Error("Inserting collapsed marker partially overlapping an existing one")
+ }
+ seeCollapsedSpans()
+ }
+
+ if (marker.addToHistory) {
+ addChangeToHistory(doc, { from: from, to: to, origin: "markText" }, doc.sel, NaN)
+ }
+
+ var curLine = from.line,
+ cm = doc.cm,
+ updateMaxLine
+ doc.iter(curLine, to.line + 1, function(line) {
+ if (
+ cm &&
+ marker.collapsed &&
+ !cm.options.lineWrapping &&
+ visualLine(line) == cm.display.maxLine
+ ) {
+ updateMaxLine = true
+ }
+ if (marker.collapsed && curLine != from.line) {
+ updateLineHeight(line, 0)
+ }
+ addMarkedSpan(
+ line,
+ new MarkedSpan(
+ marker,
+ curLine == from.line ? from.ch : null,
+ curLine == to.line ? to.ch : null
+ )
+ )
+ ++curLine
+ })
+ // lineIsHidden depends on the presence of the spans, so needs a second pass
+ if (marker.collapsed) {
+ doc.iter(from.line, to.line + 1, function(line) {
+ if (lineIsHidden(doc, line)) {
+ updateLineHeight(line, 0)
+ }
+ })
+ }
+
+ if (marker.clearOnEnter) {
+ on(marker, "beforeCursorEnter", function() {
+ return marker.clear()
+ })
+ }
+
+ if (marker.readOnly) {
+ seeReadOnlySpans()
+ if (doc.history.done.length || doc.history.undone.length) {
+ doc.clearHistory()
+ }
+ }
+ if (marker.collapsed) {
+ marker.id = ++nextMarkerId
+ marker.atomic = true
+ }
+ if (cm) {
+ // Sync editor state
+ if (updateMaxLine) {
+ cm.curOp.updateMaxLine = true
+ }
+ if (marker.collapsed) {
+ regChange(cm, from.line, to.line + 1)
+ } else if (
+ marker.className ||
+ marker.startStyle ||
+ marker.endStyle ||
+ marker.css ||
+ marker.attributes ||
+ marker.title
+ ) {
+ for (var i = from.line; i <= to.line; i++) {
+ regLineChange(cm, i, "text")
+ }
+ }
+ if (marker.atomic) {
+ reCheckSelection(cm.doc)
+ }
+ signalLater(cm, "markerAdded", cm, marker)
+ }
+ return marker
+ }
+
+ // SHARED TEXTMARKERS
+
+ // A shared marker spans multiple linked documents. It is
+ // implemented as a meta-marker-object controlling multiple normal
+ // markers.
+ var SharedTextMarker = function(markers, primary) {
+ this.markers = markers
+ this.primary = primary
+ for (var i = 0; i < markers.length; ++i) {
+ markers[i].parent = this
+ }
+ }
+
+ SharedTextMarker.prototype.clear = function() {
+ if (this.explicitlyCleared) {
+ return
+ }
+ this.explicitlyCleared = true
+ for (var i = 0; i < this.markers.length; ++i) {
+ this.markers[i].clear()
+ }
+ signalLater(this, "clear")
+ }
+
+ SharedTextMarker.prototype.find = function(side, lineObj) {
+ return this.primary.find(side, lineObj)
+ }
+ eventMixin(SharedTextMarker)
+
+ function markTextShared(doc, from, to, options, type) {
+ options = copyObj(options)
+ options.shared = false
+ var markers = [markText(doc, from, to, options, type)],
+ primary = markers[0]
+ var widget = options.widgetNode
+ linkedDocs(doc, function(doc) {
+ if (widget) {
+ options.widgetNode = widget.cloneNode(true)
+ }
+ markers.push(markText(doc, clipPos(doc, from), clipPos(doc, to), options, type))
+ for (var i = 0; i < doc.linked.length; ++i) {
+ if (doc.linked[i].isParent) {
+ return
+ }
+ }
+ primary = lst(markers)
+ })
+ return new SharedTextMarker(markers, primary)
+ }
+
+ function findSharedMarkers(doc) {
+ return doc.findMarks(Pos(doc.first, 0), doc.clipPos(Pos(doc.lastLine())), function(m) {
+ return m.parent
+ })
+ }
+
+ function copySharedMarkers(doc, markers) {
+ for (var i = 0; i < markers.length; i++) {
+ var marker = markers[i],
+ pos = marker.find()
+ var mFrom = doc.clipPos(pos.from),
+ mTo = doc.clipPos(pos.to)
+ if (cmp(mFrom, mTo)) {
+ var subMark = markText(doc, mFrom, mTo, marker.primary, marker.primary.type)
+ marker.markers.push(subMark)
+ subMark.parent = marker
+ }
+ }
+ }
+
+ function detachSharedMarkers(markers) {
+ var loop = function(i) {
+ var marker = markers[i],
+ linked = [marker.primary.doc]
+ linkedDocs(marker.primary.doc, function(d) {
+ return linked.push(d)
+ })
+ for (var j = 0; j < marker.markers.length; j++) {
+ var subMarker = marker.markers[j]
+ if (indexOf(linked, subMarker.doc) == -1) {
+ subMarker.parent = null
+ marker.markers.splice(j--, 1)
+ }
+ }
+ }
+
+ for (var i = 0; i < markers.length; i++) loop(i)
+ }
+
+ var nextDocId = 0
+ var Doc = function(text, mode, firstLine, lineSep, direction) {
+ if (!(this instanceof Doc)) {
+ return new Doc(text, mode, firstLine, lineSep, direction)
+ }
+ if (firstLine == null) {
+ firstLine = 0
+ }
+
+ BranchChunk.call(this, [new LeafChunk([new Line("", null)])])
+ this.first = firstLine
+ this.scrollTop = this.scrollLeft = 0
+ this.cantEdit = false
+ this.cleanGeneration = 1
+ this.modeFrontier = this.highlightFrontier = firstLine
+ var start = Pos(firstLine, 0)
+ this.sel = simpleSelection(start)
+ this.history = new History(null)
+ this.id = ++nextDocId
+ this.modeOption = mode
+ this.lineSep = lineSep
+ this.direction = direction == "rtl" ? "rtl" : "ltr"
+ this.extend = false
+
+ if (typeof text == "string") {
+ text = this.splitLines(text)
+ }
+ updateDoc(this, { from: start, to: start, text: text })
+ setSelection(this, simpleSelection(start), sel_dontScroll)
+ }
+
+ Doc.prototype = createObj(BranchChunk.prototype, {
+ constructor: Doc,
+ // Iterate over the document. Supports two forms -- with only one
+ // argument, it calls that for each line in the document. With
+ // three, it iterates over the range given by the first two (with
+ // the second being non-inclusive).
+ iter: function(from, to, op) {
+ if (op) {
+ this.iterN(from - this.first, to - from, op)
+ } else {
+ this.iterN(this.first, this.first + this.size, from)
+ }
+ },
+
+ // Non-public interface for adding and removing lines.
+ insert: function(at, lines) {
+ var height = 0
+ for (var i = 0; i < lines.length; ++i) {
+ height += lines[i].height
+ }
+ this.insertInner(at - this.first, lines, height)
+ },
+ remove: function(at, n) {
+ this.removeInner(at - this.first, n)
+ },
+
+ // From here, the methods are part of the public interface. Most
+ // are also available from CodeMirror (editor) instances.
+
+ getValue: function(lineSep) {
+ var lines = getLines(this, this.first, this.first + this.size)
+ if (lineSep === false) {
+ return lines
+ }
+ return lines.join(lineSep || this.lineSeparator())
+ },
+ setValue: docMethodOp(function(code) {
+ var top = Pos(this.first, 0),
+ last = this.first + this.size - 1
+ makeChange(
+ this,
+ {
+ from: top,
+ to: Pos(last, getLine(this, last).text.length),
+ text: this.splitLines(code),
+ origin: "setValue",
+ full: true
+ },
+ true
+ )
+ if (this.cm) {
+ scrollToCoords(this.cm, 0, 0)
+ }
+ setSelection(this, simpleSelection(top), sel_dontScroll)
+ }),
+ replaceRange: function(code, from, to, origin) {
+ from = clipPos(this, from)
+ to = to ? clipPos(this, to) : from
+ replaceRange(this, code, from, to, origin)
+ },
+ getRange: function(from, to, lineSep) {
+ var lines = getBetween(this, clipPos(this, from), clipPos(this, to))
+ if (lineSep === false) {
+ return lines
+ }
+ return lines.join(lineSep || this.lineSeparator())
+ },
+
+ getLine: function(line) {
+ var l = this.getLineHandle(line)
+ return l && l.text
+ },
+
+ getLineHandle: function(line) {
+ if (isLine(this, line)) {
+ return getLine(this, line)
+ }
+ },
+ getLineNumber: function(line) {
+ return lineNo(line)
+ },
+
+ getLineHandleVisualStart: function(line) {
+ if (typeof line == "number") {
+ line = getLine(this, line)
+ }
+ return visualLine(line)
+ },
+
+ lineCount: function() {
+ return this.size
+ },
+ firstLine: function() {
+ return this.first
+ },
+ lastLine: function() {
+ return this.first + this.size - 1
+ },
+
+ clipPos: function(pos) {
+ return clipPos(this, pos)
+ },
+
+ getCursor: function(start) {
+ var range = this.sel.primary(),
+ pos
+ if (start == null || start == "head") {
+ pos = range.head
+ } else if (start == "anchor") {
+ pos = range.anchor
+ } else if (start == "end" || start == "to" || start === false) {
+ pos = range.to()
+ } else {
+ pos = range.from()
+ }
+ return pos
+ },
+ listSelections: function() {
+ return this.sel.ranges
+ },
+ somethingSelected: function() {
+ return this.sel.somethingSelected()
+ },
+
+ setCursor: docMethodOp(function(line, ch, options) {
+ setSimpleSelection(
+ this,
+ clipPos(this, typeof line == "number" ? Pos(line, ch || 0) : line),
+ null,
+ options
+ )
+ }),
+ setSelection: docMethodOp(function(anchor, head, options) {
+ setSimpleSelection(this, clipPos(this, anchor), clipPos(this, head || anchor), options)
+ }),
+ extendSelection: docMethodOp(function(head, other, options) {
+ extendSelection(this, clipPos(this, head), other && clipPos(this, other), options)
+ }),
+ extendSelections: docMethodOp(function(heads, options) {
+ extendSelections(this, clipPosArray(this, heads), options)
+ }),
+ extendSelectionsBy: docMethodOp(function(f, options) {
+ var heads = map(this.sel.ranges, f)
+ extendSelections(this, clipPosArray(this, heads), options)
+ }),
+ setSelections: docMethodOp(function(ranges, primary, options) {
+ if (!ranges.length) {
+ return
+ }
+ var out = []
+ for (var i = 0; i < ranges.length; i++) {
+ out[i] = new Range(clipPos(this, ranges[i].anchor), clipPos(this, ranges[i].head))
+ }
+ if (primary == null) {
+ primary = Math.min(ranges.length - 1, this.sel.primIndex)
+ }
+ setSelection(this, normalizeSelection(this.cm, out, primary), options)
+ }),
+ addSelection: docMethodOp(function(anchor, head, options) {
+ var ranges = this.sel.ranges.slice(0)
+ ranges.push(new Range(clipPos(this, anchor), clipPos(this, head || anchor)))
+ setSelection(this, normalizeSelection(this.cm, ranges, ranges.length - 1), options)
+ }),
+
+ getSelection: function(lineSep) {
+ var ranges = this.sel.ranges,
+ lines
+ for (var i = 0; i < ranges.length; i++) {
+ var sel = getBetween(this, ranges[i].from(), ranges[i].to())
+ lines = lines ? lines.concat(sel) : sel
+ }
+ if (lineSep === false) {
+ return lines
+ } else {
+ return lines.join(lineSep || this.lineSeparator())
+ }
+ },
+ getSelections: function(lineSep) {
+ var parts = [],
+ ranges = this.sel.ranges
+ for (var i = 0; i < ranges.length; i++) {
+ var sel = getBetween(this, ranges[i].from(), ranges[i].to())
+ if (lineSep !== false) {
+ sel = sel.join(lineSep || this.lineSeparator())
+ }
+ parts[i] = sel
+ }
+ return parts
+ },
+ replaceSelection: function(code, collapse, origin) {
+ var dup = []
+ for (var i = 0; i < this.sel.ranges.length; i++) {
+ dup[i] = code
+ }
+ this.replaceSelections(dup, collapse, origin || "+input")
+ },
+ replaceSelections: docMethodOp(function(code, collapse, origin) {
+ var changes = [],
+ sel = this.sel
+ for (var i = 0; i < sel.ranges.length; i++) {
+ var range = sel.ranges[i]
+ changes[i] = {
+ from: range.from(),
+ to: range.to(),
+ text: this.splitLines(code[i]),
+ origin: origin
+ }
+ }
+ var newSel =
+ collapse && collapse != "end" && computeReplacedSel(this, changes, collapse)
+ for (var i$1 = changes.length - 1; i$1 >= 0; i$1--) {
+ makeChange(this, changes[i$1])
+ }
+ if (newSel) {
+ setSelectionReplaceHistory(this, newSel)
+ } else if (this.cm) {
+ ensureCursorVisible(this.cm)
+ }
+ }),
+ undo: docMethodOp(function() {
+ makeChangeFromHistory(this, "undo")
+ }),
+ redo: docMethodOp(function() {
+ makeChangeFromHistory(this, "redo")
+ }),
+ undoSelection: docMethodOp(function() {
+ makeChangeFromHistory(this, "undo", true)
+ }),
+ redoSelection: docMethodOp(function() {
+ makeChangeFromHistory(this, "redo", true)
+ }),
+
+ setExtending: function(val) {
+ this.extend = val
+ },
+ getExtending: function() {
+ return this.extend
+ },
+
+ historySize: function() {
+ var hist = this.history,
+ done = 0,
+ undone = 0
+ for (var i = 0; i < hist.done.length; i++) {
+ if (!hist.done[i].ranges) {
+ ++done
+ }
+ }
+ for (var i$1 = 0; i$1 < hist.undone.length; i$1++) {
+ if (!hist.undone[i$1].ranges) {
+ ++undone
+ }
+ }
+ return { undo: done, redo: undone }
+ },
+ clearHistory: function() {
+ var this$1 = this
+
+ this.history = new History(this.history.maxGeneration)
+ linkedDocs(
+ this,
+ function(doc) {
+ return (doc.history = this$1.history)
+ },
+ true
+ )
+ },
+
+ markClean: function() {
+ this.cleanGeneration = this.changeGeneration(true)
+ },
+ changeGeneration: function(forceSplit) {
+ if (forceSplit) {
+ this.history.lastOp = this.history.lastSelOp = this.history.lastOrigin = null
+ }
+ return this.history.generation
+ },
+ isClean: function(gen) {
+ return this.history.generation == (gen || this.cleanGeneration)
+ },
+
+ getHistory: function() {
+ return {
+ done: copyHistoryArray(this.history.done),
+ undone: copyHistoryArray(this.history.undone)
+ }
+ },
+ setHistory: function(histData) {
+ var hist = (this.history = new History(this.history.maxGeneration))
+ hist.done = copyHistoryArray(histData.done.slice(0), null, true)
+ hist.undone = copyHistoryArray(histData.undone.slice(0), null, true)
+ },
+
+ setGutterMarker: docMethodOp(function(line, gutterID, value) {
+ return changeLine(this, line, "gutter", function(line) {
+ var markers = line.gutterMarkers || (line.gutterMarkers = {})
+ markers[gutterID] = value
+ if (!value && isEmpty(markers)) {
+ line.gutterMarkers = null
+ }
+ return true
+ })
+ }),
+
+ clearGutter: docMethodOp(function(gutterID) {
+ var this$1 = this
+
+ this.iter(function(line) {
+ if (line.gutterMarkers && line.gutterMarkers[gutterID]) {
+ changeLine(this$1, line, "gutter", function() {
+ line.gutterMarkers[gutterID] = null
+ if (isEmpty(line.gutterMarkers)) {
+ line.gutterMarkers = null
+ }
+ return true
+ })
+ }
+ })
+ }),
+
+ lineInfo: function(line) {
+ var n
+ if (typeof line == "number") {
+ if (!isLine(this, line)) {
+ return null
+ }
+ n = line
+ line = getLine(this, line)
+ if (!line) {
+ return null
+ }
+ } else {
+ n = lineNo(line)
+ if (n == null) {
+ return null
+ }
+ }
+ return {
+ line: n,
+ handle: line,
+ text: line.text,
+ gutterMarkers: line.gutterMarkers,
+ textClass: line.textClass,
+ bgClass: line.bgClass,
+ wrapClass: line.wrapClass,
+ widgets: line.widgets
+ }
+ },
+
+ addLineClass: docMethodOp(function(handle, where, cls) {
+ return changeLine(this, handle, where == "gutter" ? "gutter" : "class", function(line) {
+ var prop =
+ where == "text"
+ ? "textClass"
+ : where == "background"
+ ? "bgClass"
+ : where == "gutter"
+ ? "gutterClass"
+ : "wrapClass"
+ if (!line[prop]) {
+ line[prop] = cls
+ } else if (classTest(cls).test(line[prop])) {
+ return false
+ } else {
+ line[prop] += " " + cls
+ }
+ return true
+ })
+ }),
+ removeLineClass: docMethodOp(function(handle, where, cls) {
+ return changeLine(this, handle, where == "gutter" ? "gutter" : "class", function(line) {
+ var prop =
+ where == "text"
+ ? "textClass"
+ : where == "background"
+ ? "bgClass"
+ : where == "gutter"
+ ? "gutterClass"
+ : "wrapClass"
+ var cur = line[prop]
+ if (!cur) {
+ return false
+ } else if (cls == null) {
+ line[prop] = null
+ } else {
+ var found = cur.match(classTest(cls))
+ if (!found) {
+ return false
+ }
+ var end = found.index + found[0].length
+ line[prop] =
+ cur.slice(0, found.index) +
+ (!found.index || end == cur.length ? "" : " ") +
+ cur.slice(end) || null
+ }
+ return true
+ })
+ }),
+
+ addLineWidget: docMethodOp(function(handle, node, options) {
+ return addLineWidget(this, handle, node, options)
+ }),
+ removeLineWidget: function(widget) {
+ widget.clear()
+ },
+
+ markText: function(from, to, options) {
+ return markText(
+ this,
+ clipPos(this, from),
+ clipPos(this, to),
+ options,
+ (options && options.type) || "range"
+ )
+ },
+ setBookmark: function(pos, options) {
+ var realOpts = {
+ replacedWith: options && (options.nodeType == null ? options.widget : options),
+ insertLeft: options && options.insertLeft,
+ clearWhenEmpty: false,
+ shared: options && options.shared,
+ handleMouseEvents: options && options.handleMouseEvents
+ }
+ pos = clipPos(this, pos)
+ return markText(this, pos, pos, realOpts, "bookmark")
+ },
+ findMarksAt: function(pos) {
+ pos = clipPos(this, pos)
+ var markers = [],
+ spans = getLine(this, pos.line).markedSpans
+ if (spans) {
+ for (var i = 0; i < spans.length; ++i) {
+ var span = spans[i]
+ if (
+ (span.from == null || span.from <= pos.ch) &&
+ (span.to == null || span.to >= pos.ch)
+ ) {
+ markers.push(span.marker.parent || span.marker)
+ }
+ }
+ }
+ return markers
+ },
+ findMarks: function(from, to, filter) {
+ from = clipPos(this, from)
+ to = clipPos(this, to)
+ var found = [],
+ lineNo = from.line
+ this.iter(from.line, to.line + 1, function(line) {
+ var spans = line.markedSpans
+ if (spans) {
+ for (var i = 0; i < spans.length; i++) {
+ var span = spans[i]
+ if (
+ !(
+ (span.to != null && lineNo == from.line && from.ch >= span.to) ||
+ (span.from == null && lineNo != from.line) ||
+ (span.from != null && lineNo == to.line && span.from >= to.ch)
+ ) &&
+ (!filter || filter(span.marker))
+ ) {
+ found.push(span.marker.parent || span.marker)
+ }
+ }
+ }
+ ++lineNo
+ })
+ return found
+ },
+ getAllMarks: function() {
+ var markers = []
+ this.iter(function(line) {
+ var sps = line.markedSpans
+ if (sps) {
+ for (var i = 0; i < sps.length; ++i) {
+ if (sps[i].from != null) {
+ markers.push(sps[i].marker)
+ }
+ }
+ }
+ })
+ return markers
+ },
+
+ posFromIndex: function(off) {
+ var ch,
+ lineNo = this.first,
+ sepSize = this.lineSeparator().length
+ this.iter(function(line) {
+ var sz = line.text.length + sepSize
+ if (sz > off) {
+ ch = off
+ return true
+ }
+ off -= sz
+ ++lineNo
+ })
+ return clipPos(this, Pos(lineNo, ch))
+ },
+ indexFromPos: function(coords) {
+ coords = clipPos(this, coords)
+ var index = coords.ch
+ if (coords.line < this.first || coords.ch < 0) {
+ return 0
+ }
+ var sepSize = this.lineSeparator().length
+ this.iter(this.first, coords.line, function(line) {
+ // iter aborts when callback returns a truthy value
+ index += line.text.length + sepSize
+ })
+ return index
+ },
+
+ copy: function(copyHistory) {
+ var doc = new Doc(
+ getLines(this, this.first, this.first + this.size),
+ this.modeOption,
+ this.first,
+ this.lineSep,
+ this.direction
+ )
+ doc.scrollTop = this.scrollTop
+ doc.scrollLeft = this.scrollLeft
+ doc.sel = this.sel
+ doc.extend = false
+ if (copyHistory) {
+ doc.history.undoDepth = this.history.undoDepth
+ doc.setHistory(this.getHistory())
+ }
+ return doc
+ },
+
+ linkedDoc: function(options) {
+ if (!options) {
+ options = {}
+ }
+ var from = this.first,
+ to = this.first + this.size
+ if (options.from != null && options.from > from) {
+ from = options.from
+ }
+ if (options.to != null && options.to < to) {
+ to = options.to
+ }
+ var copy = new Doc(
+ getLines(this, from, to),
+ options.mode || this.modeOption,
+ from,
+ this.lineSep,
+ this.direction
+ )
+ if (options.sharedHist) {
+ copy.history = this.history
+ }
+ ;(this.linked || (this.linked = [])).push({ doc: copy, sharedHist: options.sharedHist })
+ copy.linked = [{ doc: this, isParent: true, sharedHist: options.sharedHist }]
+ copySharedMarkers(copy, findSharedMarkers(this))
+ return copy
+ },
+ unlinkDoc: function(other) {
+ if (other instanceof CodeMirror) {
+ other = other.doc
+ }
+ if (this.linked) {
+ for (var i = 0; i < this.linked.length; ++i) {
+ var link = this.linked[i]
+ if (link.doc != other) {
+ continue
+ }
+ this.linked.splice(i, 1)
+ other.unlinkDoc(this)
+ detachSharedMarkers(findSharedMarkers(this))
+ break
+ }
+ }
+ // If the histories were shared, split them again
+ if (other.history == this.history) {
+ var splitIds = [other.id]
+ linkedDocs(
+ other,
+ function(doc) {
+ return splitIds.push(doc.id)
+ },
+ true
+ )
+ other.history = new History(null)
+ other.history.done = copyHistoryArray(this.history.done, splitIds)
+ other.history.undone = copyHistoryArray(this.history.undone, splitIds)
+ }
+ },
+ iterLinkedDocs: function(f) {
+ linkedDocs(this, f)
+ },
+
+ getMode: function() {
+ return this.mode
+ },
+ getEditor: function() {
+ return this.cm
+ },
+
+ splitLines: function(str) {
+ if (this.lineSep) {
+ return str.split(this.lineSep)
+ }
+ return splitLinesAuto(str)
+ },
+ lineSeparator: function() {
+ return this.lineSep || "\n"
+ },
+
+ setDirection: docMethodOp(function(dir) {
+ if (dir != "rtl") {
+ dir = "ltr"
+ }
+ if (dir == this.direction) {
+ return
+ }
+ this.direction = dir
+ this.iter(function(line) {
+ return (line.order = null)
+ })
+ if (this.cm) {
+ directionChanged(this.cm)
+ }
+ })
+ })
+
+ // Public alias.
+ Doc.prototype.eachLine = Doc.prototype.iter
+
+ // Kludge to work around strange IE behavior where it'll sometimes
+ // re-fire a series of drag-related events right after the drop (#1551)
+ var lastDrop = 0
+
+ function onDrop(e) {
+ var cm = this
+ clearDragCursor(cm)
+ if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e)) {
+ return
+ }
+ e_preventDefault(e)
+ if (ie) {
+ lastDrop = +new Date()
+ }
+ var pos = posFromMouse(cm, e, true),
+ files = e.dataTransfer.files
+ if (!pos || cm.isReadOnly()) {
+ return
+ }
+ // Might be a file drop, in which case we simply extract the text
+ // and insert it.
+ if (files && files.length && window.FileReader && window.File) {
+ var n = files.length,
+ text = Array(n),
+ read = 0
+ var markAsReadAndPasteIfAllFilesAreRead = function() {
+ if (++read == n) {
+ operation(cm, function() {
+ pos = clipPos(cm.doc, pos)
+ var change = {
+ from: pos,
+ to: pos,
+ text: cm.doc.splitLines(
+ text
+ .filter(function(t) {
+ return t != null
+ })
+ .join(cm.doc.lineSeparator())
+ ),
+ origin: "paste"
+ }
+ makeChange(cm.doc, change)
+ setSelectionReplaceHistory(
+ cm.doc,
+ simpleSelection(
+ clipPos(cm.doc, pos),
+ clipPos(cm.doc, changeEnd(change))
+ )
+ )
+ })()
+ }
+ }
+ var readTextFromFile = function(file, i) {
+ if (
+ cm.options.allowDropFileTypes &&
+ indexOf(cm.options.allowDropFileTypes, file.type) == -1
+ ) {
+ markAsReadAndPasteIfAllFilesAreRead()
+ return
+ }
+ var reader = new FileReader()
+ reader.onerror = function() {
+ return markAsReadAndPasteIfAllFilesAreRead()
+ }
+ reader.onload = function() {
+ var content = reader.result
+ if (/[\x00-\x08\x0e-\x1f]{2}/.test(content)) {
+ markAsReadAndPasteIfAllFilesAreRead()
+ return
+ }
+ text[i] = content
+ markAsReadAndPasteIfAllFilesAreRead()
+ }
+ reader.readAsText(file)
+ }
+ for (var i = 0; i < files.length; i++) {
+ readTextFromFile(files[i], i)
+ }
+ } else {
+ // Normal drop
+ // Don't do a replace if the drop happened inside of the selected text.
+ if (cm.state.draggingText && cm.doc.sel.contains(pos) > -1) {
+ cm.state.draggingText(e)
+ // Ensure the editor is re-focused
+ setTimeout(function() {
+ return cm.display.input.focus()
+ }, 20)
+ return
+ }
+ try {
+ var text$1 = e.dataTransfer.getData("Text")
+ if (text$1) {
+ var selected
+ if (cm.state.draggingText && !cm.state.draggingText.copy) {
+ selected = cm.listSelections()
+ }
+ setSelectionNoUndo(cm.doc, simpleSelection(pos, pos))
+ if (selected) {
+ for (var i$1 = 0; i$1 < selected.length; ++i$1) {
+ replaceRange(
+ cm.doc,
+ "",
+ selected[i$1].anchor,
+ selected[i$1].head,
+ "drag"
+ )
+ }
+ }
+ cm.replaceSelection(text$1, "around", "paste")
+ cm.display.input.focus()
+ }
+ } catch (e) {}
+ }
+ }
+
+ function onDragStart(cm, e) {
+ if (ie && (!cm.state.draggingText || +new Date() - lastDrop < 100)) {
+ e_stop(e)
+ return
+ }
+ if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e)) {
+ return
+ }
+
+ e.dataTransfer.setData("Text", cm.getSelection())
+ e.dataTransfer.effectAllowed = "copyMove"
+
+ // Use dummy image instead of default browsers image.
+ // Recent Safari (~6.0.2) have a tendency to segfault when this happens, so we don't do it there.
+ if (e.dataTransfer.setDragImage && !safari) {
+ var img = elt("img", null, null, "position: fixed; left: 0; top: 0;")
+ img.src = "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=="
+ if (presto) {
+ img.width = img.height = 1
+ cm.display.wrapper.appendChild(img)
+ // Force a relayout, or Opera won't use our image for some obscure reason
+ img._top = img.offsetTop
+ }
+ e.dataTransfer.setDragImage(img, 0, 0)
+ if (presto) {
+ img.parentNode.removeChild(img)
+ }
+ }
+ }
+
+ function onDragOver(cm, e) {
+ var pos = posFromMouse(cm, e)
+ if (!pos) {
+ return
+ }
+ var frag = document.createDocumentFragment()
+ drawSelectionCursor(cm, pos, frag)
+ if (!cm.display.dragCursor) {
+ cm.display.dragCursor = elt("div", null, "CodeMirror-cursors CodeMirror-dragcursors")
+ cm.display.lineSpace.insertBefore(cm.display.dragCursor, cm.display.cursorDiv)
+ }
+ removeChildrenAndAdd(cm.display.dragCursor, frag)
+ }
+
+ function clearDragCursor(cm) {
+ if (cm.display.dragCursor) {
+ cm.display.lineSpace.removeChild(cm.display.dragCursor)
+ cm.display.dragCursor = null
+ }
+ }
+
+ // These must be handled carefully, because naively registering a
+ // handler for each editor will cause the editors to never be
+ // garbage collected.
+
+ function forEachCodeMirror(f) {
+ if (!document.getElementsByClassName) {
+ return
+ }
+ var byClass = document.getElementsByClassName("CodeMirror"),
+ editors = []
+ for (var i = 0; i < byClass.length; i++) {
+ var cm = byClass[i].CodeMirror
+ if (cm) {
+ editors.push(cm)
+ }
+ }
+ if (editors.length) {
+ editors[0].operation(function() {
+ for (var i = 0; i < editors.length; i++) {
+ f(editors[i])
+ }
+ })
+ }
+ }
+
+ var globalsRegistered = false
+ function ensureGlobalHandlers() {
+ if (globalsRegistered) {
+ return
+ }
+ registerGlobalHandlers()
+ globalsRegistered = true
+ }
+ function registerGlobalHandlers() {
+ // When the window resizes, we need to refresh active editors.
+ var resizeTimer
+ on(window, "resize", function() {
+ if (resizeTimer == null) {
+ resizeTimer = setTimeout(function() {
+ resizeTimer = null
+ forEachCodeMirror(onResize)
+ }, 100)
+ }
+ })
+ // When the window loses focus, we want to show the editor as blurred
+ on(window, "blur", function() {
+ return forEachCodeMirror(onBlur)
+ })
+ }
+ // Called when the window resizes
+ function onResize(cm) {
+ var d = cm.display
+ // Might be a text scaling operation, clear size caches.
+ d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null
+ d.scrollbarsClipped = false
+ cm.setSize()
+ }
+
+ var keyNames = {
+ 3: "Pause",
+ 8: "Backspace",
+ 9: "Tab",
+ 13: "Enter",
+ 16: "Shift",
+ 17: "Ctrl",
+ 18: "Alt",
+ 19: "Pause",
+ 20: "CapsLock",
+ 27: "Esc",
+ 32: "Space",
+ 33: "PageUp",
+ 34: "PageDown",
+ 35: "End",
+ 36: "Home",
+ 37: "Left",
+ 38: "Up",
+ 39: "Right",
+ 40: "Down",
+ 44: "PrintScrn",
+ 45: "Insert",
+ 46: "Delete",
+ 59: ";",
+ 61: "=",
+ 91: "Mod",
+ 92: "Mod",
+ 93: "Mod",
+ 106: "*",
+ 107: "=",
+ 109: "-",
+ 110: ".",
+ 111: "/",
+ 145: "ScrollLock",
+ 173: "-",
+ 186: ";",
+ 187: "=",
+ 188: ",",
+ 189: "-",
+ 190: ".",
+ 191: "/",
+ 192: "`",
+ 219: "[",
+ 220: "\\",
+ 221: "]",
+ 222: "'",
+ 63232: "Up",
+ 63233: "Down",
+ 63234: "Left",
+ 63235: "Right",
+ 63272: "Delete",
+ 63273: "Home",
+ 63275: "End",
+ 63276: "PageUp",
+ 63277: "PageDown",
+ 63302: "Insert"
+ }
+
+ // Number keys
+ for (var i = 0; i < 10; i++) {
+ keyNames[i + 48] = keyNames[i + 96] = String(i)
+ }
+ // Alphabetic keys
+ for (var i$1 = 65; i$1 <= 90; i$1++) {
+ keyNames[i$1] = String.fromCharCode(i$1)
+ }
+ // Function keys
+ for (var i$2 = 1; i$2 <= 12; i$2++) {
+ keyNames[i$2 + 111] = keyNames[i$2 + 63235] = "F" + i$2
+ }
+
+ var keyMap = {}
+
+ keyMap.basic = {
+ Left: "goCharLeft",
+ Right: "goCharRight",
+ Up: "goLineUp",
+ Down: "goLineDown",
+ End: "goLineEnd",
+ Home: "goLineStartSmart",
+ PageUp: "goPageUp",
+ PageDown: "goPageDown",
+ Delete: "delCharAfter",
+ Backspace: "delCharBefore",
+ "Shift-Backspace": "delCharBefore",
+ Tab: "defaultTab",
+ "Shift-Tab": "indentAuto",
+ Enter: "newlineAndIndent",
+ Insert: "toggleOverwrite",
+ Esc: "singleSelection"
+ }
+ // Note that the save and find-related commands aren't defined by
+ // default. User code or addons can define them. Unknown commands
+ // are simply ignored.
+ keyMap.pcDefault = {
+ "Ctrl-A": "selectAll",
+ "Ctrl-D": "deleteLine",
+ "Ctrl-Z": "undo",
+ "Shift-Ctrl-Z": "redo",
+ "Ctrl-Y": "redo",
+ "Ctrl-Home": "goDocStart",
+ "Ctrl-End": "goDocEnd",
+ "Ctrl-Up": "goLineUp",
+ "Ctrl-Down": "goLineDown",
+ "Ctrl-Left": "goGroupLeft",
+ "Ctrl-Right": "goGroupRight",
+ "Alt-Left": "goLineStart",
+ "Alt-Right": "goLineEnd",
+ "Ctrl-Backspace": "delGroupBefore",
+ "Ctrl-Delete": "delGroupAfter",
+ "Ctrl-S": "save",
+ "Ctrl-F": "find",
+ "Ctrl-G": "findNext",
+ "Shift-Ctrl-G": "findPrev",
+ "Shift-Ctrl-F": "replace",
+ "Shift-Ctrl-R": "replaceAll",
+ "Ctrl-[": "indentLess",
+ "Ctrl-]": "indentMore",
+ "Ctrl-U": "undoSelection",
+ "Shift-Ctrl-U": "redoSelection",
+ "Alt-U": "redoSelection",
+ fallthrough: "basic"
+ }
+ // Very basic readline/emacs-style bindings, which are standard on Mac.
+ keyMap.emacsy = {
+ "Ctrl-F": "goCharRight",
+ "Ctrl-B": "goCharLeft",
+ "Ctrl-P": "goLineUp",
+ "Ctrl-N": "goLineDown",
+ "Alt-F": "goWordRight",
+ "Alt-B": "goWordLeft",
+ "Ctrl-A": "goLineStart",
+ "Ctrl-E": "goLineEnd",
+ "Ctrl-V": "goPageDown",
+ "Shift-Ctrl-V": "goPageUp",
+ "Ctrl-D": "delCharAfter",
+ "Ctrl-H": "delCharBefore",
+ "Alt-D": "delWordAfter",
+ "Alt-Backspace": "delWordBefore",
+ "Ctrl-K": "killLine",
+ "Ctrl-T": "transposeChars",
+ "Ctrl-O": "openLine"
+ }
+ keyMap.macDefault = {
+ "Cmd-A": "selectAll",
+ "Cmd-D": "deleteLine",
+ "Cmd-Z": "undo",
+ "Shift-Cmd-Z": "redo",
+ "Cmd-Y": "redo",
+ "Cmd-Home": "goDocStart",
+ "Cmd-Up": "goDocStart",
+ "Cmd-End": "goDocEnd",
+ "Cmd-Down": "goDocEnd",
+ "Alt-Left": "goGroupLeft",
+ "Alt-Right": "goGroupRight",
+ "Cmd-Left": "goLineLeft",
+ "Cmd-Right": "goLineRight",
+ "Alt-Backspace": "delGroupBefore",
+ "Ctrl-Alt-Backspace": "delGroupAfter",
+ "Alt-Delete": "delGroupAfter",
+ "Cmd-S": "save",
+ "Cmd-F": "find",
+ "Cmd-G": "findNext",
+ "Shift-Cmd-G": "findPrev",
+ "Cmd-Alt-F": "replace",
+ "Shift-Cmd-Alt-F": "replaceAll",
+ "Cmd-[": "indentLess",
+ "Cmd-]": "indentMore",
+ "Cmd-Backspace": "delWrappedLineLeft",
+ "Cmd-Delete": "delWrappedLineRight",
+ "Cmd-U": "undoSelection",
+ "Shift-Cmd-U": "redoSelection",
+ "Ctrl-Up": "goDocStart",
+ "Ctrl-Down": "goDocEnd",
+ fallthrough: ["basic", "emacsy"]
+ }
+ keyMap["default"] = mac ? keyMap.macDefault : keyMap.pcDefault
+
+ // KEYMAP DISPATCH
+
+ function normalizeKeyName(name) {
+ var parts = name.split(/-(?!$)/)
+ name = parts[parts.length - 1]
+ var alt, ctrl, shift, cmd
+ for (var i = 0; i < parts.length - 1; i++) {
+ var mod = parts[i]
+ if (/^(cmd|meta|m)$/i.test(mod)) {
+ cmd = true
+ } else if (/^a(lt)?$/i.test(mod)) {
+ alt = true
+ } else if (/^(c|ctrl|control)$/i.test(mod)) {
+ ctrl = true
+ } else if (/^s(hift)?$/i.test(mod)) {
+ shift = true
+ } else {
+ throw new Error("Unrecognized modifier name: " + mod)
+ }
+ }
+ if (alt) {
+ name = "Alt-" + name
+ }
+ if (ctrl) {
+ name = "Ctrl-" + name
+ }
+ if (cmd) {
+ name = "Cmd-" + name
+ }
+ if (shift) {
+ name = "Shift-" + name
+ }
+ return name
+ }
+
+ // This is a kludge to keep keymaps mostly working as raw objects
+ // (backwards compatibility) while at the same time support features
+ // like normalization and multi-stroke key bindings. It compiles a
+ // new normalized keymap, and then updates the old object to reflect
+ // this.
+ function normalizeKeyMap(keymap) {
+ var copy = {}
+ for (var keyname in keymap) {
+ if (keymap.hasOwnProperty(keyname)) {
+ var value = keymap[keyname]
+ if (/^(name|fallthrough|(de|at)tach)$/.test(keyname)) {
+ continue
+ }
+ if (value == "...") {
+ delete keymap[keyname]
+ continue
+ }
+
+ var keys = map(keyname.split(" "), normalizeKeyName)
+ for (var i = 0; i < keys.length; i++) {
+ var val = void 0,
+ name = void 0
+ if (i == keys.length - 1) {
+ name = keys.join(" ")
+ val = value
+ } else {
+ name = keys.slice(0, i + 1).join(" ")
+ val = "..."
+ }
+ var prev = copy[name]
+ if (!prev) {
+ copy[name] = val
+ } else if (prev != val) {
+ throw new Error("Inconsistent bindings for " + name)
+ }
+ }
+ delete keymap[keyname]
+ }
+ }
+ for (var prop in copy) {
+ keymap[prop] = copy[prop]
+ }
+ return keymap
+ }
+
+ function lookupKey(key, map, handle, context) {
+ map = getKeyMap(map)
+ var found = map.call ? map.call(key, context) : map[key]
+ if (found === false) {
+ return "nothing"
+ }
+ if (found === "...") {
+ return "multi"
+ }
+ if (found != null && handle(found)) {
+ return "handled"
+ }
+
+ if (map.fallthrough) {
+ if (Object.prototype.toString.call(map.fallthrough) != "[object Array]") {
+ return lookupKey(key, map.fallthrough, handle, context)
+ }
+ for (var i = 0; i < map.fallthrough.length; i++) {
+ var result = lookupKey(key, map.fallthrough[i], handle, context)
+ if (result) {
+ return result
+ }
+ }
+ }
+ }
+
+ // Modifier key presses don't count as 'real' key presses for the
+ // purpose of keymap fallthrough.
+ function isModifierKey(value) {
+ var name = typeof value == "string" ? value : keyNames[value.keyCode]
+ return name == "Ctrl" || name == "Alt" || name == "Shift" || name == "Mod"
+ }
+
+ function addModifierNames(name, event, noShift) {
+ var base = name
+ if (event.altKey && base != "Alt") {
+ name = "Alt-" + name
+ }
+ if ((flipCtrlCmd ? event.metaKey : event.ctrlKey) && base != "Ctrl") {
+ name = "Ctrl-" + name
+ }
+ if ((flipCtrlCmd ? event.ctrlKey : event.metaKey) && base != "Cmd") {
+ name = "Cmd-" + name
+ }
+ if (!noShift && event.shiftKey && base != "Shift") {
+ name = "Shift-" + name
+ }
+ return name
+ }
+
+ // Look up the name of a key as indicated by an event object.
+ function keyName(event, noShift) {
+ if (presto && event.keyCode == 34 && event["char"]) {
+ return false
+ }
+ var name = keyNames[event.keyCode]
+ if (name == null || event.altGraphKey) {
+ return false
+ }
+ // Ctrl-ScrollLock has keyCode 3, same as Ctrl-Pause,
+ // so we'll use event.code when available (Chrome 48+, FF 38+, Safari 10.1+)
+ if (event.keyCode == 3 && event.code) {
+ name = event.code
+ }
+ return addModifierNames(name, event, noShift)
+ }
+
+ function getKeyMap(val) {
+ return typeof val == "string" ? keyMap[val] : val
+ }
+
+ // Helper for deleting text near the selection(s), used to implement
+ // backspace, delete, and similar functionality.
+ function deleteNearSelection(cm, compute) {
+ var ranges = cm.doc.sel.ranges,
+ kill = []
+ // Build up a set of ranges to kill first, merging overlapping
+ // ranges.
+ for (var i = 0; i < ranges.length; i++) {
+ var toKill = compute(ranges[i])
+ while (kill.length && cmp(toKill.from, lst(kill).to) <= 0) {
+ var replaced = kill.pop()
+ if (cmp(replaced.from, toKill.from) < 0) {
+ toKill.from = replaced.from
+ break
+ }
+ }
+ kill.push(toKill)
+ }
+ // Next, remove those actual ranges.
+ runInOp(cm, function() {
+ for (var i = kill.length - 1; i >= 0; i--) {
+ replaceRange(cm.doc, "", kill[i].from, kill[i].to, "+delete")
+ }
+ ensureCursorVisible(cm)
+ })
+ }
+
+ function moveCharLogically(line, ch, dir) {
+ var target = skipExtendingChars(line.text, ch + dir, dir)
+ return target < 0 || target > line.text.length ? null : target
+ }
+
+ function moveLogically(line, start, dir) {
+ var ch = moveCharLogically(line, start.ch, dir)
+ return ch == null ? null : new Pos(start.line, ch, dir < 0 ? "after" : "before")
+ }
+
+ function endOfLine(visually, cm, lineObj, lineNo, dir) {
+ if (visually) {
+ if (cm.doc.direction == "rtl") {
+ dir = -dir
+ }
+ var order = getOrder(lineObj, cm.doc.direction)
+ if (order) {
+ var part = dir < 0 ? lst(order) : order[0]
+ var moveInStorageOrder = dir < 0 == (part.level == 1)
+ var sticky = moveInStorageOrder ? "after" : "before"
+ var ch
+ // With a wrapped rtl chunk (possibly spanning multiple bidi parts),
+ // it could be that the last bidi part is not on the last visual line,
+ // since visual lines contain content order-consecutive chunks.
+ // Thus, in rtl, we are looking for the first (content-order) character
+ // in the rtl chunk that is on the last line (that is, the same line
+ // as the last (content-order) character).
+ if (part.level > 0 || cm.doc.direction == "rtl") {
+ var prep = prepareMeasureForLine(cm, lineObj)
+ ch = dir < 0 ? lineObj.text.length - 1 : 0
+ var targetTop = measureCharPrepared(cm, prep, ch).top
+ ch = findFirst(
+ function(ch) {
+ return measureCharPrepared(cm, prep, ch).top == targetTop
+ },
+ dir < 0 == (part.level == 1) ? part.from : part.to - 1,
+ ch
+ )
+ if (sticky == "before") {
+ ch = moveCharLogically(lineObj, ch, 1)
+ }
+ } else {
+ ch = dir < 0 ? part.to : part.from
+ }
+ return new Pos(lineNo, ch, sticky)
+ }
+ }
+ return new Pos(lineNo, dir < 0 ? lineObj.text.length : 0, dir < 0 ? "before" : "after")
+ }
+
+ function moveVisually(cm, line, start, dir) {
+ var bidi = getOrder(line, cm.doc.direction)
+ if (!bidi) {
+ return moveLogically(line, start, dir)
+ }
+ if (start.ch >= line.text.length) {
+ start.ch = line.text.length
+ start.sticky = "before"
+ } else if (start.ch <= 0) {
+ start.ch = 0
+ start.sticky = "after"
+ }
+ var partPos = getBidiPartAt(bidi, start.ch, start.sticky),
+ part = bidi[partPos]
+ if (
+ cm.doc.direction == "ltr" &&
+ part.level % 2 == 0 &&
+ (dir > 0 ? part.to > start.ch : part.from < start.ch)
+ ) {
+ // Case 1: We move within an ltr part in an ltr editor. Even with wrapped lines,
+ // nothing interesting happens.
+ return moveLogically(line, start, dir)
+ }
+
+ var mv = function(pos, dir) {
+ return moveCharLogically(line, pos instanceof Pos ? pos.ch : pos, dir)
+ }
+ var prep
+ var getWrappedLineExtent = function(ch) {
+ if (!cm.options.lineWrapping) {
+ return { begin: 0, end: line.text.length }
+ }
+ prep = prep || prepareMeasureForLine(cm, line)
+ return wrappedLineExtentChar(cm, line, prep, ch)
+ }
+ var wrappedLineExtent = getWrappedLineExtent(
+ start.sticky == "before" ? mv(start, -1) : start.ch
+ )
+
+ if (cm.doc.direction == "rtl" || part.level == 1) {
+ var moveInStorageOrder = (part.level == 1) == dir < 0
+ var ch = mv(start, moveInStorageOrder ? 1 : -1)
+ if (
+ ch != null &&
+ (!moveInStorageOrder
+ ? ch >= part.from && ch >= wrappedLineExtent.begin
+ : ch <= part.to && ch <= wrappedLineExtent.end)
+ ) {
+ // Case 2: We move within an rtl part or in an rtl editor on the same visual line
+ var sticky = moveInStorageOrder ? "before" : "after"
+ return new Pos(start.line, ch, sticky)
+ }
+ }
+
+ // Case 3: Could not move within this bidi part in this visual line, so leave
+ // the current bidi part
+
+ var searchInVisualLine = function(partPos, dir, wrappedLineExtent) {
+ var getRes = function(ch, moveInStorageOrder) {
+ return moveInStorageOrder
+ ? new Pos(start.line, mv(ch, 1), "before")
+ : new Pos(start.line, ch, "after")
+ }
+
+ for (; partPos >= 0 && partPos < bidi.length; partPos += dir) {
+ var part = bidi[partPos]
+ var moveInStorageOrder = dir > 0 == (part.level != 1)
+ var ch = moveInStorageOrder
+ ? wrappedLineExtent.begin
+ : mv(wrappedLineExtent.end, -1)
+ if (part.from <= ch && ch < part.to) {
+ return getRes(ch, moveInStorageOrder)
+ }
+ ch = moveInStorageOrder ? part.from : mv(part.to, -1)
+ if (wrappedLineExtent.begin <= ch && ch < wrappedLineExtent.end) {
+ return getRes(ch, moveInStorageOrder)
+ }
+ }
+ }
+
+ // Case 3a: Look for other bidi parts on the same visual line
+ var res = searchInVisualLine(partPos + dir, dir, wrappedLineExtent)
+ if (res) {
+ return res
+ }
+
+ // Case 3b: Look for other bidi parts on the next visual line
+ var nextCh = dir > 0 ? wrappedLineExtent.end : mv(wrappedLineExtent.begin, -1)
+ if (nextCh != null && !(dir > 0 && nextCh == line.text.length)) {
+ res = searchInVisualLine(
+ dir > 0 ? 0 : bidi.length - 1,
+ dir,
+ getWrappedLineExtent(nextCh)
+ )
+ if (res) {
+ return res
+ }
+ }
+
+ // Case 4: Nowhere to move
+ return null
+ }
+
+ // Commands are parameter-less actions that can be performed on an
+ // editor, mostly used for keybindings.
+ var commands = {
+ selectAll: selectAll,
+ singleSelection: function(cm) {
+ return cm.setSelection(cm.getCursor("anchor"), cm.getCursor("head"), sel_dontScroll)
+ },
+ killLine: function(cm) {
+ return deleteNearSelection(cm, function(range) {
+ if (range.empty()) {
+ var len = getLine(cm.doc, range.head.line).text.length
+ if (range.head.ch == len && range.head.line < cm.lastLine()) {
+ return { from: range.head, to: Pos(range.head.line + 1, 0) }
+ } else {
+ return { from: range.head, to: Pos(range.head.line, len) }
+ }
+ } else {
+ return { from: range.from(), to: range.to() }
+ }
+ })
+ },
+ deleteLine: function(cm) {
+ return deleteNearSelection(cm, function(range) {
+ return {
+ from: Pos(range.from().line, 0),
+ to: clipPos(cm.doc, Pos(range.to().line + 1, 0))
+ }
+ })
+ },
+ delLineLeft: function(cm) {
+ return deleteNearSelection(cm, function(range) {
+ return {
+ from: Pos(range.from().line, 0),
+ to: range.from()
+ }
+ })
+ },
+ delWrappedLineLeft: function(cm) {
+ return deleteNearSelection(cm, function(range) {
+ var top = cm.charCoords(range.head, "div").top + 5
+ var leftPos = cm.coordsChar({ left: 0, top: top }, "div")
+ return { from: leftPos, to: range.from() }
+ })
+ },
+ delWrappedLineRight: function(cm) {
+ return deleteNearSelection(cm, function(range) {
+ var top = cm.charCoords(range.head, "div").top + 5
+ var rightPos = cm.coordsChar(
+ { left: cm.display.lineDiv.offsetWidth + 100, top: top },
+ "div"
+ )
+ return { from: range.from(), to: rightPos }
+ })
+ },
+ undo: function(cm) {
+ return cm.undo()
+ },
+ redo: function(cm) {
+ return cm.redo()
+ },
+ undoSelection: function(cm) {
+ return cm.undoSelection()
+ },
+ redoSelection: function(cm) {
+ return cm.redoSelection()
+ },
+ goDocStart: function(cm) {
+ return cm.extendSelection(Pos(cm.firstLine(), 0))
+ },
+ goDocEnd: function(cm) {
+ return cm.extendSelection(Pos(cm.lastLine()))
+ },
+ goLineStart: function(cm) {
+ return cm.extendSelectionsBy(
+ function(range) {
+ return lineStart(cm, range.head.line)
+ },
+ { origin: "+move", bias: 1 }
+ )
+ },
+ goLineStartSmart: function(cm) {
+ return cm.extendSelectionsBy(
+ function(range) {
+ return lineStartSmart(cm, range.head)
+ },
+ { origin: "+move", bias: 1 }
+ )
+ },
+ goLineEnd: function(cm) {
+ return cm.extendSelectionsBy(
+ function(range) {
+ return lineEnd(cm, range.head.line)
+ },
+ { origin: "+move", bias: -1 }
+ )
+ },
+ goLineRight: function(cm) {
+ return cm.extendSelectionsBy(function(range) {
+ var top = cm.cursorCoords(range.head, "div").top + 5
+ return cm.coordsChar(
+ { left: cm.display.lineDiv.offsetWidth + 100, top: top },
+ "div"
+ )
+ }, sel_move)
+ },
+ goLineLeft: function(cm) {
+ return cm.extendSelectionsBy(function(range) {
+ var top = cm.cursorCoords(range.head, "div").top + 5
+ return cm.coordsChar({ left: 0, top: top }, "div")
+ }, sel_move)
+ },
+ goLineLeftSmart: function(cm) {
+ return cm.extendSelectionsBy(function(range) {
+ var top = cm.cursorCoords(range.head, "div").top + 5
+ var pos = cm.coordsChar({ left: 0, top: top }, "div")
+ if (pos.ch < cm.getLine(pos.line).search(/\S/)) {
+ return lineStartSmart(cm, range.head)
+ }
+ return pos
+ }, sel_move)
+ },
+ goLineUp: function(cm) {
+ return cm.moveV(-1, "line")
+ },
+ goLineDown: function(cm) {
+ return cm.moveV(1, "line")
+ },
+ goPageUp: function(cm) {
+ return cm.moveV(-1, "page")
+ },
+ goPageDown: function(cm) {
+ return cm.moveV(1, "page")
+ },
+ goCharLeft: function(cm) {
+ return cm.moveH(-1, "char")
+ },
+ goCharRight: function(cm) {
+ return cm.moveH(1, "char")
+ },
+ goColumnLeft: function(cm) {
+ return cm.moveH(-1, "column")
+ },
+ goColumnRight: function(cm) {
+ return cm.moveH(1, "column")
+ },
+ goWordLeft: function(cm) {
+ return cm.moveH(-1, "word")
+ },
+ goGroupRight: function(cm) {
+ return cm.moveH(1, "group")
+ },
+ goGroupLeft: function(cm) {
+ return cm.moveH(-1, "group")
+ },
+ goWordRight: function(cm) {
+ return cm.moveH(1, "word")
+ },
+ delCharBefore: function(cm) {
+ return cm.deleteH(-1, "char")
+ },
+ delCharAfter: function(cm) {
+ return cm.deleteH(1, "char")
+ },
+ delWordBefore: function(cm) {
+ return cm.deleteH(-1, "word")
+ },
+ delWordAfter: function(cm) {
+ return cm.deleteH(1, "word")
+ },
+ delGroupBefore: function(cm) {
+ return cm.deleteH(-1, "group")
+ },
+ delGroupAfter: function(cm) {
+ return cm.deleteH(1, "group")
+ },
+ indentAuto: function(cm) {
+ return cm.indentSelection("smart")
+ },
+ indentMore: function(cm) {
+ return cm.indentSelection("add")
+ },
+ indentLess: function(cm) {
+ return cm.indentSelection("subtract")
+ },
+ insertTab: function(cm) {
+ return cm.replaceSelection("\t")
+ },
+ insertSoftTab: function(cm) {
+ var spaces = [],
+ ranges = cm.listSelections(),
+ tabSize = cm.options.tabSize
+ for (var i = 0; i < ranges.length; i++) {
+ var pos = ranges[i].from()
+ var col = countColumn(cm.getLine(pos.line), pos.ch, tabSize)
+ spaces.push(spaceStr(tabSize - (col % tabSize)))
+ }
+ cm.replaceSelections(spaces)
+ },
+ defaultTab: function(cm) {
+ if (cm.somethingSelected()) {
+ cm.indentSelection("add")
+ } else {
+ cm.execCommand("insertTab")
+ }
+ },
+ // Swap the two chars left and right of each selection's head.
+ // Move cursor behind the two swapped characters afterwards.
+ //
+ // Doesn't consider line feeds a character.
+ // Doesn't scan more than one line above to find a character.
+ // Doesn't do anything on an empty line.
+ // Doesn't do anything with non-empty selections.
+ transposeChars: function(cm) {
+ return runInOp(cm, function() {
+ var ranges = cm.listSelections(),
+ newSel = []
+ for (var i = 0; i < ranges.length; i++) {
+ if (!ranges[i].empty()) {
+ continue
+ }
+ var cur = ranges[i].head,
+ line = getLine(cm.doc, cur.line).text
+ if (line) {
+ if (cur.ch == line.length) {
+ cur = new Pos(cur.line, cur.ch - 1)
+ }
+ if (cur.ch > 0) {
+ cur = new Pos(cur.line, cur.ch + 1)
+ cm.replaceRange(
+ line.charAt(cur.ch - 1) + line.charAt(cur.ch - 2),
+ Pos(cur.line, cur.ch - 2),
+ cur,
+ "+transpose"
+ )
+ } else if (cur.line > cm.doc.first) {
+ var prev = getLine(cm.doc, cur.line - 1).text
+ if (prev) {
+ cur = new Pos(cur.line, 1)
+ cm.replaceRange(
+ line.charAt(0) +
+ cm.doc.lineSeparator() +
+ prev.charAt(prev.length - 1),
+ Pos(cur.line - 1, prev.length - 1),
+ cur,
+ "+transpose"
+ )
+ }
+ }
+ }
+ newSel.push(new Range(cur, cur))
+ }
+ cm.setSelections(newSel)
+ })
+ },
+ newlineAndIndent: function(cm) {
+ return runInOp(cm, function() {
+ var sels = cm.listSelections()
+ for (var i = sels.length - 1; i >= 0; i--) {
+ cm.replaceRange(cm.doc.lineSeparator(), sels[i].anchor, sels[i].head, "+input")
+ }
+ sels = cm.listSelections()
+ for (var i$1 = 0; i$1 < sels.length; i$1++) {
+ cm.indentLine(sels[i$1].from().line, null, true)
+ }
+ ensureCursorVisible(cm)
+ })
+ },
+ openLine: function(cm) {
+ return cm.replaceSelection("\n", "start")
+ },
+ toggleOverwrite: function(cm) {
+ return cm.toggleOverwrite()
+ }
+ }
+
+ function lineStart(cm, lineN) {
+ var line = getLine(cm.doc, lineN)
+ var visual = visualLine(line)
+ if (visual != line) {
+ lineN = lineNo(visual)
+ }
+ return endOfLine(true, cm, visual, lineN, 1)
+ }
+ function lineEnd(cm, lineN) {
+ var line = getLine(cm.doc, lineN)
+ var visual = visualLineEnd(line)
+ if (visual != line) {
+ lineN = lineNo(visual)
+ }
+ return endOfLine(true, cm, line, lineN, -1)
+ }
+ function lineStartSmart(cm, pos) {
+ var start = lineStart(cm, pos.line)
+ var line = getLine(cm.doc, start.line)
+ var order = getOrder(line, cm.doc.direction)
+ if (!order || order[0].level == 0) {
+ var firstNonWS = Math.max(start.ch, line.text.search(/\S/))
+ var inWS = pos.line == start.line && pos.ch <= firstNonWS && pos.ch
+ return Pos(start.line, inWS ? 0 : firstNonWS, start.sticky)
+ }
+ return start
+ }
+
+ // Run a handler that was bound to a key.
+ function doHandleBinding(cm, bound, dropShift) {
+ if (typeof bound == "string") {
+ bound = commands[bound]
+ if (!bound) {
+ return false
+ }
+ }
+ // Ensure previous input has been read, so that the handler sees a
+ // consistent view of the document
+ cm.display.input.ensurePolled()
+ var prevShift = cm.display.shift,
+ done = false
+ try {
+ if (cm.isReadOnly()) {
+ cm.state.suppressEdits = true
+ }
+ if (dropShift) {
+ cm.display.shift = false
+ }
+ done = bound(cm) != Pass
+ } finally {
+ cm.display.shift = prevShift
+ cm.state.suppressEdits = false
+ }
+ return done
+ }
+
+ function lookupKeyForEditor(cm, name, handle) {
+ for (var i = 0; i < cm.state.keyMaps.length; i++) {
+ var result = lookupKey(name, cm.state.keyMaps[i], handle, cm)
+ if (result) {
+ return result
+ }
+ }
+ return (
+ (cm.options.extraKeys && lookupKey(name, cm.options.extraKeys, handle, cm)) ||
+ lookupKey(name, cm.options.keyMap, handle, cm)
+ )
+ }
+
+ // Note that, despite the name, this function is also used to check
+ // for bound mouse clicks.
+
+ var stopSeq = new Delayed()
+
+ function dispatchKey(cm, name, e, handle) {
+ var seq = cm.state.keySeq
+ if (seq) {
+ if (isModifierKey(name)) {
+ return "handled"
+ }
+ if (/\'$/.test(name)) {
+ cm.state.keySeq = null
+ } else {
+ stopSeq.set(50, function() {
+ if (cm.state.keySeq == seq) {
+ cm.state.keySeq = null
+ cm.display.input.reset()
+ }
+ })
+ }
+ if (dispatchKeyInner(cm, seq + " " + name, e, handle)) {
+ return true
+ }
+ }
+ return dispatchKeyInner(cm, name, e, handle)
+ }
+
+ function dispatchKeyInner(cm, name, e, handle) {
+ var result = lookupKeyForEditor(cm, name, handle)
+
+ if (result == "multi") {
+ cm.state.keySeq = name
+ }
+ if (result == "handled") {
+ signalLater(cm, "keyHandled", cm, name, e)
+ }
+
+ if (result == "handled" || result == "multi") {
+ e_preventDefault(e)
+ restartBlink(cm)
+ }
+
+ return !!result
+ }
+
+ // Handle a key from the keydown event.
+ function handleKeyBinding(cm, e) {
+ var name = keyName(e, true)
+ if (!name) {
+ return false
+ }
+
+ if (e.shiftKey && !cm.state.keySeq) {
+ // First try to resolve full name (including 'Shift-'). Failing
+ // that, see if there is a cursor-motion command (starting with
+ // 'go') bound to the keyname without 'Shift-'.
+ return (
+ dispatchKey(cm, "Shift-" + name, e, function(b) {
+ return doHandleBinding(cm, b, true)
+ }) ||
+ dispatchKey(cm, name, e, function(b) {
+ if (typeof b == "string" ? /^go[A-Z]/.test(b) : b.motion) {
+ return doHandleBinding(cm, b)
+ }
+ })
+ )
+ } else {
+ return dispatchKey(cm, name, e, function(b) {
+ return doHandleBinding(cm, b)
+ })
+ }
+ }
+
+ // Handle a key from the keypress event
+ function handleCharBinding(cm, e, ch) {
+ return dispatchKey(cm, "'" + ch + "'", e, function(b) {
+ return doHandleBinding(cm, b, true)
+ })
+ }
+
+ var lastStoppedKey = null
+ function onKeyDown(e) {
+ var cm = this
+ cm.curOp.focus = activeElt()
+ if (signalDOMEvent(cm, e)) {
+ return
+ }
+ // IE does strange things with escape.
+ if (ie && ie_version < 11 && e.keyCode == 27) {
+ e.returnValue = false
+ }
+ var code = e.keyCode
+ cm.display.shift = code == 16 || e.shiftKey
+ var handled = handleKeyBinding(cm, e)
+ if (presto) {
+ lastStoppedKey = handled ? code : null
+ // Opera has no cut event... we try to at least catch the key combo
+ if (!handled && code == 88 && !hasCopyEvent && (mac ? e.metaKey : e.ctrlKey)) {
+ cm.replaceSelection("", null, "cut")
+ }
+ }
+ if (
+ gecko &&
+ !mac &&
+ !handled &&
+ code == 46 &&
+ e.shiftKey &&
+ !e.ctrlKey &&
+ document.execCommand
+ ) {
+ document.execCommand("cut")
+ }
+
+ // Turn mouse into crosshair when Alt is held on Mac.
+ if (code == 18 && !/\bCodeMirror-crosshair\b/.test(cm.display.lineDiv.className)) {
+ showCrossHair(cm)
+ }
+ }
+
+ function showCrossHair(cm) {
+ var lineDiv = cm.display.lineDiv
+ addClass(lineDiv, "CodeMirror-crosshair")
+
+ function up(e) {
+ if (e.keyCode == 18 || !e.altKey) {
+ rmClass(lineDiv, "CodeMirror-crosshair")
+ off(document, "keyup", up)
+ off(document, "mouseover", up)
+ }
+ }
+ on(document, "keyup", up)
+ on(document, "mouseover", up)
+ }
+
+ function onKeyUp(e) {
+ if (e.keyCode == 16) {
+ this.doc.sel.shift = false
+ }
+ signalDOMEvent(this, e)
+ }
+
+ function onKeyPress(e) {
+ var cm = this
+ if (
+ eventInWidget(cm.display, e) ||
+ signalDOMEvent(cm, e) ||
+ (e.ctrlKey && !e.altKey) ||
+ (mac && e.metaKey)
+ ) {
+ return
+ }
+ var keyCode = e.keyCode,
+ charCode = e.charCode
+ if (presto && keyCode == lastStoppedKey) {
+ lastStoppedKey = null
+ e_preventDefault(e)
+ return
+ }
+ if (presto && (!e.which || e.which < 10) && handleKeyBinding(cm, e)) {
+ return
+ }
+ var ch = String.fromCharCode(charCode == null ? keyCode : charCode)
+ // Some browsers fire keypress events for backspace
+ if (ch == "\x08") {
+ return
+ }
+ if (handleCharBinding(cm, e, ch)) {
+ return
+ }
+ cm.display.input.onKeyPress(e)
+ }
+
+ var DOUBLECLICK_DELAY = 400
+
+ var PastClick = function(time, pos, button) {
+ this.time = time
+ this.pos = pos
+ this.button = button
+ }
+
+ PastClick.prototype.compare = function(time, pos, button) {
+ return (
+ this.time + DOUBLECLICK_DELAY > time && cmp(pos, this.pos) == 0 && button == this.button
+ )
+ }
+
+ var lastClick, lastDoubleClick
+ function clickRepeat(pos, button) {
+ var now = +new Date()
+ if (lastDoubleClick && lastDoubleClick.compare(now, pos, button)) {
+ lastClick = lastDoubleClick = null
+ return "triple"
+ } else if (lastClick && lastClick.compare(now, pos, button)) {
+ lastDoubleClick = new PastClick(now, pos, button)
+ lastClick = null
+ return "double"
+ } else {
+ lastClick = new PastClick(now, pos, button)
+ lastDoubleClick = null
+ return "single"
+ }
+ }
+
+ // A mouse down can be a single click, double click, triple click,
+ // start of selection drag, start of text drag, new cursor
+ // (ctrl-click), rectangle drag (alt-drag), or xwin
+ // middle-click-paste. Or it might be a click on something we should
+ // not interfere with, such as a scrollbar or widget.
+ function onMouseDown(e) {
+ var cm = this,
+ display = cm.display
+ if (signalDOMEvent(cm, e) || (display.activeTouch && display.input.supportsTouch())) {
+ return
+ }
+ display.input.ensurePolled()
+ display.shift = e.shiftKey
+
+ if (eventInWidget(display, e)) {
+ if (!webkit) {
+ // Briefly turn off draggability, to allow widgets to do
+ // normal dragging things.
+ display.scroller.draggable = false
+ setTimeout(function() {
+ return (display.scroller.draggable = true)
+ }, 100)
+ }
+ return
+ }
+ if (clickInGutter(cm, e)) {
+ return
+ }
+ var pos = posFromMouse(cm, e),
+ button = e_button(e),
+ repeat = pos ? clickRepeat(pos, button) : "single"
+ window.focus()
+
+ // #3261: make sure, that we're not starting a second selection
+ if (button == 1 && cm.state.selectingText) {
+ cm.state.selectingText(e)
+ }
+
+ if (pos && handleMappedButton(cm, button, pos, repeat, e)) {
+ return
+ }
+
+ if (button == 1) {
+ if (pos) {
+ leftButtonDown(cm, pos, repeat, e)
+ } else if (e_target(e) == display.scroller) {
+ e_preventDefault(e)
+ }
+ } else if (button == 2) {
+ if (pos) {
+ extendSelection(cm.doc, pos)
+ }
+ setTimeout(function() {
+ return display.input.focus()
+ }, 20)
+ } else if (button == 3) {
+ if (captureRightClick) {
+ cm.display.input.onContextMenu(e)
+ } else {
+ delayBlurEvent(cm)
+ }
+ }
+ }
+
+ function handleMappedButton(cm, button, pos, repeat, event) {
+ var name = "Click"
+ if (repeat == "double") {
+ name = "Double" + name
+ } else if (repeat == "triple") {
+ name = "Triple" + name
+ }
+ name = (button == 1 ? "Left" : button == 2 ? "Middle" : "Right") + name
+
+ return dispatchKey(cm, addModifierNames(name, event), event, function(bound) {
+ if (typeof bound == "string") {
+ bound = commands[bound]
+ }
+ if (!bound) {
+ return false
+ }
+ var done = false
+ try {
+ if (cm.isReadOnly()) {
+ cm.state.suppressEdits = true
+ }
+ done = bound(cm, pos) != Pass
+ } finally {
+ cm.state.suppressEdits = false
+ }
+ return done
+ })
+ }
+
+ function configureMouse(cm, repeat, event) {
+ var option = cm.getOption("configureMouse")
+ var value = option ? option(cm, repeat, event) : {}
+ if (value.unit == null) {
+ var rect = chromeOS ? event.shiftKey && event.metaKey : event.altKey
+ value.unit = rect
+ ? "rectangle"
+ : repeat == "single"
+ ? "char"
+ : repeat == "double"
+ ? "word"
+ : "line"
+ }
+ if (value.extend == null || cm.doc.extend) {
+ value.extend = cm.doc.extend || event.shiftKey
+ }
+ if (value.addNew == null) {
+ value.addNew = mac ? event.metaKey : event.ctrlKey
+ }
+ if (value.moveOnDrag == null) {
+ value.moveOnDrag = !(mac ? event.altKey : event.ctrlKey)
+ }
+ return value
+ }
+
+ function leftButtonDown(cm, pos, repeat, event) {
+ if (ie) {
+ setTimeout(bind(ensureFocus, cm), 0)
+ } else {
+ cm.curOp.focus = activeElt()
+ }
+
+ var behavior = configureMouse(cm, repeat, event)
+
+ var sel = cm.doc.sel,
+ contained
+ if (
+ cm.options.dragDrop &&
+ dragAndDrop &&
+ !cm.isReadOnly() &&
+ repeat == "single" &&
+ (contained = sel.contains(pos)) > -1 &&
+ (cmp((contained = sel.ranges[contained]).from(), pos) < 0 || pos.xRel > 0) &&
+ (cmp(contained.to(), pos) > 0 || pos.xRel < 0)
+ ) {
+ leftButtonStartDrag(cm, event, pos, behavior)
+ } else {
+ leftButtonSelect(cm, event, pos, behavior)
+ }
+ }
+
+ // Start a text drag. When it ends, see if any dragging actually
+ // happen, and treat as a click if it didn't.
+ function leftButtonStartDrag(cm, event, pos, behavior) {
+ var display = cm.display,
+ moved = false
+ var dragEnd = operation(cm, function(e) {
+ if (webkit) {
+ display.scroller.draggable = false
+ }
+ cm.state.draggingText = false
+ off(display.wrapper.ownerDocument, "mouseup", dragEnd)
+ off(display.wrapper.ownerDocument, "mousemove", mouseMove)
+ off(display.scroller, "dragstart", dragStart)
+ off(display.scroller, "drop", dragEnd)
+ if (!moved) {
+ e_preventDefault(e)
+ if (!behavior.addNew) {
+ extendSelection(cm.doc, pos, null, null, behavior.extend)
+ }
+ // Work around unexplainable focus problem in IE9 (#2127) and Chrome (#3081)
+ if (webkit || (ie && ie_version == 9)) {
+ setTimeout(function() {
+ display.wrapper.ownerDocument.body.focus()
+ display.input.focus()
+ }, 20)
+ } else {
+ display.input.focus()
+ }
+ }
+ })
+ var mouseMove = function(e2) {
+ moved =
+ moved ||
+ Math.abs(event.clientX - e2.clientX) + Math.abs(event.clientY - e2.clientY) >= 10
+ }
+ var dragStart = function() {
+ return (moved = true)
+ }
+ // Let the drag handler handle this.
+ if (webkit) {
+ display.scroller.draggable = true
+ }
+ cm.state.draggingText = dragEnd
+ dragEnd.copy = !behavior.moveOnDrag
+ // IE's approach to draggable
+ if (display.scroller.dragDrop) {
+ display.scroller.dragDrop()
+ }
+ on(display.wrapper.ownerDocument, "mouseup", dragEnd)
+ on(display.wrapper.ownerDocument, "mousemove", mouseMove)
+ on(display.scroller, "dragstart", dragStart)
+ on(display.scroller, "drop", dragEnd)
+
+ delayBlurEvent(cm)
+ setTimeout(function() {
+ return display.input.focus()
+ }, 20)
+ }
+
+ function rangeForUnit(cm, pos, unit) {
+ if (unit == "char") {
+ return new Range(pos, pos)
+ }
+ if (unit == "word") {
+ return cm.findWordAt(pos)
+ }
+ if (unit == "line") {
+ return new Range(Pos(pos.line, 0), clipPos(cm.doc, Pos(pos.line + 1, 0)))
+ }
+ var result = unit(cm, pos)
+ return new Range(result.from, result.to)
+ }
+
+ // Normal selection, as opposed to text dragging.
+ function leftButtonSelect(cm, event, start, behavior) {
+ var display = cm.display,
+ doc = cm.doc
+ e_preventDefault(event)
+
+ var ourRange,
+ ourIndex,
+ startSel = doc.sel,
+ ranges = startSel.ranges
+ if (behavior.addNew && !behavior.extend) {
+ ourIndex = doc.sel.contains(start)
+ if (ourIndex > -1) {
+ ourRange = ranges[ourIndex]
+ } else {
+ ourRange = new Range(start, start)
+ }
+ } else {
+ ourRange = doc.sel.primary()
+ ourIndex = doc.sel.primIndex
+ }
+
+ if (behavior.unit == "rectangle") {
+ if (!behavior.addNew) {
+ ourRange = new Range(start, start)
+ }
+ start = posFromMouse(cm, event, true, true)
+ ourIndex = -1
+ } else {
+ var range = rangeForUnit(cm, start, behavior.unit)
+ if (behavior.extend) {
+ ourRange = extendRange(ourRange, range.anchor, range.head, behavior.extend)
+ } else {
+ ourRange = range
+ }
+ }
+
+ if (!behavior.addNew) {
+ ourIndex = 0
+ setSelection(doc, new Selection([ourRange], 0), sel_mouse)
+ startSel = doc.sel
+ } else if (ourIndex == -1) {
+ ourIndex = ranges.length
+ setSelection(doc, normalizeSelection(cm, ranges.concat([ourRange]), ourIndex), {
+ scroll: false,
+ origin: "*mouse"
+ })
+ } else if (
+ ranges.length > 1 &&
+ ranges[ourIndex].empty() &&
+ behavior.unit == "char" &&
+ !behavior.extend
+ ) {
+ setSelection(
+ doc,
+ normalizeSelection(
+ cm,
+ ranges.slice(0, ourIndex).concat(ranges.slice(ourIndex + 1)),
+ 0
+ ),
+ { scroll: false, origin: "*mouse" }
+ )
+ startSel = doc.sel
+ } else {
+ replaceOneSelection(doc, ourIndex, ourRange, sel_mouse)
+ }
+
+ var lastPos = start
+ function extendTo(pos) {
+ if (cmp(lastPos, pos) == 0) {
+ return
+ }
+ lastPos = pos
+
+ if (behavior.unit == "rectangle") {
+ var ranges = [],
+ tabSize = cm.options.tabSize
+ var startCol = countColumn(getLine(doc, start.line).text, start.ch, tabSize)
+ var posCol = countColumn(getLine(doc, pos.line).text, pos.ch, tabSize)
+ var left = Math.min(startCol, posCol),
+ right = Math.max(startCol, posCol)
+ for (
+ var line = Math.min(start.line, pos.line),
+ end = Math.min(cm.lastLine(), Math.max(start.line, pos.line));
+ line <= end;
+ line++
+ ) {
+ var text = getLine(doc, line).text,
+ leftPos = findColumn(text, left, tabSize)
+ if (left == right) {
+ ranges.push(new Range(Pos(line, leftPos), Pos(line, leftPos)))
+ } else if (text.length > leftPos) {
+ ranges.push(
+ new Range(
+ Pos(line, leftPos),
+ Pos(line, findColumn(text, right, tabSize))
+ )
+ )
+ }
+ }
+ if (!ranges.length) {
+ ranges.push(new Range(start, start))
+ }
+ setSelection(
+ doc,
+ normalizeSelection(
+ cm,
+ startSel.ranges.slice(0, ourIndex).concat(ranges),
+ ourIndex
+ ),
+ { origin: "*mouse", scroll: false }
+ )
+ cm.scrollIntoView(pos)
+ } else {
+ var oldRange = ourRange
+ var range = rangeForUnit(cm, pos, behavior.unit)
+ var anchor = oldRange.anchor,
+ head
+ if (cmp(range.anchor, anchor) > 0) {
+ head = range.head
+ anchor = minPos(oldRange.from(), range.anchor)
+ } else {
+ head = range.anchor
+ anchor = maxPos(oldRange.to(), range.head)
+ }
+ var ranges$1 = startSel.ranges.slice(0)
+ ranges$1[ourIndex] = bidiSimplify(cm, new Range(clipPos(doc, anchor), head))
+ setSelection(doc, normalizeSelection(cm, ranges$1, ourIndex), sel_mouse)
+ }
+ }
+
+ var editorSize = display.wrapper.getBoundingClientRect()
+ // Used to ensure timeout re-tries don't fire when another extend
+ // happened in the meantime (clearTimeout isn't reliable -- at
+ // least on Chrome, the timeouts still happen even when cleared,
+ // if the clear happens after their scheduled firing time).
+ var counter = 0
+
+ function extend(e) {
+ var curCount = ++counter
+ var cur = posFromMouse(cm, e, true, behavior.unit == "rectangle")
+ if (!cur) {
+ return
+ }
+ if (cmp(cur, lastPos) != 0) {
+ cm.curOp.focus = activeElt()
+ extendTo(cur)
+ var visible = visibleLines(display, doc)
+ if (cur.line >= visible.to || cur.line < visible.from) {
+ setTimeout(
+ operation(cm, function() {
+ if (counter == curCount) {
+ extend(e)
+ }
+ }),
+ 150
+ )
+ }
+ } else {
+ var outside =
+ e.clientY < editorSize.top ? -20 : e.clientY > editorSize.bottom ? 20 : 0
+ if (outside) {
+ setTimeout(
+ operation(cm, function() {
+ if (counter != curCount) {
+ return
+ }
+ display.scroller.scrollTop += outside
+ extend(e)
+ }),
+ 50
+ )
+ }
+ }
+ }
+
+ function done(e) {
+ cm.state.selectingText = false
+ counter = Infinity
+ // If e is null or undefined we interpret this as someone trying
+ // to explicitly cancel the selection rather than the user
+ // letting go of the mouse button.
+ if (e) {
+ e_preventDefault(e)
+ display.input.focus()
+ }
+ off(display.wrapper.ownerDocument, "mousemove", move)
+ off(display.wrapper.ownerDocument, "mouseup", up)
+ doc.history.lastSelOrigin = null
+ }
+
+ var move = operation(cm, function(e) {
+ if (e.buttons === 0 || !e_button(e)) {
+ done(e)
+ } else {
+ extend(e)
+ }
+ })
+ var up = operation(cm, done)
+ cm.state.selectingText = up
+ on(display.wrapper.ownerDocument, "mousemove", move)
+ on(display.wrapper.ownerDocument, "mouseup", up)
+ }
+
+ // Used when mouse-selecting to adjust the anchor to the proper side
+ // of a bidi jump depending on the visual position of the head.
+ function bidiSimplify(cm, range) {
+ var anchor = range.anchor
+ var head = range.head
+ var anchorLine = getLine(cm.doc, anchor.line)
+ if (cmp(anchor, head) == 0 && anchor.sticky == head.sticky) {
+ return range
+ }
+ var order = getOrder(anchorLine)
+ if (!order) {
+ return range
+ }
+ var index = getBidiPartAt(order, anchor.ch, anchor.sticky),
+ part = order[index]
+ if (part.from != anchor.ch && part.to != anchor.ch) {
+ return range
+ }
+ var boundary = index + ((part.from == anchor.ch) == (part.level != 1) ? 0 : 1)
+ if (boundary == 0 || boundary == order.length) {
+ return range
+ }
+
+ // Compute the relative visual position of the head compared to the
+ // anchor (<0 is to the left, >0 to the right)
+ var leftSide
+ if (head.line != anchor.line) {
+ leftSide = (head.line - anchor.line) * (cm.doc.direction == "ltr" ? 1 : -1) > 0
+ } else {
+ var headIndex = getBidiPartAt(order, head.ch, head.sticky)
+ var dir = headIndex - index || (head.ch - anchor.ch) * (part.level == 1 ? -1 : 1)
+ if (headIndex == boundary - 1 || headIndex == boundary) {
+ leftSide = dir < 0
+ } else {
+ leftSide = dir > 0
+ }
+ }
+
+ var usePart = order[boundary + (leftSide ? -1 : 0)]
+ var from = leftSide == (usePart.level == 1)
+ var ch = from ? usePart.from : usePart.to,
+ sticky = from ? "after" : "before"
+ return anchor.ch == ch && anchor.sticky == sticky
+ ? range
+ : new Range(new Pos(anchor.line, ch, sticky), head)
+ }
+
+ // Determines whether an event happened in the gutter, and fires the
+ // handlers for the corresponding event.
+ function gutterEvent(cm, e, type, prevent) {
+ var mX, mY
+ if (e.touches) {
+ mX = e.touches[0].clientX
+ mY = e.touches[0].clientY
+ } else {
+ try {
+ mX = e.clientX
+ mY = e.clientY
+ } catch (e) {
+ return false
+ }
+ }
+ if (mX >= Math.floor(cm.display.gutters.getBoundingClientRect().right)) {
+ return false
+ }
+ if (prevent) {
+ e_preventDefault(e)
+ }
+
+ var display = cm.display
+ var lineBox = display.lineDiv.getBoundingClientRect()
+
+ if (mY > lineBox.bottom || !hasHandler(cm, type)) {
+ return e_defaultPrevented(e)
+ }
+ mY -= lineBox.top - display.viewOffset
+
+ for (var i = 0; i < cm.display.gutterSpecs.length; ++i) {
+ var g = display.gutters.childNodes[i]
+ if (g && g.getBoundingClientRect().right >= mX) {
+ var line = lineAtHeight(cm.doc, mY)
+ var gutter = cm.display.gutterSpecs[i]
+ signal(cm, type, cm, line, gutter.className, e)
+ return e_defaultPrevented(e)
+ }
+ }
+ }
+
+ function clickInGutter(cm, e) {
+ return gutterEvent(cm, e, "gutterClick", true)
+ }
+
+ // CONTEXT MENU HANDLING
+
+ // To make the context menu work, we need to briefly unhide the
+ // textarea (making it as unobtrusive as possible) to let the
+ // right-click take effect on it.
+ function onContextMenu(cm, e) {
+ if (eventInWidget(cm.display, e) || contextMenuInGutter(cm, e)) {
+ return
+ }
+ if (signalDOMEvent(cm, e, "contextmenu")) {
+ return
+ }
+ if (!captureRightClick) {
+ cm.display.input.onContextMenu(e)
+ }
+ }
+
+ function contextMenuInGutter(cm, e) {
+ if (!hasHandler(cm, "gutterContextMenu")) {
+ return false
+ }
+ return gutterEvent(cm, e, "gutterContextMenu", false)
+ }
+
+ function themeChanged(cm) {
+ cm.display.wrapper.className =
+ cm.display.wrapper.className.replace(/\s*cm-s-\S+/g, "") +
+ cm.options.theme.replace(/(^|\s)\s*/g, " cm-s-")
+ clearCaches(cm)
+ }
+
+ var Init = {
+ toString: function() {
+ return "CodeMirror.Init"
+ }
+ }
+
+ var defaults = {}
+ var optionHandlers = {}
+
+ function defineOptions(CodeMirror) {
+ var optionHandlers = CodeMirror.optionHandlers
+
+ function option(name, deflt, handle, notOnInit) {
+ CodeMirror.defaults[name] = deflt
+ if (handle) {
+ optionHandlers[name] = notOnInit
+ ? function(cm, val, old) {
+ if (old != Init) {
+ handle(cm, val, old)
+ }
+ }
+ : handle
+ }
+ }
+
+ CodeMirror.defineOption = option
+
+ // Passed to option handlers when there is no old value.
+ CodeMirror.Init = Init
+
+ // These two are, on init, called from the constructor because they
+ // have to be initialized before the editor can start at all.
+ option(
+ "value",
+ "",
+ function(cm, val) {
+ return cm.setValue(val)
+ },
+ true
+ )
+ option(
+ "mode",
+ null,
+ function(cm, val) {
+ cm.doc.modeOption = val
+ loadMode(cm)
+ },
+ true
+ )
+
+ option("indentUnit", 2, loadMode, true)
+ option("indentWithTabs", false)
+ option("smartIndent", true)
+ option(
+ "tabSize",
+ 4,
+ function(cm) {
+ resetModeState(cm)
+ clearCaches(cm)
+ regChange(cm)
+ },
+ true
+ )
+
+ option("lineSeparator", null, function(cm, val) {
+ cm.doc.lineSep = val
+ if (!val) {
+ return
+ }
+ var newBreaks = [],
+ lineNo = cm.doc.first
+ cm.doc.iter(function(line) {
+ for (var pos = 0; ; ) {
+ var found = line.text.indexOf(val, pos)
+ if (found == -1) {
+ break
+ }
+ pos = found + val.length
+ newBreaks.push(Pos(lineNo, found))
+ }
+ lineNo++
+ })
+ for (var i = newBreaks.length - 1; i >= 0; i--) {
+ replaceRange(
+ cm.doc,
+ val,
+ newBreaks[i],
+ Pos(newBreaks[i].line, newBreaks[i].ch + val.length)
+ )
+ }
+ })
+ option(
+ "specialChars",
+ /[\u0000-\u001f\u007f-\u009f\u00ad\u061c\u200b-\u200f\u2028\u2029\ufeff\ufff9-\ufffc]/g,
+ function(cm, val, old) {
+ cm.state.specialChars = new RegExp(val.source + (val.test("\t") ? "" : "|\t"), "g")
+ if (old != Init) {
+ cm.refresh()
+ }
+ }
+ )
+ option(
+ "specialCharPlaceholder",
+ defaultSpecialCharPlaceholder,
+ function(cm) {
+ return cm.refresh()
+ },
+ true
+ )
+ option("electricChars", true)
+ option(
+ "inputStyle",
+ mobile ? "contenteditable" : "textarea",
+ function() {
+ throw new Error("inputStyle can not (yet) be changed in a running editor") // FIXME
+ },
+ true
+ )
+ option(
+ "spellcheck",
+ false,
+ function(cm, val) {
+ return (cm.getInputField().spellcheck = val)
+ },
+ true
+ )
+ option(
+ "autocorrect",
+ false,
+ function(cm, val) {
+ return (cm.getInputField().autocorrect = val)
+ },
+ true
+ )
+ option(
+ "autocapitalize",
+ false,
+ function(cm, val) {
+ return (cm.getInputField().autocapitalize = val)
+ },
+ true
+ )
+ option("rtlMoveVisually", !windows)
+ option("wholeLineUpdateBefore", true)
+
+ option(
+ "theme",
+ "default",
+ function(cm) {
+ themeChanged(cm)
+ updateGutters(cm)
+ },
+ true
+ )
+ option("keyMap", "default", function(cm, val, old) {
+ var next = getKeyMap(val)
+ var prev = old != Init && getKeyMap(old)
+ if (prev && prev.detach) {
+ prev.detach(cm, next)
+ }
+ if (next.attach) {
+ next.attach(cm, prev || null)
+ }
+ })
+ option("extraKeys", null)
+ option("configureMouse", null)
+
+ option("lineWrapping", false, wrappingChanged, true)
+ option(
+ "gutters",
+ [],
+ function(cm, val) {
+ cm.display.gutterSpecs = getGutters(val, cm.options.lineNumbers)
+ updateGutters(cm)
+ },
+ true
+ )
+ option(
+ "fixedGutter",
+ true,
+ function(cm, val) {
+ cm.display.gutters.style.left = val ? compensateForHScroll(cm.display) + "px" : "0"
+ cm.refresh()
+ },
+ true
+ )
+ option(
+ "coverGutterNextToScrollbar",
+ false,
+ function(cm) {
+ return updateScrollbars(cm)
+ },
+ true
+ )
+ option(
+ "scrollbarStyle",
+ "native",
+ function(cm) {
+ initScrollbars(cm)
+ updateScrollbars(cm)
+ cm.display.scrollbars.setScrollTop(cm.doc.scrollTop)
+ cm.display.scrollbars.setScrollLeft(cm.doc.scrollLeft)
+ },
+ true
+ )
+ option(
+ "lineNumbers",
+ false,
+ function(cm, val) {
+ cm.display.gutterSpecs = getGutters(cm.options.gutters, val)
+ updateGutters(cm)
+ },
+ true
+ )
+ option("firstLineNumber", 1, updateGutters, true)
+ option(
+ "lineNumberFormatter",
+ function(integer) {
+ return integer
+ },
+ updateGutters,
+ true
+ )
+ option("showCursorWhenSelecting", false, updateSelection, true)
+
+ option("resetSelectionOnContextMenu", true)
+ option("lineWiseCopyCut", true)
+ option("pasteLinesPerSelection", true)
+ option("selectionsMayTouch", false)
+
+ option("readOnly", false, function(cm, val) {
+ if (val == "nocursor") {
+ onBlur(cm)
+ cm.display.input.blur()
+ }
+ cm.display.input.readOnlyChanged(val)
+ })
+ option(
+ "disableInput",
+ false,
+ function(cm, val) {
+ if (!val) {
+ cm.display.input.reset()
+ }
+ },
+ true
+ )
+ option("dragDrop", true, dragDropChanged)
+ option("allowDropFileTypes", null)
+
+ option("cursorBlinkRate", 530)
+ option("cursorScrollMargin", 0)
+ option("cursorHeight", 1, updateSelection, true)
+ option("singleCursorHeightPerLine", true, updateSelection, true)
+ option("workTime", 100)
+ option("workDelay", 100)
+ option("flattenSpans", true, resetModeState, true)
+ option("addModeClass", false, resetModeState, true)
+ option("pollInterval", 100)
+ option("undoDepth", 200, function(cm, val) {
+ return (cm.doc.history.undoDepth = val)
+ })
+ option("historyEventDelay", 1250)
+ option(
+ "viewportMargin",
+ 10,
+ function(cm) {
+ return cm.refresh()
+ },
+ true
+ )
+ option("maxHighlightLength", 10000, resetModeState, true)
+ option("moveInputWithCursor", true, function(cm, val) {
+ if (!val) {
+ cm.display.input.resetPosition()
+ }
+ })
+
+ option("tabindex", null, function(cm, val) {
+ return (cm.display.input.getField().tabIndex = val || "")
+ })
+ option("autofocus", null)
+ option(
+ "direction",
+ "ltr",
+ function(cm, val) {
+ return cm.doc.setDirection(val)
+ },
+ true
+ )
+ option("phrases", null)
+ }
+
+ function dragDropChanged(cm, value, old) {
+ var wasOn = old && old != Init
+ if (!value != !wasOn) {
+ var funcs = cm.display.dragFunctions
+ var toggle = value ? on : off
+ toggle(cm.display.scroller, "dragstart", funcs.start)
+ toggle(cm.display.scroller, "dragenter", funcs.enter)
+ toggle(cm.display.scroller, "dragover", funcs.over)
+ toggle(cm.display.scroller, "dragleave", funcs.leave)
+ toggle(cm.display.scroller, "drop", funcs.drop)
+ }
+ }
+
+ function wrappingChanged(cm) {
+ if (cm.options.lineWrapping) {
+ addClass(cm.display.wrapper, "CodeMirror-wrap")
+ cm.display.sizer.style.minWidth = ""
+ cm.display.sizerWidth = null
+ } else {
+ rmClass(cm.display.wrapper, "CodeMirror-wrap")
+ findMaxLine(cm)
+ }
+ estimateLineHeights(cm)
+ regChange(cm)
+ clearCaches(cm)
+ setTimeout(function() {
+ return updateScrollbars(cm)
+ }, 100)
+ }
+
+ // A CodeMirror instance represents an editor. This is the object
+ // that user code is usually dealing with.
+
+ function CodeMirror(place, options) {
+ var this$1 = this
+
+ if (!(this instanceof CodeMirror)) {
+ return new CodeMirror(place, options)
+ }
+
+ this.options = options = options ? copyObj(options) : {}
+ // Determine effective options based on given values and defaults.
+ copyObj(defaults, options, false)
+
+ var doc = options.value
+ if (typeof doc == "string") {
+ doc = new Doc(doc, options.mode, null, options.lineSeparator, options.direction)
+ } else if (options.mode) {
+ doc.modeOption = options.mode
+ }
+ this.doc = doc
+
+ var input = new CodeMirror.inputStyles[options.inputStyle](this)
+ var display = (this.display = new Display(place, doc, input, options))
+ display.wrapper.CodeMirror = this
+ themeChanged(this)
+ if (options.lineWrapping) {
+ this.display.wrapper.className += " CodeMirror-wrap"
+ }
+ initScrollbars(this)
+
+ this.state = {
+ keyMaps: [], // stores maps added by addKeyMap
+ overlays: [], // highlighting overlays, as added by addOverlay
+ modeGen: 0, // bumped when mode/overlay changes, used to invalidate highlighting info
+ overwrite: false,
+ delayingBlurEvent: false,
+ focused: false,
+ suppressEdits: false, // used to disable editing during key handlers when in readOnly mode
+ pasteIncoming: -1,
+ cutIncoming: -1, // help recognize paste/cut edits in input.poll
+ selectingText: false,
+ draggingText: false,
+ highlight: new Delayed(), // stores highlight worker timeout
+ keySeq: null, // Unfinished key sequence
+ specialChars: null
+ }
+
+ if (options.autofocus && !mobile) {
+ display.input.focus()
+ }
+
+ // Override magic textarea content restore that IE sometimes does
+ // on our hidden textarea on reload
+ if (ie && ie_version < 11) {
+ setTimeout(function() {
+ return this$1.display.input.reset(true)
+ }, 20)
+ }
+
+ registerEventHandlers(this)
+ ensureGlobalHandlers()
+
+ startOperation(this)
+ this.curOp.forceUpdate = true
+ attachDoc(this, doc)
+
+ if ((options.autofocus && !mobile) || this.hasFocus()) {
+ setTimeout(bind(onFocus, this), 20)
+ } else {
+ onBlur(this)
+ }
+
+ for (var opt in optionHandlers) {
+ if (optionHandlers.hasOwnProperty(opt)) {
+ optionHandlers[opt](this, options[opt], Init)
+ }
+ }
+ maybeUpdateLineNumberWidth(this)
+ if (options.finishInit) {
+ options.finishInit(this)
+ }
+ for (var i = 0; i < initHooks.length; ++i) {
+ initHooks[i](this)
+ }
+ endOperation(this)
+ // Suppress optimizelegibility in Webkit, since it breaks text
+ // measuring on line wrapping boundaries.
+ if (
+ webkit &&
+ options.lineWrapping &&
+ getComputedStyle(display.lineDiv).textRendering == "optimizelegibility"
+ ) {
+ display.lineDiv.style.textRendering = "auto"
+ }
+ }
+
+ // The default configuration options.
+ CodeMirror.defaults = defaults
+ // Functions to run when options are changed.
+ CodeMirror.optionHandlers = optionHandlers
+
+ // Attach the necessary event handlers when initializing the editor
+ function registerEventHandlers(cm) {
+ var d = cm.display
+ on(d.scroller, "mousedown", operation(cm, onMouseDown))
+ // Older IE's will not fire a second mousedown for a double click
+ if (ie && ie_version < 11) {
+ on(
+ d.scroller,
+ "dblclick",
+ operation(cm, function(e) {
+ if (signalDOMEvent(cm, e)) {
+ return
+ }
+ var pos = posFromMouse(cm, e)
+ if (!pos || clickInGutter(cm, e) || eventInWidget(cm.display, e)) {
+ return
+ }
+ e_preventDefault(e)
+ var word = cm.findWordAt(pos)
+ extendSelection(cm.doc, word.anchor, word.head)
+ })
+ )
+ } else {
+ on(d.scroller, "dblclick", function(e) {
+ return signalDOMEvent(cm, e) || e_preventDefault(e)
+ })
+ }
+ // Some browsers fire contextmenu *after* opening the menu, at
+ // which point we can't mess with it anymore. Context menu is
+ // handled in onMouseDown for these browsers.
+ on(d.scroller, "contextmenu", function(e) {
+ return onContextMenu(cm, e)
+ })
+ on(d.input.getField(), "contextmenu", function(e) {
+ if (!d.scroller.contains(e.target)) {
+ onContextMenu(cm, e)
+ }
+ })
+
+ // Used to suppress mouse event handling when a touch happens
+ var touchFinished,
+ prevTouch = { end: 0 }
+ function finishTouch() {
+ if (d.activeTouch) {
+ touchFinished = setTimeout(function() {
+ return (d.activeTouch = null)
+ }, 1000)
+ prevTouch = d.activeTouch
+ prevTouch.end = +new Date()
+ }
+ }
+ function isMouseLikeTouchEvent(e) {
+ if (e.touches.length != 1) {
+ return false
+ }
+ var touch = e.touches[0]
+ return touch.radiusX <= 1 && touch.radiusY <= 1
+ }
+ function farAway(touch, other) {
+ if (other.left == null) {
+ return true
+ }
+ var dx = other.left - touch.left,
+ dy = other.top - touch.top
+ return dx * dx + dy * dy > 20 * 20
+ }
+ on(d.scroller, "touchstart", function(e) {
+ if (!signalDOMEvent(cm, e) && !isMouseLikeTouchEvent(e) && !clickInGutter(cm, e)) {
+ d.input.ensurePolled()
+ clearTimeout(touchFinished)
+ var now = +new Date()
+ d.activeTouch = {
+ start: now,
+ moved: false,
+ prev: now - prevTouch.end <= 300 ? prevTouch : null
+ }
+ if (e.touches.length == 1) {
+ d.activeTouch.left = e.touches[0].pageX
+ d.activeTouch.top = e.touches[0].pageY
+ }
+ }
+ })
+ on(d.scroller, "touchmove", function() {
+ if (d.activeTouch) {
+ d.activeTouch.moved = true
+ }
+ })
+ on(d.scroller, "touchend", function(e) {
+ var touch = d.activeTouch
+ if (
+ touch &&
+ !eventInWidget(d, e) &&
+ touch.left != null &&
+ !touch.moved &&
+ new Date() - touch.start < 300
+ ) {
+ var pos = cm.coordsChar(d.activeTouch, "page"),
+ range
+ if (!touch.prev || farAway(touch, touch.prev)) {
+ // Single tap
+ range = new Range(pos, pos)
+ } else if (!touch.prev.prev || farAway(touch, touch.prev.prev)) {
+ // Double tap
+ range = cm.findWordAt(pos)
+ } // Triple tap
+ else {
+ range = new Range(Pos(pos.line, 0), clipPos(cm.doc, Pos(pos.line + 1, 0)))
+ }
+ cm.setSelection(range.anchor, range.head)
+ cm.focus()
+ e_preventDefault(e)
+ }
+ finishTouch()
+ })
+ on(d.scroller, "touchcancel", finishTouch)
+
+ // Sync scrolling between fake scrollbars and real scrollable
+ // area, ensure viewport is updated when scrolling.
+ on(d.scroller, "scroll", function() {
+ if (d.scroller.clientHeight) {
+ updateScrollTop(cm, d.scroller.scrollTop)
+ setScrollLeft(cm, d.scroller.scrollLeft, true)
+ signal(cm, "scroll", cm)
+ }
+ })
+
+ // Listen to wheel events in order to try and update the viewport on time.
+ on(d.scroller, "mousewheel", function(e) {
+ return onScrollWheel(cm, e)
+ })
+ on(d.scroller, "DOMMouseScroll", function(e) {
+ return onScrollWheel(cm, e)
+ })
+
+ // Prevent wrapper from ever scrolling
+ on(d.wrapper, "scroll", function() {
+ return (d.wrapper.scrollTop = d.wrapper.scrollLeft = 0)
+ })
+
+ d.dragFunctions = {
+ enter: function(e) {
+ if (!signalDOMEvent(cm, e)) {
+ e_stop(e)
+ }
+ },
+ over: function(e) {
+ if (!signalDOMEvent(cm, e)) {
+ onDragOver(cm, e)
+ e_stop(e)
+ }
+ },
+ start: function(e) {
+ return onDragStart(cm, e)
+ },
+ drop: operation(cm, onDrop),
+ leave: function(e) {
+ if (!signalDOMEvent(cm, e)) {
+ clearDragCursor(cm)
+ }
+ }
+ }
+
+ var inp = d.input.getField()
+ on(inp, "keyup", function(e) {
+ return onKeyUp.call(cm, e)
+ })
+ on(inp, "keydown", operation(cm, onKeyDown))
+ on(inp, "keypress", operation(cm, onKeyPress))
+ on(inp, "focus", function(e) {
+ return onFocus(cm, e)
+ })
+ on(inp, "blur", function(e) {
+ return onBlur(cm, e)
+ })
+ }
+
+ var initHooks = []
+ CodeMirror.defineInitHook = function(f) {
+ return initHooks.push(f)
+ }
+
+ // Indent the given line. The how parameter can be "smart",
+ // "add"/null, "subtract", or "prev". When aggressive is false
+ // (typically set to true for forced single-line indents), empty
+ // lines are not indented, and places where the mode returns Pass
+ // are left alone.
+ function indentLine(cm, n, how, aggressive) {
+ var doc = cm.doc,
+ state
+ if (how == null) {
+ how = "add"
+ }
+ if (how == "smart") {
+ // Fall back to "prev" when the mode doesn't have an indentation
+ // method.
+ if (!doc.mode.indent) {
+ how = "prev"
+ } else {
+ state = getContextBefore(cm, n).state
+ }
+ }
+
+ var tabSize = cm.options.tabSize
+ var line = getLine(doc, n),
+ curSpace = countColumn(line.text, null, tabSize)
+ if (line.stateAfter) {
+ line.stateAfter = null
+ }
+ var curSpaceString = line.text.match(/^\s*/)[0],
+ indentation
+ if (!aggressive && !/\S/.test(line.text)) {
+ indentation = 0
+ how = "not"
+ } else if (how == "smart") {
+ indentation = doc.mode.indent(state, line.text.slice(curSpaceString.length), line.text)
+ if (indentation == Pass || indentation > 150) {
+ if (!aggressive) {
+ return
+ }
+ how = "prev"
+ }
+ }
+ if (how == "prev") {
+ if (n > doc.first) {
+ indentation = countColumn(getLine(doc, n - 1).text, null, tabSize)
+ } else {
+ indentation = 0
+ }
+ } else if (how == "add") {
+ indentation = curSpace + cm.options.indentUnit
+ } else if (how == "subtract") {
+ indentation = curSpace - cm.options.indentUnit
+ } else if (typeof how == "number") {
+ indentation = curSpace + how
+ }
+ indentation = Math.max(0, indentation)
+
+ var indentString = "",
+ pos = 0
+ if (cm.options.indentWithTabs) {
+ for (var i = Math.floor(indentation / tabSize); i; --i) {
+ pos += tabSize
+ indentString += "\t"
+ }
+ }
+ if (pos < indentation) {
+ indentString += spaceStr(indentation - pos)
+ }
+
+ if (indentString != curSpaceString) {
+ replaceRange(doc, indentString, Pos(n, 0), Pos(n, curSpaceString.length), "+input")
+ line.stateAfter = null
+ return true
+ } else {
+ // Ensure that, if the cursor was in the whitespace at the start
+ // of the line, it is moved to the end of that space.
+ for (var i$1 = 0; i$1 < doc.sel.ranges.length; i$1++) {
+ var range = doc.sel.ranges[i$1]
+ if (range.head.line == n && range.head.ch < curSpaceString.length) {
+ var pos$1 = Pos(n, curSpaceString.length)
+ replaceOneSelection(doc, i$1, new Range(pos$1, pos$1))
+ break
+ }
+ }
+ }
+ }
+
+ // This will be set to a {lineWise: bool, text: [string]} object, so
+ // that, when pasting, we know what kind of selections the copied
+ // text was made out of.
+ var lastCopied = null
+
+ function setLastCopied(newLastCopied) {
+ lastCopied = newLastCopied
+ }
+
+ function applyTextInput(cm, inserted, deleted, sel, origin) {
+ var doc = cm.doc
+ cm.display.shift = false
+ if (!sel) {
+ sel = doc.sel
+ }
+
+ var recent = +new Date() - 200
+ var paste = origin == "paste" || cm.state.pasteIncoming > recent
+ var textLines = splitLinesAuto(inserted),
+ multiPaste = null
+ // When pasting N lines into N selections, insert one line per selection
+ if (paste && sel.ranges.length > 1) {
+ if (lastCopied && lastCopied.text.join("\n") == inserted) {
+ if (sel.ranges.length % lastCopied.text.length == 0) {
+ multiPaste = []
+ for (var i = 0; i < lastCopied.text.length; i++) {
+ multiPaste.push(doc.splitLines(lastCopied.text[i]))
+ }
+ }
+ } else if (textLines.length == sel.ranges.length && cm.options.pasteLinesPerSelection) {
+ multiPaste = map(textLines, function(l) {
+ return [l]
+ })
+ }
+ }
+
+ var updateInput = cm.curOp.updateInput
+ // Normal behavior is to insert the new text into every selection
+ for (var i$1 = sel.ranges.length - 1; i$1 >= 0; i$1--) {
+ var range = sel.ranges[i$1]
+ var from = range.from(),
+ to = range.to()
+ if (range.empty()) {
+ if (deleted && deleted > 0) {
+ // Handle deletion
+ from = Pos(from.line, from.ch - deleted)
+ } else if (cm.state.overwrite && !paste) {
+ // Handle overwrite
+ to = Pos(
+ to.line,
+ Math.min(getLine(doc, to.line).text.length, to.ch + lst(textLines).length)
+ )
+ } else if (
+ paste &&
+ lastCopied &&
+ lastCopied.lineWise &&
+ lastCopied.text.join("\n") == inserted
+ ) {
+ from = to = Pos(from.line, 0)
+ }
+ }
+ var changeEvent = {
+ from: from,
+ to: to,
+ text: multiPaste ? multiPaste[i$1 % multiPaste.length] : textLines,
+ origin:
+ origin || (paste ? "paste" : cm.state.cutIncoming > recent ? "cut" : "+input")
+ }
+ makeChange(cm.doc, changeEvent)
+ signalLater(cm, "inputRead", cm, changeEvent)
+ }
+ if (inserted && !paste) {
+ triggerElectric(cm, inserted)
+ }
+
+ ensureCursorVisible(cm)
+ if (cm.curOp.updateInput < 2) {
+ cm.curOp.updateInput = updateInput
+ }
+ cm.curOp.typing = true
+ cm.state.pasteIncoming = cm.state.cutIncoming = -1
+ }
+
+ function handlePaste(e, cm) {
+ var pasted = e.clipboardData && e.clipboardData.getData("Text")
+ if (pasted) {
+ e.preventDefault()
+ if (!cm.isReadOnly() && !cm.options.disableInput) {
+ runInOp(cm, function() {
+ return applyTextInput(cm, pasted, 0, null, "paste")
+ })
+ }
+ return true
+ }
+ }
+
+ function triggerElectric(cm, inserted) {
+ // When an 'electric' character is inserted, immediately trigger a reindent
+ if (!cm.options.electricChars || !cm.options.smartIndent) {
+ return
+ }
+ var sel = cm.doc.sel
+
+ for (var i = sel.ranges.length - 1; i >= 0; i--) {
+ var range = sel.ranges[i]
+ if (range.head.ch > 100 || (i && sel.ranges[i - 1].head.line == range.head.line)) {
+ continue
+ }
+ var mode = cm.getModeAt(range.head)
+ var indented = false
+ if (mode.electricChars) {
+ for (var j = 0; j < mode.electricChars.length; j++) {
+ if (inserted.indexOf(mode.electricChars.charAt(j)) > -1) {
+ indented = indentLine(cm, range.head.line, "smart")
+ break
+ }
+ }
+ } else if (mode.electricInput) {
+ if (
+ mode.electricInput.test(
+ getLine(cm.doc, range.head.line).text.slice(0, range.head.ch)
+ )
+ ) {
+ indented = indentLine(cm, range.head.line, "smart")
+ }
+ }
+ if (indented) {
+ signalLater(cm, "electricInput", cm, range.head.line)
+ }
+ }
+ }
+
+ function copyableRanges(cm) {
+ var text = [],
+ ranges = []
+ for (var i = 0; i < cm.doc.sel.ranges.length; i++) {
+ var line = cm.doc.sel.ranges[i].head.line
+ var lineRange = { anchor: Pos(line, 0), head: Pos(line + 1, 0) }
+ ranges.push(lineRange)
+ text.push(cm.getRange(lineRange.anchor, lineRange.head))
+ }
+ return { text: text, ranges: ranges }
+ }
+
+ function disableBrowserMagic(field, spellcheck, autocorrect, autocapitalize) {
+ field.setAttribute("autocorrect", autocorrect ? "" : "off")
+ field.setAttribute("autocapitalize", autocapitalize ? "" : "off")
+ field.setAttribute("spellcheck", !!spellcheck)
+ }
+
+ function hiddenTextarea() {
+ var te = elt(
+ "textarea",
+ null,
+ null,
+ "position: absolute; bottom: -1em; padding: 0; width: 1px; height: 1em; outline: none"
+ )
+ var div = elt(
+ "div",
+ [te],
+ null,
+ "overflow: hidden; position: relative; width: 3px; height: 0px;"
+ )
+ // The textarea is kept positioned near the cursor to prevent the
+ // fact that it'll be scrolled into view on input from scrolling
+ // our fake cursor out of view. On webkit, when wrap=off, paste is
+ // very slow. So make the area wide instead.
+ if (webkit) {
+ te.style.width = "1000px"
+ } else {
+ te.setAttribute("wrap", "off")
+ }
+ // If border: 0; -- iOS fails to open keyboard (issue #1287)
+ if (ios) {
+ te.style.border = "1px solid black"
+ }
+ disableBrowserMagic(te)
+ return div
+ }
+
+ // The publicly visible API. Note that methodOp(f) means
+ // 'wrap f in an operation, performed on its `this` parameter'.
+
+ // This is not the complete set of editor methods. Most of the
+ // methods defined on the Doc type are also injected into
+ // CodeMirror.prototype, for backwards compatibility and
+ // convenience.
+
+ function addEditorMethods(CodeMirror) {
+ var optionHandlers = CodeMirror.optionHandlers
+
+ var helpers = (CodeMirror.helpers = {})
+
+ CodeMirror.prototype = {
+ constructor: CodeMirror,
+ focus: function() {
+ window.focus()
+ this.display.input.focus()
+ },
+
+ setOption: function(option, value) {
+ var options = this.options,
+ old = options[option]
+ if (options[option] == value && option != "mode") {
+ return
+ }
+ options[option] = value
+ if (optionHandlers.hasOwnProperty(option)) {
+ operation(this, optionHandlers[option])(this, value, old)
+ }
+ signal(this, "optionChange", this, option)
+ },
+
+ getOption: function(option) {
+ return this.options[option]
+ },
+ getDoc: function() {
+ return this.doc
+ },
+
+ addKeyMap: function(map, bottom) {
+ this.state.keyMaps[bottom ? "push" : "unshift"](getKeyMap(map))
+ },
+ removeKeyMap: function(map) {
+ var maps = this.state.keyMaps
+ for (var i = 0; i < maps.length; ++i) {
+ if (maps[i] == map || maps[i].name == map) {
+ maps.splice(i, 1)
+ return true
+ }
+ }
+ },
+
+ addOverlay: methodOp(function(spec, options) {
+ var mode = spec.token ? spec : CodeMirror.getMode(this.options, spec)
+ if (mode.startState) {
+ throw new Error("Overlays may not be stateful.")
+ }
+ insertSorted(
+ this.state.overlays,
+ {
+ mode: mode,
+ modeSpec: spec,
+ opaque: options && options.opaque,
+ priority: (options && options.priority) || 0
+ },
+ function(overlay) {
+ return overlay.priority
+ }
+ )
+ this.state.modeGen++
+ regChange(this)
+ }),
+ removeOverlay: methodOp(function(spec) {
+ var overlays = this.state.overlays
+ for (var i = 0; i < overlays.length; ++i) {
+ var cur = overlays[i].modeSpec
+ if (cur == spec || (typeof spec == "string" && cur.name == spec)) {
+ overlays.splice(i, 1)
+ this.state.modeGen++
+ regChange(this)
+ return
+ }
+ }
+ }),
+
+ indentLine: methodOp(function(n, dir, aggressive) {
+ if (typeof dir != "string" && typeof dir != "number") {
+ if (dir == null) {
+ dir = this.options.smartIndent ? "smart" : "prev"
+ } else {
+ dir = dir ? "add" : "subtract"
+ }
+ }
+ if (isLine(this.doc, n)) {
+ indentLine(this, n, dir, aggressive)
+ }
+ }),
+ indentSelection: methodOp(function(how) {
+ var ranges = this.doc.sel.ranges,
+ end = -1
+ for (var i = 0; i < ranges.length; i++) {
+ var range = ranges[i]
+ if (!range.empty()) {
+ var from = range.from(),
+ to = range.to()
+ var start = Math.max(end, from.line)
+ end = Math.min(this.lastLine(), to.line - (to.ch ? 0 : 1)) + 1
+ for (var j = start; j < end; ++j) {
+ indentLine(this, j, how)
+ }
+ var newRanges = this.doc.sel.ranges
+ if (
+ from.ch == 0 &&
+ ranges.length == newRanges.length &&
+ newRanges[i].from().ch > 0
+ ) {
+ replaceOneSelection(
+ this.doc,
+ i,
+ new Range(from, newRanges[i].to()),
+ sel_dontScroll
+ )
+ }
+ } else if (range.head.line > end) {
+ indentLine(this, range.head.line, how, true)
+ end = range.head.line
+ if (i == this.doc.sel.primIndex) {
+ ensureCursorVisible(this)
+ }
+ }
+ }
+ }),
+
+ // Fetch the parser token for a given character. Useful for hacks
+ // that want to inspect the mode state (say, for completion).
+ getTokenAt: function(pos, precise) {
+ return takeToken(this, pos, precise)
+ },
+
+ getLineTokens: function(line, precise) {
+ return takeToken(this, Pos(line), precise, true)
+ },
+
+ getTokenTypeAt: function(pos) {
+ pos = clipPos(this.doc, pos)
+ var styles = getLineStyles(this, getLine(this.doc, pos.line))
+ var before = 0,
+ after = (styles.length - 1) / 2,
+ ch = pos.ch
+ var type
+ if (ch == 0) {
+ type = styles[2]
+ } else {
+ for (;;) {
+ var mid = (before + after) >> 1
+ if ((mid ? styles[mid * 2 - 1] : 0) >= ch) {
+ after = mid
+ } else if (styles[mid * 2 + 1] < ch) {
+ before = mid + 1
+ } else {
+ type = styles[mid * 2 + 2]
+ break
+ }
+ }
+ }
+ var cut = type ? type.indexOf("overlay ") : -1
+ return cut < 0 ? type : cut == 0 ? null : type.slice(0, cut - 1)
+ },
+
+ getModeAt: function(pos) {
+ var mode = this.doc.mode
+ if (!mode.innerMode) {
+ return mode
+ }
+ return CodeMirror.innerMode(mode, this.getTokenAt(pos).state).mode
+ },
+
+ getHelper: function(pos, type) {
+ return this.getHelpers(pos, type)[0]
+ },
+
+ getHelpers: function(pos, type) {
+ var found = []
+ if (!helpers.hasOwnProperty(type)) {
+ return found
+ }
+ var help = helpers[type],
+ mode = this.getModeAt(pos)
+ if (typeof mode[type] == "string") {
+ if (help[mode[type]]) {
+ found.push(help[mode[type]])
+ }
+ } else if (mode[type]) {
+ for (var i = 0; i < mode[type].length; i++) {
+ var val = help[mode[type][i]]
+ if (val) {
+ found.push(val)
+ }
+ }
+ } else if (mode.helperType && help[mode.helperType]) {
+ found.push(help[mode.helperType])
+ } else if (help[mode.name]) {
+ found.push(help[mode.name])
+ }
+ for (var i$1 = 0; i$1 < help._global.length; i$1++) {
+ var cur = help._global[i$1]
+ if (cur.pred(mode, this) && indexOf(found, cur.val) == -1) {
+ found.push(cur.val)
+ }
+ }
+ return found
+ },
+
+ getStateAfter: function(line, precise) {
+ var doc = this.doc
+ line = clipLine(doc, line == null ? doc.first + doc.size - 1 : line)
+ return getContextBefore(this, line + 1, precise).state
+ },
+
+ cursorCoords: function(start, mode) {
+ var pos,
+ range = this.doc.sel.primary()
+ if (start == null) {
+ pos = range.head
+ } else if (typeof start == "object") {
+ pos = clipPos(this.doc, start)
+ } else {
+ pos = start ? range.from() : range.to()
+ }
+ return cursorCoords(this, pos, mode || "page")
+ },
+
+ charCoords: function(pos, mode) {
+ return charCoords(this, clipPos(this.doc, pos), mode || "page")
+ },
+
+ coordsChar: function(coords, mode) {
+ coords = fromCoordSystem(this, coords, mode || "page")
+ return coordsChar(this, coords.left, coords.top)
+ },
+
+ lineAtHeight: function(height, mode) {
+ height = fromCoordSystem(this, { top: height, left: 0 }, mode || "page").top
+ return lineAtHeight(this.doc, height + this.display.viewOffset)
+ },
+ heightAtLine: function(line, mode, includeWidgets) {
+ var end = false,
+ lineObj
+ if (typeof line == "number") {
+ var last = this.doc.first + this.doc.size - 1
+ if (line < this.doc.first) {
+ line = this.doc.first
+ } else if (line > last) {
+ line = last
+ end = true
+ }
+ lineObj = getLine(this.doc, line)
+ } else {
+ lineObj = line
+ }
+ return (
+ intoCoordSystem(
+ this,
+ lineObj,
+ { top: 0, left: 0 },
+ mode || "page",
+ includeWidgets || end
+ ).top + (end ? this.doc.height - heightAtLine(lineObj) : 0)
+ )
+ },
+
+ defaultTextHeight: function() {
+ return textHeight(this.display)
+ },
+ defaultCharWidth: function() {
+ return charWidth(this.display)
+ },
+
+ getViewport: function() {
+ return { from: this.display.viewFrom, to: this.display.viewTo }
+ },
+
+ addWidget: function(pos, node, scroll, vert, horiz) {
+ var display = this.display
+ pos = cursorCoords(this, clipPos(this.doc, pos))
+ var top = pos.bottom,
+ left = pos.left
+ node.style.position = "absolute"
+ node.setAttribute("cm-ignore-events", "true")
+ this.display.input.setUneditable(node)
+ display.sizer.appendChild(node)
+ if (vert == "over") {
+ top = pos.top
+ } else if (vert == "above" || vert == "near") {
+ var vspace = Math.max(display.wrapper.clientHeight, this.doc.height),
+ hspace = Math.max(display.sizer.clientWidth, display.lineSpace.clientWidth)
+ // Default to positioning above (if specified and possible); otherwise default to positioning below
+ if (
+ (vert == "above" || pos.bottom + node.offsetHeight > vspace) &&
+ pos.top > node.offsetHeight
+ ) {
+ top = pos.top - node.offsetHeight
+ } else if (pos.bottom + node.offsetHeight <= vspace) {
+ top = pos.bottom
+ }
+ if (left + node.offsetWidth > hspace) {
+ left = hspace - node.offsetWidth
+ }
+ }
+ node.style.top = top + "px"
+ node.style.left = node.style.right = ""
+ if (horiz == "right") {
+ left = display.sizer.clientWidth - node.offsetWidth
+ node.style.right = "0px"
+ } else {
+ if (horiz == "left") {
+ left = 0
+ } else if (horiz == "middle") {
+ left = (display.sizer.clientWidth - node.offsetWidth) / 2
+ }
+ node.style.left = left + "px"
+ }
+ if (scroll) {
+ scrollIntoView(this, {
+ left: left,
+ top: top,
+ right: left + node.offsetWidth,
+ bottom: top + node.offsetHeight
+ })
+ }
+ },
+
+ triggerOnKeyDown: methodOp(onKeyDown),
+ triggerOnKeyPress: methodOp(onKeyPress),
+ triggerOnKeyUp: onKeyUp,
+ triggerOnMouseDown: methodOp(onMouseDown),
+
+ execCommand: function(cmd) {
+ if (commands.hasOwnProperty(cmd)) {
+ return commands[cmd].call(null, this)
+ }
+ },
+
+ triggerElectric: methodOp(function(text) {
+ triggerElectric(this, text)
+ }),
+
+ findPosH: function(from, amount, unit, visually) {
+ var dir = 1
+ if (amount < 0) {
+ dir = -1
+ amount = -amount
+ }
+ var cur = clipPos(this.doc, from)
+ for (var i = 0; i < amount; ++i) {
+ cur = findPosH(this.doc, cur, dir, unit, visually)
+ if (cur.hitSide) {
+ break
+ }
+ }
+ return cur
+ },
+
+ moveH: methodOp(function(dir, unit) {
+ var this$1 = this
+
+ this.extendSelectionsBy(function(range) {
+ if (this$1.display.shift || this$1.doc.extend || range.empty()) {
+ return findPosH(
+ this$1.doc,
+ range.head,
+ dir,
+ unit,
+ this$1.options.rtlMoveVisually
+ )
+ } else {
+ return dir < 0 ? range.from() : range.to()
+ }
+ }, sel_move)
+ }),
+
+ deleteH: methodOp(function(dir, unit) {
+ var sel = this.doc.sel,
+ doc = this.doc
+ if (sel.somethingSelected()) {
+ doc.replaceSelection("", null, "+delete")
+ } else {
+ deleteNearSelection(this, function(range) {
+ var other = findPosH(doc, range.head, dir, unit, false)
+ return dir < 0
+ ? { from: other, to: range.head }
+ : { from: range.head, to: other }
+ })
+ }
+ }),
+
+ findPosV: function(from, amount, unit, goalColumn) {
+ var dir = 1,
+ x = goalColumn
+ if (amount < 0) {
+ dir = -1
+ amount = -amount
+ }
+ var cur = clipPos(this.doc, from)
+ for (var i = 0; i < amount; ++i) {
+ var coords = cursorCoords(this, cur, "div")
+ if (x == null) {
+ x = coords.left
+ } else {
+ coords.left = x
+ }
+ cur = findPosV(this, coords, dir, unit)
+ if (cur.hitSide) {
+ break
+ }
+ }
+ return cur
+ },
+
+ moveV: methodOp(function(dir, unit) {
+ var this$1 = this
+
+ var doc = this.doc,
+ goals = []
+ var collapse = !this.display.shift && !doc.extend && doc.sel.somethingSelected()
+ doc.extendSelectionsBy(function(range) {
+ if (collapse) {
+ return dir < 0 ? range.from() : range.to()
+ }
+ var headPos = cursorCoords(this$1, range.head, "div")
+ if (range.goalColumn != null) {
+ headPos.left = range.goalColumn
+ }
+ goals.push(headPos.left)
+ var pos = findPosV(this$1, headPos, dir, unit)
+ if (unit == "page" && range == doc.sel.primary()) {
+ addToScrollTop(this$1, charCoords(this$1, pos, "div").top - headPos.top)
+ }
+ return pos
+ }, sel_move)
+ if (goals.length) {
+ for (var i = 0; i < doc.sel.ranges.length; i++) {
+ doc.sel.ranges[i].goalColumn = goals[i]
+ }
+ }
+ }),
+
+ // Find the word at the given position (as returned by coordsChar).
+ findWordAt: function(pos) {
+ var doc = this.doc,
+ line = getLine(doc, pos.line).text
+ var start = pos.ch,
+ end = pos.ch
+ if (line) {
+ var helper = this.getHelper(pos, "wordChars")
+ if ((pos.sticky == "before" || end == line.length) && start) {
+ --start
+ } else {
+ ++end
+ }
+ var startChar = line.charAt(start)
+ var check = isWordChar(startChar, helper)
+ ? function(ch) {
+ return isWordChar(ch, helper)
+ }
+ : /\s/.test(startChar)
+ ? function(ch) {
+ return /\s/.test(ch)
+ }
+ : function(ch) {
+ return !/\s/.test(ch) && !isWordChar(ch)
+ }
+ while (start > 0 && check(line.charAt(start - 1))) {
+ --start
+ }
+ while (end < line.length && check(line.charAt(end))) {
+ ++end
+ }
+ }
+ return new Range(Pos(pos.line, start), Pos(pos.line, end))
+ },
+
+ toggleOverwrite: function(value) {
+ if (value != null && value == this.state.overwrite) {
+ return
+ }
+ if ((this.state.overwrite = !this.state.overwrite)) {
+ addClass(this.display.cursorDiv, "CodeMirror-overwrite")
+ } else {
+ rmClass(this.display.cursorDiv, "CodeMirror-overwrite")
+ }
+
+ signal(this, "overwriteToggle", this, this.state.overwrite)
+ },
+ hasFocus: function() {
+ return this.display.input.getField() == activeElt()
+ },
+ isReadOnly: function() {
+ return !!(this.options.readOnly || this.doc.cantEdit)
+ },
+
+ scrollTo: methodOp(function(x, y) {
+ scrollToCoords(this, x, y)
+ }),
+ getScrollInfo: function() {
+ var scroller = this.display.scroller
+ return {
+ left: scroller.scrollLeft,
+ top: scroller.scrollTop,
+ height: scroller.scrollHeight - scrollGap(this) - this.display.barHeight,
+ width: scroller.scrollWidth - scrollGap(this) - this.display.barWidth,
+ clientHeight: displayHeight(this),
+ clientWidth: displayWidth(this)
+ }
+ },
+
+ scrollIntoView: methodOp(function(range, margin) {
+ if (range == null) {
+ range = { from: this.doc.sel.primary().head, to: null }
+ if (margin == null) {
+ margin = this.options.cursorScrollMargin
+ }
+ } else if (typeof range == "number") {
+ range = { from: Pos(range, 0), to: null }
+ } else if (range.from == null) {
+ range = { from: range, to: null }
+ }
+ if (!range.to) {
+ range.to = range.from
+ }
+ range.margin = margin || 0
+
+ if (range.from.line != null) {
+ scrollToRange(this, range)
+ } else {
+ scrollToCoordsRange(this, range.from, range.to, range.margin)
+ }
+ }),
+
+ setSize: methodOp(function(width, height) {
+ var this$1 = this
+
+ var interpret = function(val) {
+ return typeof val == "number" || /^\d+$/.test(String(val)) ? val + "px" : val
+ }
+ if (width != null) {
+ this.display.wrapper.style.width = interpret(width)
+ }
+ if (height != null) {
+ this.display.wrapper.style.height = interpret(height)
+ }
+ if (this.options.lineWrapping) {
+ clearLineMeasurementCache(this)
+ }
+ var lineNo = this.display.viewFrom
+ this.doc.iter(lineNo, this.display.viewTo, function(line) {
+ if (line.widgets) {
+ for (var i = 0; i < line.widgets.length; i++) {
+ if (line.widgets[i].noHScroll) {
+ regLineChange(this$1, lineNo, "widget")
+ break
+ }
+ }
+ }
+ ++lineNo
+ })
+ this.curOp.forceUpdate = true
+ signal(this, "refresh", this)
+ }),
+
+ operation: function(f) {
+ return runInOp(this, f)
+ },
+ startOperation: function() {
+ return startOperation(this)
+ },
+ endOperation: function() {
+ return endOperation(this)
+ },
+
+ refresh: methodOp(function() {
+ var oldHeight = this.display.cachedTextHeight
+ regChange(this)
+ this.curOp.forceUpdate = true
+ clearCaches(this)
+ scrollToCoords(this, this.doc.scrollLeft, this.doc.scrollTop)
+ updateGutterSpace(this.display)
+ if (oldHeight == null || Math.abs(oldHeight - textHeight(this.display)) > 0.5) {
+ estimateLineHeights(this)
+ }
+ signal(this, "refresh", this)
+ }),
+
+ swapDoc: methodOp(function(doc) {
+ var old = this.doc
+ old.cm = null
+ // Cancel the current text selection if any (#5821)
+ if (this.state.selectingText) {
+ this.state.selectingText()
+ }
+ attachDoc(this, doc)
+ clearCaches(this)
+ this.display.input.reset()
+ scrollToCoords(this, doc.scrollLeft, doc.scrollTop)
+ this.curOp.forceScroll = true
+ signalLater(this, "swapDoc", this, old)
+ return old
+ }),
+
+ phrase: function(phraseText) {
+ var phrases = this.options.phrases
+ return phrases && Object.prototype.hasOwnProperty.call(phrases, phraseText)
+ ? phrases[phraseText]
+ : phraseText
+ },
+
+ getInputField: function() {
+ return this.display.input.getField()
+ },
+ getWrapperElement: function() {
+ return this.display.wrapper
+ },
+ getScrollerElement: function() {
+ return this.display.scroller
+ },
+ getGutterElement: function() {
+ return this.display.gutters
+ }
+ }
+ eventMixin(CodeMirror)
+
+ CodeMirror.registerHelper = function(type, name, value) {
+ if (!helpers.hasOwnProperty(type)) {
+ helpers[type] = CodeMirror[type] = { _global: [] }
+ }
+ helpers[type][name] = value
+ }
+ CodeMirror.registerGlobalHelper = function(type, name, predicate, value) {
+ CodeMirror.registerHelper(type, name, value)
+ helpers[type]._global.push({ pred: predicate, val: value })
+ }
+ }
+
+ // Used for horizontal relative motion. Dir is -1 or 1 (left or
+ // right), unit can be "char", "column" (like char, but doesn't
+ // cross line boundaries), "word" (across next word), or "group" (to
+ // the start of next group of word or non-word-non-whitespace
+ // chars). The visually param controls whether, in right-to-left
+ // text, direction 1 means to move towards the next index in the
+ // string, or towards the character to the right of the current
+ // position. The resulting position will have a hitSide=true
+ // property if it reached the end of the document.
+ function findPosH(doc, pos, dir, unit, visually) {
+ var oldPos = pos
+ var origDir = dir
+ var lineObj = getLine(doc, pos.line)
+ var lineDir = visually && doc.direction == "rtl" ? -dir : dir
+ function findNextLine() {
+ var l = pos.line + lineDir
+ if (l < doc.first || l >= doc.first + doc.size) {
+ return false
+ }
+ pos = new Pos(l, pos.ch, pos.sticky)
+ return (lineObj = getLine(doc, l))
+ }
+ function moveOnce(boundToLine) {
+ var next
+ if (visually) {
+ next = moveVisually(doc.cm, lineObj, pos, dir)
+ } else {
+ next = moveLogically(lineObj, pos, dir)
+ }
+ if (next == null) {
+ if (!boundToLine && findNextLine()) {
+ pos = endOfLine(visually, doc.cm, lineObj, pos.line, lineDir)
+ } else {
+ return false
+ }
+ } else {
+ pos = next
+ }
+ return true
+ }
+
+ if (unit == "char") {
+ moveOnce()
+ } else if (unit == "column") {
+ moveOnce(true)
+ } else if (unit == "word" || unit == "group") {
+ var sawType = null,
+ group = unit == "group"
+ var helper = doc.cm && doc.cm.getHelper(pos, "wordChars")
+ for (var first = true; ; first = false) {
+ if (dir < 0 && !moveOnce(!first)) {
+ break
+ }
+ var cur = lineObj.text.charAt(pos.ch) || "\n"
+ var type = isWordChar(cur, helper)
+ ? "w"
+ : group && cur == "\n"
+ ? "n"
+ : !group || /\s/.test(cur)
+ ? null
+ : "p"
+ if (group && !first && !type) {
+ type = "s"
+ }
+ if (sawType && sawType != type) {
+ if (dir < 0) {
+ dir = 1
+ moveOnce()
+ pos.sticky = "after"
+ }
+ break
+ }
+
+ if (type) {
+ sawType = type
+ }
+ if (dir > 0 && !moveOnce(!first)) {
+ break
+ }
+ }
+ }
+ var result = skipAtomic(doc, pos, oldPos, origDir, true)
+ if (equalCursorPos(oldPos, result)) {
+ result.hitSide = true
+ }
+ return result
+ }
+
+ // For relative vertical movement. Dir may be -1 or 1. Unit can be
+ // "page" or "line". The resulting position will have a hitSide=true
+ // property if it reached the end of the document.
+ function findPosV(cm, pos, dir, unit) {
+ var doc = cm.doc,
+ x = pos.left,
+ y
+ if (unit == "page") {
+ var pageSize = Math.min(
+ cm.display.wrapper.clientHeight,
+ window.innerHeight || document.documentElement.clientHeight
+ )
+ var moveAmount = Math.max(pageSize - 0.5 * textHeight(cm.display), 3)
+ y = (dir > 0 ? pos.bottom : pos.top) + dir * moveAmount
+ } else if (unit == "line") {
+ y = dir > 0 ? pos.bottom + 3 : pos.top - 3
+ }
+ var target
+ for (;;) {
+ target = coordsChar(cm, x, y)
+ if (!target.outside) {
+ break
+ }
+ if (dir < 0 ? y <= 0 : y >= doc.height) {
+ target.hitSide = true
+ break
+ }
+ y += dir * 5
+ }
+ return target
+ }
+
+ // CONTENTEDITABLE INPUT STYLE
+
+ var ContentEditableInput = function(cm) {
+ this.cm = cm
+ this.lastAnchorNode = this.lastAnchorOffset = this.lastFocusNode = this.lastFocusOffset = null
+ this.polling = new Delayed()
+ this.composing = null
+ this.gracePeriod = false
+ this.readDOMTimeout = null
+ }
+
+ ContentEditableInput.prototype.init = function(display) {
+ var this$1 = this
+
+ var input = this,
+ cm = input.cm
+ var div = (input.div = display.lineDiv)
+ disableBrowserMagic(
+ div,
+ cm.options.spellcheck,
+ cm.options.autocorrect,
+ cm.options.autocapitalize
+ )
+
+ on(div, "paste", function(e) {
+ if (signalDOMEvent(cm, e) || handlePaste(e, cm)) {
+ return
+ }
+ // IE doesn't fire input events, so we schedule a read for the pasted content in this way
+ if (ie_version <= 11) {
+ setTimeout(
+ operation(cm, function() {
+ return this$1.updateFromDOM()
+ }),
+ 20
+ )
+ }
+ })
+
+ on(div, "compositionstart", function(e) {
+ this$1.composing = { data: e.data, done: false }
+ })
+ on(div, "compositionupdate", function(e) {
+ if (!this$1.composing) {
+ this$1.composing = { data: e.data, done: false }
+ }
+ })
+ on(div, "compositionend", function(e) {
+ if (this$1.composing) {
+ if (e.data != this$1.composing.data) {
+ this$1.readFromDOMSoon()
+ }
+ this$1.composing.done = true
+ }
+ })
+
+ on(div, "touchstart", function() {
+ return input.forceCompositionEnd()
+ })
+
+ on(div, "input", function() {
+ if (!this$1.composing) {
+ this$1.readFromDOMSoon()
+ }
+ })
+
+ function onCopyCut(e) {
+ if (signalDOMEvent(cm, e)) {
+ return
+ }
+ if (cm.somethingSelected()) {
+ setLastCopied({ lineWise: false, text: cm.getSelections() })
+ if (e.type == "cut") {
+ cm.replaceSelection("", null, "cut")
+ }
+ } else if (!cm.options.lineWiseCopyCut) {
+ return
+ } else {
+ var ranges = copyableRanges(cm)
+ setLastCopied({ lineWise: true, text: ranges.text })
+ if (e.type == "cut") {
+ cm.operation(function() {
+ cm.setSelections(ranges.ranges, 0, sel_dontScroll)
+ cm.replaceSelection("", null, "cut")
+ })
+ }
+ }
+ if (e.clipboardData) {
+ e.clipboardData.clearData()
+ var content = lastCopied.text.join("\n")
+ // iOS exposes the clipboard API, but seems to discard content inserted into it
+ e.clipboardData.setData("Text", content)
+ if (e.clipboardData.getData("Text") == content) {
+ e.preventDefault()
+ return
+ }
+ }
+ // Old-fashioned briefly-focus-a-textarea hack
+ var kludge = hiddenTextarea(),
+ te = kludge.firstChild
+ cm.display.lineSpace.insertBefore(kludge, cm.display.lineSpace.firstChild)
+ te.value = lastCopied.text.join("\n")
+ var hadFocus = document.activeElement
+ selectInput(te)
+ setTimeout(function() {
+ cm.display.lineSpace.removeChild(kludge)
+ hadFocus.focus()
+ if (hadFocus == div) {
+ input.showPrimarySelection()
+ }
+ }, 50)
+ }
+ on(div, "copy", onCopyCut)
+ on(div, "cut", onCopyCut)
+ }
+
+ ContentEditableInput.prototype.prepareSelection = function() {
+ var result = prepareSelection(this.cm, false)
+ result.focus = this.cm.state.focused
+ return result
+ }
+
+ ContentEditableInput.prototype.showSelection = function(info, takeFocus) {
+ if (!info || !this.cm.display.view.length) {
+ return
+ }
+ if (info.focus || takeFocus) {
+ this.showPrimarySelection()
+ }
+ this.showMultipleSelections(info)
+ }
+
+ ContentEditableInput.prototype.getSelection = function() {
+ return this.cm.display.wrapper.ownerDocument.getSelection()
+ }
+
+ ContentEditableInput.prototype.showPrimarySelection = function() {
+ var sel = this.getSelection(),
+ cm = this.cm,
+ prim = cm.doc.sel.primary()
+ var from = prim.from(),
+ to = prim.to()
+
+ if (
+ cm.display.viewTo == cm.display.viewFrom ||
+ from.line >= cm.display.viewTo ||
+ to.line < cm.display.viewFrom
+ ) {
+ sel.removeAllRanges()
+ return
+ }
+
+ var curAnchor = domToPos(cm, sel.anchorNode, sel.anchorOffset)
+ var curFocus = domToPos(cm, sel.focusNode, sel.focusOffset)
+ if (
+ curAnchor &&
+ !curAnchor.bad &&
+ curFocus &&
+ !curFocus.bad &&
+ cmp(minPos(curAnchor, curFocus), from) == 0 &&
+ cmp(maxPos(curAnchor, curFocus), to) == 0
+ ) {
+ return
+ }
+
+ var view = cm.display.view
+ var start = (from.line >= cm.display.viewFrom && posToDOM(cm, from)) || {
+ node: view[0].measure.map[2],
+ offset: 0
+ }
+ var end = to.line < cm.display.viewTo && posToDOM(cm, to)
+ if (!end) {
+ var measure = view[view.length - 1].measure
+ var map = measure.maps ? measure.maps[measure.maps.length - 1] : measure.map
+ end = { node: map[map.length - 1], offset: map[map.length - 2] - map[map.length - 3] }
+ }
+
+ if (!start || !end) {
+ sel.removeAllRanges()
+ return
+ }
+
+ var old = sel.rangeCount && sel.getRangeAt(0),
+ rng
+ try {
+ rng = range(start.node, start.offset, end.offset, end.node)
+ } catch (e) {} // Our model of the DOM might be outdated, in which case the range we try to set can be impossible
+ if (rng) {
+ if (!gecko && cm.state.focused) {
+ sel.collapse(start.node, start.offset)
+ if (!rng.collapsed) {
+ sel.removeAllRanges()
+ sel.addRange(rng)
+ }
+ } else {
+ sel.removeAllRanges()
+ sel.addRange(rng)
+ }
+ if (old && sel.anchorNode == null) {
+ sel.addRange(old)
+ } else if (gecko) {
+ this.startGracePeriod()
+ }
+ }
+ this.rememberSelection()
+ }
+
+ ContentEditableInput.prototype.startGracePeriod = function() {
+ var this$1 = this
+
+ clearTimeout(this.gracePeriod)
+ this.gracePeriod = setTimeout(function() {
+ this$1.gracePeriod = false
+ if (this$1.selectionChanged()) {
+ this$1.cm.operation(function() {
+ return (this$1.cm.curOp.selectionChanged = true)
+ })
+ }
+ }, 20)
+ }
+
+ ContentEditableInput.prototype.showMultipleSelections = function(info) {
+ removeChildrenAndAdd(this.cm.display.cursorDiv, info.cursors)
+ removeChildrenAndAdd(this.cm.display.selectionDiv, info.selection)
+ }
+
+ ContentEditableInput.prototype.rememberSelection = function() {
+ var sel = this.getSelection()
+ this.lastAnchorNode = sel.anchorNode
+ this.lastAnchorOffset = sel.anchorOffset
+ this.lastFocusNode = sel.focusNode
+ this.lastFocusOffset = sel.focusOffset
+ }
+
+ ContentEditableInput.prototype.selectionInEditor = function() {
+ var sel = this.getSelection()
+ if (!sel.rangeCount) {
+ return false
+ }
+ var node = sel.getRangeAt(0).commonAncestorContainer
+ return contains(this.div, node)
+ }
+
+ ContentEditableInput.prototype.focus = function() {
+ if (this.cm.options.readOnly != "nocursor") {
+ if (!this.selectionInEditor()) {
+ this.showSelection(this.prepareSelection(), true)
+ }
+ this.div.focus()
+ }
+ }
+ ContentEditableInput.prototype.blur = function() {
+ this.div.blur()
+ }
+ ContentEditableInput.prototype.getField = function() {
+ return this.div
+ }
+
+ ContentEditableInput.prototype.supportsTouch = function() {
+ return true
+ }
+
+ ContentEditableInput.prototype.receivedFocus = function() {
+ var input = this
+ if (this.selectionInEditor()) {
+ this.pollSelection()
+ } else {
+ runInOp(this.cm, function() {
+ return (input.cm.curOp.selectionChanged = true)
+ })
+ }
+
+ function poll() {
+ if (input.cm.state.focused) {
+ input.pollSelection()
+ input.polling.set(input.cm.options.pollInterval, poll)
+ }
+ }
+ this.polling.set(this.cm.options.pollInterval, poll)
+ }
+
+ ContentEditableInput.prototype.selectionChanged = function() {
+ var sel = this.getSelection()
+ return (
+ sel.anchorNode != this.lastAnchorNode ||
+ sel.anchorOffset != this.lastAnchorOffset ||
+ sel.focusNode != this.lastFocusNode ||
+ sel.focusOffset != this.lastFocusOffset
+ )
+ }
+
+ ContentEditableInput.prototype.pollSelection = function() {
+ if (this.readDOMTimeout != null || this.gracePeriod || !this.selectionChanged()) {
+ return
+ }
+ var sel = this.getSelection(),
+ cm = this.cm
+ // On Android Chrome (version 56, at least), backspacing into an
+ // uneditable block element will put the cursor in that element,
+ // and then, because it's not editable, hide the virtual keyboard.
+ // Because Android doesn't allow us to actually detect backspace
+ // presses in a sane way, this code checks for when that happens
+ // and simulates a backspace press in this case.
+ if (android && chrome && this.cm.display.gutterSpecs.length && isInGutter(sel.anchorNode)) {
+ this.cm.triggerOnKeyDown({ type: "keydown", keyCode: 8, preventDefault: Math.abs })
+ this.blur()
+ this.focus()
+ return
+ }
+ if (this.composing) {
+ return
+ }
+ this.rememberSelection()
+ var anchor = domToPos(cm, sel.anchorNode, sel.anchorOffset)
+ var head = domToPos(cm, sel.focusNode, sel.focusOffset)
+ if (anchor && head) {
+ runInOp(cm, function() {
+ setSelection(cm.doc, simpleSelection(anchor, head), sel_dontScroll)
+ if (anchor.bad || head.bad) {
+ cm.curOp.selectionChanged = true
+ }
+ })
+ }
+ }
+
+ ContentEditableInput.prototype.pollContent = function() {
+ if (this.readDOMTimeout != null) {
+ clearTimeout(this.readDOMTimeout)
+ this.readDOMTimeout = null
+ }
+
+ var cm = this.cm,
+ display = cm.display,
+ sel = cm.doc.sel.primary()
+ var from = sel.from(),
+ to = sel.to()
+ if (from.ch == 0 && from.line > cm.firstLine()) {
+ from = Pos(from.line - 1, getLine(cm.doc, from.line - 1).length)
+ }
+ if (to.ch == getLine(cm.doc, to.line).text.length && to.line < cm.lastLine()) {
+ to = Pos(to.line + 1, 0)
+ }
+ if (from.line < display.viewFrom || to.line > display.viewTo - 1) {
+ return false
+ }
+
+ var fromIndex, fromLine, fromNode
+ if (from.line == display.viewFrom || (fromIndex = findViewIndex(cm, from.line)) == 0) {
+ fromLine = lineNo(display.view[0].line)
+ fromNode = display.view[0].node
+ } else {
+ fromLine = lineNo(display.view[fromIndex].line)
+ fromNode = display.view[fromIndex - 1].node.nextSibling
+ }
+ var toIndex = findViewIndex(cm, to.line)
+ var toLine, toNode
+ if (toIndex == display.view.length - 1) {
+ toLine = display.viewTo - 1
+ toNode = display.lineDiv.lastChild
+ } else {
+ toLine = lineNo(display.view[toIndex + 1].line) - 1
+ toNode = display.view[toIndex + 1].node.previousSibling
+ }
+
+ if (!fromNode) {
+ return false
+ }
+ var newText = cm.doc.splitLines(domTextBetween(cm, fromNode, toNode, fromLine, toLine))
+ var oldText = getBetween(
+ cm.doc,
+ Pos(fromLine, 0),
+ Pos(toLine, getLine(cm.doc, toLine).text.length)
+ )
+ while (newText.length > 1 && oldText.length > 1) {
+ if (lst(newText) == lst(oldText)) {
+ newText.pop()
+ oldText.pop()
+ toLine--
+ } else if (newText[0] == oldText[0]) {
+ newText.shift()
+ oldText.shift()
+ fromLine++
+ } else {
+ break
+ }
+ }
+
+ var cutFront = 0,
+ cutEnd = 0
+ var newTop = newText[0],
+ oldTop = oldText[0],
+ maxCutFront = Math.min(newTop.length, oldTop.length)
+ while (
+ cutFront < maxCutFront &&
+ newTop.charCodeAt(cutFront) == oldTop.charCodeAt(cutFront)
+ ) {
+ ++cutFront
+ }
+ var newBot = lst(newText),
+ oldBot = lst(oldText)
+ var maxCutEnd = Math.min(
+ newBot.length - (newText.length == 1 ? cutFront : 0),
+ oldBot.length - (oldText.length == 1 ? cutFront : 0)
+ )
+ while (
+ cutEnd < maxCutEnd &&
+ newBot.charCodeAt(newBot.length - cutEnd - 1) ==
+ oldBot.charCodeAt(oldBot.length - cutEnd - 1)
+ ) {
+ ++cutEnd
+ }
+ // Try to move start of change to start of selection if ambiguous
+ if (newText.length == 1 && oldText.length == 1 && fromLine == from.line) {
+ while (
+ cutFront &&
+ cutFront > from.ch &&
+ newBot.charCodeAt(newBot.length - cutEnd - 1) ==
+ oldBot.charCodeAt(oldBot.length - cutEnd - 1)
+ ) {
+ cutFront--
+ cutEnd++
+ }
+ }
+
+ newText[newText.length - 1] = newBot
+ .slice(0, newBot.length - cutEnd)
+ .replace(/^\u200b+/, "")
+ newText[0] = newText[0].slice(cutFront).replace(/\u200b+$/, "")
+
+ var chFrom = Pos(fromLine, cutFront)
+ var chTo = Pos(toLine, oldText.length ? lst(oldText).length - cutEnd : 0)
+ if (newText.length > 1 || newText[0] || cmp(chFrom, chTo)) {
+ replaceRange(cm.doc, newText, chFrom, chTo, "+input")
+ return true
+ }
+ }
+
+ ContentEditableInput.prototype.ensurePolled = function() {
+ this.forceCompositionEnd()
+ }
+ ContentEditableInput.prototype.reset = function() {
+ this.forceCompositionEnd()
+ }
+ ContentEditableInput.prototype.forceCompositionEnd = function() {
+ if (!this.composing) {
+ return
+ }
+ clearTimeout(this.readDOMTimeout)
+ this.composing = null
+ this.updateFromDOM()
+ this.div.blur()
+ this.div.focus()
+ }
+ ContentEditableInput.prototype.readFromDOMSoon = function() {
+ var this$1 = this
+
+ if (this.readDOMTimeout != null) {
+ return
+ }
+ this.readDOMTimeout = setTimeout(function() {
+ this$1.readDOMTimeout = null
+ if (this$1.composing) {
+ if (this$1.composing.done) {
+ this$1.composing = null
+ } else {
+ return
+ }
+ }
+ this$1.updateFromDOM()
+ }, 80)
+ }
+
+ ContentEditableInput.prototype.updateFromDOM = function() {
+ var this$1 = this
+
+ if (this.cm.isReadOnly() || !this.pollContent()) {
+ runInOp(this.cm, function() {
+ return regChange(this$1.cm)
+ })
+ }
+ }
+
+ ContentEditableInput.prototype.setUneditable = function(node) {
+ node.contentEditable = "false"
+ }
+
+ ContentEditableInput.prototype.onKeyPress = function(e) {
+ if (e.charCode == 0 || this.composing) {
+ return
+ }
+ e.preventDefault()
+ if (!this.cm.isReadOnly()) {
+ operation(this.cm, applyTextInput)(
+ this.cm,
+ String.fromCharCode(e.charCode == null ? e.keyCode : e.charCode),
+ 0
+ )
+ }
+ }
+
+ ContentEditableInput.prototype.readOnlyChanged = function(val) {
+ this.div.contentEditable = String(val != "nocursor")
+ }
+
+ ContentEditableInput.prototype.onContextMenu = function() {}
+ ContentEditableInput.prototype.resetPosition = function() {}
+
+ ContentEditableInput.prototype.needsContentAttribute = true
+
+ function posToDOM(cm, pos) {
+ var view = findViewForLine(cm, pos.line)
+ if (!view || view.hidden) {
+ return null
+ }
+ var line = getLine(cm.doc, pos.line)
+ var info = mapFromLineView(view, line, pos.line)
+
+ var order = getOrder(line, cm.doc.direction),
+ side = "left"
+ if (order) {
+ var partPos = getBidiPartAt(order, pos.ch)
+ side = partPos % 2 ? "right" : "left"
+ }
+ var result = nodeAndOffsetInLineMap(info.map, pos.ch, side)
+ result.offset = result.collapse == "right" ? result.end : result.start
+ return result
+ }
+
+ function isInGutter(node) {
+ for (var scan = node; scan; scan = scan.parentNode) {
+ if (/CodeMirror-gutter-wrapper/.test(scan.className)) {
+ return true
+ }
+ }
+ return false
+ }
+
+ function badPos(pos, bad) {
+ if (bad) {
+ pos.bad = true
+ }
+ return pos
+ }
+
+ function domTextBetween(cm, from, to, fromLine, toLine) {
+ var text = "",
+ closing = false,
+ lineSep = cm.doc.lineSeparator(),
+ extraLinebreak = false
+ function recognizeMarker(id) {
+ return function(marker) {
+ return marker.id == id
+ }
+ }
+ function close() {
+ if (closing) {
+ text += lineSep
+ if (extraLinebreak) {
+ text += lineSep
+ }
+ closing = extraLinebreak = false
+ }
+ }
+ function addText(str) {
+ if (str) {
+ close()
+ text += str
+ }
+ }
+ function walk(node) {
+ if (node.nodeType == 1) {
+ var cmText = node.getAttribute("cm-text")
+ if (cmText) {
+ addText(cmText)
+ return
+ }
+ var markerID = node.getAttribute("cm-marker"),
+ range
+ if (markerID) {
+ var found = cm.findMarks(
+ Pos(fromLine, 0),
+ Pos(toLine + 1, 0),
+ recognizeMarker(+markerID)
+ )
+ if (found.length && (range = found[0].find(0))) {
+ addText(getBetween(cm.doc, range.from, range.to).join(lineSep))
+ }
+ return
+ }
+ if (node.getAttribute("contenteditable") == "false") {
+ return
+ }
+ var isBlock = /^(pre|div|p|li|table|br)$/i.test(node.nodeName)
+ if (!/^br$/i.test(node.nodeName) && node.textContent.length == 0) {
+ return
+ }
+
+ if (isBlock) {
+ close()
+ }
+ for (var i = 0; i < node.childNodes.length; i++) {
+ walk(node.childNodes[i])
+ }
+
+ if (/^(pre|p)$/i.test(node.nodeName)) {
+ extraLinebreak = true
+ }
+ if (isBlock) {
+ closing = true
+ }
+ } else if (node.nodeType == 3) {
+ addText(node.nodeValue.replace(/\u200b/g, "").replace(/\u00a0/g, " "))
+ }
+ }
+ for (;;) {
+ walk(from)
+ if (from == to) {
+ break
+ }
+ from = from.nextSibling
+ extraLinebreak = false
+ }
+ return text
+ }
+
+ function domToPos(cm, node, offset) {
+ var lineNode
+ if (node == cm.display.lineDiv) {
+ lineNode = cm.display.lineDiv.childNodes[offset]
+ if (!lineNode) {
+ return badPos(cm.clipPos(Pos(cm.display.viewTo - 1)), true)
+ }
+ node = null
+ offset = 0
+ } else {
+ for (lineNode = node; ; lineNode = lineNode.parentNode) {
+ if (!lineNode || lineNode == cm.display.lineDiv) {
+ return null
+ }
+ if (lineNode.parentNode && lineNode.parentNode == cm.display.lineDiv) {
+ break
+ }
+ }
+ }
+ for (var i = 0; i < cm.display.view.length; i++) {
+ var lineView = cm.display.view[i]
+ if (lineView.node == lineNode) {
+ return locateNodeInLineView(lineView, node, offset)
+ }
+ }
+ }
+
+ function locateNodeInLineView(lineView, node, offset) {
+ var wrapper = lineView.text.firstChild,
+ bad = false
+ if (!node || !contains(wrapper, node)) {
+ return badPos(Pos(lineNo(lineView.line), 0), true)
+ }
+ if (node == wrapper) {
+ bad = true
+ node = wrapper.childNodes[offset]
+ offset = 0
+ if (!node) {
+ var line = lineView.rest ? lst(lineView.rest) : lineView.line
+ return badPos(Pos(lineNo(line), line.text.length), bad)
+ }
+ }
+
+ var textNode = node.nodeType == 3 ? node : null,
+ topNode = node
+ if (!textNode && node.childNodes.length == 1 && node.firstChild.nodeType == 3) {
+ textNode = node.firstChild
+ if (offset) {
+ offset = textNode.nodeValue.length
+ }
+ }
+ while (topNode.parentNode != wrapper) {
+ topNode = topNode.parentNode
+ }
+ var measure = lineView.measure,
+ maps = measure.maps
+
+ function find(textNode, topNode, offset) {
+ for (var i = -1; i < (maps ? maps.length : 0); i++) {
+ var map = i < 0 ? measure.map : maps[i]
+ for (var j = 0; j < map.length; j += 3) {
+ var curNode = map[j + 2]
+ if (curNode == textNode || curNode == topNode) {
+ var line = lineNo(i < 0 ? lineView.line : lineView.rest[i])
+ var ch = map[j] + offset
+ if (offset < 0 || curNode != textNode) {
+ ch = map[j + (offset ? 1 : 0)]
+ }
+ return Pos(line, ch)
+ }
+ }
+ }
+ }
+ var found = find(textNode, topNode, offset)
+ if (found) {
+ return badPos(found, bad)
+ }
+
+ // FIXME this is all really shaky. might handle the few cases it needs to handle, but likely to cause problems
+ for (
+ var after = topNode.nextSibling,
+ dist = textNode ? textNode.nodeValue.length - offset : 0;
+ after;
+ after = after.nextSibling
+ ) {
+ found = find(after, after.firstChild, 0)
+ if (found) {
+ return badPos(Pos(found.line, found.ch - dist), bad)
+ } else {
+ dist += after.textContent.length
+ }
+ }
+ for (
+ var before = topNode.previousSibling, dist$1 = offset;
+ before;
+ before = before.previousSibling
+ ) {
+ found = find(before, before.firstChild, -1)
+ if (found) {
+ return badPos(Pos(found.line, found.ch + dist$1), bad)
+ } else {
+ dist$1 += before.textContent.length
+ }
+ }
+ }
+
+ // TEXTAREA INPUT STYLE
+
+ var TextareaInput = function(cm) {
+ this.cm = cm
+ // See input.poll and input.reset
+ this.prevInput = ""
+
+ // Flag that indicates whether we expect input to appear real soon
+ // now (after some event like 'keypress' or 'input') and are
+ // polling intensively.
+ this.pollingFast = false
+ // Self-resetting timeout for the poller
+ this.polling = new Delayed()
+ // Used to work around IE issue with selection being forgotten when focus moves away from textarea
+ this.hasSelection = false
+ this.composing = null
+ }
+
+ TextareaInput.prototype.init = function(display) {
+ var this$1 = this
+
+ var input = this,
+ cm = this.cm
+ this.createField(display)
+ var te = this.textarea
+
+ display.wrapper.insertBefore(this.wrapper, display.wrapper.firstChild)
+
+ // Needed to hide big blue blinking cursor on Mobile Safari (doesn't seem to work in iOS 8 anymore)
+ if (ios) {
+ te.style.width = "0px"
+ }
+
+ on(te, "input", function() {
+ if (ie && ie_version >= 9 && this$1.hasSelection) {
+ this$1.hasSelection = null
+ }
+ input.poll()
+ })
+
+ on(te, "paste", function(e) {
+ if (signalDOMEvent(cm, e) || handlePaste(e, cm)) {
+ return
+ }
+
+ cm.state.pasteIncoming = +new Date()
+ input.fastPoll()
+ })
+
+ function prepareCopyCut(e) {
+ if (signalDOMEvent(cm, e)) {
+ return
+ }
+ if (cm.somethingSelected()) {
+ setLastCopied({ lineWise: false, text: cm.getSelections() })
+ } else if (!cm.options.lineWiseCopyCut) {
+ return
+ } else {
+ var ranges = copyableRanges(cm)
+ setLastCopied({ lineWise: true, text: ranges.text })
+ if (e.type == "cut") {
+ cm.setSelections(ranges.ranges, null, sel_dontScroll)
+ } else {
+ input.prevInput = ""
+ te.value = ranges.text.join("\n")
+ selectInput(te)
+ }
+ }
+ if (e.type == "cut") {
+ cm.state.cutIncoming = +new Date()
+ }
+ }
+ on(te, "cut", prepareCopyCut)
+ on(te, "copy", prepareCopyCut)
+
+ on(display.scroller, "paste", function(e) {
+ if (eventInWidget(display, e) || signalDOMEvent(cm, e)) {
+ return
+ }
+ if (!te.dispatchEvent) {
+ cm.state.pasteIncoming = +new Date()
+ input.focus()
+ return
+ }
+
+ // Pass the `paste` event to the textarea so it's handled by its event listener.
+ var event = new Event("paste")
+ event.clipboardData = e.clipboardData
+ te.dispatchEvent(event)
+ })
+
+ // Prevent normal selection in the editor (we handle our own)
+ on(display.lineSpace, "selectstart", function(e) {
+ if (!eventInWidget(display, e)) {
+ e_preventDefault(e)
+ }
+ })
+
+ on(te, "compositionstart", function() {
+ var start = cm.getCursor("from")
+ if (input.composing) {
+ input.composing.range.clear()
+ }
+ input.composing = {
+ start: start,
+ range: cm.markText(start, cm.getCursor("to"), { className: "CodeMirror-composing" })
+ }
+ })
+ on(te, "compositionend", function() {
+ if (input.composing) {
+ input.poll()
+ input.composing.range.clear()
+ input.composing = null
+ }
+ })
+ }
+
+ TextareaInput.prototype.createField = function(_display) {
+ // Wraps and hides input textarea
+ this.wrapper = hiddenTextarea()
+ // The semihidden textarea that is focused when the editor is
+ // focused, and receives input.
+ this.textarea = this.wrapper.firstChild
+ }
+
+ TextareaInput.prototype.prepareSelection = function() {
+ // Redraw the selection and/or cursor
+ var cm = this.cm,
+ display = cm.display,
+ doc = cm.doc
+ var result = prepareSelection(cm)
+
+ // Move the hidden textarea near the cursor to prevent scrolling artifacts
+ if (cm.options.moveInputWithCursor) {
+ var headPos = cursorCoords(cm, doc.sel.primary().head, "div")
+ var wrapOff = display.wrapper.getBoundingClientRect(),
+ lineOff = display.lineDiv.getBoundingClientRect()
+ result.teTop = Math.max(
+ 0,
+ Math.min(display.wrapper.clientHeight - 10, headPos.top + lineOff.top - wrapOff.top)
+ )
+ result.teLeft = Math.max(
+ 0,
+ Math.min(
+ display.wrapper.clientWidth - 10,
+ headPos.left + lineOff.left - wrapOff.left
+ )
+ )
+ }
+
+ return result
+ }
+
+ TextareaInput.prototype.showSelection = function(drawn) {
+ var cm = this.cm,
+ display = cm.display
+ removeChildrenAndAdd(display.cursorDiv, drawn.cursors)
+ removeChildrenAndAdd(display.selectionDiv, drawn.selection)
+ if (drawn.teTop != null) {
+ this.wrapper.style.top = drawn.teTop + "px"
+ this.wrapper.style.left = drawn.teLeft + "px"
+ }
+ }
+
+ // Reset the input to correspond to the selection (or to be empty,
+ // when not typing and nothing is selected)
+ TextareaInput.prototype.reset = function(typing) {
+ if (this.contextMenuPending || this.composing) {
+ return
+ }
+ var cm = this.cm
+ if (cm.somethingSelected()) {
+ this.prevInput = ""
+ var content = cm.getSelection()
+ this.textarea.value = content
+ if (cm.state.focused) {
+ selectInput(this.textarea)
+ }
+ if (ie && ie_version >= 9) {
+ this.hasSelection = content
+ }
+ } else if (!typing) {
+ this.prevInput = this.textarea.value = ""
+ if (ie && ie_version >= 9) {
+ this.hasSelection = null
+ }
+ }
+ }
+
+ TextareaInput.prototype.getField = function() {
+ return this.textarea
+ }
+
+ TextareaInput.prototype.supportsTouch = function() {
+ return false
+ }
+
+ TextareaInput.prototype.focus = function() {
+ if (this.cm.options.readOnly != "nocursor" && (!mobile || activeElt() != this.textarea)) {
+ try {
+ this.textarea.focus()
+ } catch (e) {} // IE8 will throw if the textarea is display: none or not in DOM
+ }
+ }
+
+ TextareaInput.prototype.blur = function() {
+ this.textarea.blur()
+ }
+
+ TextareaInput.prototype.resetPosition = function() {
+ this.wrapper.style.top = this.wrapper.style.left = 0
+ }
+
+ TextareaInput.prototype.receivedFocus = function() {
+ this.slowPoll()
+ }
+
+ // Poll for input changes, using the normal rate of polling. This
+ // runs as long as the editor is focused.
+ TextareaInput.prototype.slowPoll = function() {
+ var this$1 = this
+
+ if (this.pollingFast) {
+ return
+ }
+ this.polling.set(this.cm.options.pollInterval, function() {
+ this$1.poll()
+ if (this$1.cm.state.focused) {
+ this$1.slowPoll()
+ }
+ })
+ }
+
+ // When an event has just come in that is likely to add or change
+ // something in the input textarea, we poll faster, to ensure that
+ // the change appears on the screen quickly.
+ TextareaInput.prototype.fastPoll = function() {
+ var missed = false,
+ input = this
+ input.pollingFast = true
+ function p() {
+ var changed = input.poll()
+ if (!changed && !missed) {
+ missed = true
+ input.polling.set(60, p)
+ } else {
+ input.pollingFast = false
+ input.slowPoll()
+ }
+ }
+ input.polling.set(20, p)
+ }
+
+ // Read input from the textarea, and update the document to match.
+ // When something is selected, it is present in the textarea, and
+ // selected (unless it is huge, in which case a placeholder is
+ // used). When nothing is selected, the cursor sits after previously
+ // seen text (can be empty), which is stored in prevInput (we must
+ // not reset the textarea when typing, because that breaks IME).
+ TextareaInput.prototype.poll = function() {
+ var this$1 = this
+
+ var cm = this.cm,
+ input = this.textarea,
+ prevInput = this.prevInput
+ // Since this is called a *lot*, try to bail out as cheaply as
+ // possible when it is clear that nothing happened. hasSelection
+ // will be the case when there is a lot of text in the textarea,
+ // in which case reading its value would be expensive.
+ if (
+ this.contextMenuPending ||
+ !cm.state.focused ||
+ (hasSelection(input) && !prevInput && !this.composing) ||
+ cm.isReadOnly() ||
+ cm.options.disableInput ||
+ cm.state.keySeq
+ ) {
+ return false
+ }
+
+ var text = input.value
+ // If nothing changed, bail.
+ if (text == prevInput && !cm.somethingSelected()) {
+ return false
+ }
+ // Work around nonsensical selection resetting in IE9/10, and
+ // inexplicable appearance of private area unicode characters on
+ // some key combos in Mac (#2689).
+ if (
+ (ie && ie_version >= 9 && this.hasSelection === text) ||
+ (mac && /[\uf700-\uf7ff]/.test(text))
+ ) {
+ cm.display.input.reset()
+ return false
+ }
+
+ if (cm.doc.sel == cm.display.selForContextMenu) {
+ var first = text.charCodeAt(0)
+ if (first == 0x200b && !prevInput) {
+ prevInput = "\u200b"
+ }
+ if (first == 0x21da) {
+ this.reset()
+ return this.cm.execCommand("undo")
+ }
+ }
+ // Find the part of the input that is actually new
+ var same = 0,
+ l = Math.min(prevInput.length, text.length)
+ while (same < l && prevInput.charCodeAt(same) == text.charCodeAt(same)) {
+ ++same
+ }
+
+ runInOp(cm, function() {
+ applyTextInput(
+ cm,
+ text.slice(same),
+ prevInput.length - same,
+ null,
+ this$1.composing ? "*compose" : null
+ )
+
+ // Don't leave long text in the textarea, since it makes further polling slow
+ if (text.length > 1000 || text.indexOf("\n") > -1) {
+ input.value = this$1.prevInput = ""
+ } else {
+ this$1.prevInput = text
+ }
+
+ if (this$1.composing) {
+ this$1.composing.range.clear()
+ this$1.composing.range = cm.markText(this$1.composing.start, cm.getCursor("to"), {
+ className: "CodeMirror-composing"
+ })
+ }
+ })
+ return true
+ }
+
+ TextareaInput.prototype.ensurePolled = function() {
+ if (this.pollingFast && this.poll()) {
+ this.pollingFast = false
+ }
+ }
+
+ TextareaInput.prototype.onKeyPress = function() {
+ if (ie && ie_version >= 9) {
+ this.hasSelection = null
+ }
+ this.fastPoll()
+ }
+
+ TextareaInput.prototype.onContextMenu = function(e) {
+ var input = this,
+ cm = input.cm,
+ display = cm.display,
+ te = input.textarea
+ if (input.contextMenuPending) {
+ input.contextMenuPending()
+ }
+ var pos = posFromMouse(cm, e),
+ scrollPos = display.scroller.scrollTop
+ if (!pos || presto) {
+ return
+ } // Opera is difficult.
+
+ // Reset the current text selection only if the click is done outside of the selection
+ // and 'resetSelectionOnContextMenu' option is true.
+ var reset = cm.options.resetSelectionOnContextMenu
+ if (reset && cm.doc.sel.contains(pos) == -1) {
+ operation(cm, setSelection)(cm.doc, simpleSelection(pos), sel_dontScroll)
+ }
+
+ var oldCSS = te.style.cssText,
+ oldWrapperCSS = input.wrapper.style.cssText
+ var wrapperBox = input.wrapper.offsetParent.getBoundingClientRect()
+ input.wrapper.style.cssText = "position: static"
+ te.style.cssText =
+ "position: absolute; width: 30px; height: 30px;\n top: " +
+ (e.clientY - wrapperBox.top - 5) +
+ "px; left: " +
+ (e.clientX - wrapperBox.left - 5) +
+ "px;\n z-index: 1000; background: " +
+ (ie ? "rgba(255, 255, 255, .05)" : "transparent") +
+ ";\n outline: none; border-width: 0; outline: none; overflow: hidden; opacity: .05; filter: alpha(opacity=5);"
+ var oldScrollY
+ if (webkit) {
+ oldScrollY = window.scrollY
+ } // Work around Chrome issue (#2712)
+ display.input.focus()
+ if (webkit) {
+ window.scrollTo(null, oldScrollY)
+ }
+ display.input.reset()
+ // Adds "Select all" to context menu in FF
+ if (!cm.somethingSelected()) {
+ te.value = input.prevInput = " "
+ }
+ input.contextMenuPending = rehide
+ display.selForContextMenu = cm.doc.sel
+ clearTimeout(display.detectingSelectAll)
+
+ // Select-all will be greyed out if there's nothing to select, so
+ // this adds a zero-width space so that we can later check whether
+ // it got selected.
+ function prepareSelectAllHack() {
+ if (te.selectionStart != null) {
+ var selected = cm.somethingSelected()
+ var extval = "\u200b" + (selected ? te.value : "")
+ te.value = "\u21da" // Used to catch context-menu undo
+ te.value = extval
+ input.prevInput = selected ? "" : "\u200b"
+ te.selectionStart = 1
+ te.selectionEnd = extval.length
+ // Re-set this, in case some other handler touched the
+ // selection in the meantime.
+ display.selForContextMenu = cm.doc.sel
+ }
+ }
+ function rehide() {
+ if (input.contextMenuPending != rehide) {
+ return
+ }
+ input.contextMenuPending = false
+ input.wrapper.style.cssText = oldWrapperCSS
+ te.style.cssText = oldCSS
+ if (ie && ie_version < 9) {
+ display.scrollbars.setScrollTop((display.scroller.scrollTop = scrollPos))
+ }
+
+ // Try to detect the user choosing select-all
+ if (te.selectionStart != null) {
+ if (!ie || (ie && ie_version < 9)) {
+ prepareSelectAllHack()
+ }
+ var i = 0,
+ poll = function() {
+ if (
+ display.selForContextMenu == cm.doc.sel &&
+ te.selectionStart == 0 &&
+ te.selectionEnd > 0 &&
+ input.prevInput == "\u200b"
+ ) {
+ operation(cm, selectAll)(cm)
+ } else if (i++ < 10) {
+ display.detectingSelectAll = setTimeout(poll, 500)
+ } else {
+ display.selForContextMenu = null
+ display.input.reset()
+ }
+ }
+ display.detectingSelectAll = setTimeout(poll, 200)
+ }
+ }
+
+ if (ie && ie_version >= 9) {
+ prepareSelectAllHack()
+ }
+ if (captureRightClick) {
+ e_stop(e)
+ var mouseup = function() {
+ off(window, "mouseup", mouseup)
+ setTimeout(rehide, 20)
+ }
+ on(window, "mouseup", mouseup)
+ } else {
+ setTimeout(rehide, 50)
+ }
+ }
+
+ TextareaInput.prototype.readOnlyChanged = function(val) {
+ if (!val) {
+ this.reset()
+ }
+ this.textarea.disabled = val == "nocursor"
+ }
+
+ TextareaInput.prototype.setUneditable = function() {}
+
+ TextareaInput.prototype.needsContentAttribute = false
+
+ function fromTextArea(textarea, options) {
+ options = options ? copyObj(options) : {}
+ options.value = textarea.value
+ if (!options.tabindex && textarea.tabIndex) {
+ options.tabindex = textarea.tabIndex
+ }
+ if (!options.placeholder && textarea.placeholder) {
+ options.placeholder = textarea.placeholder
+ }
+ // Set autofocus to true if this textarea is focused, or if it has
+ // autofocus and no other element is focused.
+ if (options.autofocus == null) {
+ var hasFocus = activeElt()
+ options.autofocus =
+ hasFocus == textarea ||
+ (textarea.getAttribute("autofocus") != null && hasFocus == document.body)
+ }
+
+ function save() {
+ textarea.value = cm.getValue()
+ }
+
+ var realSubmit
+ if (textarea.form) {
+ on(textarea.form, "submit", save)
+ // Deplorable hack to make the submit method do the right thing.
+ if (!options.leaveSubmitMethodAlone) {
+ var form = textarea.form
+ realSubmit = form.submit
+ try {
+ var wrappedSubmit = (form.submit = function() {
+ save()
+ form.submit = realSubmit
+ form.submit()
+ form.submit = wrappedSubmit
+ })
+ } catch (e) {}
+ }
+ }
+
+ options.finishInit = function(cm) {
+ cm.save = save
+ cm.getTextArea = function() {
+ return textarea
+ }
+ cm.toTextArea = function() {
+ cm.toTextArea = isNaN // Prevent this from being ran twice
+ save()
+ textarea.parentNode.removeChild(cm.getWrapperElement())
+ textarea.style.display = ""
+ if (textarea.form) {
+ off(textarea.form, "submit", save)
+ if (
+ !options.leaveSubmitMethodAlone &&
+ typeof textarea.form.submit == "function"
+ ) {
+ textarea.form.submit = realSubmit
+ }
+ }
+ }
+ }
+
+ textarea.style.display = "none"
+ var cm = CodeMirror(function(node) {
+ return textarea.parentNode.insertBefore(node, textarea.nextSibling)
+ }, options)
+ return cm
+ }
+
+ function addLegacyProps(CodeMirror) {
+ CodeMirror.off = off
+ CodeMirror.on = on
+ CodeMirror.wheelEventPixels = wheelEventPixels
+ CodeMirror.Doc = Doc
+ CodeMirror.splitLines = splitLinesAuto
+ CodeMirror.countColumn = countColumn
+ CodeMirror.findColumn = findColumn
+ CodeMirror.isWordChar = isWordCharBasic
+ CodeMirror.Pass = Pass
+ CodeMirror.signal = signal
+ CodeMirror.Line = Line
+ CodeMirror.changeEnd = changeEnd
+ CodeMirror.scrollbarModel = scrollbarModel
+ CodeMirror.Pos = Pos
+ CodeMirror.cmpPos = cmp
+ CodeMirror.modes = modes
+ CodeMirror.mimeModes = mimeModes
+ CodeMirror.resolveMode = resolveMode
+ CodeMirror.getMode = getMode
+ CodeMirror.modeExtensions = modeExtensions
+ CodeMirror.extendMode = extendMode
+ CodeMirror.copyState = copyState
+ CodeMirror.startState = startState
+ CodeMirror.innerMode = innerMode
+ CodeMirror.commands = commands
+ CodeMirror.keyMap = keyMap
+ CodeMirror.keyName = keyName
+ CodeMirror.isModifierKey = isModifierKey
+ CodeMirror.lookupKey = lookupKey
+ CodeMirror.normalizeKeyMap = normalizeKeyMap
+ CodeMirror.StringStream = StringStream
+ CodeMirror.SharedTextMarker = SharedTextMarker
+ CodeMirror.TextMarker = TextMarker
+ CodeMirror.LineWidget = LineWidget
+ CodeMirror.e_preventDefault = e_preventDefault
+ CodeMirror.e_stopPropagation = e_stopPropagation
+ CodeMirror.e_stop = e_stop
+ CodeMirror.addClass = addClass
+ CodeMirror.contains = contains
+ CodeMirror.rmClass = rmClass
+ CodeMirror.keyNames = keyNames
+ }
+
+ // EDITOR CONSTRUCTOR
+
+ defineOptions(CodeMirror)
+
+ addEditorMethods(CodeMirror)
+
+ // Set up methods on CodeMirror's prototype to redirect to the editor's document.
+ var dontDelegate = "iter insert remove copy getEditor constructor".split(" ")
+ for (var prop in Doc.prototype) {
+ if (Doc.prototype.hasOwnProperty(prop) && indexOf(dontDelegate, prop) < 0) {
+ CodeMirror.prototype[prop] = (function(method) {
+ return function() {
+ return method.apply(this.doc, arguments)
+ }
+ })(Doc.prototype[prop])
+ }
+ }
+
+ eventMixin(Doc)
+ CodeMirror.inputStyles = { textarea: TextareaInput, contenteditable: ContentEditableInput }
+
+ // Extra arguments are stored as the mode's dependencies, which is
+ // used by (legacy) mechanisms like loadmode.js to automatically
+ // load a mode. (Preferred mechanism is the require/define calls.)
+ CodeMirror.defineMode = function(name /*, mode, …*/) {
+ if (!CodeMirror.defaults.mode && name != "null") {
+ CodeMirror.defaults.mode = name
+ }
+ defineMode.apply(this, arguments)
+ }
+
+ CodeMirror.defineMIME = defineMIME
+
+ // Minimal default mode.
+ CodeMirror.defineMode("null", function() {
+ return {
+ token: function(stream) {
+ return stream.skipToEnd()
+ }
+ }
+ })
+ CodeMirror.defineMIME("text/plain", "null")
+
+ // EXTENSIONS
+
+ CodeMirror.defineExtension = function(name, func) {
+ CodeMirror.prototype[name] = func
+ }
+ CodeMirror.defineDocExtension = function(name, func) {
+ Doc.prototype[name] = func
+ }
+
+ CodeMirror.fromTextArea = fromTextArea
+
+ addLegacyProps(CodeMirror)
+
+ CodeMirror.version = "5.51.0"
+
+ return CodeMirror
+})
diff --git a/docs/assets/getting-started-assets/javascripts/codemirror/theme/xq-light.css b/docs/assets/getting-started-assets/javascripts/codemirror/theme/xq-light.css
new file mode 100755
index 0000000000..20b5c79614
--- /dev/null
+++ b/docs/assets/getting-started-assets/javascripts/codemirror/theme/xq-light.css
@@ -0,0 +1,43 @@
+/*
+Copyright (C) 2011 by MarkLogic Corporation
+Author: Mike Brevoort
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+*/
+.cm-s-xq-light span.cm-keyword {line-height: 1em; font-weight: bold; color: #5A5CAD; }
+.cm-s-xq-light span.cm-atom {color: #6C8CD5;}
+.cm-s-xq-light span.cm-number {color: #164;}
+.cm-s-xq-light span.cm-def {text-decoration:underline;}
+.cm-s-xq-light span.cm-variable {color: black; }
+.cm-s-xq-light span.cm-variable-2 {color:black;}
+.cm-s-xq-light span.cm-variable-3 {color: black; }
+.cm-s-xq-light span.cm-property {}
+.cm-s-xq-light span.cm-operator {}
+.cm-s-xq-light span.cm-comment {color: #0080FF; font-style: italic;}
+.cm-s-xq-light span.cm-string {color: red;}
+.cm-s-xq-light span.cm-meta {color: yellow;}
+.cm-s-xq-light span.cm-qualifier {color: grey}
+.cm-s-xq-light span.cm-builtin {color: #7EA656;}
+.cm-s-xq-light span.cm-bracket {color: #cc7;}
+.cm-s-xq-light span.cm-tag {color: #3F7F7F;}
+.cm-s-xq-light span.cm-attribute {color: #7F007F;}
+.cm-s-xq-light span.cm-error {color: #f00;}
+
+.cm-s-xq-light .CodeMirror-activeline-background {background: #e8f2ff !important;}
+.cm-s-xq-light .CodeMirror-matchingbracket {outline:1px solid grey;color:black !important;background:yellow;}
\ No newline at end of file
diff --git a/docs/assets/getting-started-assets/javascripts/jquery-2.1.4.min.js b/docs/assets/getting-started-assets/javascripts/jquery-2.1.4.min.js
new file mode 100755
index 0000000000..7c734648aa
--- /dev/null
+++ b/docs/assets/getting-started-assets/javascripts/jquery-2.1.4.min.js
@@ -0,0 +1,4913 @@
+/*! jQuery v2.1.4 | (c) 2005, 2015 jQuery Foundation, Inc. | jquery.org/license */
+!(function(a, b) {
+ "object" == typeof module && "object" == typeof module.exports
+ ? (module.exports = a.document
+ ? b(a, !0)
+ : function(a) {
+ if (!a.document) throw new Error("jQuery requires a window with a document")
+ return b(a)
+ })
+ : b(a)
+})("undefined" != typeof window ? window : this, function(a, b) {
+ var c = [],
+ d = c.slice,
+ e = c.concat,
+ f = c.push,
+ g = c.indexOf,
+ h = {},
+ i = h.toString,
+ j = h.hasOwnProperty,
+ k = {},
+ l = a.document,
+ m = "2.1.4",
+ n = function(a, b) {
+ return new n.fn.init(a, b)
+ },
+ o = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,
+ p = /^-ms-/,
+ q = /-([\da-z])/gi,
+ r = function(a, b) {
+ return b.toUpperCase()
+ }
+ ;(n.fn = n.prototype = {
+ jquery: m,
+ constructor: n,
+ selector: "",
+ length: 0,
+ toArray: function() {
+ return d.call(this)
+ },
+ get: function(a) {
+ return null != a ? (0 > a ? this[a + this.length] : this[a]) : d.call(this)
+ },
+ pushStack: function(a) {
+ var b = n.merge(this.constructor(), a)
+ return (b.prevObject = this), (b.context = this.context), b
+ },
+ each: function(a, b) {
+ return n.each(this, a, b)
+ },
+ map: function(a) {
+ return this.pushStack(
+ n.map(this, function(b, c) {
+ return a.call(b, c, b)
+ })
+ )
+ },
+ slice: function() {
+ return this.pushStack(d.apply(this, arguments))
+ },
+ first: function() {
+ return this.eq(0)
+ },
+ last: function() {
+ return this.eq(-1)
+ },
+ eq: function(a) {
+ var b = this.length,
+ c = +a + (0 > a ? b : 0)
+ return this.pushStack(c >= 0 && b > c ? [this[c]] : [])
+ },
+ end: function() {
+ return this.prevObject || this.constructor(null)
+ },
+ push: f,
+ sort: c.sort,
+ splice: c.splice
+ }),
+ (n.extend = n.fn.extend = function() {
+ var a,
+ b,
+ c,
+ d,
+ e,
+ f,
+ g = arguments[0] || {},
+ h = 1,
+ i = arguments.length,
+ j = !1
+ for (
+ "boolean" == typeof g && ((j = g), (g = arguments[h] || {}), h++),
+ "object" == typeof g || n.isFunction(g) || (g = {}),
+ h === i && ((g = this), h--);
+ i > h;
+ h++
+ )
+ if (null != (a = arguments[h]))
+ for (b in a)
+ (c = g[b]),
+ (d = a[b]),
+ g !== d &&
+ (j && d && (n.isPlainObject(d) || (e = n.isArray(d)))
+ ? (e
+ ? ((e = !1), (f = c && n.isArray(c) ? c : []))
+ : (f = c && n.isPlainObject(c) ? c : {}),
+ (g[b] = n.extend(j, f, d)))
+ : void 0 !== d && (g[b] = d))
+ return g
+ }),
+ n.extend({
+ expando: "jQuery" + (m + Math.random()).replace(/\D/g, ""),
+ isReady: !0,
+ error: function(a) {
+ throw new Error(a)
+ },
+ noop: function() {},
+ isFunction: function(a) {
+ return "function" === n.type(a)
+ },
+ isArray: Array.isArray,
+ isWindow: function(a) {
+ return null != a && a === a.window
+ },
+ isNumeric: function(a) {
+ return !n.isArray(a) && a - parseFloat(a) + 1 >= 0
+ },
+ isPlainObject: function(a) {
+ return "object" !== n.type(a) || a.nodeType || n.isWindow(a)
+ ? !1
+ : a.constructor && !j.call(a.constructor.prototype, "isPrototypeOf")
+ ? !1
+ : !0
+ },
+ isEmptyObject: function(a) {
+ var b
+ for (b in a) return !1
+ return !0
+ },
+ type: function(a) {
+ return null == a
+ ? a + ""
+ : "object" == typeof a || "function" == typeof a
+ ? h[i.call(a)] || "object"
+ : typeof a
+ },
+ globalEval: function(a) {
+ var b,
+ c = eval
+ ;(a = n.trim(a)),
+ a &&
+ (1 === a.indexOf("use strict")
+ ? ((b = l.createElement("script")),
+ (b.text = a),
+ l.head.appendChild(b).parentNode.removeChild(b))
+ : c(a))
+ },
+ camelCase: function(a) {
+ return a.replace(p, "ms-").replace(q, r)
+ },
+ nodeName: function(a, b) {
+ return a.nodeName && a.nodeName.toLowerCase() === b.toLowerCase()
+ },
+ each: function(a, b, c) {
+ var d,
+ e = 0,
+ f = a.length,
+ g = s(a)
+ if (c) {
+ if (g) {
+ for (; f > e; e++) if (((d = b.apply(a[e], c)), d === !1)) break
+ } else for (e in a) if (((d = b.apply(a[e], c)), d === !1)) break
+ } else if (g) {
+ for (; f > e; e++) if (((d = b.call(a[e], e, a[e])), d === !1)) break
+ } else for (e in a) if (((d = b.call(a[e], e, a[e])), d === !1)) break
+ return a
+ },
+ trim: function(a) {
+ return null == a ? "" : (a + "").replace(o, "")
+ },
+ makeArray: function(a, b) {
+ var c = b || []
+ return (
+ null != a &&
+ (s(Object(a)) ? n.merge(c, "string" == typeof a ? [a] : a) : f.call(c, a)),
+ c
+ )
+ },
+ inArray: function(a, b, c) {
+ return null == b ? -1 : g.call(b, a, c)
+ },
+ merge: function(a, b) {
+ for (var c = +b.length, d = 0, e = a.length; c > d; d++) a[e++] = b[d]
+ return (a.length = e), a
+ },
+ grep: function(a, b, c) {
+ for (var d, e = [], f = 0, g = a.length, h = !c; g > f; f++)
+ (d = !b(a[f], f)), d !== h && e.push(a[f])
+ return e
+ },
+ map: function(a, b, c) {
+ var d,
+ f = 0,
+ g = a.length,
+ h = s(a),
+ i = []
+ if (h) for (; g > f; f++) (d = b(a[f], f, c)), null != d && i.push(d)
+ else for (f in a) (d = b(a[f], f, c)), null != d && i.push(d)
+ return e.apply([], i)
+ },
+ guid: 1,
+ proxy: function(a, b) {
+ var c, e, f
+ return (
+ "string" == typeof b && ((c = a[b]), (b = a), (a = c)),
+ n.isFunction(a)
+ ? ((e = d.call(arguments, 2)),
+ (f = function() {
+ return a.apply(b || this, e.concat(d.call(arguments)))
+ }),
+ (f.guid = a.guid = a.guid || n.guid++),
+ f)
+ : void 0
+ )
+ },
+ now: Date.now,
+ support: k
+ }),
+ n.each("Boolean Number String Function Array Date RegExp Object Error".split(" "), function(
+ a,
+ b
+ ) {
+ h["[object " + b + "]"] = b.toLowerCase()
+ })
+ function s(a) {
+ var b = "length" in a && a.length,
+ c = n.type(a)
+ return "function" === c || n.isWindow(a)
+ ? !1
+ : 1 === a.nodeType && b
+ ? !0
+ : "array" === c || 0 === b || ("number" == typeof b && b > 0 && b - 1 in a)
+ }
+ var t = (function(a) {
+ var b,
+ c,
+ d,
+ e,
+ f,
+ g,
+ h,
+ i,
+ j,
+ k,
+ l,
+ m,
+ n,
+ o,
+ p,
+ q,
+ r,
+ s,
+ t,
+ u = "sizzle" + 1 * new Date(),
+ v = a.document,
+ w = 0,
+ x = 0,
+ y = ha(),
+ z = ha(),
+ A = ha(),
+ B = function(a, b) {
+ return a === b && (l = !0), 0
+ },
+ C = 1 << 31,
+ D = {}.hasOwnProperty,
+ E = [],
+ F = E.pop,
+ G = E.push,
+ H = E.push,
+ I = E.slice,
+ J = function(a, b) {
+ for (var c = 0, d = a.length; d > c; c++) if (a[c] === b) return c
+ return -1
+ },
+ K =
+ "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",
+ L = "[\\x20\\t\\r\\n\\f]",
+ M = "(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",
+ N = M.replace("w", "w#"),
+ O =
+ "\\[" +
+ L +
+ "*(" +
+ M +
+ ")(?:" +
+ L +
+ "*([*^$|!~]?=)" +
+ L +
+ "*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|(" +
+ N +
+ "))|)" +
+ L +
+ "*\\]",
+ P =
+ ":(" +
+ M +
+ ")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|" +
+ O +
+ ")*)|.*)\\)|)",
+ Q = new RegExp(L + "+", "g"),
+ R = new RegExp("^" + L + "+|((?:^|[^\\\\])(?:\\\\.)*)" + L + "+$", "g"),
+ S = new RegExp("^" + L + "*," + L + "*"),
+ T = new RegExp("^" + L + "*([>+~]|" + L + ")" + L + "*"),
+ U = new RegExp("=" + L + "*([^\\]'\"]*?)" + L + "*\\]", "g"),
+ V = new RegExp(P),
+ W = new RegExp("^" + N + "$"),
+ X = {
+ ID: new RegExp("^#(" + M + ")"),
+ CLASS: new RegExp("^\\.(" + M + ")"),
+ TAG: new RegExp("^(" + M.replace("w", "w*") + ")"),
+ ATTR: new RegExp("^" + O),
+ PSEUDO: new RegExp("^" + P),
+ CHILD: new RegExp(
+ "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" +
+ L +
+ "*(even|odd|(([+-]|)(\\d*)n|)" +
+ L +
+ "*(?:([+-]|)" +
+ L +
+ "*(\\d+)|))" +
+ L +
+ "*\\)|)",
+ "i"
+ ),
+ bool: new RegExp("^(?:" + K + ")$", "i"),
+ needsContext: new RegExp(
+ "^" +
+ L +
+ "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" +
+ L +
+ "*((?:-\\d)?\\d*)" +
+ L +
+ "*\\)|)(?=[^-]|$)",
+ "i"
+ )
+ },
+ Y = /^(?:input|select|textarea|button)$/i,
+ Z = /^h\d$/i,
+ $ = /^[^{]+\{\s*\[native \w/,
+ _ = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,
+ aa = /[+~]/,
+ ba = /'|\\/g,
+ ca = new RegExp("\\\\([\\da-f]{1,6}" + L + "?|(" + L + ")|.)", "ig"),
+ da = function(a, b, c) {
+ var d = "0x" + b - 65536
+ return d !== d || c
+ ? b
+ : 0 > d
+ ? String.fromCharCode(d + 65536)
+ : String.fromCharCode((d >> 10) | 55296, (1023 & d) | 56320)
+ },
+ ea = function() {
+ m()
+ }
+ try {
+ H.apply((E = I.call(v.childNodes)), v.childNodes), E[v.childNodes.length].nodeType
+ } catch (fa) {
+ H = {
+ apply: E.length
+ ? function(a, b) {
+ G.apply(a, I.call(b))
+ }
+ : function(a, b) {
+ var c = a.length,
+ d = 0
+ while ((a[c++] = b[d++]));
+ a.length = c - 1
+ }
+ }
+ }
+ function ga(a, b, d, e) {
+ var f, h, j, k, l, o, r, s, w, x
+ if (
+ ((b ? b.ownerDocument || b : v) !== n && m(b),
+ (b = b || n),
+ (d = d || []),
+ (k = b.nodeType),
+ "string" != typeof a || !a || (1 !== k && 9 !== k && 11 !== k))
+ )
+ return d
+ if (!e && p) {
+ if (11 !== k && (f = _.exec(a)))
+ if ((j = f[1])) {
+ if (9 === k) {
+ if (((h = b.getElementById(j)), !h || !h.parentNode)) return d
+ if (h.id === j) return d.push(h), d
+ } else if (
+ b.ownerDocument &&
+ (h = b.ownerDocument.getElementById(j)) &&
+ t(b, h) &&
+ h.id === j
+ )
+ return d.push(h), d
+ } else {
+ if (f[2]) return H.apply(d, b.getElementsByTagName(a)), d
+ if ((j = f[3]) && c.getElementsByClassName)
+ return H.apply(d, b.getElementsByClassName(j)), d
+ }
+ if (c.qsa && (!q || !q.test(a))) {
+ if (
+ ((s = r = u),
+ (w = b),
+ (x = 1 !== k && a),
+ 1 === k && "object" !== b.nodeName.toLowerCase())
+ ) {
+ ;(o = g(a)),
+ (r = b.getAttribute("id"))
+ ? (s = r.replace(ba, "\\$&"))
+ : b.setAttribute("id", s),
+ (s = "[id='" + s + "'] "),
+ (l = o.length)
+ while (l--) o[l] = s + ra(o[l])
+ ;(w = (aa.test(a) && pa(b.parentNode)) || b), (x = o.join(","))
+ }
+ if (x)
+ try {
+ return H.apply(d, w.querySelectorAll(x)), d
+ } catch (y) {
+ } finally {
+ r || b.removeAttribute("id")
+ }
+ }
+ }
+ return i(a.replace(R, "$1"), b, d, e)
+ }
+ function ha() {
+ var a = []
+ function b(c, e) {
+ return a.push(c + " ") > d.cacheLength && delete b[a.shift()], (b[c + " "] = e)
+ }
+ return b
+ }
+ function ia(a) {
+ return (a[u] = !0), a
+ }
+ function ja(a) {
+ var b = n.createElement("div")
+ try {
+ return !!a(b)
+ } catch (c) {
+ return !1
+ } finally {
+ b.parentNode && b.parentNode.removeChild(b), (b = null)
+ }
+ }
+ function ka(a, b) {
+ var c = a.split("|"),
+ e = a.length
+ while (e--) d.attrHandle[c[e]] = b
+ }
+ function la(a, b) {
+ var c = b && a,
+ d =
+ c &&
+ 1 === a.nodeType &&
+ 1 === b.nodeType &&
+ (~b.sourceIndex || C) - (~a.sourceIndex || C)
+ if (d) return d
+ if (c) while ((c = c.nextSibling)) if (c === b) return -1
+ return a ? 1 : -1
+ }
+ function ma(a) {
+ return function(b) {
+ var c = b.nodeName.toLowerCase()
+ return "input" === c && b.type === a
+ }
+ }
+ function na(a) {
+ return function(b) {
+ var c = b.nodeName.toLowerCase()
+ return ("input" === c || "button" === c) && b.type === a
+ }
+ }
+ function oa(a) {
+ return ia(function(b) {
+ return (
+ (b = +b),
+ ia(function(c, d) {
+ var e,
+ f = a([], c.length, b),
+ g = f.length
+ while (g--) c[(e = f[g])] && (c[e] = !(d[e] = c[e]))
+ })
+ )
+ })
+ }
+ function pa(a) {
+ return a && "undefined" != typeof a.getElementsByTagName && a
+ }
+ ;(c = ga.support = {}),
+ (f = ga.isXML = function(a) {
+ var b = a && (a.ownerDocument || a).documentElement
+ return b ? "HTML" !== b.nodeName : !1
+ }),
+ (m = ga.setDocument = function(a) {
+ var b,
+ e,
+ g = a ? a.ownerDocument || a : v
+ return g !== n && 9 === g.nodeType && g.documentElement
+ ? ((n = g),
+ (o = g.documentElement),
+ (e = g.defaultView),
+ e &&
+ e !== e.top &&
+ (e.addEventListener
+ ? e.addEventListener("unload", ea, !1)
+ : e.attachEvent && e.attachEvent("onunload", ea)),
+ (p = !f(g)),
+ (c.attributes = ja(function(a) {
+ return (a.className = "i"), !a.getAttribute("className")
+ })),
+ (c.getElementsByTagName = ja(function(a) {
+ return (
+ a.appendChild(g.createComment("")),
+ !a.getElementsByTagName("*").length
+ )
+ })),
+ (c.getElementsByClassName = $.test(g.getElementsByClassName)),
+ (c.getById = ja(function(a) {
+ return (
+ (o.appendChild(a).id = u),
+ !g.getElementsByName || !g.getElementsByName(u).length
+ )
+ })),
+ c.getById
+ ? ((d.find.ID = function(a, b) {
+ if ("undefined" != typeof b.getElementById && p) {
+ var c = b.getElementById(a)
+ return c && c.parentNode ? [c] : []
+ }
+ }),
+ (d.filter.ID = function(a) {
+ var b = a.replace(ca, da)
+ return function(a) {
+ return a.getAttribute("id") === b
+ }
+ }))
+ : (delete d.find.ID,
+ (d.filter.ID = function(a) {
+ var b = a.replace(ca, da)
+ return function(a) {
+ var c =
+ "undefined" != typeof a.getAttributeNode &&
+ a.getAttributeNode("id")
+ return c && c.value === b
+ }
+ })),
+ (d.find.TAG = c.getElementsByTagName
+ ? function(a, b) {
+ return "undefined" != typeof b.getElementsByTagName
+ ? b.getElementsByTagName(a)
+ : c.qsa
+ ? b.querySelectorAll(a)
+ : void 0
+ }
+ : function(a, b) {
+ var c,
+ d = [],
+ e = 0,
+ f = b.getElementsByTagName(a)
+ if ("*" === a) {
+ while ((c = f[e++])) 1 === c.nodeType && d.push(c)
+ return d
+ }
+ return f
+ }),
+ (d.find.CLASS =
+ c.getElementsByClassName &&
+ function(a, b) {
+ return p ? b.getElementsByClassName(a) : void 0
+ }),
+ (r = []),
+ (q = []),
+ (c.qsa = $.test(g.querySelectorAll)) &&
+ (ja(function(a) {
+ ;(o.appendChild(a).innerHTML =
+ ""),
+ a.querySelectorAll("[msallowcapture^='']").length &&
+ q.push("[*^$]=" + L + "*(?:''|\"\")"),
+ a.querySelectorAll("[selected]").length ||
+ q.push("\\[" + L + "*(?:value|" + K + ")"),
+ a.querySelectorAll("[id~=" + u + "-]").length || q.push("~="),
+ a.querySelectorAll(":checked").length || q.push(":checked"),
+ a.querySelectorAll("a#" + u + "+*").length || q.push(".#.+[+~]")
+ }),
+ ja(function(a) {
+ var b = g.createElement("input")
+ b.setAttribute("type", "hidden"),
+ a.appendChild(b).setAttribute("name", "D"),
+ a.querySelectorAll("[name=d]").length &&
+ q.push("name" + L + "*[*^$|!~]?="),
+ a.querySelectorAll(":enabled").length ||
+ q.push(":enabled", ":disabled"),
+ a.querySelectorAll("*,:x"),
+ q.push(",.*:")
+ })),
+ (c.matchesSelector = $.test(
+ (s =
+ o.matches ||
+ o.webkitMatchesSelector ||
+ o.mozMatchesSelector ||
+ o.oMatchesSelector ||
+ o.msMatchesSelector)
+ )) &&
+ ja(function(a) {
+ ;(c.disconnectedMatch = s.call(a, "div")),
+ s.call(a, "[s!='']:x"),
+ r.push("!=", P)
+ }),
+ (q = q.length && new RegExp(q.join("|"))),
+ (r = r.length && new RegExp(r.join("|"))),
+ (b = $.test(o.compareDocumentPosition)),
+ (t =
+ b || $.test(o.contains)
+ ? function(a, b) {
+ var c = 9 === a.nodeType ? a.documentElement : a,
+ d = b && b.parentNode
+ return (
+ a === d ||
+ !(
+ !d ||
+ 1 !== d.nodeType ||
+ !(c.contains
+ ? c.contains(d)
+ : a.compareDocumentPosition &&
+ 16 & a.compareDocumentPosition(d))
+ )
+ )
+ }
+ : function(a, b) {
+ if (b) while ((b = b.parentNode)) if (b === a) return !0
+ return !1
+ }),
+ (B = b
+ ? function(a, b) {
+ if (a === b) return (l = !0), 0
+ var d = !a.compareDocumentPosition - !b.compareDocumentPosition
+ return d
+ ? d
+ : ((d =
+ (a.ownerDocument || a) === (b.ownerDocument || b)
+ ? a.compareDocumentPosition(b)
+ : 1),
+ 1 & d ||
+ (!c.sortDetached && b.compareDocumentPosition(a) === d)
+ ? a === g || (a.ownerDocument === v && t(v, a))
+ ? -1
+ : b === g || (b.ownerDocument === v && t(v, b))
+ ? 1
+ : k
+ ? J(k, a) - J(k, b)
+ : 0
+ : 4 & d
+ ? -1
+ : 1)
+ }
+ : function(a, b) {
+ if (a === b) return (l = !0), 0
+ var c,
+ d = 0,
+ e = a.parentNode,
+ f = b.parentNode,
+ h = [a],
+ i = [b]
+ if (!e || !f)
+ return a === g
+ ? -1
+ : b === g
+ ? 1
+ : e
+ ? -1
+ : f
+ ? 1
+ : k
+ ? J(k, a) - J(k, b)
+ : 0
+ if (e === f) return la(a, b)
+ c = a
+ while ((c = c.parentNode)) h.unshift(c)
+ c = b
+ while ((c = c.parentNode)) i.unshift(c)
+ while (h[d] === i[d]) d++
+ return d ? la(h[d], i[d]) : h[d] === v ? -1 : i[d] === v ? 1 : 0
+ }),
+ g)
+ : n
+ }),
+ (ga.matches = function(a, b) {
+ return ga(a, null, null, b)
+ }),
+ (ga.matchesSelector = function(a, b) {
+ if (
+ ((a.ownerDocument || a) !== n && m(a),
+ (b = b.replace(U, "='$1']")),
+ !(!c.matchesSelector || !p || (r && r.test(b)) || (q && q.test(b))))
+ )
+ try {
+ var d = s.call(a, b)
+ if (d || c.disconnectedMatch || (a.document && 11 !== a.document.nodeType))
+ return d
+ } catch (e) {}
+ return ga(b, n, null, [a]).length > 0
+ }),
+ (ga.contains = function(a, b) {
+ return (a.ownerDocument || a) !== n && m(a), t(a, b)
+ }),
+ (ga.attr = function(a, b) {
+ ;(a.ownerDocument || a) !== n && m(a)
+ var e = d.attrHandle[b.toLowerCase()],
+ f = e && D.call(d.attrHandle, b.toLowerCase()) ? e(a, b, !p) : void 0
+ return void 0 !== f
+ ? f
+ : c.attributes || !p
+ ? a.getAttribute(b)
+ : (f = a.getAttributeNode(b)) && f.specified
+ ? f.value
+ : null
+ }),
+ (ga.error = function(a) {
+ throw new Error("Syntax error, unrecognized expression: " + a)
+ }),
+ (ga.uniqueSort = function(a) {
+ var b,
+ d = [],
+ e = 0,
+ f = 0
+ if (((l = !c.detectDuplicates), (k = !c.sortStable && a.slice(0)), a.sort(B), l)) {
+ while ((b = a[f++])) b === a[f] && (e = d.push(f))
+ while (e--) a.splice(d[e], 1)
+ }
+ return (k = null), a
+ }),
+ (e = ga.getText = function(a) {
+ var b,
+ c = "",
+ d = 0,
+ f = a.nodeType
+ if (f) {
+ if (1 === f || 9 === f || 11 === f) {
+ if ("string" == typeof a.textContent) return a.textContent
+ for (a = a.firstChild; a; a = a.nextSibling) c += e(a)
+ } else if (3 === f || 4 === f) return a.nodeValue
+ } else while ((b = a[d++])) c += e(b)
+ return c
+ }),
+ (d = ga.selectors = {
+ cacheLength: 50,
+ createPseudo: ia,
+ match: X,
+ attrHandle: {},
+ find: {},
+ relative: {
+ ">": { dir: "parentNode", first: !0 },
+ " ": { dir: "parentNode" },
+ "+": { dir: "previousSibling", first: !0 },
+ "~": { dir: "previousSibling" }
+ },
+ preFilter: {
+ ATTR: function(a) {
+ return (
+ (a[1] = a[1].replace(ca, da)),
+ (a[3] = (a[3] || a[4] || a[5] || "").replace(ca, da)),
+ "~=" === a[2] && (a[3] = " " + a[3] + " "),
+ a.slice(0, 4)
+ )
+ },
+ CHILD: function(a) {
+ return (
+ (a[1] = a[1].toLowerCase()),
+ "nth" === a[1].slice(0, 3)
+ ? (a[3] || ga.error(a[0]),
+ (a[4] = +(a[4]
+ ? a[5] + (a[6] || 1)
+ : 2 * ("even" === a[3] || "odd" === a[3]))),
+ (a[5] = +(a[7] + a[8] || "odd" === a[3])))
+ : a[3] && ga.error(a[0]),
+ a
+ )
+ },
+ PSEUDO: function(a) {
+ var b,
+ c = !a[6] && a[2]
+ return X.CHILD.test(a[0])
+ ? null
+ : (a[3]
+ ? (a[2] = a[4] || a[5] || "")
+ : c &&
+ V.test(c) &&
+ (b = g(c, !0)) &&
+ (b = c.indexOf(")", c.length - b) - c.length) &&
+ ((a[0] = a[0].slice(0, b)), (a[2] = c.slice(0, b))),
+ a.slice(0, 3))
+ }
+ },
+ filter: {
+ TAG: function(a) {
+ var b = a.replace(ca, da).toLowerCase()
+ return "*" === a
+ ? function() {
+ return !0
+ }
+ : function(a) {
+ return a.nodeName && a.nodeName.toLowerCase() === b
+ }
+ },
+ CLASS: function(a) {
+ var b = y[a + " "]
+ return (
+ b ||
+ ((b = new RegExp("(^|" + L + ")" + a + "(" + L + "|$)")) &&
+ y(a, function(a) {
+ return b.test(
+ ("string" == typeof a.className && a.className) ||
+ ("undefined" != typeof a.getAttribute &&
+ a.getAttribute("class")) ||
+ ""
+ )
+ }))
+ )
+ },
+ ATTR: function(a, b, c) {
+ return function(d) {
+ var e = ga.attr(d, a)
+ return null == e
+ ? "!=" === b
+ : b
+ ? ((e += ""),
+ "=" === b
+ ? e === c
+ : "!=" === b
+ ? e !== c
+ : "^=" === b
+ ? c && 0 === e.indexOf(c)
+ : "*=" === b
+ ? c && e.indexOf(c) > -1
+ : "$=" === b
+ ? c && e.slice(-c.length) === c
+ : "~=" === b
+ ? (" " + e.replace(Q, " ") + " ").indexOf(c) > -1
+ : "|=" === b
+ ? e === c || e.slice(0, c.length + 1) === c + "-"
+ : !1)
+ : !0
+ }
+ },
+ CHILD: function(a, b, c, d, e) {
+ var f = "nth" !== a.slice(0, 3),
+ g = "last" !== a.slice(-4),
+ h = "of-type" === b
+ return 1 === d && 0 === e
+ ? function(a) {
+ return !!a.parentNode
+ }
+ : function(b, c, i) {
+ var j,
+ k,
+ l,
+ m,
+ n,
+ o,
+ p = f !== g ? "nextSibling" : "previousSibling",
+ q = b.parentNode,
+ r = h && b.nodeName.toLowerCase(),
+ s = !i && !h
+ if (q) {
+ if (f) {
+ while (p) {
+ l = b
+ while ((l = l[p]))
+ if (
+ h
+ ? l.nodeName.toLowerCase() === r
+ : 1 === l.nodeType
+ )
+ return !1
+ o = p = "only" === a && !o && "nextSibling"
+ }
+ return !0
+ }
+ if (((o = [g ? q.firstChild : q.lastChild]), g && s)) {
+ ;(k = q[u] || (q[u] = {})),
+ (j = k[a] || []),
+ (n = j[0] === w && j[1]),
+ (m = j[0] === w && j[2]),
+ (l = n && q.childNodes[n])
+ while ((l = (++n && l && l[p]) || (m = n = 0) || o.pop()))
+ if (1 === l.nodeType && ++m && l === b) {
+ k[a] = [w, n, m]
+ break
+ }
+ } else if (s && (j = (b[u] || (b[u] = {}))[a]) && j[0] === w)
+ m = j[1]
+ else
+ while ((l = (++n && l && l[p]) || (m = n = 0) || o.pop()))
+ if (
+ (h
+ ? l.nodeName.toLowerCase() === r
+ : 1 === l.nodeType) &&
+ ++m &&
+ (s && ((l[u] || (l[u] = {}))[a] = [w, m]),
+ l === b)
+ )
+ break
+ return (m -= e), m === d || (m % d === 0 && m / d >= 0)
+ }
+ }
+ },
+ PSEUDO: function(a, b) {
+ var c,
+ e =
+ d.pseudos[a] ||
+ d.setFilters[a.toLowerCase()] ||
+ ga.error("unsupported pseudo: " + a)
+ return e[u]
+ ? e(b)
+ : e.length > 1
+ ? ((c = [a, a, "", b]),
+ d.setFilters.hasOwnProperty(a.toLowerCase())
+ ? ia(function(a, c) {
+ var d,
+ f = e(a, b),
+ g = f.length
+ while (g--) (d = J(a, f[g])), (a[d] = !(c[d] = f[g]))
+ })
+ : function(a) {
+ return e(a, 0, c)
+ })
+ : e
+ }
+ },
+ pseudos: {
+ not: ia(function(a) {
+ var b = [],
+ c = [],
+ d = h(a.replace(R, "$1"))
+ return d[u]
+ ? ia(function(a, b, c, e) {
+ var f,
+ g = d(a, null, e, []),
+ h = a.length
+ while (h--) (f = g[h]) && (a[h] = !(b[h] = f))
+ })
+ : function(a, e, f) {
+ return (b[0] = a), d(b, null, f, c), (b[0] = null), !c.pop()
+ }
+ }),
+ has: ia(function(a) {
+ return function(b) {
+ return ga(a, b).length > 0
+ }
+ }),
+ contains: ia(function(a) {
+ return (
+ (a = a.replace(ca, da)),
+ function(b) {
+ return (b.textContent || b.innerText || e(b)).indexOf(a) > -1
+ }
+ )
+ }),
+ lang: ia(function(a) {
+ return (
+ W.test(a || "") || ga.error("unsupported lang: " + a),
+ (a = a.replace(ca, da).toLowerCase()),
+ function(b) {
+ var c
+ do
+ if (
+ (c = p
+ ? b.lang
+ : b.getAttribute("xml:lang") || b.getAttribute("lang"))
+ )
+ return (
+ (c = c.toLowerCase()),
+ c === a || 0 === c.indexOf(a + "-")
+ )
+ while ((b = b.parentNode) && 1 === b.nodeType)
+ return !1
+ }
+ )
+ }),
+ target: function(b) {
+ var c = a.location && a.location.hash
+ return c && c.slice(1) === b.id
+ },
+ root: function(a) {
+ return a === o
+ },
+ focus: function(a) {
+ return (
+ a === n.activeElement &&
+ (!n.hasFocus || n.hasFocus()) &&
+ !!(a.type || a.href || ~a.tabIndex)
+ )
+ },
+ enabled: function(a) {
+ return a.disabled === !1
+ },
+ disabled: function(a) {
+ return a.disabled === !0
+ },
+ checked: function(a) {
+ var b = a.nodeName.toLowerCase()
+ return ("input" === b && !!a.checked) || ("option" === b && !!a.selected)
+ },
+ selected: function(a) {
+ return a.parentNode && a.parentNode.selectedIndex, a.selected === !0
+ },
+ empty: function(a) {
+ for (a = a.firstChild; a; a = a.nextSibling) if (a.nodeType < 6) return !1
+ return !0
+ },
+ parent: function(a) {
+ return !d.pseudos.empty(a)
+ },
+ header: function(a) {
+ return Z.test(a.nodeName)
+ },
+ input: function(a) {
+ return Y.test(a.nodeName)
+ },
+ button: function(a) {
+ var b = a.nodeName.toLowerCase()
+ return ("input" === b && "button" === a.type) || "button" === b
+ },
+ text: function(a) {
+ var b
+ return (
+ "input" === a.nodeName.toLowerCase() &&
+ "text" === a.type &&
+ (null == (b = a.getAttribute("type")) || "text" === b.toLowerCase())
+ )
+ },
+ first: oa(function() {
+ return [0]
+ }),
+ last: oa(function(a, b) {
+ return [b - 1]
+ }),
+ eq: oa(function(a, b, c) {
+ return [0 > c ? c + b : c]
+ }),
+ even: oa(function(a, b) {
+ for (var c = 0; b > c; c += 2) a.push(c)
+ return a
+ }),
+ odd: oa(function(a, b) {
+ for (var c = 1; b > c; c += 2) a.push(c)
+ return a
+ }),
+ lt: oa(function(a, b, c) {
+ for (var d = 0 > c ? c + b : c; --d >= 0; ) a.push(d)
+ return a
+ }),
+ gt: oa(function(a, b, c) {
+ for (var d = 0 > c ? c + b : c; ++d < b; ) a.push(d)
+ return a
+ })
+ }
+ }),
+ (d.pseudos.nth = d.pseudos.eq)
+ for (b in { radio: !0, checkbox: !0, file: !0, password: !0, image: !0 })
+ d.pseudos[b] = ma(b)
+ for (b in { submit: !0, reset: !0 }) d.pseudos[b] = na(b)
+ function qa() {}
+ ;(qa.prototype = d.filters = d.pseudos),
+ (d.setFilters = new qa()),
+ (g = ga.tokenize = function(a, b) {
+ var c,
+ e,
+ f,
+ g,
+ h,
+ i,
+ j,
+ k = z[a + " "]
+ if (k) return b ? 0 : k.slice(0)
+ ;(h = a), (i = []), (j = d.preFilter)
+ while (h) {
+ ;(!c || (e = S.exec(h))) &&
+ (e && (h = h.slice(e[0].length) || h), i.push((f = []))),
+ (c = !1),
+ (e = T.exec(h)) &&
+ ((c = e.shift()),
+ f.push({ value: c, type: e[0].replace(R, " ") }),
+ (h = h.slice(c.length)))
+ for (g in d.filter)
+ !(e = X[g].exec(h)) ||
+ (j[g] && !(e = j[g](e))) ||
+ ((c = e.shift()),
+ f.push({ value: c, type: g, matches: e }),
+ (h = h.slice(c.length)))
+ if (!c) break
+ }
+ return b ? h.length : h ? ga.error(a) : z(a, i).slice(0)
+ })
+ function ra(a) {
+ for (var b = 0, c = a.length, d = ""; c > b; b++) d += a[b].value
+ return d
+ }
+ function sa(a, b, c) {
+ var d = b.dir,
+ e = c && "parentNode" === d,
+ f = x++
+ return b.first
+ ? function(b, c, f) {
+ while ((b = b[d])) if (1 === b.nodeType || e) return a(b, c, f)
+ }
+ : function(b, c, g) {
+ var h,
+ i,
+ j = [w, f]
+ if (g) {
+ while ((b = b[d])) if ((1 === b.nodeType || e) && a(b, c, g)) return !0
+ } else
+ while ((b = b[d]))
+ if (1 === b.nodeType || e) {
+ if (
+ ((i = b[u] || (b[u] = {})),
+ (h = i[d]) && h[0] === w && h[1] === f)
+ )
+ return (j[2] = h[2])
+ if (((i[d] = j), (j[2] = a(b, c, g)))) return !0
+ }
+ }
+ }
+ function ta(a) {
+ return a.length > 1
+ ? function(b, c, d) {
+ var e = a.length
+ while (e--) if (!a[e](b, c, d)) return !1
+ return !0
+ }
+ : a[0]
+ }
+ function ua(a, b, c) {
+ for (var d = 0, e = b.length; e > d; d++) ga(a, b[d], c)
+ return c
+ }
+ function va(a, b, c, d, e) {
+ for (var f, g = [], h = 0, i = a.length, j = null != b; i > h; h++)
+ (f = a[h]) && (!c || c(f, d, e)) && (g.push(f), j && b.push(h))
+ return g
+ }
+ function wa(a, b, c, d, e, f) {
+ return (
+ d && !d[u] && (d = wa(d)),
+ e && !e[u] && (e = wa(e, f)),
+ ia(function(f, g, h, i) {
+ var j,
+ k,
+ l,
+ m = [],
+ n = [],
+ o = g.length,
+ p = f || ua(b || "*", h.nodeType ? [h] : h, []),
+ q = !a || (!f && b) ? p : va(p, m, a, h, i),
+ r = c ? (e || (f ? a : o || d) ? [] : g) : q
+ if ((c && c(q, r, h, i), d)) {
+ ;(j = va(r, n)), d(j, [], h, i), (k = j.length)
+ while (k--) (l = j[k]) && (r[n[k]] = !(q[n[k]] = l))
+ }
+ if (f) {
+ if (e || a) {
+ if (e) {
+ ;(j = []), (k = r.length)
+ while (k--) (l = r[k]) && j.push((q[k] = l))
+ e(null, (r = []), j, i)
+ }
+ k = r.length
+ while (k--)
+ (l = r[k]) && (j = e ? J(f, l) : m[k]) > -1 && (f[j] = !(g[j] = l))
+ }
+ } else (r = va(r === g ? r.splice(o, r.length) : r)), e ? e(null, g, r, i) : H.apply(g, r)
+ })
+ )
+ }
+ function xa(a) {
+ for (
+ var b,
+ c,
+ e,
+ f = a.length,
+ g = d.relative[a[0].type],
+ h = g || d.relative[" "],
+ i = g ? 1 : 0,
+ k = sa(
+ function(a) {
+ return a === b
+ },
+ h,
+ !0
+ ),
+ l = sa(
+ function(a) {
+ return J(b, a) > -1
+ },
+ h,
+ !0
+ ),
+ m = [
+ function(a, c, d) {
+ var e =
+ (!g && (d || c !== j)) ||
+ ((b = c).nodeType ? k(a, c, d) : l(a, c, d))
+ return (b = null), e
+ }
+ ];
+ f > i;
+ i++
+ )
+ if ((c = d.relative[a[i].type])) m = [sa(ta(m), c)]
+ else {
+ if (((c = d.filter[a[i].type].apply(null, a[i].matches)), c[u])) {
+ for (e = ++i; f > e; e++) if (d.relative[a[e].type]) break
+ return wa(
+ i > 1 && ta(m),
+ i > 1 &&
+ ra(
+ a
+ .slice(0, i - 1)
+ .concat({ value: " " === a[i - 2].type ? "*" : "" })
+ ).replace(R, "$1"),
+ c,
+ e > i && xa(a.slice(i, e)),
+ f > e && xa((a = a.slice(e))),
+ f > e && ra(a)
+ )
+ }
+ m.push(c)
+ }
+ return ta(m)
+ }
+ function ya(a, b) {
+ var c = b.length > 0,
+ e = a.length > 0,
+ f = function(f, g, h, i, k) {
+ var l,
+ m,
+ o,
+ p = 0,
+ q = "0",
+ r = f && [],
+ s = [],
+ t = j,
+ u = f || (e && d.find.TAG("*", k)),
+ v = (w += null == t ? 1 : Math.random() || 0.1),
+ x = u.length
+ for (k && (j = g !== n && g); q !== x && null != (l = u[q]); q++) {
+ if (e && l) {
+ m = 0
+ while ((o = a[m++]))
+ if (o(l, g, h)) {
+ i.push(l)
+ break
+ }
+ k && (w = v)
+ }
+ c && ((l = !o && l) && p--, f && r.push(l))
+ }
+ if (((p += q), c && q !== p)) {
+ m = 0
+ while ((o = b[m++])) o(r, s, g, h)
+ if (f) {
+ if (p > 0) while (q--) r[q] || s[q] || (s[q] = F.call(i))
+ s = va(s)
+ }
+ H.apply(i, s),
+ k && !f && s.length > 0 && p + b.length > 1 && ga.uniqueSort(i)
+ }
+ return k && ((w = v), (j = t)), r
+ }
+ return c ? ia(f) : f
+ }
+ return (
+ (h = ga.compile = function(a, b) {
+ var c,
+ d = [],
+ e = [],
+ f = A[a + " "]
+ if (!f) {
+ b || (b = g(a)), (c = b.length)
+ while (c--) (f = xa(b[c])), f[u] ? d.push(f) : e.push(f)
+ ;(f = A(a, ya(e, d))), (f.selector = a)
+ }
+ return f
+ }),
+ (i = ga.select = function(a, b, e, f) {
+ var i,
+ j,
+ k,
+ l,
+ m,
+ n = "function" == typeof a && a,
+ o = !f && g((a = n.selector || a))
+ if (((e = e || []), 1 === o.length)) {
+ if (
+ ((j = o[0] = o[0].slice(0)),
+ j.length > 2 &&
+ "ID" === (k = j[0]).type &&
+ c.getById &&
+ 9 === b.nodeType &&
+ p &&
+ d.relative[j[1].type])
+ ) {
+ if (((b = (d.find.ID(k.matches[0].replace(ca, da), b) || [])[0]), !b))
+ return e
+ n && (b = b.parentNode), (a = a.slice(j.shift().value.length))
+ }
+ i = X.needsContext.test(a) ? 0 : j.length
+ while (i--) {
+ if (((k = j[i]), d.relative[(l = k.type)])) break
+ if (
+ (m = d.find[l]) &&
+ (f = m(
+ k.matches[0].replace(ca, da),
+ (aa.test(j[0].type) && pa(b.parentNode)) || b
+ ))
+ ) {
+ if ((j.splice(i, 1), (a = f.length && ra(j)), !a))
+ return H.apply(e, f), e
+ break
+ }
+ }
+ }
+ return (n || h(a, o))(f, b, !p, e, (aa.test(a) && pa(b.parentNode)) || b), e
+ }),
+ (c.sortStable =
+ u
+ .split("")
+ .sort(B)
+ .join("") === u),
+ (c.detectDuplicates = !!l),
+ m(),
+ (c.sortDetached = ja(function(a) {
+ return 1 & a.compareDocumentPosition(n.createElement("div"))
+ })),
+ ja(function(a) {
+ return (a.innerHTML = ""), "#" === a.firstChild.getAttribute("href")
+ }) ||
+ ka("type|href|height|width", function(a, b, c) {
+ return c ? void 0 : a.getAttribute(b, "type" === b.toLowerCase() ? 1 : 2)
+ }),
+ (c.attributes &&
+ ja(function(a) {
+ return (
+ (a.innerHTML = ""),
+ a.firstChild.setAttribute("value", ""),
+ "" === a.firstChild.getAttribute("value")
+ )
+ })) ||
+ ka("value", function(a, b, c) {
+ return c || "input" !== a.nodeName.toLowerCase() ? void 0 : a.defaultValue
+ }),
+ ja(function(a) {
+ return null == a.getAttribute("disabled")
+ }) ||
+ ka(K, function(a, b, c) {
+ var d
+ return c
+ ? void 0
+ : a[b] === !0
+ ? b.toLowerCase()
+ : (d = a.getAttributeNode(b)) && d.specified
+ ? d.value
+ : null
+ }),
+ ga
+ )
+ })(a)
+ ;(n.find = t),
+ (n.expr = t.selectors),
+ (n.expr[":"] = n.expr.pseudos),
+ (n.unique = t.uniqueSort),
+ (n.text = t.getText),
+ (n.isXMLDoc = t.isXML),
+ (n.contains = t.contains)
+ var u = n.expr.match.needsContext,
+ v = /^<(\w+)\s*\/?>(?:<\/\1>|)$/,
+ w = /^.[^:#\[\.,]*$/
+ function x(a, b, c) {
+ if (n.isFunction(b))
+ return n.grep(a, function(a, d) {
+ return !!b.call(a, d, a) !== c
+ })
+ if (b.nodeType)
+ return n.grep(a, function(a) {
+ return (a === b) !== c
+ })
+ if ("string" == typeof b) {
+ if (w.test(b)) return n.filter(b, a, c)
+ b = n.filter(b, a)
+ }
+ return n.grep(a, function(a) {
+ return g.call(b, a) >= 0 !== c
+ })
+ }
+ ;(n.filter = function(a, b, c) {
+ var d = b[0]
+ return (
+ c && (a = ":not(" + a + ")"),
+ 1 === b.length && 1 === d.nodeType
+ ? n.find.matchesSelector(d, a)
+ ? [d]
+ : []
+ : n.find.matches(
+ a,
+ n.grep(b, function(a) {
+ return 1 === a.nodeType
+ })
+ )
+ )
+ }),
+ n.fn.extend({
+ find: function(a) {
+ var b,
+ c = this.length,
+ d = [],
+ e = this
+ if ("string" != typeof a)
+ return this.pushStack(
+ n(a).filter(function() {
+ for (b = 0; c > b; b++) if (n.contains(e[b], this)) return !0
+ })
+ )
+ for (b = 0; c > b; b++) n.find(a, e[b], d)
+ return (
+ (d = this.pushStack(c > 1 ? n.unique(d) : d)),
+ (d.selector = this.selector ? this.selector + " " + a : a),
+ d
+ )
+ },
+ filter: function(a) {
+ return this.pushStack(x(this, a || [], !1))
+ },
+ not: function(a) {
+ return this.pushStack(x(this, a || [], !0))
+ },
+ is: function(a) {
+ return !!x(this, "string" == typeof a && u.test(a) ? n(a) : a || [], !1).length
+ }
+ })
+ var y,
+ z = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,
+ A = (n.fn.init = function(a, b) {
+ var c, d
+ if (!a) return this
+ if ("string" == typeof a) {
+ if (
+ ((c =
+ "<" === a[0] && ">" === a[a.length - 1] && a.length >= 3
+ ? [null, a, null]
+ : z.exec(a)),
+ !c || (!c[1] && b))
+ )
+ return !b || b.jquery ? (b || y).find(a) : this.constructor(b).find(a)
+ if (c[1]) {
+ if (
+ ((b = b instanceof n ? b[0] : b),
+ n.merge(
+ this,
+ n.parseHTML(c[1], b && b.nodeType ? b.ownerDocument || b : l, !0)
+ ),
+ v.test(c[1]) && n.isPlainObject(b))
+ )
+ for (c in b) n.isFunction(this[c]) ? this[c](b[c]) : this.attr(c, b[c])
+ return this
+ }
+ return (
+ (d = l.getElementById(c[2])),
+ d && d.parentNode && ((this.length = 1), (this[0] = d)),
+ (this.context = l),
+ (this.selector = a),
+ this
+ )
+ }
+ return a.nodeType
+ ? ((this.context = this[0] = a), (this.length = 1), this)
+ : n.isFunction(a)
+ ? "undefined" != typeof y.ready
+ ? y.ready(a)
+ : a(n)
+ : (void 0 !== a.selector &&
+ ((this.selector = a.selector), (this.context = a.context)),
+ n.makeArray(a, this))
+ })
+ ;(A.prototype = n.fn), (y = n(l))
+ var B = /^(?:parents|prev(?:Until|All))/,
+ C = { children: !0, contents: !0, next: !0, prev: !0 }
+ n.extend({
+ dir: function(a, b, c) {
+ var d = [],
+ e = void 0 !== c
+ while ((a = a[b]) && 9 !== a.nodeType)
+ if (1 === a.nodeType) {
+ if (e && n(a).is(c)) break
+ d.push(a)
+ }
+ return d
+ },
+ sibling: function(a, b) {
+ for (var c = []; a; a = a.nextSibling) 1 === a.nodeType && a !== b && c.push(a)
+ return c
+ }
+ }),
+ n.fn.extend({
+ has: function(a) {
+ var b = n(a, this),
+ c = b.length
+ return this.filter(function() {
+ for (var a = 0; c > a; a++) if (n.contains(this, b[a])) return !0
+ })
+ },
+ closest: function(a, b) {
+ for (
+ var c,
+ d = 0,
+ e = this.length,
+ f = [],
+ g = u.test(a) || "string" != typeof a ? n(a, b || this.context) : 0;
+ e > d;
+ d++
+ )
+ for (c = this[d]; c && c !== b; c = c.parentNode)
+ if (
+ c.nodeType < 11 &&
+ (g ? g.index(c) > -1 : 1 === c.nodeType && n.find.matchesSelector(c, a))
+ ) {
+ f.push(c)
+ break
+ }
+ return this.pushStack(f.length > 1 ? n.unique(f) : f)
+ },
+ index: function(a) {
+ return a
+ ? "string" == typeof a
+ ? g.call(n(a), this[0])
+ : g.call(this, a.jquery ? a[0] : a)
+ : this[0] && this[0].parentNode
+ ? this.first().prevAll().length
+ : -1
+ },
+ add: function(a, b) {
+ return this.pushStack(n.unique(n.merge(this.get(), n(a, b))))
+ },
+ addBack: function(a) {
+ return this.add(null == a ? this.prevObject : this.prevObject.filter(a))
+ }
+ })
+ function D(a, b) {
+ while ((a = a[b]) && 1 !== a.nodeType);
+ return a
+ }
+ n.each(
+ {
+ parent: function(a) {
+ var b = a.parentNode
+ return b && 11 !== b.nodeType ? b : null
+ },
+ parents: function(a) {
+ return n.dir(a, "parentNode")
+ },
+ parentsUntil: function(a, b, c) {
+ return n.dir(a, "parentNode", c)
+ },
+ next: function(a) {
+ return D(a, "nextSibling")
+ },
+ prev: function(a) {
+ return D(a, "previousSibling")
+ },
+ nextAll: function(a) {
+ return n.dir(a, "nextSibling")
+ },
+ prevAll: function(a) {
+ return n.dir(a, "previousSibling")
+ },
+ nextUntil: function(a, b, c) {
+ return n.dir(a, "nextSibling", c)
+ },
+ prevUntil: function(a, b, c) {
+ return n.dir(a, "previousSibling", c)
+ },
+ siblings: function(a) {
+ return n.sibling((a.parentNode || {}).firstChild, a)
+ },
+ children: function(a) {
+ return n.sibling(a.firstChild)
+ },
+ contents: function(a) {
+ return a.contentDocument || n.merge([], a.childNodes)
+ }
+ },
+ function(a, b) {
+ n.fn[a] = function(c, d) {
+ var e = n.map(this, b, c)
+ return (
+ "Until" !== a.slice(-5) && (d = c),
+ d && "string" == typeof d && (e = n.filter(d, e)),
+ this.length > 1 && (C[a] || n.unique(e), B.test(a) && e.reverse()),
+ this.pushStack(e)
+ )
+ }
+ }
+ )
+ var E = /\S+/g,
+ F = {}
+ function G(a) {
+ var b = (F[a] = {})
+ return (
+ n.each(a.match(E) || [], function(a, c) {
+ b[c] = !0
+ }),
+ b
+ )
+ }
+ ;(n.Callbacks = function(a) {
+ a = "string" == typeof a ? F[a] || G(a) : n.extend({}, a)
+ var b,
+ c,
+ d,
+ e,
+ f,
+ g,
+ h = [],
+ i = !a.once && [],
+ j = function(l) {
+ for (
+ b = a.memory && l, c = !0, g = e || 0, e = 0, f = h.length, d = !0;
+ h && f > g;
+ g++
+ )
+ if (h[g].apply(l[0], l[1]) === !1 && a.stopOnFalse) {
+ b = !1
+ break
+ }
+ ;(d = !1), h && (i ? i.length && j(i.shift()) : b ? (h = []) : k.disable())
+ },
+ k = {
+ add: function() {
+ if (h) {
+ var c = h.length
+ !(function g(b) {
+ n.each(b, function(b, c) {
+ var d = n.type(c)
+ "function" === d
+ ? (a.unique && k.has(c)) || h.push(c)
+ : c && c.length && "string" !== d && g(c)
+ })
+ })(arguments),
+ d ? (f = h.length) : b && ((e = c), j(b))
+ }
+ return this
+ },
+ remove: function() {
+ return (
+ h &&
+ n.each(arguments, function(a, b) {
+ var c
+ while ((c = n.inArray(b, h, c)) > -1)
+ h.splice(c, 1), d && (f >= c && f--, g >= c && g--)
+ }),
+ this
+ )
+ },
+ has: function(a) {
+ return a ? n.inArray(a, h) > -1 : !(!h || !h.length)
+ },
+ empty: function() {
+ return (h = []), (f = 0), this
+ },
+ disable: function() {
+ return (h = i = b = void 0), this
+ },
+ disabled: function() {
+ return !h
+ },
+ lock: function() {
+ return (i = void 0), b || k.disable(), this
+ },
+ locked: function() {
+ return !i
+ },
+ fireWith: function(a, b) {
+ return (
+ !h ||
+ (c && !i) ||
+ ((b = b || []),
+ (b = [a, b.slice ? b.slice() : b]),
+ d ? i.push(b) : j(b)),
+ this
+ )
+ },
+ fire: function() {
+ return k.fireWith(this, arguments), this
+ },
+ fired: function() {
+ return !!c
+ }
+ }
+ return k
+ }),
+ n.extend({
+ Deferred: function(a) {
+ var b = [
+ ["resolve", "done", n.Callbacks("once memory"), "resolved"],
+ ["reject", "fail", n.Callbacks("once memory"), "rejected"],
+ ["notify", "progress", n.Callbacks("memory")]
+ ],
+ c = "pending",
+ d = {
+ state: function() {
+ return c
+ },
+ always: function() {
+ return e.done(arguments).fail(arguments), this
+ },
+ then: function() {
+ var a = arguments
+ return n
+ .Deferred(function(c) {
+ n.each(b, function(b, f) {
+ var g = n.isFunction(a[b]) && a[b]
+ e[f[1]](function() {
+ var a = g && g.apply(this, arguments)
+ a && n.isFunction(a.promise)
+ ? a
+ .promise()
+ .done(c.resolve)
+ .fail(c.reject)
+ .progress(c.notify)
+ : c[f[0] + "With"](
+ this === d ? c.promise() : this,
+ g ? [a] : arguments
+ )
+ })
+ }),
+ (a = null)
+ })
+ .promise()
+ },
+ promise: function(a) {
+ return null != a ? n.extend(a, d) : d
+ }
+ },
+ e = {}
+ return (
+ (d.pipe = d.then),
+ n.each(b, function(a, f) {
+ var g = f[2],
+ h = f[3]
+ ;(d[f[1]] = g.add),
+ h &&
+ g.add(
+ function() {
+ c = h
+ },
+ b[1 ^ a][2].disable,
+ b[2][2].lock
+ ),
+ (e[f[0]] = function() {
+ return e[f[0] + "With"](this === e ? d : this, arguments), this
+ }),
+ (e[f[0] + "With"] = g.fireWith)
+ }),
+ d.promise(e),
+ a && a.call(e, e),
+ e
+ )
+ },
+ when: function(a) {
+ var b = 0,
+ c = d.call(arguments),
+ e = c.length,
+ f = 1 !== e || (a && n.isFunction(a.promise)) ? e : 0,
+ g = 1 === f ? a : n.Deferred(),
+ h = function(a, b, c) {
+ return function(e) {
+ ;(b[a] = this),
+ (c[a] = arguments.length > 1 ? d.call(arguments) : e),
+ c === i ? g.notifyWith(b, c) : --f || g.resolveWith(b, c)
+ }
+ },
+ i,
+ j,
+ k
+ if (e > 1)
+ for (i = new Array(e), j = new Array(e), k = new Array(e); e > b; b++)
+ c[b] && n.isFunction(c[b].promise)
+ ? c[b]
+ .promise()
+ .done(h(b, k, c))
+ .fail(g.reject)
+ .progress(h(b, j, i))
+ : --f
+ return f || g.resolveWith(k, c), g.promise()
+ }
+ })
+ var H
+ ;(n.fn.ready = function(a) {
+ return n.ready.promise().done(a), this
+ }),
+ n.extend({
+ isReady: !1,
+ readyWait: 1,
+ holdReady: function(a) {
+ a ? n.readyWait++ : n.ready(!0)
+ },
+ ready: function(a) {
+ ;(a === !0 ? --n.readyWait : n.isReady) ||
+ ((n.isReady = !0),
+ (a !== !0 && --n.readyWait > 0) ||
+ (H.resolveWith(l, [n]),
+ n.fn.triggerHandler && (n(l).triggerHandler("ready"), n(l).off("ready"))))
+ }
+ })
+ function I() {
+ l.removeEventListener("DOMContentLoaded", I, !1),
+ a.removeEventListener("load", I, !1),
+ n.ready()
+ }
+ ;(n.ready.promise = function(b) {
+ return (
+ H ||
+ ((H = n.Deferred()),
+ "complete" === l.readyState
+ ? setTimeout(n.ready)
+ : (l.addEventListener("DOMContentLoaded", I, !1),
+ a.addEventListener("load", I, !1))),
+ H.promise(b)
+ )
+ }),
+ n.ready.promise()
+ var J = (n.access = function(a, b, c, d, e, f, g) {
+ var h = 0,
+ i = a.length,
+ j = null == c
+ if ("object" === n.type(c)) {
+ e = !0
+ for (h in c) n.access(a, b, h, c[h], !0, f, g)
+ } else if (
+ void 0 !== d &&
+ ((e = !0),
+ n.isFunction(d) || (g = !0),
+ j &&
+ (g
+ ? (b.call(a, d), (b = null))
+ : ((j = b),
+ (b = function(a, b, c) {
+ return j.call(n(a), c)
+ }))),
+ b)
+ )
+ for (; i > h; h++) b(a[h], c, g ? d : d.call(a[h], h, b(a[h], c)))
+ return e ? a : j ? b.call(a) : i ? b(a[0], c) : f
+ })
+ n.acceptData = function(a) {
+ return 1 === a.nodeType || 9 === a.nodeType || !+a.nodeType
+ }
+ function K() {
+ Object.defineProperty((this.cache = {}), 0, {
+ get: function() {
+ return {}
+ }
+ }),
+ (this.expando = n.expando + K.uid++)
+ }
+ ;(K.uid = 1),
+ (K.accepts = n.acceptData),
+ (K.prototype = {
+ key: function(a) {
+ if (!K.accepts(a)) return 0
+ var b = {},
+ c = a[this.expando]
+ if (!c) {
+ c = K.uid++
+ try {
+ ;(b[this.expando] = { value: c }), Object.defineProperties(a, b)
+ } catch (d) {
+ ;(b[this.expando] = c), n.extend(a, b)
+ }
+ }
+ return this.cache[c] || (this.cache[c] = {}), c
+ },
+ set: function(a, b, c) {
+ var d,
+ e = this.key(a),
+ f = this.cache[e]
+ if ("string" == typeof b) f[b] = c
+ else if (n.isEmptyObject(f)) n.extend(this.cache[e], b)
+ else for (d in b) f[d] = b[d]
+ return f
+ },
+ get: function(a, b) {
+ var c = this.cache[this.key(a)]
+ return void 0 === b ? c : c[b]
+ },
+ access: function(a, b, c) {
+ var d
+ return void 0 === b || (b && "string" == typeof b && void 0 === c)
+ ? ((d = this.get(a, b)), void 0 !== d ? d : this.get(a, n.camelCase(b)))
+ : (this.set(a, b, c), void 0 !== c ? c : b)
+ },
+ remove: function(a, b) {
+ var c,
+ d,
+ e,
+ f = this.key(a),
+ g = this.cache[f]
+ if (void 0 === b) this.cache[f] = {}
+ else {
+ n.isArray(b)
+ ? (d = b.concat(b.map(n.camelCase)))
+ : ((e = n.camelCase(b)),
+ b in g ? (d = [b, e]) : ((d = e), (d = d in g ? [d] : d.match(E) || []))),
+ (c = d.length)
+ while (c--) delete g[d[c]]
+ }
+ },
+ hasData: function(a) {
+ return !n.isEmptyObject(this.cache[a[this.expando]] || {})
+ },
+ discard: function(a) {
+ a[this.expando] && delete this.cache[a[this.expando]]
+ }
+ })
+ var L = new K(),
+ M = new K(),
+ N = /^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,
+ O = /([A-Z])/g
+ function P(a, b, c) {
+ var d
+ if (void 0 === c && 1 === a.nodeType)
+ if (
+ ((d = "data-" + b.replace(O, "-$1").toLowerCase()),
+ (c = a.getAttribute(d)),
+ "string" == typeof c)
+ ) {
+ try {
+ c =
+ "true" === c
+ ? !0
+ : "false" === c
+ ? !1
+ : "null" === c
+ ? null
+ : +c + "" === c
+ ? +c
+ : N.test(c)
+ ? n.parseJSON(c)
+ : c
+ } catch (e) {}
+ M.set(a, b, c)
+ } else c = void 0
+ return c
+ }
+ n.extend({
+ hasData: function(a) {
+ return M.hasData(a) || L.hasData(a)
+ },
+ data: function(a, b, c) {
+ return M.access(a, b, c)
+ },
+ removeData: function(a, b) {
+ M.remove(a, b)
+ },
+ _data: function(a, b, c) {
+ return L.access(a, b, c)
+ },
+ _removeData: function(a, b) {
+ L.remove(a, b)
+ }
+ }),
+ n.fn.extend({
+ data: function(a, b) {
+ var c,
+ d,
+ e,
+ f = this[0],
+ g = f && f.attributes
+ if (void 0 === a) {
+ if (
+ this.length &&
+ ((e = M.get(f)), 1 === f.nodeType && !L.get(f, "hasDataAttrs"))
+ ) {
+ c = g.length
+ while (c--)
+ g[c] &&
+ ((d = g[c].name),
+ 0 === d.indexOf("data-") &&
+ ((d = n.camelCase(d.slice(5))), P(f, d, e[d])))
+ L.set(f, "hasDataAttrs", !0)
+ }
+ return e
+ }
+ return "object" == typeof a
+ ? this.each(function() {
+ M.set(this, a)
+ })
+ : J(
+ this,
+ function(b) {
+ var c,
+ d = n.camelCase(a)
+ if (f && void 0 === b) {
+ if (((c = M.get(f, a)), void 0 !== c)) return c
+ if (((c = M.get(f, d)), void 0 !== c)) return c
+ if (((c = P(f, d, void 0)), void 0 !== c)) return c
+ } else
+ this.each(function() {
+ var c = M.get(this, d)
+ M.set(this, d, b),
+ -1 !== a.indexOf("-") && void 0 !== c && M.set(this, a, b)
+ })
+ },
+ null,
+ b,
+ arguments.length > 1,
+ null,
+ !0
+ )
+ },
+ removeData: function(a) {
+ return this.each(function() {
+ M.remove(this, a)
+ })
+ }
+ }),
+ n.extend({
+ queue: function(a, b, c) {
+ var d
+ return a
+ ? ((b = (b || "fx") + "queue"),
+ (d = L.get(a, b)),
+ c && (!d || n.isArray(c) ? (d = L.access(a, b, n.makeArray(c))) : d.push(c)),
+ d || [])
+ : void 0
+ },
+ dequeue: function(a, b) {
+ b = b || "fx"
+ var c = n.queue(a, b),
+ d = c.length,
+ e = c.shift(),
+ f = n._queueHooks(a, b),
+ g = function() {
+ n.dequeue(a, b)
+ }
+ "inprogress" === e && ((e = c.shift()), d--),
+ e && ("fx" === b && c.unshift("inprogress"), delete f.stop, e.call(a, g, f)),
+ !d && f && f.empty.fire()
+ },
+ _queueHooks: function(a, b) {
+ var c = b + "queueHooks"
+ return (
+ L.get(a, c) ||
+ L.access(a, c, {
+ empty: n.Callbacks("once memory").add(function() {
+ L.remove(a, [b + "queue", c])
+ })
+ })
+ )
+ }
+ }),
+ n.fn.extend({
+ queue: function(a, b) {
+ var c = 2
+ return (
+ "string" != typeof a && ((b = a), (a = "fx"), c--),
+ arguments.length < c
+ ? n.queue(this[0], a)
+ : void 0 === b
+ ? this
+ : this.each(function() {
+ var c = n.queue(this, a, b)
+ n._queueHooks(this, a),
+ "fx" === a && "inprogress" !== c[0] && n.dequeue(this, a)
+ })
+ )
+ },
+ dequeue: function(a) {
+ return this.each(function() {
+ n.dequeue(this, a)
+ })
+ },
+ clearQueue: function(a) {
+ return this.queue(a || "fx", [])
+ },
+ promise: function(a, b) {
+ var c,
+ d = 1,
+ e = n.Deferred(),
+ f = this,
+ g = this.length,
+ h = function() {
+ --d || e.resolveWith(f, [f])
+ }
+ "string" != typeof a && ((b = a), (a = void 0)), (a = a || "fx")
+ while (g--)
+ (c = L.get(f[g], a + "queueHooks")), c && c.empty && (d++, c.empty.add(h))
+ return h(), e.promise(b)
+ }
+ })
+ var Q = /[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,
+ R = ["Top", "Right", "Bottom", "Left"],
+ S = function(a, b) {
+ return (a = b || a), "none" === n.css(a, "display") || !n.contains(a.ownerDocument, a)
+ },
+ T = /^(?:checkbox|radio)$/i
+ !(function() {
+ var a = l.createDocumentFragment(),
+ b = a.appendChild(l.createElement("div")),
+ c = l.createElement("input")
+ c.setAttribute("type", "radio"),
+ c.setAttribute("checked", "checked"),
+ c.setAttribute("name", "t"),
+ b.appendChild(c),
+ (k.checkClone = b.cloneNode(!0).cloneNode(!0).lastChild.checked),
+ (b.innerHTML = ""),
+ (k.noCloneChecked = !!b.cloneNode(!0).lastChild.defaultValue)
+ })()
+ var U = "undefined"
+ k.focusinBubbles = "onfocusin" in a
+ var V = /^key/,
+ W = /^(?:mouse|pointer|contextmenu)|click/,
+ X = /^(?:focusinfocus|focusoutblur)$/,
+ Y = /^([^.]*)(?:\.(.+)|)$/
+ function Z() {
+ return !0
+ }
+ function $() {
+ return !1
+ }
+ function _() {
+ try {
+ return l.activeElement
+ } catch (a) {}
+ }
+ ;(n.event = {
+ global: {},
+ add: function(a, b, c, d, e) {
+ var f,
+ g,
+ h,
+ i,
+ j,
+ k,
+ l,
+ m,
+ o,
+ p,
+ q,
+ r = L.get(a)
+ if (r) {
+ c.handler && ((f = c), (c = f.handler), (e = f.selector)),
+ c.guid || (c.guid = n.guid++),
+ (i = r.events) || (i = r.events = {}),
+ (g = r.handle) ||
+ (g = r.handle = function(b) {
+ return typeof n !== U && n.event.triggered !== b.type
+ ? n.event.dispatch.apply(a, arguments)
+ : void 0
+ }),
+ (b = (b || "").match(E) || [""]),
+ (j = b.length)
+ while (j--)
+ (h = Y.exec(b[j]) || []),
+ (o = q = h[1]),
+ (p = (h[2] || "").split(".").sort()),
+ o &&
+ ((l = n.event.special[o] || {}),
+ (o = (e ? l.delegateType : l.bindType) || o),
+ (l = n.event.special[o] || {}),
+ (k = n.extend(
+ {
+ type: o,
+ origType: q,
+ data: d,
+ handler: c,
+ guid: c.guid,
+ selector: e,
+ needsContext: e && n.expr.match.needsContext.test(e),
+ namespace: p.join(".")
+ },
+ f
+ )),
+ (m = i[o]) ||
+ ((m = i[o] = []),
+ (m.delegateCount = 0),
+ (l.setup && l.setup.call(a, d, p, g) !== !1) ||
+ (a.addEventListener && a.addEventListener(o, g, !1))),
+ l.add &&
+ (l.add.call(a, k), k.handler.guid || (k.handler.guid = c.guid)),
+ e ? m.splice(m.delegateCount++, 0, k) : m.push(k),
+ (n.event.global[o] = !0))
+ }
+ },
+ remove: function(a, b, c, d, e) {
+ var f,
+ g,
+ h,
+ i,
+ j,
+ k,
+ l,
+ m,
+ o,
+ p,
+ q,
+ r = L.hasData(a) && L.get(a)
+ if (r && (i = r.events)) {
+ ;(b = (b || "").match(E) || [""]), (j = b.length)
+ while (j--)
+ if (
+ ((h = Y.exec(b[j]) || []),
+ (o = q = h[1]),
+ (p = (h[2] || "").split(".").sort()),
+ o)
+ ) {
+ ;(l = n.event.special[o] || {}),
+ (o = (d ? l.delegateType : l.bindType) || o),
+ (m = i[o] || []),
+ (h =
+ h[2] &&
+ new RegExp("(^|\\.)" + p.join("\\.(?:.*\\.|)") + "(\\.|$)")),
+ (g = f = m.length)
+ while (f--)
+ (k = m[f]),
+ (!e && q !== k.origType) ||
+ (c && c.guid !== k.guid) ||
+ (h && !h.test(k.namespace)) ||
+ (d && d !== k.selector && ("**" !== d || !k.selector)) ||
+ (m.splice(f, 1),
+ k.selector && m.delegateCount--,
+ l.remove && l.remove.call(a, k))
+ g &&
+ !m.length &&
+ ((l.teardown && l.teardown.call(a, p, r.handle) !== !1) ||
+ n.removeEvent(a, o, r.handle),
+ delete i[o])
+ } else for (o in i) n.event.remove(a, o + b[j], c, d, !0)
+ n.isEmptyObject(i) && (delete r.handle, L.remove(a, "events"))
+ }
+ },
+ trigger: function(b, c, d, e) {
+ var f,
+ g,
+ h,
+ i,
+ k,
+ m,
+ o,
+ p = [d || l],
+ q = j.call(b, "type") ? b.type : b,
+ r = j.call(b, "namespace") ? b.namespace.split(".") : []
+ if (
+ ((g = h = d = d || l),
+ 3 !== d.nodeType &&
+ 8 !== d.nodeType &&
+ !X.test(q + n.event.triggered) &&
+ (q.indexOf(".") >= 0 && ((r = q.split(".")), (q = r.shift()), r.sort()),
+ (k = q.indexOf(":") < 0 && "on" + q),
+ (b = b[n.expando] ? b : new n.Event(q, "object" == typeof b && b)),
+ (b.isTrigger = e ? 2 : 3),
+ (b.namespace = r.join(".")),
+ (b.namespace_re = b.namespace
+ ? new RegExp("(^|\\.)" + r.join("\\.(?:.*\\.|)") + "(\\.|$)")
+ : null),
+ (b.result = void 0),
+ b.target || (b.target = d),
+ (c = null == c ? [b] : n.makeArray(c, [b])),
+ (o = n.event.special[q] || {}),
+ e || !o.trigger || o.trigger.apply(d, c) !== !1))
+ ) {
+ if (!e && !o.noBubble && !n.isWindow(d)) {
+ for (
+ i = o.delegateType || q, X.test(i + q) || (g = g.parentNode);
+ g;
+ g = g.parentNode
+ )
+ p.push(g), (h = g)
+ h === (d.ownerDocument || l) && p.push(h.defaultView || h.parentWindow || a)
+ }
+ f = 0
+ while ((g = p[f++]) && !b.isPropagationStopped())
+ (b.type = f > 1 ? i : o.bindType || q),
+ (m = (L.get(g, "events") || {})[b.type] && L.get(g, "handle")),
+ m && m.apply(g, c),
+ (m = k && g[k]),
+ m &&
+ m.apply &&
+ n.acceptData(g) &&
+ ((b.result = m.apply(g, c)), b.result === !1 && b.preventDefault())
+ return (
+ (b.type = q),
+ e ||
+ b.isDefaultPrevented() ||
+ (o._default && o._default.apply(p.pop(), c) !== !1) ||
+ !n.acceptData(d) ||
+ (k &&
+ n.isFunction(d[q]) &&
+ !n.isWindow(d) &&
+ ((h = d[k]),
+ h && (d[k] = null),
+ (n.event.triggered = q),
+ d[q](),
+ (n.event.triggered = void 0),
+ h && (d[k] = h))),
+ b.result
+ )
+ }
+ },
+ dispatch: function(a) {
+ a = n.event.fix(a)
+ var b,
+ c,
+ e,
+ f,
+ g,
+ h = [],
+ i = d.call(arguments),
+ j = (L.get(this, "events") || {})[a.type] || [],
+ k = n.event.special[a.type] || {}
+ if (
+ ((i[0] = a),
+ (a.delegateTarget = this),
+ !k.preDispatch || k.preDispatch.call(this, a) !== !1)
+ ) {
+ ;(h = n.event.handlers.call(this, a, j)), (b = 0)
+ while ((f = h[b++]) && !a.isPropagationStopped()) {
+ ;(a.currentTarget = f.elem), (c = 0)
+ while ((g = f.handlers[c++]) && !a.isImmediatePropagationStopped())
+ (!a.namespace_re || a.namespace_re.test(g.namespace)) &&
+ ((a.handleObj = g),
+ (a.data = g.data),
+ (e = ((n.event.special[g.origType] || {}).handle || g.handler).apply(
+ f.elem,
+ i
+ )),
+ void 0 !== e &&
+ (a.result = e) === !1 &&
+ (a.preventDefault(), a.stopPropagation()))
+ }
+ return k.postDispatch && k.postDispatch.call(this, a), a.result
+ }
+ },
+ handlers: function(a, b) {
+ var c,
+ d,
+ e,
+ f,
+ g = [],
+ h = b.delegateCount,
+ i = a.target
+ if (h && i.nodeType && (!a.button || "click" !== a.type))
+ for (; i !== this; i = i.parentNode || this)
+ if (i.disabled !== !0 || "click" !== a.type) {
+ for (d = [], c = 0; h > c; c++)
+ (f = b[c]),
+ (e = f.selector + " "),
+ void 0 === d[e] &&
+ (d[e] = f.needsContext
+ ? n(e, this).index(i) >= 0
+ : n.find(e, this, null, [i]).length),
+ d[e] && d.push(f)
+ d.length && g.push({ elem: i, handlers: d })
+ }
+ return h < b.length && g.push({ elem: this, handlers: b.slice(h) }), g
+ },
+ props: "altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(
+ " "
+ ),
+ fixHooks: {},
+ keyHooks: {
+ props: "char charCode key keyCode".split(" "),
+ filter: function(a, b) {
+ return null == a.which && (a.which = null != b.charCode ? b.charCode : b.keyCode), a
+ }
+ },
+ mouseHooks: {
+ props: "button buttons clientX clientY offsetX offsetY pageX pageY screenX screenY toElement".split(
+ " "
+ ),
+ filter: function(a, b) {
+ var c,
+ d,
+ e,
+ f = b.button
+ return (
+ null == a.pageX &&
+ null != b.clientX &&
+ ((c = a.target.ownerDocument || l),
+ (d = c.documentElement),
+ (e = c.body),
+ (a.pageX =
+ b.clientX +
+ ((d && d.scrollLeft) || (e && e.scrollLeft) || 0) -
+ ((d && d.clientLeft) || (e && e.clientLeft) || 0)),
+ (a.pageY =
+ b.clientY +
+ ((d && d.scrollTop) || (e && e.scrollTop) || 0) -
+ ((d && d.clientTop) || (e && e.clientTop) || 0))),
+ a.which || void 0 === f || (a.which = 1 & f ? 1 : 2 & f ? 3 : 4 & f ? 2 : 0),
+ a
+ )
+ }
+ },
+ fix: function(a) {
+ if (a[n.expando]) return a
+ var b,
+ c,
+ d,
+ e = a.type,
+ f = a,
+ g = this.fixHooks[e]
+ g ||
+ (this.fixHooks[e] = g = W.test(e)
+ ? this.mouseHooks
+ : V.test(e)
+ ? this.keyHooks
+ : {}),
+ (d = g.props ? this.props.concat(g.props) : this.props),
+ (a = new n.Event(f)),
+ (b = d.length)
+ while (b--) (c = d[b]), (a[c] = f[c])
+ return (
+ a.target || (a.target = l),
+ 3 === a.target.nodeType && (a.target = a.target.parentNode),
+ g.filter ? g.filter(a, f) : a
+ )
+ },
+ special: {
+ load: { noBubble: !0 },
+ focus: {
+ trigger: function() {
+ return this !== _() && this.focus ? (this.focus(), !1) : void 0
+ },
+ delegateType: "focusin"
+ },
+ blur: {
+ trigger: function() {
+ return this === _() && this.blur ? (this.blur(), !1) : void 0
+ },
+ delegateType: "focusout"
+ },
+ click: {
+ trigger: function() {
+ return "checkbox" === this.type && this.click && n.nodeName(this, "input")
+ ? (this.click(), !1)
+ : void 0
+ },
+ _default: function(a) {
+ return n.nodeName(a.target, "a")
+ }
+ },
+ beforeunload: {
+ postDispatch: function(a) {
+ void 0 !== a.result &&
+ a.originalEvent &&
+ (a.originalEvent.returnValue = a.result)
+ }
+ }
+ },
+ simulate: function(a, b, c, d) {
+ var e = n.extend(new n.Event(), c, { type: a, isSimulated: !0, originalEvent: {} })
+ d ? n.event.trigger(e, null, b) : n.event.dispatch.call(b, e),
+ e.isDefaultPrevented() && c.preventDefault()
+ }
+ }),
+ (n.removeEvent = function(a, b, c) {
+ a.removeEventListener && a.removeEventListener(b, c, !1)
+ }),
+ (n.Event = function(a, b) {
+ return this instanceof n.Event
+ ? (a && a.type
+ ? ((this.originalEvent = a),
+ (this.type = a.type),
+ (this.isDefaultPrevented =
+ a.defaultPrevented ||
+ (void 0 === a.defaultPrevented && a.returnValue === !1)
+ ? Z
+ : $))
+ : (this.type = a),
+ b && n.extend(this, b),
+ (this.timeStamp = (a && a.timeStamp) || n.now()),
+ void (this[n.expando] = !0))
+ : new n.Event(a, b)
+ }),
+ (n.Event.prototype = {
+ isDefaultPrevented: $,
+ isPropagationStopped: $,
+ isImmediatePropagationStopped: $,
+ preventDefault: function() {
+ var a = this.originalEvent
+ ;(this.isDefaultPrevented = Z), a && a.preventDefault && a.preventDefault()
+ },
+ stopPropagation: function() {
+ var a = this.originalEvent
+ ;(this.isPropagationStopped = Z), a && a.stopPropagation && a.stopPropagation()
+ },
+ stopImmediatePropagation: function() {
+ var a = this.originalEvent
+ ;(this.isImmediatePropagationStopped = Z),
+ a && a.stopImmediatePropagation && a.stopImmediatePropagation(),
+ this.stopPropagation()
+ }
+ }),
+ n.each(
+ {
+ mouseenter: "mouseover",
+ mouseleave: "mouseout",
+ pointerenter: "pointerover",
+ pointerleave: "pointerout"
+ },
+ function(a, b) {
+ n.event.special[a] = {
+ delegateType: b,
+ bindType: b,
+ handle: function(a) {
+ var c,
+ d = this,
+ e = a.relatedTarget,
+ f = a.handleObj
+ return (
+ (!e || (e !== d && !n.contains(d, e))) &&
+ ((a.type = f.origType),
+ (c = f.handler.apply(this, arguments)),
+ (a.type = b)),
+ c
+ )
+ }
+ }
+ }
+ ),
+ k.focusinBubbles ||
+ n.each({ focus: "focusin", blur: "focusout" }, function(a, b) {
+ var c = function(a) {
+ n.event.simulate(b, a.target, n.event.fix(a), !0)
+ }
+ n.event.special[b] = {
+ setup: function() {
+ var d = this.ownerDocument || this,
+ e = L.access(d, b)
+ e || d.addEventListener(a, c, !0), L.access(d, b, (e || 0) + 1)
+ },
+ teardown: function() {
+ var d = this.ownerDocument || this,
+ e = L.access(d, b) - 1
+ e ? L.access(d, b, e) : (d.removeEventListener(a, c, !0), L.remove(d, b))
+ }
+ }
+ }),
+ n.fn.extend({
+ on: function(a, b, c, d, e) {
+ var f, g
+ if ("object" == typeof a) {
+ "string" != typeof b && ((c = c || b), (b = void 0))
+ for (g in a) this.on(g, b, c, a[g], e)
+ return this
+ }
+ if (
+ (null == c && null == d
+ ? ((d = b), (c = b = void 0))
+ : null == d &&
+ ("string" == typeof b
+ ? ((d = c), (c = void 0))
+ : ((d = c), (c = b), (b = void 0))),
+ d === !1)
+ )
+ d = $
+ else if (!d) return this
+ return (
+ 1 === e &&
+ ((f = d),
+ (d = function(a) {
+ return n().off(a), f.apply(this, arguments)
+ }),
+ (d.guid = f.guid || (f.guid = n.guid++))),
+ this.each(function() {
+ n.event.add(this, a, d, c, b)
+ })
+ )
+ },
+ one: function(a, b, c, d) {
+ return this.on(a, b, c, d, 1)
+ },
+ off: function(a, b, c) {
+ var d, e
+ if (a && a.preventDefault && a.handleObj)
+ return (
+ (d = a.handleObj),
+ n(a.delegateTarget).off(
+ d.namespace ? d.origType + "." + d.namespace : d.origType,
+ d.selector,
+ d.handler
+ ),
+ this
+ )
+ if ("object" == typeof a) {
+ for (e in a) this.off(e, b, a[e])
+ return this
+ }
+ return (
+ (b === !1 || "function" == typeof b) && ((c = b), (b = void 0)),
+ c === !1 && (c = $),
+ this.each(function() {
+ n.event.remove(this, a, c, b)
+ })
+ )
+ },
+ trigger: function(a, b) {
+ return this.each(function() {
+ n.event.trigger(a, b, this)
+ })
+ },
+ triggerHandler: function(a, b) {
+ var c = this[0]
+ return c ? n.event.trigger(a, b, c, !0) : void 0
+ }
+ })
+ var aa = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,
+ ba = /<([\w:]+)/,
+ ca = /<|?\w+;/,
+ da = /<(?:script|style|link)/i,
+ ea = /checked\s*(?:[^=]|=\s*.checked.)/i,
+ fa = /^$|\/(?:java|ecma)script/i,
+ ga = /^true\/(.*)/,
+ ha = /^\s*\s*$/g,
+ ia = {
+ option: [1, ""],
+ thead: [1, "
", "
"],
+ col: [2, "
", "
"],
+ tr: [2, "
", "
"],
+ td: [3, "
", "
"],
+ _default: [0, "", ""]
+ }
+ ;(ia.optgroup = ia.option),
+ (ia.tbody = ia.tfoot = ia.colgroup = ia.caption = ia.thead),
+ (ia.th = ia.td)
+ function ja(a, b) {
+ return n.nodeName(a, "table") && n.nodeName(11 !== b.nodeType ? b : b.firstChild, "tr")
+ ? a.getElementsByTagName("tbody")[0] ||
+ a.appendChild(a.ownerDocument.createElement("tbody"))
+ : a
+ }
+ function ka(a) {
+ return (a.type = (null !== a.getAttribute("type")) + "/" + a.type), a
+ }
+ function la(a) {
+ var b = ga.exec(a.type)
+ return b ? (a.type = b[1]) : a.removeAttribute("type"), a
+ }
+ function ma(a, b) {
+ for (var c = 0, d = a.length; d > c; c++)
+ L.set(a[c], "globalEval", !b || L.get(b[c], "globalEval"))
+ }
+ function na(a, b) {
+ var c, d, e, f, g, h, i, j
+ if (1 === b.nodeType) {
+ if (L.hasData(a) && ((f = L.access(a)), (g = L.set(b, f)), (j = f.events))) {
+ delete g.handle, (g.events = {})
+ for (e in j) for (c = 0, d = j[e].length; d > c; c++) n.event.add(b, e, j[e][c])
+ }
+ M.hasData(a) && ((h = M.access(a)), (i = n.extend({}, h)), M.set(b, i))
+ }
+ }
+ function oa(a, b) {
+ var c = a.getElementsByTagName
+ ? a.getElementsByTagName(b || "*")
+ : a.querySelectorAll
+ ? a.querySelectorAll(b || "*")
+ : []
+ return void 0 === b || (b && n.nodeName(a, b)) ? n.merge([a], c) : c
+ }
+ function pa(a, b) {
+ var c = b.nodeName.toLowerCase()
+ "input" === c && T.test(a.type)
+ ? (b.checked = a.checked)
+ : ("input" === c || "textarea" === c) && (b.defaultValue = a.defaultValue)
+ }
+ n.extend({
+ clone: function(a, b, c) {
+ var d,
+ e,
+ f,
+ g,
+ h = a.cloneNode(!0),
+ i = n.contains(a.ownerDocument, a)
+ if (!(k.noCloneChecked || (1 !== a.nodeType && 11 !== a.nodeType) || n.isXMLDoc(a)))
+ for (g = oa(h), f = oa(a), d = 0, e = f.length; e > d; d++) pa(f[d], g[d])
+ if (b)
+ if (c)
+ for (f = f || oa(a), g = g || oa(h), d = 0, e = f.length; e > d; d++)
+ na(f[d], g[d])
+ else na(a, h)
+ return (g = oa(h, "script")), g.length > 0 && ma(g, !i && oa(a, "script")), h
+ },
+ buildFragment: function(a, b, c, d) {
+ for (
+ var e, f, g, h, i, j, k = b.createDocumentFragment(), l = [], m = 0, o = a.length;
+ o > m;
+ m++
+ )
+ if (((e = a[m]), e || 0 === e))
+ if ("object" === n.type(e)) n.merge(l, e.nodeType ? [e] : e)
+ else if (ca.test(e)) {
+ ;(f = f || k.appendChild(b.createElement("div"))),
+ (g = (ba.exec(e) || ["", ""])[1].toLowerCase()),
+ (h = ia[g] || ia._default),
+ (f.innerHTML = h[1] + e.replace(aa, "<$1>$2>") + h[2]),
+ (j = h[0])
+ while (j--) f = f.lastChild
+ n.merge(l, f.childNodes), (f = k.firstChild), (f.textContent = "")
+ } else l.push(b.createTextNode(e))
+ ;(k.textContent = ""), (m = 0)
+ while ((e = l[m++]))
+ if (
+ (!d || -1 === n.inArray(e, d)) &&
+ ((i = n.contains(e.ownerDocument, e)),
+ (f = oa(k.appendChild(e), "script")),
+ i && ma(f),
+ c)
+ ) {
+ j = 0
+ while ((e = f[j++])) fa.test(e.type || "") && c.push(e)
+ }
+ return k
+ },
+ cleanData: function(a) {
+ for (var b, c, d, e, f = n.event.special, g = 0; void 0 !== (c = a[g]); g++) {
+ if (n.acceptData(c) && ((e = c[L.expando]), e && (b = L.cache[e]))) {
+ if (b.events)
+ for (d in b.events)
+ f[d] ? n.event.remove(c, d) : n.removeEvent(c, d, b.handle)
+ L.cache[e] && delete L.cache[e]
+ }
+ delete M.cache[c[M.expando]]
+ }
+ }
+ }),
+ n.fn.extend({
+ text: function(a) {
+ return J(
+ this,
+ function(a) {
+ return void 0 === a
+ ? n.text(this)
+ : this.empty().each(function() {
+ ;(1 === this.nodeType ||
+ 11 === this.nodeType ||
+ 9 === this.nodeType) &&
+ (this.textContent = a)
+ })
+ },
+ null,
+ a,
+ arguments.length
+ )
+ },
+ append: function() {
+ return this.domManip(arguments, function(a) {
+ if (1 === this.nodeType || 11 === this.nodeType || 9 === this.nodeType) {
+ var b = ja(this, a)
+ b.appendChild(a)
+ }
+ })
+ },
+ prepend: function() {
+ return this.domManip(arguments, function(a) {
+ if (1 === this.nodeType || 11 === this.nodeType || 9 === this.nodeType) {
+ var b = ja(this, a)
+ b.insertBefore(a, b.firstChild)
+ }
+ })
+ },
+ before: function() {
+ return this.domManip(arguments, function(a) {
+ this.parentNode && this.parentNode.insertBefore(a, this)
+ })
+ },
+ after: function() {
+ return this.domManip(arguments, function(a) {
+ this.parentNode && this.parentNode.insertBefore(a, this.nextSibling)
+ })
+ },
+ remove: function(a, b) {
+ for (var c, d = a ? n.filter(a, this) : this, e = 0; null != (c = d[e]); e++)
+ b || 1 !== c.nodeType || n.cleanData(oa(c)),
+ c.parentNode &&
+ (b && n.contains(c.ownerDocument, c) && ma(oa(c, "script")),
+ c.parentNode.removeChild(c))
+ return this
+ },
+ empty: function() {
+ for (var a, b = 0; null != (a = this[b]); b++)
+ 1 === a.nodeType && (n.cleanData(oa(a, !1)), (a.textContent = ""))
+ return this
+ },
+ clone: function(a, b) {
+ return (
+ (a = null == a ? !1 : a),
+ (b = null == b ? a : b),
+ this.map(function() {
+ return n.clone(this, a, b)
+ })
+ )
+ },
+ html: function(a) {
+ return J(
+ this,
+ function(a) {
+ var b = this[0] || {},
+ c = 0,
+ d = this.length
+ if (void 0 === a && 1 === b.nodeType) return b.innerHTML
+ if (
+ "string" == typeof a &&
+ !da.test(a) &&
+ !ia[(ba.exec(a) || ["", ""])[1].toLowerCase()]
+ ) {
+ a = a.replace(aa, "<$1>$2>")
+ try {
+ for (; d > c; c++)
+ (b = this[c] || {}),
+ 1 === b.nodeType &&
+ (n.cleanData(oa(b, !1)), (b.innerHTML = a))
+ b = 0
+ } catch (e) {}
+ }
+ b && this.empty().append(a)
+ },
+ null,
+ a,
+ arguments.length
+ )
+ },
+ replaceWith: function() {
+ var a = arguments[0]
+ return (
+ this.domManip(arguments, function(b) {
+ ;(a = this.parentNode), n.cleanData(oa(this)), a && a.replaceChild(b, this)
+ }),
+ a && (a.length || a.nodeType) ? this : this.remove()
+ )
+ },
+ detach: function(a) {
+ return this.remove(a, !0)
+ },
+ domManip: function(a, b) {
+ a = e.apply([], a)
+ var c,
+ d,
+ f,
+ g,
+ h,
+ i,
+ j = 0,
+ l = this.length,
+ m = this,
+ o = l - 1,
+ p = a[0],
+ q = n.isFunction(p)
+ if (q || (l > 1 && "string" == typeof p && !k.checkClone && ea.test(p)))
+ return this.each(function(c) {
+ var d = m.eq(c)
+ q && (a[0] = p.call(this, c, d.html())), d.domManip(a, b)
+ })
+ if (
+ l &&
+ ((c = n.buildFragment(a, this[0].ownerDocument, !1, this)),
+ (d = c.firstChild),
+ 1 === c.childNodes.length && (c = d),
+ d)
+ ) {
+ for (f = n.map(oa(c, "script"), ka), g = f.length; l > j; j++)
+ (h = c),
+ j !== o && ((h = n.clone(h, !0, !0)), g && n.merge(f, oa(h, "script"))),
+ b.call(this[j], h, j)
+ if (g)
+ for (i = f[f.length - 1].ownerDocument, n.map(f, la), j = 0; g > j; j++)
+ (h = f[j]),
+ fa.test(h.type || "") &&
+ !L.access(h, "globalEval") &&
+ n.contains(i, h) &&
+ (h.src
+ ? n._evalUrl && n._evalUrl(h.src)
+ : n.globalEval(h.textContent.replace(ha, "")))
+ }
+ return this
+ }
+ }),
+ n.each(
+ {
+ appendTo: "append",
+ prependTo: "prepend",
+ insertBefore: "before",
+ insertAfter: "after",
+ replaceAll: "replaceWith"
+ },
+ function(a, b) {
+ n.fn[a] = function(a) {
+ for (var c, d = [], e = n(a), g = e.length - 1, h = 0; g >= h; h++)
+ (c = h === g ? this : this.clone(!0)), n(e[h])[b](c), f.apply(d, c.get())
+ return this.pushStack(d)
+ }
+ }
+ )
+ var qa,
+ ra = {}
+ function sa(b, c) {
+ var d,
+ e = n(c.createElement(b)).appendTo(c.body),
+ f =
+ a.getDefaultComputedStyle && (d = a.getDefaultComputedStyle(e[0]))
+ ? d.display
+ : n.css(e[0], "display")
+ return e.detach(), f
+ }
+ function ta(a) {
+ var b = l,
+ c = ra[a]
+ return (
+ c ||
+ ((c = sa(a, b)),
+ ("none" !== c && c) ||
+ ((qa = (qa || n("")).appendTo(
+ b.documentElement
+ )),
+ (b = qa[0].contentDocument),
+ b.write(),
+ b.close(),
+ (c = sa(a, b)),
+ qa.detach()),
+ (ra[a] = c)),
+ c
+ )
+ }
+ var ua = /^margin/,
+ va = new RegExp("^(" + Q + ")(?!px)[a-z%]+$", "i"),
+ wa = function(b) {
+ return b.ownerDocument.defaultView.opener
+ ? b.ownerDocument.defaultView.getComputedStyle(b, null)
+ : a.getComputedStyle(b, null)
+ }
+ function xa(a, b, c) {
+ var d,
+ e,
+ f,
+ g,
+ h = a.style
+ return (
+ (c = c || wa(a)),
+ c && (g = c.getPropertyValue(b) || c[b]),
+ c &&
+ ("" !== g || n.contains(a.ownerDocument, a) || (g = n.style(a, b)),
+ va.test(g) &&
+ ua.test(b) &&
+ ((d = h.width),
+ (e = h.minWidth),
+ (f = h.maxWidth),
+ (h.minWidth = h.maxWidth = h.width = g),
+ (g = c.width),
+ (h.width = d),
+ (h.minWidth = e),
+ (h.maxWidth = f))),
+ void 0 !== g ? g + "" : g
+ )
+ }
+ function ya(a, b) {
+ return {
+ get: function() {
+ return a() ? void delete this.get : (this.get = b).apply(this, arguments)
+ }
+ }
+ }
+ !(function() {
+ var b,
+ c,
+ d = l.documentElement,
+ e = l.createElement("div"),
+ f = l.createElement("div")
+ if (f.style) {
+ ;(f.style.backgroundClip = "content-box"),
+ (f.cloneNode(!0).style.backgroundClip = ""),
+ (k.clearCloneStyle = "content-box" === f.style.backgroundClip),
+ (e.style.cssText =
+ "border:0;width:0;height:0;top:0;left:-9999px;margin-top:1px;position:absolute"),
+ e.appendChild(f)
+ function g() {
+ ;(f.style.cssText =
+ "-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;display:block;margin-top:1%;top:1%;border:1px;padding:1px;width:4px;position:absolute"),
+ (f.innerHTML = ""),
+ d.appendChild(e)
+ var g = a.getComputedStyle(f, null)
+ ;(b = "1%" !== g.top), (c = "4px" === g.width), d.removeChild(e)
+ }
+ a.getComputedStyle &&
+ n.extend(k, {
+ pixelPosition: function() {
+ return g(), b
+ },
+ boxSizingReliable: function() {
+ return null == c && g(), c
+ },
+ reliableMarginRight: function() {
+ var b,
+ c = f.appendChild(l.createElement("div"))
+ return (
+ (c.style.cssText = f.style.cssText =
+ "-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;display:block;margin:0;border:0;padding:0"),
+ (c.style.marginRight = c.style.width = "0"),
+ (f.style.width = "1px"),
+ d.appendChild(e),
+ (b = !parseFloat(a.getComputedStyle(c, null).marginRight)),
+ d.removeChild(e),
+ f.removeChild(c),
+ b
+ )
+ }
+ })
+ }
+ })(),
+ (n.swap = function(a, b, c, d) {
+ var e,
+ f,
+ g = {}
+ for (f in b) (g[f] = a.style[f]), (a.style[f] = b[f])
+ e = c.apply(a, d || [])
+ for (f in b) a.style[f] = g[f]
+ return e
+ })
+ var za = /^(none|table(?!-c[ea]).+)/,
+ Aa = new RegExp("^(" + Q + ")(.*)$", "i"),
+ Ba = new RegExp("^([+-])=(" + Q + ")", "i"),
+ Ca = { position: "absolute", visibility: "hidden", display: "block" },
+ Da = { letterSpacing: "0", fontWeight: "400" },
+ Ea = ["Webkit", "O", "Moz", "ms"]
+ function Fa(a, b) {
+ if (b in a) return b
+ var c = b[0].toUpperCase() + b.slice(1),
+ d = b,
+ e = Ea.length
+ while (e--) if (((b = Ea[e] + c), b in a)) return b
+ return d
+ }
+ function Ga(a, b, c) {
+ var d = Aa.exec(b)
+ return d ? Math.max(0, d[1] - (c || 0)) + (d[2] || "px") : b
+ }
+ function Ha(a, b, c, d, e) {
+ for (
+ var f = c === (d ? "border" : "content") ? 4 : "width" === b ? 1 : 0, g = 0;
+ 4 > f;
+ f += 2
+ )
+ "margin" === c && (g += n.css(a, c + R[f], !0, e)),
+ d
+ ? ("content" === c && (g -= n.css(a, "padding" + R[f], !0, e)),
+ "margin" !== c && (g -= n.css(a, "border" + R[f] + "Width", !0, e)))
+ : ((g += n.css(a, "padding" + R[f], !0, e)),
+ "padding" !== c && (g += n.css(a, "border" + R[f] + "Width", !0, e)))
+ return g
+ }
+ function Ia(a, b, c) {
+ var d = !0,
+ e = "width" === b ? a.offsetWidth : a.offsetHeight,
+ f = wa(a),
+ g = "border-box" === n.css(a, "boxSizing", !1, f)
+ if (0 >= e || null == e) {
+ if (((e = xa(a, b, f)), (0 > e || null == e) && (e = a.style[b]), va.test(e))) return e
+ ;(d = g && (k.boxSizingReliable() || e === a.style[b])), (e = parseFloat(e) || 0)
+ }
+ return e + Ha(a, b, c || (g ? "border" : "content"), d, f) + "px"
+ }
+ function Ja(a, b) {
+ for (var c, d, e, f = [], g = 0, h = a.length; h > g; g++)
+ (d = a[g]),
+ d.style &&
+ ((f[g] = L.get(d, "olddisplay")),
+ (c = d.style.display),
+ b
+ ? (f[g] || "none" !== c || (d.style.display = ""),
+ "" === d.style.display &&
+ S(d) &&
+ (f[g] = L.access(d, "olddisplay", ta(d.nodeName))))
+ : ((e = S(d)),
+ ("none" === c && e) ||
+ L.set(d, "olddisplay", e ? c : n.css(d, "display"))))
+ for (g = 0; h > g; g++)
+ (d = a[g]),
+ d.style &&
+ ((b && "none" !== d.style.display && "" !== d.style.display) ||
+ (d.style.display = b ? f[g] || "" : "none"))
+ return a
+ }
+ n.extend({
+ cssHooks: {
+ opacity: {
+ get: function(a, b) {
+ if (b) {
+ var c = xa(a, "opacity")
+ return "" === c ? "1" : c
+ }
+ }
+ }
+ },
+ cssNumber: {
+ columnCount: !0,
+ fillOpacity: !0,
+ flexGrow: !0,
+ flexShrink: !0,
+ fontWeight: !0,
+ lineHeight: !0,
+ opacity: !0,
+ order: !0,
+ orphans: !0,
+ widows: !0,
+ zIndex: !0,
+ zoom: !0
+ },
+ cssProps: { float: "cssFloat" },
+ style: function(a, b, c, d) {
+ if (a && 3 !== a.nodeType && 8 !== a.nodeType && a.style) {
+ var e,
+ f,
+ g,
+ h = n.camelCase(b),
+ i = a.style
+ return (
+ (b = n.cssProps[h] || (n.cssProps[h] = Fa(i, h))),
+ (g = n.cssHooks[b] || n.cssHooks[h]),
+ void 0 === c
+ ? g && "get" in g && void 0 !== (e = g.get(a, !1, d))
+ ? e
+ : i[b]
+ : ((f = typeof c),
+ "string" === f &&
+ (e = Ba.exec(c)) &&
+ ((c = (e[1] + 1) * e[2] + parseFloat(n.css(a, b))), (f = "number")),
+ null != c &&
+ c === c &&
+ ("number" !== f || n.cssNumber[h] || (c += "px"),
+ k.clearCloneStyle ||
+ "" !== c ||
+ 0 !== b.indexOf("background") ||
+ (i[b] = "inherit"),
+ (g && "set" in g && void 0 === (c = g.set(a, c, d))) || (i[b] = c)),
+ void 0)
+ )
+ }
+ },
+ css: function(a, b, c, d) {
+ var e,
+ f,
+ g,
+ h = n.camelCase(b)
+ return (
+ (b = n.cssProps[h] || (n.cssProps[h] = Fa(a.style, h))),
+ (g = n.cssHooks[b] || n.cssHooks[h]),
+ g && "get" in g && (e = g.get(a, !0, c)),
+ void 0 === e && (e = xa(a, b, d)),
+ "normal" === e && b in Da && (e = Da[b]),
+ "" === c || c ? ((f = parseFloat(e)), c === !0 || n.isNumeric(f) ? f || 0 : e) : e
+ )
+ }
+ }),
+ n.each(["height", "width"], function(a, b) {
+ n.cssHooks[b] = {
+ get: function(a, c, d) {
+ return c
+ ? za.test(n.css(a, "display")) && 0 === a.offsetWidth
+ ? n.swap(a, Ca, function() {
+ return Ia(a, b, d)
+ })
+ : Ia(a, b, d)
+ : void 0
+ },
+ set: function(a, c, d) {
+ var e = d && wa(a)
+ return Ga(
+ a,
+ c,
+ d ? Ha(a, b, d, "border-box" === n.css(a, "boxSizing", !1, e), e) : 0
+ )
+ }
+ }
+ }),
+ (n.cssHooks.marginRight = ya(k.reliableMarginRight, function(a, b) {
+ return b ? n.swap(a, { display: "inline-block" }, xa, [a, "marginRight"]) : void 0
+ })),
+ n.each({ margin: "", padding: "", border: "Width" }, function(a, b) {
+ ;(n.cssHooks[a + b] = {
+ expand: function(c) {
+ for (
+ var d = 0, e = {}, f = "string" == typeof c ? c.split(" ") : [c];
+ 4 > d;
+ d++
+ )
+ e[a + R[d] + b] = f[d] || f[d - 2] || f[0]
+ return e
+ }
+ }),
+ ua.test(a) || (n.cssHooks[a + b].set = Ga)
+ }),
+ n.fn.extend({
+ css: function(a, b) {
+ return J(
+ this,
+ function(a, b, c) {
+ var d,
+ e,
+ f = {},
+ g = 0
+ if (n.isArray(b)) {
+ for (d = wa(a), e = b.length; e > g; g++)
+ f[b[g]] = n.css(a, b[g], !1, d)
+ return f
+ }
+ return void 0 !== c ? n.style(a, b, c) : n.css(a, b)
+ },
+ a,
+ b,
+ arguments.length > 1
+ )
+ },
+ show: function() {
+ return Ja(this, !0)
+ },
+ hide: function() {
+ return Ja(this)
+ },
+ toggle: function(a) {
+ return "boolean" == typeof a
+ ? a
+ ? this.show()
+ : this.hide()
+ : this.each(function() {
+ S(this) ? n(this).show() : n(this).hide()
+ })
+ }
+ })
+ function Ka(a, b, c, d, e) {
+ return new Ka.prototype.init(a, b, c, d, e)
+ }
+ ;(n.Tween = Ka),
+ (Ka.prototype = {
+ constructor: Ka,
+ init: function(a, b, c, d, e, f) {
+ ;(this.elem = a),
+ (this.prop = c),
+ (this.easing = e || "swing"),
+ (this.options = b),
+ (this.start = this.now = this.cur()),
+ (this.end = d),
+ (this.unit = f || (n.cssNumber[c] ? "" : "px"))
+ },
+ cur: function() {
+ var a = Ka.propHooks[this.prop]
+ return a && a.get ? a.get(this) : Ka.propHooks._default.get(this)
+ },
+ run: function(a) {
+ var b,
+ c = Ka.propHooks[this.prop]
+ return (
+ this.options.duration
+ ? (this.pos = b = n.easing[this.easing](
+ a,
+ this.options.duration * a,
+ 0,
+ 1,
+ this.options.duration
+ ))
+ : (this.pos = b = a),
+ (this.now = (this.end - this.start) * b + this.start),
+ this.options.step && this.options.step.call(this.elem, this.now, this),
+ c && c.set ? c.set(this) : Ka.propHooks._default.set(this),
+ this
+ )
+ }
+ }),
+ (Ka.prototype.init.prototype = Ka.prototype),
+ (Ka.propHooks = {
+ _default: {
+ get: function(a) {
+ var b
+ return null == a.elem[a.prop] || (a.elem.style && null != a.elem.style[a.prop])
+ ? ((b = n.css(a.elem, a.prop, "")), b && "auto" !== b ? b : 0)
+ : a.elem[a.prop]
+ },
+ set: function(a) {
+ n.fx.step[a.prop]
+ ? n.fx.step[a.prop](a)
+ : a.elem.style &&
+ (null != a.elem.style[n.cssProps[a.prop]] || n.cssHooks[a.prop])
+ ? n.style(a.elem, a.prop, a.now + a.unit)
+ : (a.elem[a.prop] = a.now)
+ }
+ }
+ }),
+ (Ka.propHooks.scrollTop = Ka.propHooks.scrollLeft = {
+ set: function(a) {
+ a.elem.nodeType && a.elem.parentNode && (a.elem[a.prop] = a.now)
+ }
+ }),
+ (n.easing = {
+ linear: function(a) {
+ return a
+ },
+ swing: function(a) {
+ return 0.5 - Math.cos(a * Math.PI) / 2
+ }
+ }),
+ (n.fx = Ka.prototype.init),
+ (n.fx.step = {})
+ var La,
+ Ma,
+ Na = /^(?:toggle|show|hide)$/,
+ Oa = new RegExp("^(?:([+-])=|)(" + Q + ")([a-z%]*)$", "i"),
+ Pa = /queueHooks$/,
+ Qa = [Va],
+ Ra = {
+ "*": [
+ function(a, b) {
+ var c = this.createTween(a, b),
+ d = c.cur(),
+ e = Oa.exec(b),
+ f = (e && e[3]) || (n.cssNumber[a] ? "" : "px"),
+ g = (n.cssNumber[a] || ("px" !== f && +d)) && Oa.exec(n.css(c.elem, a)),
+ h = 1,
+ i = 20
+ if (g && g[3] !== f) {
+ ;(f = f || g[3]), (e = e || []), (g = +d || 1)
+ do (h = h || ".5"), (g /= h), n.style(c.elem, a, g + f)
+ while (h !== (h = c.cur() / d) && 1 !== h && --i)
+ }
+ return (
+ e &&
+ ((g = c.start = +g || +d || 0),
+ (c.unit = f),
+ (c.end = e[1] ? g + (e[1] + 1) * e[2] : +e[2])),
+ c
+ )
+ }
+ ]
+ }
+ function Sa() {
+ return (
+ setTimeout(function() {
+ La = void 0
+ }),
+ (La = n.now())
+ )
+ }
+ function Ta(a, b) {
+ var c,
+ d = 0,
+ e = { height: a }
+ for (b = b ? 1 : 0; 4 > d; d += 2 - b) (c = R[d]), (e["margin" + c] = e["padding" + c] = a)
+ return b && (e.opacity = e.width = a), e
+ }
+ function Ua(a, b, c) {
+ for (var d, e = (Ra[b] || []).concat(Ra["*"]), f = 0, g = e.length; g > f; f++)
+ if ((d = e[f].call(c, b, a))) return d
+ }
+ function Va(a, b, c) {
+ var d,
+ e,
+ f,
+ g,
+ h,
+ i,
+ j,
+ k,
+ l = this,
+ m = {},
+ o = a.style,
+ p = a.nodeType && S(a),
+ q = L.get(a, "fxshow")
+ c.queue ||
+ ((h = n._queueHooks(a, "fx")),
+ null == h.unqueued &&
+ ((h.unqueued = 0),
+ (i = h.empty.fire),
+ (h.empty.fire = function() {
+ h.unqueued || i()
+ })),
+ h.unqueued++,
+ l.always(function() {
+ l.always(function() {
+ h.unqueued--, n.queue(a, "fx").length || h.empty.fire()
+ })
+ })),
+ 1 === a.nodeType &&
+ ("height" in b || "width" in b) &&
+ ((c.overflow = [o.overflow, o.overflowX, o.overflowY]),
+ (j = n.css(a, "display")),
+ (k = "none" === j ? L.get(a, "olddisplay") || ta(a.nodeName) : j),
+ "inline" === k && "none" === n.css(a, "float") && (o.display = "inline-block")),
+ c.overflow &&
+ ((o.overflow = "hidden"),
+ l.always(function() {
+ ;(o.overflow = c.overflow[0]),
+ (o.overflowX = c.overflow[1]),
+ (o.overflowY = c.overflow[2])
+ }))
+ for (d in b)
+ if (((e = b[d]), Na.exec(e))) {
+ if ((delete b[d], (f = f || "toggle" === e), e === (p ? "hide" : "show"))) {
+ if ("show" !== e || !q || void 0 === q[d]) continue
+ p = !0
+ }
+ m[d] = (q && q[d]) || n.style(a, d)
+ } else j = void 0
+ if (n.isEmptyObject(m)) "inline" === ("none" === j ? ta(a.nodeName) : j) && (o.display = j)
+ else {
+ q ? "hidden" in q && (p = q.hidden) : (q = L.access(a, "fxshow", {})),
+ f && (q.hidden = !p),
+ p
+ ? n(a).show()
+ : l.done(function() {
+ n(a).hide()
+ }),
+ l.done(function() {
+ var b
+ L.remove(a, "fxshow")
+ for (b in m) n.style(a, b, m[b])
+ })
+ for (d in m)
+ (g = Ua(p ? q[d] : 0, d, l)),
+ d in q ||
+ ((q[d] = g.start),
+ p &&
+ ((g.end = g.start),
+ (g.start = "width" === d || "height" === d ? 1 : 0)))
+ }
+ }
+ function Wa(a, b) {
+ var c, d, e, f, g
+ for (c in a)
+ if (
+ ((d = n.camelCase(c)),
+ (e = b[d]),
+ (f = a[c]),
+ n.isArray(f) && ((e = f[1]), (f = a[c] = f[0])),
+ c !== d && ((a[d] = f), delete a[c]),
+ (g = n.cssHooks[d]),
+ g && "expand" in g)
+ ) {
+ ;(f = g.expand(f)), delete a[d]
+ for (c in f) c in a || ((a[c] = f[c]), (b[c] = e))
+ } else b[d] = e
+ }
+ function Xa(a, b, c) {
+ var d,
+ e,
+ f = 0,
+ g = Qa.length,
+ h = n.Deferred().always(function() {
+ delete i.elem
+ }),
+ i = function() {
+ if (e) return !1
+ for (
+ var b = La || Sa(),
+ c = Math.max(0, j.startTime + j.duration - b),
+ d = c / j.duration || 0,
+ f = 1 - d,
+ g = 0,
+ i = j.tweens.length;
+ i > g;
+ g++
+ )
+ j.tweens[g].run(f)
+ return h.notifyWith(a, [j, f, c]), 1 > f && i ? c : (h.resolveWith(a, [j]), !1)
+ },
+ j = h.promise({
+ elem: a,
+ props: n.extend({}, b),
+ opts: n.extend(!0, { specialEasing: {} }, c),
+ originalProperties: b,
+ originalOptions: c,
+ startTime: La || Sa(),
+ duration: c.duration,
+ tweens: [],
+ createTween: function(b, c) {
+ var d = n.Tween(a, j.opts, b, c, j.opts.specialEasing[b] || j.opts.easing)
+ return j.tweens.push(d), d
+ },
+ stop: function(b) {
+ var c = 0,
+ d = b ? j.tweens.length : 0
+ if (e) return this
+ for (e = !0; d > c; c++) j.tweens[c].run(1)
+ return b ? h.resolveWith(a, [j, b]) : h.rejectWith(a, [j, b]), this
+ }
+ }),
+ k = j.props
+ for (Wa(k, j.opts.specialEasing); g > f; f++)
+ if ((d = Qa[f].call(j, a, k, j.opts))) return d
+ return (
+ n.map(k, Ua, j),
+ n.isFunction(j.opts.start) && j.opts.start.call(a, j),
+ n.fx.timer(n.extend(i, { elem: a, anim: j, queue: j.opts.queue })),
+ j
+ .progress(j.opts.progress)
+ .done(j.opts.done, j.opts.complete)
+ .fail(j.opts.fail)
+ .always(j.opts.always)
+ )
+ }
+ ;(n.Animation = n.extend(Xa, {
+ tweener: function(a, b) {
+ n.isFunction(a) ? ((b = a), (a = ["*"])) : (a = a.split(" "))
+ for (var c, d = 0, e = a.length; e > d; d++)
+ (c = a[d]), (Ra[c] = Ra[c] || []), Ra[c].unshift(b)
+ },
+ prefilter: function(a, b) {
+ b ? Qa.unshift(a) : Qa.push(a)
+ }
+ })),
+ (n.speed = function(a, b, c) {
+ var d =
+ a && "object" == typeof a
+ ? n.extend({}, a)
+ : {
+ complete: c || (!c && b) || (n.isFunction(a) && a),
+ duration: a,
+ easing: (c && b) || (b && !n.isFunction(b) && b)
+ }
+ return (
+ (d.duration = n.fx.off
+ ? 0
+ : "number" == typeof d.duration
+ ? d.duration
+ : d.duration in n.fx.speeds
+ ? n.fx.speeds[d.duration]
+ : n.fx.speeds._default),
+ (null == d.queue || d.queue === !0) && (d.queue = "fx"),
+ (d.old = d.complete),
+ (d.complete = function() {
+ n.isFunction(d.old) && d.old.call(this), d.queue && n.dequeue(this, d.queue)
+ }),
+ d
+ )
+ }),
+ n.fn.extend({
+ fadeTo: function(a, b, c, d) {
+ return this.filter(S)
+ .css("opacity", 0)
+ .show()
+ .end()
+ .animate({ opacity: b }, a, c, d)
+ },
+ animate: function(a, b, c, d) {
+ var e = n.isEmptyObject(a),
+ f = n.speed(b, c, d),
+ g = function() {
+ var b = Xa(this, n.extend({}, a), f)
+ ;(e || L.get(this, "finish")) && b.stop(!0)
+ }
+ return (g.finish = g), e || f.queue === !1 ? this.each(g) : this.queue(f.queue, g)
+ },
+ stop: function(a, b, c) {
+ var d = function(a) {
+ var b = a.stop
+ delete a.stop, b(c)
+ }
+ return (
+ "string" != typeof a && ((c = b), (b = a), (a = void 0)),
+ b && a !== !1 && this.queue(a || "fx", []),
+ this.each(function() {
+ var b = !0,
+ e = null != a && a + "queueHooks",
+ f = n.timers,
+ g = L.get(this)
+ if (e) g[e] && g[e].stop && d(g[e])
+ else for (e in g) g[e] && g[e].stop && Pa.test(e) && d(g[e])
+ for (e = f.length; e--; )
+ f[e].elem !== this ||
+ (null != a && f[e].queue !== a) ||
+ (f[e].anim.stop(c), (b = !1), f.splice(e, 1))
+ ;(b || !c) && n.dequeue(this, a)
+ })
+ )
+ },
+ finish: function(a) {
+ return (
+ a !== !1 && (a = a || "fx"),
+ this.each(function() {
+ var b,
+ c = L.get(this),
+ d = c[a + "queue"],
+ e = c[a + "queueHooks"],
+ f = n.timers,
+ g = d ? d.length : 0
+ for (
+ c.finish = !0,
+ n.queue(this, a, []),
+ e && e.stop && e.stop.call(this, !0),
+ b = f.length;
+ b--;
+
+ )
+ f[b].elem === this &&
+ f[b].queue === a &&
+ (f[b].anim.stop(!0), f.splice(b, 1))
+ for (b = 0; g > b; b++) d[b] && d[b].finish && d[b].finish.call(this)
+ delete c.finish
+ })
+ )
+ }
+ }),
+ n.each(["toggle", "show", "hide"], function(a, b) {
+ var c = n.fn[b]
+ n.fn[b] = function(a, d, e) {
+ return null == a || "boolean" == typeof a
+ ? c.apply(this, arguments)
+ : this.animate(Ta(b, !0), a, d, e)
+ }
+ }),
+ n.each(
+ {
+ slideDown: Ta("show"),
+ slideUp: Ta("hide"),
+ slideToggle: Ta("toggle"),
+ fadeIn: { opacity: "show" },
+ fadeOut: { opacity: "hide" },
+ fadeToggle: { opacity: "toggle" }
+ },
+ function(a, b) {
+ n.fn[a] = function(a, c, d) {
+ return this.animate(b, a, c, d)
+ }
+ }
+ ),
+ (n.timers = []),
+ (n.fx.tick = function() {
+ var a,
+ b = 0,
+ c = n.timers
+ for (La = n.now(); b < c.length; b++) (a = c[b]), a() || c[b] !== a || c.splice(b--, 1)
+ c.length || n.fx.stop(), (La = void 0)
+ }),
+ (n.fx.timer = function(a) {
+ n.timers.push(a), a() ? n.fx.start() : n.timers.pop()
+ }),
+ (n.fx.interval = 13),
+ (n.fx.start = function() {
+ Ma || (Ma = setInterval(n.fx.tick, n.fx.interval))
+ }),
+ (n.fx.stop = function() {
+ clearInterval(Ma), (Ma = null)
+ }),
+ (n.fx.speeds = { slow: 600, fast: 200, _default: 400 }),
+ (n.fn.delay = function(a, b) {
+ return (
+ (a = n.fx ? n.fx.speeds[a] || a : a),
+ (b = b || "fx"),
+ this.queue(b, function(b, c) {
+ var d = setTimeout(b, a)
+ c.stop = function() {
+ clearTimeout(d)
+ }
+ })
+ )
+ }),
+ (function() {
+ var a = l.createElement("input"),
+ b = l.createElement("select"),
+ c = b.appendChild(l.createElement("option"))
+ ;(a.type = "checkbox"),
+ (k.checkOn = "" !== a.value),
+ (k.optSelected = c.selected),
+ (b.disabled = !0),
+ (k.optDisabled = !c.disabled),
+ (a = l.createElement("input")),
+ (a.value = "t"),
+ (a.type = "radio"),
+ (k.radioValue = "t" === a.value)
+ })()
+ var Ya,
+ Za,
+ $a = n.expr.attrHandle
+ n.fn.extend({
+ attr: function(a, b) {
+ return J(this, n.attr, a, b, arguments.length > 1)
+ },
+ removeAttr: function(a) {
+ return this.each(function() {
+ n.removeAttr(this, a)
+ })
+ }
+ }),
+ n.extend({
+ attr: function(a, b, c) {
+ var d,
+ e,
+ f = a.nodeType
+ if (a && 3 !== f && 8 !== f && 2 !== f)
+ return typeof a.getAttribute === U
+ ? n.prop(a, b, c)
+ : ((1 === f && n.isXMLDoc(a)) ||
+ ((b = b.toLowerCase()),
+ (d = n.attrHooks[b] || (n.expr.match.bool.test(b) ? Za : Ya))),
+ void 0 === c
+ ? d && "get" in d && null !== (e = d.get(a, b))
+ ? e
+ : ((e = n.find.attr(a, b)), null == e ? void 0 : e)
+ : null !== c
+ ? d && "set" in d && void 0 !== (e = d.set(a, c, b))
+ ? e
+ : (a.setAttribute(b, c + ""), c)
+ : void n.removeAttr(a, b))
+ },
+ removeAttr: function(a, b) {
+ var c,
+ d,
+ e = 0,
+ f = b && b.match(E)
+ if (f && 1 === a.nodeType)
+ while ((c = f[e++]))
+ (d = n.propFix[c] || c),
+ n.expr.match.bool.test(c) && (a[d] = !1),
+ a.removeAttribute(c)
+ },
+ attrHooks: {
+ type: {
+ set: function(a, b) {
+ if (!k.radioValue && "radio" === b && n.nodeName(a, "input")) {
+ var c = a.value
+ return a.setAttribute("type", b), c && (a.value = c), b
+ }
+ }
+ }
+ }
+ }),
+ (Za = {
+ set: function(a, b, c) {
+ return b === !1 ? n.removeAttr(a, c) : a.setAttribute(c, c), c
+ }
+ }),
+ n.each(n.expr.match.bool.source.match(/\w+/g), function(a, b) {
+ var c = $a[b] || n.find.attr
+ $a[b] = function(a, b, d) {
+ var e, f
+ return (
+ d ||
+ ((f = $a[b]),
+ ($a[b] = e),
+ (e = null != c(a, b, d) ? b.toLowerCase() : null),
+ ($a[b] = f)),
+ e
+ )
+ }
+ })
+ var _a = /^(?:input|select|textarea|button)$/i
+ n.fn.extend({
+ prop: function(a, b) {
+ return J(this, n.prop, a, b, arguments.length > 1)
+ },
+ removeProp: function(a) {
+ return this.each(function() {
+ delete this[n.propFix[a] || a]
+ })
+ }
+ }),
+ n.extend({
+ propFix: { for: "htmlFor", class: "className" },
+ prop: function(a, b, c) {
+ var d,
+ e,
+ f,
+ g = a.nodeType
+ if (a && 3 !== g && 8 !== g && 2 !== g)
+ return (
+ (f = 1 !== g || !n.isXMLDoc(a)),
+ f && ((b = n.propFix[b] || b), (e = n.propHooks[b])),
+ void 0 !== c
+ ? e && "set" in e && void 0 !== (d = e.set(a, c, b))
+ ? d
+ : (a[b] = c)
+ : e && "get" in e && null !== (d = e.get(a, b))
+ ? d
+ : a[b]
+ )
+ },
+ propHooks: {
+ tabIndex: {
+ get: function(a) {
+ return a.hasAttribute("tabindex") || _a.test(a.nodeName) || a.href
+ ? a.tabIndex
+ : -1
+ }
+ }
+ }
+ }),
+ k.optSelected ||
+ (n.propHooks.selected = {
+ get: function(a) {
+ var b = a.parentNode
+ return b && b.parentNode && b.parentNode.selectedIndex, null
+ }
+ }),
+ n.each(
+ [
+ "tabIndex",
+ "readOnly",
+ "maxLength",
+ "cellSpacing",
+ "cellPadding",
+ "rowSpan",
+ "colSpan",
+ "useMap",
+ "frameBorder",
+ "contentEditable"
+ ],
+ function() {
+ n.propFix[this.toLowerCase()] = this
+ }
+ )
+ var ab = /[\t\r\n\f]/g
+ n.fn.extend({
+ addClass: function(a) {
+ var b,
+ c,
+ d,
+ e,
+ f,
+ g,
+ h = "string" == typeof a && a,
+ i = 0,
+ j = this.length
+ if (n.isFunction(a))
+ return this.each(function(b) {
+ n(this).addClass(a.call(this, b, this.className))
+ })
+ if (h)
+ for (b = (a || "").match(E) || []; j > i; i++)
+ if (
+ ((c = this[i]),
+ (d =
+ 1 === c.nodeType &&
+ (c.className ? (" " + c.className + " ").replace(ab, " ") : " ")))
+ ) {
+ f = 0
+ while ((e = b[f++])) d.indexOf(" " + e + " ") < 0 && (d += e + " ")
+ ;(g = n.trim(d)), c.className !== g && (c.className = g)
+ }
+ return this
+ },
+ removeClass: function(a) {
+ var b,
+ c,
+ d,
+ e,
+ f,
+ g,
+ h = 0 === arguments.length || ("string" == typeof a && a),
+ i = 0,
+ j = this.length
+ if (n.isFunction(a))
+ return this.each(function(b) {
+ n(this).removeClass(a.call(this, b, this.className))
+ })
+ if (h)
+ for (b = (a || "").match(E) || []; j > i; i++)
+ if (
+ ((c = this[i]),
+ (d =
+ 1 === c.nodeType &&
+ (c.className ? (" " + c.className + " ").replace(ab, " ") : "")))
+ ) {
+ f = 0
+ while ((e = b[f++]))
+ while (d.indexOf(" " + e + " ") >= 0) d = d.replace(" " + e + " ", " ")
+ ;(g = a ? n.trim(d) : ""), c.className !== g && (c.className = g)
+ }
+ return this
+ },
+ toggleClass: function(a, b) {
+ var c = typeof a
+ return "boolean" == typeof b && "string" === c
+ ? b
+ ? this.addClass(a)
+ : this.removeClass(a)
+ : this.each(
+ n.isFunction(a)
+ ? function(c) {
+ n(this).toggleClass(a.call(this, c, this.className, b), b)
+ }
+ : function() {
+ if ("string" === c) {
+ var b,
+ d = 0,
+ e = n(this),
+ f = a.match(E) || []
+ while ((b = f[d++]))
+ e.hasClass(b) ? e.removeClass(b) : e.addClass(b)
+ } else
+ (c === U || "boolean" === c) &&
+ (this.className &&
+ L.set(this, "__className__", this.className),
+ (this.className =
+ this.className || a === !1
+ ? ""
+ : L.get(this, "__className__") || ""))
+ }
+ )
+ },
+ hasClass: function(a) {
+ for (var b = " " + a + " ", c = 0, d = this.length; d > c; c++)
+ if (
+ 1 === this[c].nodeType &&
+ (" " + this[c].className + " ").replace(ab, " ").indexOf(b) >= 0
+ )
+ return !0
+ return !1
+ }
+ })
+ var bb = /\r/g
+ n.fn.extend({
+ val: function(a) {
+ var b,
+ c,
+ d,
+ e = this[0]
+ {
+ if (arguments.length)
+ return (
+ (d = n.isFunction(a)),
+ this.each(function(c) {
+ var e
+ 1 === this.nodeType &&
+ ((e = d ? a.call(this, c, n(this).val()) : a),
+ null == e
+ ? (e = "")
+ : "number" == typeof e
+ ? (e += "")
+ : n.isArray(e) &&
+ (e = n.map(e, function(a) {
+ return null == a ? "" : a + ""
+ })),
+ (b =
+ n.valHooks[this.type] ||
+ n.valHooks[this.nodeName.toLowerCase()]),
+ (b && "set" in b && void 0 !== b.set(this, e, "value")) ||
+ (this.value = e))
+ })
+ )
+ if (e)
+ return (
+ (b = n.valHooks[e.type] || n.valHooks[e.nodeName.toLowerCase()]),
+ b && "get" in b && void 0 !== (c = b.get(e, "value"))
+ ? c
+ : ((c = e.value),
+ "string" == typeof c ? c.replace(bb, "") : null == c ? "" : c)
+ )
+ }
+ }
+ }),
+ n.extend({
+ valHooks: {
+ option: {
+ get: function(a) {
+ var b = n.find.attr(a, "value")
+ return null != b ? b : n.trim(n.text(a))
+ }
+ },
+ select: {
+ get: function(a) {
+ for (
+ var b,
+ c,
+ d = a.options,
+ e = a.selectedIndex,
+ f = "select-one" === a.type || 0 > e,
+ g = f ? null : [],
+ h = f ? e + 1 : d.length,
+ i = 0 > e ? h : f ? e : 0;
+ h > i;
+ i++
+ )
+ if (
+ ((c = d[i]),
+ !(
+ (!c.selected && i !== e) ||
+ (k.optDisabled
+ ? c.disabled
+ : null !== c.getAttribute("disabled")) ||
+ (c.parentNode.disabled && n.nodeName(c.parentNode, "optgroup"))
+ ))
+ ) {
+ if (((b = n(c).val()), f)) return b
+ g.push(b)
+ }
+ return g
+ },
+ set: function(a, b) {
+ var c,
+ d,
+ e = a.options,
+ f = n.makeArray(b),
+ g = e.length
+ while (g--)
+ (d = e[g]), (d.selected = n.inArray(d.value, f) >= 0) && (c = !0)
+ return c || (a.selectedIndex = -1), f
+ }
+ }
+ }
+ }),
+ n.each(["radio", "checkbox"], function() {
+ ;(n.valHooks[this] = {
+ set: function(a, b) {
+ return n.isArray(b) ? (a.checked = n.inArray(n(a).val(), b) >= 0) : void 0
+ }
+ }),
+ k.checkOn ||
+ (n.valHooks[this].get = function(a) {
+ return null === a.getAttribute("value") ? "on" : a.value
+ })
+ }),
+ n.each(
+ "blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error contextmenu".split(
+ " "
+ ),
+ function(a, b) {
+ n.fn[b] = function(a, c) {
+ return arguments.length > 0 ? this.on(b, null, a, c) : this.trigger(b)
+ }
+ }
+ ),
+ n.fn.extend({
+ hover: function(a, b) {
+ return this.mouseenter(a).mouseleave(b || a)
+ },
+ bind: function(a, b, c) {
+ return this.on(a, null, b, c)
+ },
+ unbind: function(a, b) {
+ return this.off(a, null, b)
+ },
+ delegate: function(a, b, c, d) {
+ return this.on(b, a, c, d)
+ },
+ undelegate: function(a, b, c) {
+ return 1 === arguments.length ? this.off(a, "**") : this.off(b, a || "**", c)
+ }
+ })
+ var cb = n.now(),
+ db = /\?/
+ ;(n.parseJSON = function(a) {
+ return JSON.parse(a + "")
+ }),
+ (n.parseXML = function(a) {
+ var b, c
+ if (!a || "string" != typeof a) return null
+ try {
+ ;(c = new DOMParser()), (b = c.parseFromString(a, "text/xml"))
+ } catch (d) {
+ b = void 0
+ }
+ return (
+ (!b || b.getElementsByTagName("parsererror").length) &&
+ n.error("Invalid XML: " + a),
+ b
+ )
+ })
+ var eb = /#.*$/,
+ fb = /([?&])_=[^&]*/,
+ gb = /^(.*?):[ \t]*([^\r\n]*)$/gm,
+ hb = /^(?:about|app|app-storage|.+-extension|file|res|widget):$/,
+ ib = /^(?:GET|HEAD)$/,
+ jb = /^\/\//,
+ kb = /^([\w.+-]+:)(?:\/\/(?:[^\/?#]*@|)([^\/?#:]*)(?::(\d+)|)|)/,
+ lb = {},
+ mb = {},
+ nb = "*/".concat("*"),
+ ob = a.location.href,
+ pb = kb.exec(ob.toLowerCase()) || []
+ function qb(a) {
+ return function(b, c) {
+ "string" != typeof b && ((c = b), (b = "*"))
+ var d,
+ e = 0,
+ f = b.toLowerCase().match(E) || []
+ if (n.isFunction(c))
+ while ((d = f[e++]))
+ "+" === d[0]
+ ? ((d = d.slice(1) || "*"), (a[d] = a[d] || []).unshift(c))
+ : (a[d] = a[d] || []).push(c)
+ }
+ }
+ function rb(a, b, c, d) {
+ var e = {},
+ f = a === mb
+ function g(h) {
+ var i
+ return (
+ (e[h] = !0),
+ n.each(a[h] || [], function(a, h) {
+ var j = h(b, c, d)
+ return "string" != typeof j || f || e[j]
+ ? f
+ ? !(i = j)
+ : void 0
+ : (b.dataTypes.unshift(j), g(j), !1)
+ }),
+ i
+ )
+ }
+ return g(b.dataTypes[0]) || (!e["*"] && g("*"))
+ }
+ function sb(a, b) {
+ var c,
+ d,
+ e = n.ajaxSettings.flatOptions || {}
+ for (c in b) void 0 !== b[c] && ((e[c] ? a : d || (d = {}))[c] = b[c])
+ return d && n.extend(!0, a, d), a
+ }
+ function tb(a, b, c) {
+ var d,
+ e,
+ f,
+ g,
+ h = a.contents,
+ i = a.dataTypes
+ while ("*" === i[0])
+ i.shift(), void 0 === d && (d = a.mimeType || b.getResponseHeader("Content-Type"))
+ if (d)
+ for (e in h)
+ if (h[e] && h[e].test(d)) {
+ i.unshift(e)
+ break
+ }
+ if (i[0] in c) f = i[0]
+ else {
+ for (e in c) {
+ if (!i[0] || a.converters[e + " " + i[0]]) {
+ f = e
+ break
+ }
+ g || (g = e)
+ }
+ f = f || g
+ }
+ return f ? (f !== i[0] && i.unshift(f), c[f]) : void 0
+ }
+ function ub(a, b, c, d) {
+ var e,
+ f,
+ g,
+ h,
+ i,
+ j = {},
+ k = a.dataTypes.slice()
+ if (k[1]) for (g in a.converters) j[g.toLowerCase()] = a.converters[g]
+ f = k.shift()
+ while (f)
+ if (
+ (a.responseFields[f] && (c[a.responseFields[f]] = b),
+ !i && d && a.dataFilter && (b = a.dataFilter(b, a.dataType)),
+ (i = f),
+ (f = k.shift()))
+ )
+ if ("*" === f) f = i
+ else if ("*" !== i && i !== f) {
+ if (((g = j[i + " " + f] || j["* " + f]), !g))
+ for (e in j)
+ if (
+ ((h = e.split(" ")),
+ h[1] === f && (g = j[i + " " + h[0]] || j["* " + h[0]]))
+ ) {
+ g === !0 ? (g = j[e]) : j[e] !== !0 && ((f = h[0]), k.unshift(h[1]))
+ break
+ }
+ if (g !== !0)
+ if (g && a["throws"]) b = g(b)
+ else
+ try {
+ b = g(b)
+ } catch (l) {
+ return {
+ state: "parsererror",
+ error: g ? l : "No conversion from " + i + " to " + f
+ }
+ }
+ }
+ return { state: "success", data: b }
+ }
+ n.extend({
+ active: 0,
+ lastModified: {},
+ etag: {},
+ ajaxSettings: {
+ url: ob,
+ type: "GET",
+ isLocal: hb.test(pb[1]),
+ global: !0,
+ processData: !0,
+ async: !0,
+ contentType: "application/x-www-form-urlencoded; charset=UTF-8",
+ accepts: {
+ "*": nb,
+ text: "text/plain",
+ html: "text/html",
+ xml: "application/xml, text/xml",
+ json: "application/json, text/javascript"
+ },
+ contents: { xml: /xml/, html: /html/, json: /json/ },
+ responseFields: { xml: "responseXML", text: "responseText", json: "responseJSON" },
+ converters: {
+ "* text": String,
+ "text html": !0,
+ "text json": n.parseJSON,
+ "text xml": n.parseXML
+ },
+ flatOptions: { url: !0, context: !0 }
+ },
+ ajaxSetup: function(a, b) {
+ return b ? sb(sb(a, n.ajaxSettings), b) : sb(n.ajaxSettings, a)
+ },
+ ajaxPrefilter: qb(lb),
+ ajaxTransport: qb(mb),
+ ajax: function(a, b) {
+ "object" == typeof a && ((b = a), (a = void 0)), (b = b || {})
+ var c,
+ d,
+ e,
+ f,
+ g,
+ h,
+ i,
+ j,
+ k = n.ajaxSetup({}, b),
+ l = k.context || k,
+ m = k.context && (l.nodeType || l.jquery) ? n(l) : n.event,
+ o = n.Deferred(),
+ p = n.Callbacks("once memory"),
+ q = k.statusCode || {},
+ r = {},
+ s = {},
+ t = 0,
+ u = "canceled",
+ v = {
+ readyState: 0,
+ getResponseHeader: function(a) {
+ var b
+ if (2 === t) {
+ if (!f) {
+ f = {}
+ while ((b = gb.exec(e))) f[b[1].toLowerCase()] = b[2]
+ }
+ b = f[a.toLowerCase()]
+ }
+ return null == b ? null : b
+ },
+ getAllResponseHeaders: function() {
+ return 2 === t ? e : null
+ },
+ setRequestHeader: function(a, b) {
+ var c = a.toLowerCase()
+ return t || ((a = s[c] = s[c] || a), (r[a] = b)), this
+ },
+ overrideMimeType: function(a) {
+ return t || (k.mimeType = a), this
+ },
+ statusCode: function(a) {
+ var b
+ if (a)
+ if (2 > t) for (b in a) q[b] = [q[b], a[b]]
+ else v.always(a[v.status])
+ return this
+ },
+ abort: function(a) {
+ var b = a || u
+ return c && c.abort(b), x(0, b), this
+ }
+ }
+ if (
+ ((o.promise(v).complete = p.add),
+ (v.success = v.done),
+ (v.error = v.fail),
+ (k.url = ((a || k.url || ob) + "").replace(eb, "").replace(jb, pb[1] + "//")),
+ (k.type = b.method || b.type || k.method || k.type),
+ (k.dataTypes = n
+ .trim(k.dataType || "*")
+ .toLowerCase()
+ .match(E) || [""]),
+ null == k.crossDomain &&
+ ((h = kb.exec(k.url.toLowerCase())),
+ (k.crossDomain = !(
+ !h ||
+ (h[1] === pb[1] &&
+ h[2] === pb[2] &&
+ (h[3] || ("http:" === h[1] ? "80" : "443")) ===
+ (pb[3] || ("http:" === pb[1] ? "80" : "443")))
+ ))),
+ k.data &&
+ k.processData &&
+ "string" != typeof k.data &&
+ (k.data = n.param(k.data, k.traditional)),
+ rb(lb, k, b, v),
+ 2 === t)
+ )
+ return v
+ ;(i = n.event && k.global),
+ i && 0 === n.active++ && n.event.trigger("ajaxStart"),
+ (k.type = k.type.toUpperCase()),
+ (k.hasContent = !ib.test(k.type)),
+ (d = k.url),
+ k.hasContent ||
+ (k.data && ((d = k.url += (db.test(d) ? "&" : "?") + k.data), delete k.data),
+ k.cache === !1 &&
+ (k.url = fb.test(d)
+ ? d.replace(fb, "$1_=" + cb++)
+ : d + (db.test(d) ? "&" : "?") + "_=" + cb++)),
+ k.ifModified &&
+ (n.lastModified[d] &&
+ v.setRequestHeader("If-Modified-Since", n.lastModified[d]),
+ n.etag[d] && v.setRequestHeader("If-None-Match", n.etag[d])),
+ ((k.data && k.hasContent && k.contentType !== !1) || b.contentType) &&
+ v.setRequestHeader("Content-Type", k.contentType),
+ v.setRequestHeader(
+ "Accept",
+ k.dataTypes[0] && k.accepts[k.dataTypes[0]]
+ ? k.accepts[k.dataTypes[0]] +
+ ("*" !== k.dataTypes[0] ? ", " + nb + "; q=0.01" : "")
+ : k.accepts["*"]
+ )
+ for (j in k.headers) v.setRequestHeader(j, k.headers[j])
+ if (k.beforeSend && (k.beforeSend.call(l, v, k) === !1 || 2 === t)) return v.abort()
+ u = "abort"
+ for (j in { success: 1, error: 1, complete: 1 }) v[j](k[j])
+ if ((c = rb(mb, k, b, v))) {
+ ;(v.readyState = 1),
+ i && m.trigger("ajaxSend", [v, k]),
+ k.async &&
+ k.timeout > 0 &&
+ (g = setTimeout(function() {
+ v.abort("timeout")
+ }, k.timeout))
+ try {
+ ;(t = 1), c.send(r, x)
+ } catch (w) {
+ if (!(2 > t)) throw w
+ x(-1, w)
+ }
+ } else x(-1, "No Transport")
+ function x(a, b, f, h) {
+ var j,
+ r,
+ s,
+ u,
+ w,
+ x = b
+ 2 !== t &&
+ ((t = 2),
+ g && clearTimeout(g),
+ (c = void 0),
+ (e = h || ""),
+ (v.readyState = a > 0 ? 4 : 0),
+ (j = (a >= 200 && 300 > a) || 304 === a),
+ f && (u = tb(k, v, f)),
+ (u = ub(k, u, v, j)),
+ j
+ ? (k.ifModified &&
+ ((w = v.getResponseHeader("Last-Modified")),
+ w && (n.lastModified[d] = w),
+ (w = v.getResponseHeader("etag")),
+ w && (n.etag[d] = w)),
+ 204 === a || "HEAD" === k.type
+ ? (x = "nocontent")
+ : 304 === a
+ ? (x = "notmodified")
+ : ((x = u.state), (r = u.data), (s = u.error), (j = !s)))
+ : ((s = x), (a || !x) && ((x = "error"), 0 > a && (a = 0))),
+ (v.status = a),
+ (v.statusText = (b || x) + ""),
+ j ? o.resolveWith(l, [r, x, v]) : o.rejectWith(l, [v, x, s]),
+ v.statusCode(q),
+ (q = void 0),
+ i && m.trigger(j ? "ajaxSuccess" : "ajaxError", [v, k, j ? r : s]),
+ p.fireWith(l, [v, x]),
+ i &&
+ (m.trigger("ajaxComplete", [v, k]),
+ --n.active || n.event.trigger("ajaxStop")))
+ }
+ return v
+ },
+ getJSON: function(a, b, c) {
+ return n.get(a, b, c, "json")
+ },
+ getScript: function(a, b) {
+ return n.get(a, void 0, b, "script")
+ }
+ }),
+ n.each(["get", "post"], function(a, b) {
+ n[b] = function(a, c, d, e) {
+ return (
+ n.isFunction(c) && ((e = e || d), (d = c), (c = void 0)),
+ n.ajax({ url: a, type: b, dataType: e, data: c, success: d })
+ )
+ }
+ }),
+ (n._evalUrl = function(a) {
+ return n.ajax({
+ url: a,
+ type: "GET",
+ dataType: "script",
+ async: !1,
+ global: !1,
+ throws: !0
+ })
+ }),
+ n.fn.extend({
+ wrapAll: function(a) {
+ var b
+ return n.isFunction(a)
+ ? this.each(function(b) {
+ n(this).wrapAll(a.call(this, b))
+ })
+ : (this[0] &&
+ ((b = n(a, this[0].ownerDocument)
+ .eq(0)
+ .clone(!0)),
+ this[0].parentNode && b.insertBefore(this[0]),
+ b
+ .map(function() {
+ var a = this
+ while (a.firstElementChild) a = a.firstElementChild
+ return a
+ })
+ .append(this)),
+ this)
+ },
+ wrapInner: function(a) {
+ return this.each(
+ n.isFunction(a)
+ ? function(b) {
+ n(this).wrapInner(a.call(this, b))
+ }
+ : function() {
+ var b = n(this),
+ c = b.contents()
+ c.length ? c.wrapAll(a) : b.append(a)
+ }
+ )
+ },
+ wrap: function(a) {
+ var b = n.isFunction(a)
+ return this.each(function(c) {
+ n(this).wrapAll(b ? a.call(this, c) : a)
+ })
+ },
+ unwrap: function() {
+ return this.parent()
+ .each(function() {
+ n.nodeName(this, "body") || n(this).replaceWith(this.childNodes)
+ })
+ .end()
+ }
+ }),
+ (n.expr.filters.hidden = function(a) {
+ return a.offsetWidth <= 0 && a.offsetHeight <= 0
+ }),
+ (n.expr.filters.visible = function(a) {
+ return !n.expr.filters.hidden(a)
+ })
+ var vb = /%20/g,
+ wb = /\[\]$/,
+ xb = /\r?\n/g,
+ yb = /^(?:submit|button|image|reset|file)$/i,
+ zb = /^(?:input|select|textarea|keygen)/i
+ function Ab(a, b, c, d) {
+ var e
+ if (n.isArray(b))
+ n.each(b, function(b, e) {
+ c || wb.test(a)
+ ? d(a, e)
+ : Ab(a + "[" + ("object" == typeof e ? b : "") + "]", e, c, d)
+ })
+ else if (c || "object" !== n.type(b)) d(a, b)
+ else for (e in b) Ab(a + "[" + e + "]", b[e], c, d)
+ }
+ ;(n.param = function(a, b) {
+ var c,
+ d = [],
+ e = function(a, b) {
+ ;(b = n.isFunction(b) ? b() : null == b ? "" : b),
+ (d[d.length] = encodeURIComponent(a) + "=" + encodeURIComponent(b))
+ }
+ if (
+ (void 0 === b && (b = n.ajaxSettings && n.ajaxSettings.traditional),
+ n.isArray(a) || (a.jquery && !n.isPlainObject(a)))
+ )
+ n.each(a, function() {
+ e(this.name, this.value)
+ })
+ else for (c in a) Ab(c, a[c], b, e)
+ return d.join("&").replace(vb, "+")
+ }),
+ n.fn.extend({
+ serialize: function() {
+ return n.param(this.serializeArray())
+ },
+ serializeArray: function() {
+ return this.map(function() {
+ var a = n.prop(this, "elements")
+ return a ? n.makeArray(a) : this
+ })
+ .filter(function() {
+ var a = this.type
+ return (
+ this.name &&
+ !n(this).is(":disabled") &&
+ zb.test(this.nodeName) &&
+ !yb.test(a) &&
+ (this.checked || !T.test(a))
+ )
+ })
+ .map(function(a, b) {
+ var c = n(this).val()
+ return null == c
+ ? null
+ : n.isArray(c)
+ ? n.map(c, function(a) {
+ return { name: b.name, value: a.replace(xb, "\r\n") }
+ })
+ : { name: b.name, value: c.replace(xb, "\r\n") }
+ })
+ .get()
+ }
+ }),
+ (n.ajaxSettings.xhr = function() {
+ try {
+ return new XMLHttpRequest()
+ } catch (a) {}
+ })
+ var Bb = 0,
+ Cb = {},
+ Db = { 0: 200, 1223: 204 },
+ Eb = n.ajaxSettings.xhr()
+ a.attachEvent &&
+ a.attachEvent("onunload", function() {
+ for (var a in Cb) Cb[a]()
+ }),
+ (k.cors = !!Eb && "withCredentials" in Eb),
+ (k.ajax = Eb = !!Eb),
+ n.ajaxTransport(function(a) {
+ var b
+ return k.cors || (Eb && !a.crossDomain)
+ ? {
+ send: function(c, d) {
+ var e,
+ f = a.xhr(),
+ g = ++Bb
+ if ((f.open(a.type, a.url, a.async, a.username, a.password), a.xhrFields))
+ for (e in a.xhrFields) f[e] = a.xhrFields[e]
+ a.mimeType && f.overrideMimeType && f.overrideMimeType(a.mimeType),
+ a.crossDomain ||
+ c["X-Requested-With"] ||
+ (c["X-Requested-With"] = "XMLHttpRequest")
+ for (e in c) f.setRequestHeader(e, c[e])
+ ;(b = function(a) {
+ return function() {
+ b &&
+ (delete Cb[g],
+ (b = f.onload = f.onerror = null),
+ "abort" === a
+ ? f.abort()
+ : "error" === a
+ ? d(f.status, f.statusText)
+ : d(
+ Db[f.status] || f.status,
+ f.statusText,
+ "string" == typeof f.responseText
+ ? { text: f.responseText }
+ : void 0,
+ f.getAllResponseHeaders()
+ ))
+ }
+ }),
+ (f.onload = b()),
+ (f.onerror = b("error")),
+ (b = Cb[g] = b("abort"))
+ try {
+ f.send((a.hasContent && a.data) || null)
+ } catch (h) {
+ if (b) throw h
+ }
+ },
+ abort: function() {
+ b && b()
+ }
+ }
+ : void 0
+ }),
+ n.ajaxSetup({
+ accepts: {
+ script:
+ "text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"
+ },
+ contents: { script: /(?:java|ecma)script/ },
+ converters: {
+ "text script": function(a) {
+ return n.globalEval(a), a
+ }
+ }
+ }),
+ n.ajaxPrefilter("script", function(a) {
+ void 0 === a.cache && (a.cache = !1), a.crossDomain && (a.type = "GET")
+ }),
+ n.ajaxTransport("script", function(a) {
+ if (a.crossDomain) {
+ var b, c
+ return {
+ send: function(d, e) {
+ ;(b = n("
+
+# MobX Backers and Sponsors
+
+Thanks to your backers and sponsors for their generous support!
+
+## Backers
+
+Support us with a monthly donation and help us continue our activities. [[Become a backer](https://opencollective.com/mobx#backer)]
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+## Sponsors
+
+Become a sponsor and get your logo on our README on Github with a link to your site. [[Become a sponsor](https://opencollective.com/mobx#sponsor)]
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/docs/best/debugging-mobx.md b/docs/best/debugging-mobx.md
new file mode 100644
index 0000000000..20bcd51d42
--- /dev/null
+++ b/docs/best/debugging-mobx.md
@@ -0,0 +1,10 @@
+---
+title: Analyzing reactivity
+hide_title: true
+---
+
+
+
+# This document has been updated and moved
+
+[Please click on this link to open the updated version.](../analyzing-reactivity.md)
diff --git a/docs/best/decorators.md b/docs/best/decorators.md
new file mode 100644
index 0000000000..72fac91f20
--- /dev/null
+++ b/docs/best/decorators.md
@@ -0,0 +1,10 @@
+---
+title: Enabling decorators
+hide_title: true
+---
+
+
+
+# This document has been updated and moved
+
+[Please click on this link to open the updated version.](../enabling-decorators.md)
diff --git a/docs/best/store.md b/docs/best/store.md
new file mode 100644
index 0000000000..53b219dada
--- /dev/null
+++ b/docs/best/store.md
@@ -0,0 +1,10 @@
+---
+title: Defining data stores
+hide_title: true
+---
+
+
+
+# This document has been updated and moved
+
+[Please click on this link to open the updated version.](../defining-data-stores.md)
diff --git a/docs/best/what-does-mobx-react-to.md b/docs/best/what-does-mobx-react-to.md
new file mode 100644
index 0000000000..579a21c7d3
--- /dev/null
+++ b/docs/best/what-does-mobx-react-to.md
@@ -0,0 +1,10 @@
+---
+title: Understanding reactivity
+hide_title: true
+---
+
+
+
+# This document has been updated and moved
+
+[Please click on this link to open the updated version.](../understanding-reactivity.md)
diff --git a/docs/canva.png b/docs/canva.png
deleted file mode 100644
index 8139efe536..0000000000
Binary files a/docs/canva.png and /dev/null differ
diff --git a/docs/collection-utilities.md b/docs/collection-utilities.md
new file mode 100644
index 0000000000..f97bf9ae9a
--- /dev/null
+++ b/docs/collection-utilities.md
@@ -0,0 +1,50 @@
+---
+title: Collection utilities
+sidebar_label: Collection utilities {🚀}
+hide_title: true
+---
+
+
+
+# Collection utilities {🚀}
+
+They enable manipulating observable arrays, objects and Maps with the same generic API.
+These APIs are fully reactive, which means that even [without `Proxy` support](configuration.md#limitations-without-proxy-support) new property declarations can be detected by MobX if `set` is used to add them, and `values` or `keys` are used to iterate over them.
+
+Another benefit of `values`, `keys` and `entries` is that they return arrays rather than iterators, which makes it possible to, for example, immediately call `.map(fn)` on the results.
+
+All that being said, a typical project has little reason to use these APIs.
+
+Access:
+
+- `values(collection)` returns an array of all the values in the collection.
+- `keys(collection)` returns an array of all the keys in the collection.
+- `entries(collection)` returns an array of all the entries `[key, value]` pairs in the collection.
+
+Mutation:
+
+- `set(collection, key, value)` or `set(collection, { key: value })` update the given collection with the provided key / value pair(s).
+- `remove(collection, key)` removes the specified child from the collection. Splicing is used for arrays.
+- `has(collection, key)` returns _true_ if the collection has the specified _observable_ property.
+- `get(collection, key)` returns the child under the specified key.
+
+If you use the access APIs in an environment without `Proxy` support, then also use the mutation APIs so they can detect the changes.
+
+```javascript
+import { autorun, get, set, observable, values } from "mobx"
+
+const twitterUrls = observable.object({
+ Joe: "twitter.com/joey"
+})
+
+autorun(() => {
+ // Get can track not yet existing properties.
+ console.log(get(twitterUrls, "Sara"))
+})
+
+autorun(() => {
+ console.log("All urls: " + values(twitterUrls).join(", "))
+})
+
+set(twitterUrls, { Sara: "twitter.com/horsejs" })
+```
diff --git a/docs/computeds-with-args.md b/docs/computeds-with-args.md
new file mode 100644
index 0000000000..c217b28cc1
--- /dev/null
+++ b/docs/computeds-with-args.md
@@ -0,0 +1,84 @@
+---
+title: Computeds with arguments
+sidebar_label: Computeds with arguments {🚀}
+hide_title: true
+---
+
+
+
+# Computeds with arguments {🚀}
+
+The `computed` annotation can only be used on getters, which don't take arguments.
+What about computations that do take arguments?
+Take the below example of a React component that renders a specific `Item`,
+and the application supports multi-selection.
+
+How can we implement a derivation like `store.isSelected(item.id)`?
+
+```javascript
+import * as React from 'react'
+import { observer } from 'mobx-react-lite'
+
+const Item = observer(({ item, store }) => (
+
+ {item.title}
+
+))
+```
+
+There are four ways in which we can approach this. You can try the solutions below in [this CodeSandbox](https://codesandbox.io/s/multi-selection-odup1?file=/src/index.tsx).
+
+## 1. Derivations don't _need_ to be `computed`
+
+A function doesn't need to be marked as `computed` in order for MobX to track it.
+The above example would already work completely fine out of the box.
+It is important to realize that computed values are only _caching points_.
+If the derivations are pure (and they should be), having a getter or function without `computed` doesn't change the behavior, it is just slightly less efficient.
+
+The above example works fine despite `isSelected` not being a `computed`. The `observer` component will detect and subscribe to any observables that were read by `isSelected` because the function executes as part of rendering that is tracked.
+
+It is good to realize that all `Item` components, in this case, will respond to future selection changes,
+as they all subscribe directly to the observables that capture the selection.
+This is a worst-case example. In general, it is completely fine to have unmarked functions that derive information, and this is a good default strategy until numbers prove anything else should be done.
+
+## 2. Close over the arguments
+
+This is a more efficient implementation compared to the original.
+
+```javascript
+import * as React from 'react'
+import { computed } from 'mobx'
+import { observer } from 'mobx-react-lite'
+
+const Item = observer(({ item, store }) => {
+ const isSelected = computed(() => store.isSelected(item.id)).get()
+ return (
+
+ {item.title}
+
+ )
+})
+```
+
+We create a fresh computed value in the middle of a reaction. This works fine and does introduce that additional caching point, avoiding all components having to directly respond to every selection change.
+The advantage of this approach is that the component itself will only re-render if the
+`isSelected` state toggles, in which case we indeed have to re-render to swap the `className`.
+
+The fact that we create a new `computed` in a next render is fine, this one will now become the caching
+point and the previous one will be cleaned up nicely.
+This is a great and advanced optimization technique.
+
+## 3. Move the state
+
+In this specific case the selection could also be stored as an `isSelected` observable on the `Item`. The selection in the store could then be expressed as a `computed` rather than an observable: `get selection() { return this.items.filter(item => item.isSelected) }`, and we don't need `isSelected` anymore.
+
+## 4. Use computedFn {🚀}
+
+Finally,
+[`computedFn`](https://github.com/mobxjs/mobx-utils#computedfn) from `mobx-utils` can be used in the definition of `todoStore.selected` to automatically memoize `isSelected`.
+It creates a function that memoizes the output for every combination of input arguments.
+
+We recommend to not resort to this one too quickly. It is typical for memoization, that you will need to think about how many different arguments the function is going to be called with, before you can reason about the memory consumption.
+It does however automatically clean up entries if their results aren't observed by any reaction, so it won't leak memory in normal circumstances.
+
+Again, check out the [linked CodeSandbox](https://codesandbox.io/s/multi-selection-odup1?file=/src/index.tsx) to try this one out.
diff --git a/docs/computeds.md b/docs/computeds.md
new file mode 100644
index 0000000000..9e4f78a937
--- /dev/null
+++ b/docs/computeds.md
@@ -0,0 +1,240 @@
+---
+title: Deriving information with computeds
+sidebar_label: Computeds
+hide_title: true
+---
+
+
+
+# Deriving information with computeds
+
+Usage:
+
+- `computed` _(annotation)_
+- `computed(options)` _(annotation)_
+- `computed(fn, options?)`
+- `@computed` _(getter decorator)_
+- `@computed(options)` _(getter decorator)_
+
+Computed values can be used to derive information from other observables.
+They evaluate lazily, caching their output and only recomputing if one of the underlying observables has changed.
+If they are not observed by anything, they suspend entirely.
+
+Conceptually, they are very similar to formulas in spreadsheets, and can't be underestimated. They help in reducing the amount of state you have to store and are highly optimized. Use them wherever possible.
+
+## Example
+
+Computed values can be created by annotating JavaScript [getters](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/get) with `computed`.
+Use `makeObservable` to declare a getter as computed. If you instead want all getters to be automatically declared as `computed`, you can use either `makeAutoObservable`, `observable` or `extendObservable`. Computed getters become non-enumerable.
+
+To help illustrate the point of computed values, the example below relies on [`autorun`](reactions.md#autorun) from the [Reactions {🚀}](reactions.md) advanced section.
+
+```javascript
+import { makeObservable, observable, computed, autorun } from "mobx"
+
+class OrderLine {
+ price = 0
+ amount = 1
+
+ constructor(price) {
+ makeObservable(this, {
+ price: observable,
+ amount: observable,
+ total: computed
+ })
+ this.price = price
+ }
+
+ get total() {
+ console.log("Computing...")
+ return this.price * this.amount
+ }
+}
+
+const order = new OrderLine(0)
+
+const stop = autorun(() => {
+ console.log("Total: " + order.total)
+})
+// Computing...
+// Total: 0
+
+console.log(order.total)
+// (No recomputing!)
+// 0
+
+order.amount = 5
+// Computing...
+// (No autorun)
+
+order.price = 2
+// Computing...
+// Total: 10
+
+stop()
+
+order.price = 3
+// Neither the computation nor autorun will be recomputed.
+```
+
+The above example nicely demonstrates the benefits of a `computed` value, it acts as a caching point.
+Even though we change the `amount`, and this will trigger the `total` to recompute,
+it won't trigger the `autorun`, as `total` will detect its output hasn't been affected, so there is no need to update the `autorun`.
+
+In comparison, if `total` would not be annotated, the `autorun` would run its effect 3 times,
+as it would directly depend on `price` and `amount`. [Try it out yourself](https://codesandbox.io/s/computed-3cjo9?file=/src/index.tsx).
+
+
+
+This is the dependency graph that would be created for the above example.
+
+## Rules
+
+When using computed values there are a couple of best practices to follow:
+
+1. They should not have side effects or update other observables.
+2. Avoid creating and returning new observables.
+3. They should not depend on non-observable values.
+
+## Tips
+
+**Tip:** computed values will be suspended if they are _not_ observed
+
+It sometimes confuses people new to MobX, perhaps used to a library like [Reselect](https://github.com/reduxjs/reselect), that if you create a computed property but don't use it anywhere in a reaction, it is not memoized and appears to be recomputed more often than necessary.
+For example, if we extended the above example with calling `console.log(order.total)` twice, after we called `stop()`, the value would be recomputed twice.
+
+This allows MobX to automatically suspend computations that are not actively in use
+to avoid unnecessary updates to computed values that are not being accessed. But if a computed property is _not_ in use by some reaction, then computed expressions are evaluated each time their value is requested, so they behave just like a normal property.
+
+If you only fiddle around computed properties might not seem efficient, but when applied in a project that uses `observer`, `autorun`, etc., they become very efficient.
+
+The following code demonstrates the issue:
+
+```javascript
+// OrderLine has a computed property `total`.
+const line = new OrderLine(2.0)
+
+// If you access `line.total` outside of a reaction, it is recomputed every time.
+setInterval(() => {
+ console.log(line.total)
+}, 60)
+```
+
+It can be overridden by setting the annotation with the `keepAlive` option ([try it out yourself](https://codesandbox.io/s/computed-3cjo9?file=/src/index.tsx)) or by creating a no-op `autorun(() => { someObject.someComputed })`, which can be nicely cleaned up later if needed.
+Note that both solutions have the risk of creating memory leaks. Changing the default behavior here is an anti-pattern.
+
+MobX can also be configured with the [`computedRequiresReaction`](configuration.md#computedrequiresreaction-boolean) option, to report an error when computeds are accessed outside of a reactive context.
+
+
+
+**Tip:** computed values can have setters
+
+It is possible to define a [setter](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/set) for computed values as well. Note that these setters cannot be used to alter the value of the computed property directly,
+but they can be used as an "inverse" of the derivation. Setters are automatically marked as actions. For example:
+
+```javascript
+class Dimension {
+ length = 2
+
+ constructor() {
+ makeAutoObservable(this)
+ }
+
+ get squared() {
+ return this.length * this.length
+ }
+ set squared(value) {
+ this.length = Math.sqrt(value)
+ }
+}
+```
+
+
+
+{🚀} **Tip:** `computed.struct` for comparing output structurally
+
+If the output of a computed value that is structurally equivalent to the previous computation doesn't need to notify observers, `computed.struct` can be used. It will make a structural comparison first, rather than a reference equality check, before notifying observers. For example:
+
+```javascript
+class Box {
+ width = 0
+ height = 0
+
+ constructor() {
+ makeObservable(this, {
+ width: observable,
+ height: observable,
+ topRight: computed.struct
+ })
+ }
+
+ get topRight() {
+ return {
+ x: this.width,
+ y: this.height
+ }
+ }
+}
+```
+
+By default, the output of a `computed` is compared by reference. Since `topRight` in the above example will always produce a new result object, it is never going to be considered equal to a previous output. Unless `computed.struct` is used.
+
+However, in the above example _we actually don't need `computed.struct`_!
+Computed values normally only re-evaluate if the backing values change.
+That's why `topRight` will only react to changes in `width` or `height`.
+Since if any of those change, we would get a different `topRight` coordinate anyway. `computed.struct` would never have a cache hit and be a waste of effort, so we don't need it.
+
+In practice, `computed.struct` is less useful than it sounds. Only use it if changes in the underlying observables can still lead to the same output. For example, if we were rounding the coordinates first, the rounded coordinates might be equal to the previously rounded coordinates even though the underlying values aren't.
+
+Check out the [`equals`](#equals) option for further customizations on determining whether the output has changed.
+
+
+
+{🚀} **Tip:** computed values with arguments
+
+Although getters don't take arguments, several strategies to work with derived values that need arguments are discussed [here](computeds-with-args.md).
+
+
+
+{🚀} **Tip:** create standalone computed values with `computed(expression)`
+
+`computed` can also be invoked directly as a function, just like [`observable.box`](api.md#observablebox) creates a standalone computed value.
+Use `.get()` on the returned object to get the current value of the computation.
+This form of `computed` is not used very often, but in some cases where you need to pass a "boxed" computed value around it might prove itself useful, one such case is discussed [here](computeds-with-args.md).
+
+
+
+## Options {🚀}
+
+`computed` usually behaves the way you want it to out of the box, but it's possible to customize its behavior by passing in an `options` argument.
+
+### `name`
+
+This string is used as a debug name in the [Spy event listeners](analyzing-reactivity.md#spy) and [MobX developer tools](https://github.com/mobxjs/mobx-devtools).
+
+### `equals`
+
+Set to `comparer.default` by default. It acts as a comparison function for comparing the previous value with the next value. If this function considers the values to be equal, then the observers will not be re-evaluated.
+
+This is useful when working with structural data and types from other libraries. For example, a computed [moment](https://momentjs.com/) instance could use `(a, b) => a.isSame(b)`. `comparer.structural` and `comparer.shallow` come in handy if you want to use structural / shallow comparison to determine whether the new value is different from the previous value, and as a result notify its observers.
+
+Check out the [`computed.struct`](#computed-struct) section above.
+
+#### Built-in comparers
+
+MobX provides four built-in `comparer` methods which should cover most needs of the `equals` option of `computed`:
+
+- `comparer.identity` uses the identity (`===`) operator to determine if two values are the same.
+- `comparer.default` is the same as `comparer.identity`, but also considers `NaN` to be equal to `NaN`.
+- `comparer.structural` performs deep structural comparison to determine if two values are the same.
+- `comparer.shallow` performs shallow structural comparison to determine if two values are the same.
+
+You can import `comparer` from `mobx` to access these methods. They can be used for `reaction` as well.
+
+### `requiresReaction`
+
+It is recommended to set this one to `true` on very expensive computed values. If you try to read its value outside of the reactive context, in which case it might not be cached, it will cause the computed to throw instead of doing an expensive re-evalution.
+
+### `keepAlive`
+
+This avoids suspending computed values when they are not being observed by anything (see the above explanation). Can potentially create memory leaks, similar to the ones discussed for [reactions](reactions.md#always-dispose-of-reactions).
diff --git a/docs/configuration.md b/docs/configuration.md
new file mode 100644
index 0000000000..0b3486357b
--- /dev/null
+++ b/docs/configuration.md
@@ -0,0 +1,224 @@
+---
+title: Configuration
+sidebar_label: Configuration {🚀}
+hide_title: true
+---
+
+
+
+# Configuration {🚀}
+
+MobX has several configurations depending on how you prefer to use it, which JavaScript engines you want to target, and whether you want MobX to hint at best practices.
+Most configuration options can be set by using the `configure` method.
+
+## Proxy support
+
+By default, MobX uses proxies to make arrays and plain objects observable. Proxies provide the best performance and most consistent behavior across environments.
+However, if you are targeting an environment that doesn't support proxies, proxy support has to be disabled.
+Most notably this is the case when targeting Internet Explorer or React Native without using the Hermes engine.
+
+Proxy support can be disabled by using `configure`:
+
+```typescript
+import { configure } from "mobx"
+
+configure({
+ useProxies: "never"
+})
+```
+
+Accepted values for the `useProxies` configuration are:
+
+- `"always"` (**default**): MobX expects to run only in environments with [`Proxy` support](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy) and it will error if such an environment is not available.
+- `"never"`: Proxies are not used and MobX falls back on non-proxy alternatives. This is compatible with all ES5 environments, but causes various [limitations](#limitations-without-proxy-support).
+- `"ifavailable"` (experimental): Proxies are used if they are available, and otherwise MobX falls back to non-proxy alternatives. The benefit of this mode is that MobX will try to warn if APIs or language features that wouldn't work in ES5 environments are used, triggering errors when hitting an ES5 limitation running on a modern environment.
+
+**Note:** before MobX 6, one had to pick either MobX 4 for older engines, or MobX 5 for new engines. However, MobX 6 supports both, although polyfills for certain APIs like Map will be required when targetting older JavaScript engines.
+Proxies cannot be polyfilled. Even though polyfills do exist, they don't support the full spec and are unsuitable for MobX. Don't use them.
+
+### Limitations without Proxy support
+
+1. Observable arrays are not real arrays, so they won't pass the `Array.isArray()` check. The practical consequence is that you often need to `.slice()` the array first (to get a shallow copy of the real array) before passing it to third party libraries. For example, concatenating observable arrays doesn't work as expected, so `.slice()` them first.
+2. Adding or deleting properties of existing observable plain objects after creation is not automatically picked up. If you intend to use objects as index based lookup maps, in other words, as dynamic collections of things, use observable Maps instead.
+
+It is possible to dynamically add properties to objects, and detect their additions, even when Proxies aren't enabled.
+This can be achieved by using the [Collection utilities {🚀}](collection-utilities.md). Make sure that (new) properties are set using the `set` utility, and that the objects are iterated using one of the `values` / `keys` or `entries` utilities, rather than the built-in JavaScript mechanisms.
+But, since this is really easy to forget, we instead recommend using observable Maps if possible.
+
+## Decorator support
+
+For enabling experimental decorator support check out the [Enabling decorators {🚀}](enabling-decorators.md) section.
+
+## Linting options
+
+To help you adopt the patterns advocated by MobX, a strict separation between actions, state and derivations, MobX can _"lint"_ your coding patterns at runtime by hinting at smells. To make sure MobX is as strict as possible, adopt the following settings and read on for their explanations:
+
+```typescript
+import { configure } from "mobx"
+
+configure({
+ enforceActions: "always",
+ computedRequiresReaction: true,
+ reactionRequiresObservable: true,
+ observableRequiresReaction: true,
+ disableErrorBoundaries: true
+})
+```
+
+At some point you will discover that this level of strictness can be pretty annoying.
+It is fine to disable these rules to gain productivity once you are sure you (and your colleagues) grokked the mental model of MobX.
+
+Also, occasionally you will have a case where you have to suppress the warnings triggered by these rules (for example by wrapping in `runInAction`).
+That is fine, there are good exceptions to these recommendations.
+Don't be fundamentalist about them.
+
+Make sure to also try our [`eslint` plugin](https://github.com/mobxjs/mobx/blob/main/packages/eslint-plugin-mobx/README.md).
+While some problems are discoverable statically, others are detectable only at runtime.
+The plugin is intended to complement these rules, not to replace them.
+The autofix feature can also help with the boilerplate code.
+
+#### `enforceActions`
+
+The goal of _enforceActions_ is that you don't forget to wrap event handlers in [`action`](actions.md).
+
+Possible options:
+
+- `"observed"` (**default**): All state that is observed _somewhere_ needs to be changed through actions. This is the default, and the recommended strictness mode in non-trivial applications.
+- `"never"`: State can be changed from anywhere.
+- `"always"`: State always needs to be changed through actions, which in practice also includes creation.
+
+The benefit of `"observed"` is that it allows you to create observables outside of actions and modify them freely, as long as they aren't used anywhere yet.
+
+Since state should in principle always be created from some event handlers, and event handlers should be wrapped, `"always"` captures this the best. But you probably don't want to use this mode in unit tests.
+
+In the rare case where you create observables lazily, for example in a computed property, you can wrap the creation ad-hoc in an action using `runInAction`.
+
+#### `computedRequiresReaction: boolean`
+
+Forbids the direct access of any unobserved computed value from outside an action or reaction.
+This guarantees you aren't using computed values in a way where MobX won't cache them. **Default: `false`**.
+
+In the following example, MobX won't cache the computed value in the first code block, but will cache the result in the second and third block:
+
+```javascript
+class Clock {
+ seconds = 0
+
+ get milliseconds() {
+ console.log("computing")
+ return this.seconds * 1000
+ }
+
+ constructor() {
+ makeAutoObservable(this)
+ }
+}
+
+const clock = new Clock()
+{
+ // This would compute twice, but is warned against by this flag.
+ console.log(clock.milliseconds)
+ console.log(clock.milliseconds)
+}
+{
+ runInAction(() => {
+ // Will compute only once.
+ console.log(clock.milliseconds)
+ console.log(clock.milliseconds)
+ })
+}
+{
+ autorun(() => {
+ // Will compute only once.
+ console.log(clock.milliseconds)
+ console.log(clock.milliseconds)
+ })
+}
+```
+
+#### `observableRequiresReaction: boolean`
+
+Warns about any unobserved observable access.
+Use this if you want to check whether you are using observables without a "MobX context".
+This is a great way to find any missing `observer` wrappers, for example in React components. But it will find missing actions as well. **Default: `false`**
+
+```javascript
+configure({ observableRequiresReaction: true })
+```
+
+**Note:** using propTypes on components that are wrapped with `observer` might trigger false positives for this rule.
+
+#### `reactionRequiresObservable: boolean`
+
+Warns when a reaction (e.g. `autorun`) is created without accessing any observables.
+Use this to check whether you are unnecessarily wrapping React components with `observer`, wrapping functions with `action`, or find cases where you simply forgot to make some data structures or properties observable. **Default: `false`**
+
+```javascript
+configure({ reactionRequiresObservable: true })
+```
+
+#### `disableErrorBoundaries: boolean`
+
+By default, MobX will catch and re-throw exceptions happening in your code to make sure that a reaction in one exception does not prevent the scheduled execution of other, possibly unrelated, reactions. This means exceptions are not propagated back to the original causing code and therefore you won't be able to catch them using try/catch.
+
+By disabling error boundaries, exceptions can escape derivations. This might ease debugging, but might leave MobX and by extension your application in an unrecoverable broken state. **Default: `false`**.
+
+This option is great for unit tests, but remember to call `_resetGlobalState` after each test, for example by using `afterEach` in jest, for example:
+
+```js
+import { _resetGlobalState, observable, autorun, configure } from "mobx"
+
+configure({ disableErrorBoundaries: true })
+
+test("Throw if age is negative", () => {
+ expect(() => {
+ const age = observable.box(10)
+ autorun(() => {
+ if (age.get() < 0) throw new Error("Age should not be negative")
+ })
+ age.set(-1)
+ }).toThrow("Age should not be negative")
+})
+
+afterEach(() => {
+ _resetGlobalState()
+})
+```
+
+#### `safeDescriptors: boolean`
+
+MobX makes some fields **non-configurable** or **non-writable** to prevent you from doing things that are not supported or would most likely break your code. However this can also prevent **spying/mocking/stubbing** in your tests.
+`configure({ safeDescriptors: false })` disables this safety measure, making everything **configurable** and **writable**.
+Note it doesn't affect existing observables, only the ones created after it's been configured.
+**Use with caution** and only when needed - do not turn this off globally for all tests, otherwise you risk false positives (passing tests with broken code). **Default: `true`**
+
+```javascript
+configure({ safeDescriptors: false })
+```
+
+## Further configuration options
+
+#### `isolateGlobalState: boolean`
+
+Isolates the global state of MobX when there are multiple instances of MobX active in the same environment. This is useful when you have an encapsulated library that is using MobX, living in the same page as the app that is using MobX. The reactivity inside the library will remain self-contained when you call `configure({ isolateGlobalState: true })` from it.
+
+Without this option, if multiple MobX instances are active, their internal state will be shared. The benefit is that observables from both instances work together, the downside is that the MobX versions have to match. **Default: `false`**
+
+```javascript
+configure({ isolateGlobalState: true })
+```
+
+#### `reactionScheduler: (f: () => void) => void`
+
+Sets a new function that executes all MobX reactions.
+By default `reactionScheduler` just runs the `f` reaction without any other behavior.
+This can be useful for basic debugging, or slowing down reactions to visualize application updates. **Default: `f => f()`**
+
+```javascript
+configure({
+ reactionScheduler: (f): void => {
+ console.log("Running an event after a delay:", f)
+ setTimeout(f, 100)
+ }
+})
+```
diff --git a/docs/custom-observables.md b/docs/custom-observables.md
new file mode 100644
index 0000000000..642e154b9d
--- /dev/null
+++ b/docs/custom-observables.md
@@ -0,0 +1,90 @@
+---
+title: Creating custom observables
+sidebar_label: Custom observables {🚀}
+hide_title: true
+---
+
+
+
+# Creating custom observables {🚀}
+
+At some point you might want to have more data structures or other things (like streams) that can be used in reactive computations.
+Achieving this is pretty simple by using **atoms**, which is the class that MobX uses internally for all observable data types.
+Atoms can be used to signal to MobX that some observable data source has been observed or changed, and MobX will let the atom know when it's being used and when it's not.
+
+> _**Tip**: In many cases you can avoid the need to create your own atoms just by creating a normal observable, and using
+the [`onBecomeObserved`](lazy-observables.md) utility to be notified when MobX starts tracking it._
+
+The following example demonstrates how you can create an observable `Clock` that returns the current date-time, which can then be used in reactive functions.
+This clock will only actually tick if it is being observed by someone.
+
+The complete API of the `Atom` class is demonstrated by this example. For further information, see [`createAtom`](api.md#createAtom).
+
+```javascript
+import { createAtom, autorun } from "mobx"
+
+class Clock {
+ atom
+ intervalHandler = null
+ currentDateTime
+
+ constructor() {
+ // Creates an atom to interact with the MobX core algorithm.
+ this.atom = createAtom(
+ // 1st parameter:
+ // - Atom's name, for debugging purposes.
+ "Clock",
+ // 2nd (optional) parameter:
+ // - Callback for when this atom transitions from unobserved to observed.
+ () => this.startTicking(),
+ // 3rd (optional) parameter:
+ // - Callback for when this atom transitions from observed to unobserved.
+ () => this.stopTicking()
+ // The same atom transitions between these two states multiple times.
+ )
+ }
+
+ getTime() {
+ // Let MobX know this observable data source has been used.
+ //
+ // reportObserved will return true if the atom is currently being observed
+ // by some reaction. If needed, it will also trigger the startTicking
+ // onBecomeObserved event handler.
+ if (this.atom.reportObserved()) {
+ return this.currentDateTime
+ } else {
+ // getTime was called, but not while a reaction was running, hence
+ // nobody depends on this value, and the startTicking onBecomeObserved
+ // handler won't be fired.
+ //
+ // Depending on the nature of your atom it might behave differently
+ // in such circumstances, like throwing an error, returning a default
+ // value, etc.
+ return new Date()
+ }
+ }
+
+ tick() {
+ this.currentDateTime = new Date()
+ this.atom.reportChanged() // Let MobX know that this data source has changed.
+ }
+
+ startTicking() {
+ this.tick() // Initial tick.
+ this.intervalHandler = setInterval(() => this.tick(), 1000)
+ }
+
+ stopTicking() {
+ clearInterval(this.intervalHandler)
+ this.intervalHandler = null
+ }
+}
+
+const clock = new Clock()
+
+const disposer = autorun(() => console.log(clock.getTime()))
+// Prints the time every second.
+
+// Stop printing. If nobody else uses the same `clock`, it will stop ticking as well.
+disposer()
+```
diff --git a/docs/defining-data-stores.md b/docs/defining-data-stores.md
new file mode 100644
index 0000000000..982d5490e4
--- /dev/null
+++ b/docs/defining-data-stores.md
@@ -0,0 +1,298 @@
+---
+title: Defining data stores
+sidebar_label: Defining data stores
+hide_title: true
+---
+
+
+
+# Defining data stores
+
+This section contains some of the best practices for building large scale maintainable projects we discovered at Mendix while working with MobX.
+This section is opinionated and you are in no way forced to apply these practices.
+There are many ways of working with MobX and React, and this is just one of them.
+
+This section focuses on an unobtrusive way of working with MobX, which works well in existing codebases, or with classic MVC patterns. Alternative, more opinionated ways of organizing stores are [mobx-state-tree](https://github.com/mobxjs/mobx-state-tree) and [mobx-keystone](https://mobx-keystone.js.org/). Both ship with cool features such as structurally shared snapshots, action middlewares, JSON patch support etc. out of the box.
+
+## Stores
+
+Stores can be found in any Flux architecture and can be compared a bit with controllers in the MVC pattern.
+The main responsibility of stores is to move _logic_ and _state_ out of your components into a standalone testable unit that can be used in both frontend and backend JavaScript.
+
+Most applications benefit from having at least two stores: one for the _domain state_ and another one for the _UI state_. The advantage of separating those two is you can reuse and test _domain state_ universally, and you might very well reuse it in other applications.
+
+## Domain Stores
+
+Your application will contain one or multiple _domain_ stores.
+These stores store the data your application is all about.
+Todo items, users, books, movies, orders, you name it.
+Your application will most probably have at least one domain store.
+
+A single domain store should be responsible for a single concept in your application. A single store is often organized as a tree structure with
+multiple domain objects inside.
+
+For example: one domain store for your products, and one for your orders and orderlines.
+As a rule of thumb: if the nature of the relationship between two items is containment, they should typically be in the same store.
+So a store just manages _domain objects_.
+
+These are the responsibilities of a store:
+
+- Instantiate domain objects. Make sure domain objects know the store they belong to.
+- Make sure there is only one instance of each of your domain objects.
+ The same user, order or todo should not be stored twice in memory.
+ This way you can safely use references and also be sure you are looking at the latest instance, without ever having to resolve a reference.
+ This is fast, straightforward and convenient when debugging.
+- Provide backend integration. Store data when needed.
+- Update existing instances if updates are received from the backend.
+- Provide a standalone, universal, testable component of your application.
+- To make sure your store is testable and can be run server-side, you will probably move doing actual websocket / http requests to a separate object so that you can abstract over your communication layer.
+- There should be only one instance of a store.
+
+### Domain objects
+
+Each domain object should be expressed using its own class (or constructor function).
+There is no need to treat your client-side application state as some kind of database.
+Real references, cyclic data structures and instance methods are powerful concepts in JavaScript.
+Domain objects are allowed to refer directly to domain objects from other stores.
+Remember: we want to keep our actions and views as simple as possible and needing to manage references and doing garbage collection yourself might be a step backward.
+Unlike many Flux architectures such as Redux, with MobX there is no need to normalize your data, and this makes it a lot simpler to build the _essentially_ complex parts of your application:
+your business rules, actions and user interface.
+
+Domain objects can delegate all their logic to the store they belong to if that suits your application well.
+It is possible to express your domain objects as plain objects, but classes have some important advantages over plain objects:
+
+- They can have methods.
+ This makes your domain concepts easier to use standalone and reduces the amount of contextual awareness that is needed in your application.
+ Just pass objects around.
+ You don't have to pass stores around, or have to figure out which actions can be applied to an object if they are just available as instance methods.
+ This is especially important in large applications.
+- They offer fine grained control over the visibility of attributes and methods.
+- Objects created using a constructor function can freely mix observable properties and methods, and non-observable properties and methods.
+- They are easily recognizable and can be strictly type-checked.
+
+### Example domain store
+
+```javascript
+import { makeAutoObservable, runInAction, reaction } from "mobx"
+import uuid from "node-uuid"
+
+export class TodoStore {
+ authorStore
+ transportLayer
+ todos = []
+ isLoading = true
+
+ constructor(transportLayer, authorStore) {
+ makeAutoObservable(this)
+ this.authorStore = authorStore // Store that can resolve authors.
+ this.transportLayer = transportLayer // Thing that can make server requests.
+ this.transportLayer.onReceiveTodoUpdate(updatedTodo =>
+ this.updateTodoFromServer(updatedTodo)
+ )
+ this.loadTodos()
+ }
+
+ // Fetches all Todos from the server.
+ loadTodos() {
+ this.isLoading = true
+ this.transportLayer.fetchTodos().then(fetchedTodos => {
+ runInAction(() => {
+ fetchedTodos.forEach(json => this.updateTodoFromServer(json))
+ this.isLoading = false
+ })
+ })
+ }
+
+ // Update a Todo with information from the server. Guarantees a Todo only
+ // exists once. Might either construct a new Todo, update an existing one,
+ // or remove a Todo if it has been deleted on the server.
+ updateTodoFromServer(json) {
+ let todo = this.todos.find(todo => todo.id === json.id)
+ if (!todo) {
+ todo = new Todo(this, json.id)
+ this.todos.push(todo)
+ }
+ if (json.isDeleted) {
+ this.removeTodo(todo)
+ } else {
+ todo.updateFromJson(json)
+ }
+ }
+
+ // Creates a fresh Todo on the client and the server.
+ createTodo() {
+ const todo = new Todo(this)
+ this.todos.push(todo)
+ return todo
+ }
+
+ // A Todo was somehow deleted, clean it from the client memory.
+ removeTodo(todo) {
+ this.todos.splice(this.todos.indexOf(todo), 1)
+ todo.dispose()
+ }
+}
+
+// Domain object Todo.
+export class Todo {
+ id = null // Unique id of this Todo, immutable.
+ completed = false
+ task = ""
+ author = null // Reference to an Author object (from the authorStore).
+ store = null
+ autoSave = true // Indicator for submitting changes in this Todo to the server.
+ saveHandler = null // Disposer of the side effect auto-saving this Todo (dispose).
+
+ constructor(store, id = uuid.v4()) {
+ makeAutoObservable(this, {
+ id: false,
+ store: false,
+ autoSave: false,
+ saveHandler: false,
+ dispose: false
+ })
+ this.store = store
+ this.id = id
+
+ this.saveHandler = reaction(
+ () => this.asJson, // Observe everything that is used in the JSON.
+ json => {
+ // If autoSave is true, send JSON to the server.
+ if (this.autoSave) {
+ this.store.transportLayer.saveTodo(json)
+ }
+ }
+ )
+ }
+
+ // Remove this Todo from the client and the server.
+ delete() {
+ this.store.transportLayer.deleteTodo(this.id)
+ this.store.removeTodo(this)
+ }
+
+ get asJson() {
+ return {
+ id: this.id,
+ completed: this.completed,
+ task: this.task,
+ authorId: this.author ? this.author.id : null
+ }
+ }
+
+ // Update this Todo with information from the server.
+ updateFromJson(json) {
+ this.autoSave = false // Prevent sending of our changes back to the server.
+ this.completed = json.completed
+ this.task = json.task
+ this.author = this.store.authorStore.resolveAuthor(json.authorId)
+ this.autoSave = true
+ }
+
+ // Clean up the observer.
+ dispose() {
+ this.saveHandler()
+ }
+}
+```
+
+## UI stores
+
+The _ui-state-store_ is often very specific for your application, but usually very simple as well.
+This store typically doesn't have much logic in it, but will store a plethora of loosely coupled pieces of information about the UI.
+This is ideal as most applications will change the UI state often during the development process.
+
+Things you will typically find in UI stores:
+
+- Session information
+- Information about how far your application has loaded
+- Information that will not be stored in the backend
+- Information that affects the UI globally
+ - Window dimensions
+ - Accessibility information
+ - Current language
+ - Currently active theme
+- User interface state as soon as it affects multiple, further unrelated components:
+ - Current selection
+ - Visibility of toolbars, etc.
+ - State of a wizard
+ - State of a global overlay
+
+It might very well be that these pieces of information start as internal state of a specific component (for example the visibility of a toolbar), but after a while you discover that you need this information somewhere else in your application.
+Instead of pushing state in such a case upwards in the component tree, like you would do in plain React apps, you just move that state to the _ui-state-store_.
+
+For isomorphic applications you might also want to provide a stub implementation of this store with sane defaults so that all components render as expected.
+You might distribute the _ui-state-store_ through your application by passing it as React context.
+
+Example of a store (using ES6 syntax):
+
+```javascript
+import { makeAutoObservable, observable, computed } from "mobx"
+
+export class UiState {
+ language = "en_US"
+ pendingRequestCount = 0
+
+ // .struct makes sure observer won't be signaled unless the
+ // dimensions object changed in a deepEqual manner.
+ windowDimensions = {
+ width: window.innerWidth,
+ height: window.innerHeight
+ }
+
+ constructor() {
+ makeAutoObservable(this, { windowDimensions: observable.struct })
+ window.onresize = () => {
+ this.windowDimensions = getWindowDimensions()
+ }
+ }
+
+ get appIsInSync() {
+ return this.pendingRequestCount === 0
+ }
+}
+```
+
+## Combining multiple stores
+
+An often asked question is how to combine multiple stores without using singletons. How will they know about each other?
+
+An effective pattern is to create a `RootStore` that instantiates all stores, and share references. The advantage of this pattern is:
+
+1. Simple to set up.
+2. Supports strong typing well.
+3. Makes complex unit tests easy as you just have to instantiate a root store.
+
+Example:
+
+```javascript
+class RootStore {
+ constructor() {
+ this.userStore = new UserStore(this)
+ this.todoStore = new TodoStore(this)
+ }
+}
+
+class UserStore {
+ constructor(rootStore) {
+ this.rootStore = rootStore
+ }
+
+ getTodos(user) {
+ // Access todoStore through the root store.
+ return this.rootStore.todoStore.todos.filter(todo => todo.author === user)
+ }
+}
+
+class TodoStore {
+ todos = []
+ rootStore
+
+ constructor(rootStore) {
+ makeAutoObservable(this)
+ this.rootStore = rootStore
+ }
+}
+```
+
+When using React, this root store is typically inserted into the component tree by using React context.
diff --git a/docs/enabling-decorators.md b/docs/enabling-decorators.md
new file mode 100644
index 0000000000..29850a8523
--- /dev/null
+++ b/docs/enabling-decorators.md
@@ -0,0 +1,170 @@
+---
+title: Decorators
+sidebar_label: Decorators {🚀}
+hide_title: true
+---
+
+
+
+# Decorators
+
+## Enabling decorators
+
+After years of alterations, ES decorators have finally reached Stage 3 in the TC39 process, meaning that they are quite stable and won't undergo breaking changes again like the previous decorator proposals have. MobX has implemented support for this new "2022.3/Stage 3" decorator syntax.
+With modern decorators, it is no longer needed to call `makeObservable` / `makeAutoObservable`.
+
+2022.3 Decorators are supported in:
+
+- TypeScript (5.0 and higher, make sure that the `experimentalDecorators` flag is NOT enabled). [Example commit](https://github.com/mweststrate/currencies-demo/commit/acb9ac8c148e8beef88042c847bb395131e85d60).
+- For Babel make sure the plugin [`proposal-decorators`](https://babeljs.io/docs/babel-plugin-proposal-decorators) is enabled with the highest version (currently `2023-05`). [Example commit](https://github.com/mweststrate/currencies-demo/commit/4999d2228208f3e1e10bc00a272046eaefde8585).
+
+```js
+// tsconfig.json
+{
+ "compilerOptions": {
+ "experimentalDecorators": false /* or just remove the flag */
+ }
+}
+
+// babel.config.json (or equivalent)
+{
+ "plugins": [
+ [
+ "@babel/plugin-proposal-decorators",
+ {
+ "version": "2023-05"
+ }
+ ]
+ ]
+}
+```
+
+- Vite configuration
+
+```js
+// vite.config.js
+{
+ plugins: [
+ react({
+ babel: {
+ plugins: [
+ [
+ "@babel/plugin-proposal-decorators",
+ {
+ version: "2023-05"
+ }
+ ]
+ ]
+ }
+ })
+ ]
+}
+```
+
+## Using decorators
+
+```javascript
+import { observable, computed, action } from "mobx"
+
+class Todo {
+ id = Math.random()
+ @observable accessor title = ""
+ @observable accessor finished = false
+
+ @action
+ toggle() {
+ this.finished = !this.finished
+ }
+}
+
+class TodoList {
+ @observable accessor todos = []
+
+ @computed
+ get unfinishedTodoCount() {
+ return this.todos.filter(todo => !todo.finished).length
+ }
+}
+```
+
+Notice the usage of the new `accessor` keyword when using `@observable`.
+It is part of the 2022.3 spec and is required if you want to use modern decorators.
+
+Using legacy decorators
+
+We do not recommend codebases to use TypeScript / Babel legacy decorators since they well never become an official part of the language, but you can still use them. It does require a specific setup for transpilation:
+
+MobX before version 6 encouraged the use of legacy decorators and mark things as `observable`, `computed` and `action`.
+While MobX 6 recommends against using these decorators (and instead use either modern decorators or [`makeObservable` / `makeAutoObservable`](observable-state.md)), it is in the current major version still possible.
+Support for legacy decorators will be removed in MobX 7.
+
+```javascript
+import { makeObservable, observable, computed, action } from "mobx"
+
+class Todo {
+ id = Math.random()
+ @observable title = ""
+ @observable finished = false
+
+ constructor() {
+ makeObservable(this)
+ }
+
+ @action
+ toggle() {
+ this.finished = !this.finished
+ }
+}
+
+class TodoList {
+ @observable todos = []
+
+ @computed
+ get unfinishedTodoCount() {
+ return this.todos.filter(todo => !todo.finished).length
+ }
+
+ constructor() {
+ makeObservable(this)
+ }
+}
+```
+
+
+
+Migrating from legacy decorators
+
+To migrate from legacy decorators to modern decorators, perform the following steps:
+
+1. Disable / remove the `experimentalDecorators` flag from your TypeScript configuration (or Babel equivalent)
+2. Remove all `makeObservable(this)` calls from class constructors that use decorators.
+3. Replace all instances of `@observable` (and variations) with `@observable accessor`
+
+Please note that adding `accessor` to a class property will change it into `get` and `set` class methods. Unlike class properties, class methods are not enumerable. This may introduce new behavior with some APIs, such as `Object.keys`, `JSON.stringify`, etc.
+
+
+
+Decorator changes / gotchas
+
+MobX' 2022.3 Decorators are very similar to the MobX 5 decorators, so usage is mostly the same, but there are some gotchas:
+
+- `@observable accessor` decorators are _not_ enumerable. `accessor`s do not have a direct equivalent in the past - they're a new concept in the language. We've chosen to make them non-enumerable, non-own properties in order to better follow the spirit of the ES language and what `accessor` means.
+ The main cases for enumerability seem to have been around serialization and rest destructuring.
+ - Regarding serialization, implicitly serializing all properties probably isn't ideal in an OOP-world anyway, so this doesn't seem like a substantial issue (consider implementing `toJSON` or using `serializr` as possible alternatives)
+ - Addressing rest-destructuring, such is an anti-pattern in MobX - doing so would (likely unwantedly) touch all observables and make the observer overly-reactive).
+- `@action some_field = () => {}` was and is valid usage. However, inheritance is different between legacy decorators and modern decorators.
+ - In legacy decorators, if superclass has a field decorated by `@action`, and subclass tries to override the same field, it will throw a `TypeError: Cannot redefine property`.
+ - In modern decorators, if superclass has a field decorated by `@action`, and subclass tries to override the same field, it's allowed to override the field. However, the field on subclass is not an action unless it's also decorated with `@action` in subclass declaration.
+
+
+
+## Using `observer` as a decorator
+
+The `observer` function from `mobx-react` is both a function and a decorator that can be used on class components:
+
+```javascript
+@observer
+class Timer extends React.Component {
+ /* ... */
+}
+```
diff --git a/docs/faq/migrate-to-6.md b/docs/faq/migrate-to-6.md
new file mode 100644
index 0000000000..6da00d7762
--- /dev/null
+++ b/docs/faq/migrate-to-6.md
@@ -0,0 +1,10 @@
+---
+title: Migrating from MobX 4/5
+hide_title: true
+---
+
+
+
+# This document has been updated and moved
+
+[Please click on this link to open the updated version.](../migrating-from-4-or-5.md)
diff --git a/docs/installation.md b/docs/installation.md
new file mode 100644
index 0000000000..b6bae03c20
--- /dev/null
+++ b/docs/installation.md
@@ -0,0 +1,77 @@
+---
+title: Installation
+sidebar_label: Installation
+hide_title: true
+---
+
+
+
+# Installation
+
+MobX works in any ES5 environment, which includes browsers and NodeJS.
+
+There are three types of React bindings:
+- [mobx-react-lite](https://github.com/mobxjs/mobx/tree/main/packages/mobx-react-lite). Utilities to manually apply observation
+- [mobx-react-observer](https://github.com/christianalfoni/mobx-react-observer). Babel/swc plugin to automatically apply observation to components
+- [mobx-react](https://github.com/mobxjs/mobx/tree/main/packages/mobx-react). Support for class components
+
+Append the appropriate bindings for your use case to the _Yarn_ or _NPM_ command below:
+
+**Yarn:** `yarn add mobx`
+
+**NPM:** `npm install --save mobx`
+
+**CDN:** https://cdnjs.com/libraries/mobx / https://unpkg.com/mobx/dist/mobx.umd.production.min.js
+
+# Transpilation settings
+
+## MobX and Decorators
+
+Based on your preference, MobX can be used with or without decorators.
+Both the legacy implementation and the standardised TC-39 version of decorators are currently supported.
+See [enabling-decorators](enabling-decorators.md) for more details on how to enable them.
+Legacy decorator support will be removed in MobX 7, in favor of the standard.
+
+## Use spec compliant transpilation for class properties
+
+When using MobX with TypeScript or Babel, and you plan to use classes; make sure to update your configuration to use a TC-39 spec compliant transpilation for class fields, since this is not always the default. Without this, class fields cannot be made observable before they are initialized.
+
+- **TypeScript**: Set the compiler option `"useDefineForClassFields": true`.
+- **Babel**: Make sure to use at least version 7.12, with the following configuration:
+ ```json
+ {
+ // Babel < 7.13.0
+ "plugins": [["@babel/plugin-proposal-class-properties", { "loose": false }]],
+
+ // Babel >= 7.13.0 (https://babeljs.io/docs/en/assumptions)
+ "plugins": [["@babel/plugin-proposal-class-properties"]],
+ "assumptions": {
+ "setPublicClassFields": false
+ }
+ }
+ ```
+For verification insert this piece of code at the beginning of your sources (eg. `index.js`)
+```javascript
+if (!new class { x }().hasOwnProperty('x')) throw new Error('Transpiler is not configured correctly');
+```
+
+## MobX on older JavaScript environments
+
+By default, MobX uses proxies for optimal performance and compatibility. However, on older JavaScript engines `Proxy` is not available (check out [Proxy support](https://compat-table.github.io/compat-table/es6/#test-Proxy)). Examples of such are Internet Explorer (before Edge), Node.js < 6, iOS < 10, Android before RN 0.59.
+
+In such cases, MobX can fallback to an ES5 compatible implementation which works almost identically, although there are a few [limitations without Proxy support](configuration.md#limitations-without-proxy-support). You will have to explicitly enable the fallback implementation by configuring [`useProxies`](configuration.md#proxy-support):
+
+```javascript
+import { configure } from "mobx"
+
+configure({ useProxies: "never" }) // Or "ifavailable".
+```
+
+This option will be removed in MobX 7.
+
+## MobX on other frameworks / platforms
+
+- [MobX.dart](https://mobx.netlify.app/): MobX for Flutter / Dart
+- [lit-mobx](https://github.com/adobe/lit-mobx): MobX for lit-element
+- [mobx-angular](https://github.com/mobxjs/mobx-angular): MobX for angular
+- [mobx-vue](https://github.com/mobxjs/mobx-vue): MobX for Vue
diff --git a/docs/intercept-and-observe.md b/docs/intercept-and-observe.md
new file mode 100644
index 0000000000..e65f15d8fe
--- /dev/null
+++ b/docs/intercept-and-observe.md
@@ -0,0 +1,154 @@
+---
+title: Intercept & Observe
+sidebar_label: Intercept & Observe {🚀}
+hide_title: true
+---
+
+
+
+# Intercept & Observe {🚀}
+
+_⚠️ **Warning**: intercept and observe are low level utilities, and should not be needed in practice. Use some form of [reaction](reactions.md) instead, as `observe` doesn't respect transactions and doesn't support deep observing of changes. Using these utilities is an anti-pattern. If you intend to get access to the old and new value using `observe`, use [`reaction`](reactions.md#reaction) instead. ⚠️_
+
+`observe` and `intercept` can be used to monitor the changes of a single observable, but they **_don't_** track nested observables.
+
+- `intercept` can be used to detect and modify mutations before they are applied to the observable (validating, normalizing or cancelling).
+- `observe` allows you to intercept changes after they have been made.
+
+## Intercept
+
+Usage: `intercept(target, propertyName?, interceptor)`
+
+_Please avoid this API. It basically provides a bit of aspect-oriented programming, creating flows that are really hard to debug. Instead, do things like data validation **before** updating any state, rather than during._
+
+- `target`: the observable to guard.
+- `propertyName`: optional parameter to specify a specific property to intercept. Note that `intercept(user.name, interceptor)` is fundamentally different from `intercept(user, "name", interceptor)`. The first tries to add an interceptor to the _current_ `value` inside `user.name`, which might not be an observable at all. The latter intercepts changes to the `name` _property_ of `user`.
+- `interceptor`: callback that is invoked for _each_ change that is made to the observable. Receives a single change object describing the mutation.
+
+The `intercept` should tell MobX what needs to happen with the current change.
+Therefore it should do one of the following things:
+
+1. Return the received `change` object as-is from the function, in which case the mutation will be applied.
+2. Modify the `change` object and return it, for example to normalize the data. Not all fields are modifiable, see below.
+3. Return `null`, this indicates that the change can be ignored and shouldn't be applied. This is a powerful concept with which you can for example make your objects temporarily immutable.
+4. Throw an exception, if for example some invariant isn't met.
+
+The function returns a `disposer` function that can be used to cancel the interceptor when invoked.
+It is possible to register multiple interceptors to the same observable.
+They will be chained in registration order.
+If one of the interceptors returns `null` or throws an exception, the other interceptors won't be evaluated anymore.
+It is also possible to register an interceptor both on a parent object and on an individual property.
+In that case the parent object interceptors are run before the property interceptors.
+
+```javascript
+const theme = observable({
+ backgroundColor: "#ffffff"
+})
+
+const disposer = intercept(theme, "backgroundColor", change => {
+ if (!change.newValue) {
+ // Ignore attempts to unset the background color.
+ return null
+ }
+ if (change.newValue.length === 6) {
+ // Correct missing '#' prefix.
+ change.newValue = "#" + change.newValue
+ return change
+ }
+ if (change.newValue.length === 7) {
+ // This must be a properly formatted color code!
+ return change
+ }
+ if (change.newValue.length > 10) {
+ // Stop intercepting future changes.
+ disposer()
+ }
+ throw new Error("This doesn't look like a color at all: " + change.newValue)
+})
+```
+
+## Observe
+
+Usage: `observe(target, propertyName?, listener, invokeImmediately?)`
+
+_See above notice, please avoid this API and use [`reaction`](reactions.md#reaction) instead._
+
+- `target`: the observable to observe.
+- `propertyName`: optional parameter to specify a specific property to observe. Note that `observe(user.name, listener)` is fundamentally different from `observe(user, "name", listener)`. The first observes the _current_ `value` inside `user.name`, which might not be an observable at all. The latter observes the `name` _property_ of `user`.
+- `listener`: callback that will be invoked for _each_ change that is made to the observable. Receives a single change object describing the mutation, except for boxed observables, which will invoke the `listener` with two parameters: `newValue, oldValue`.
+- `invokeImmediately`: _false_ by default. Set it to _true_ if you want `observe` to invoke the `listener` directly with the state of the observable, instead of waiting for the first change. Not supported (yet) by all kinds of observables.
+
+The function returns a `disposer` function that can be used to cancel the observer.
+Note that `transaction` does not affect the working of the `observe` method(s).
+This means that even inside a transaction `observe` will fire its listeners for each mutation.
+Hence [`autorun`](reactions.md#autorun) is usually a more powerful and declarative alternative to `observe`.
+
+_`observe` reacts to **mutations** when they are being made, while reactions like `autorun` or `reaction` react to **new values** when they become available. In many cases the latter is sufficient._
+
+Example:
+
+```javascript
+import { observable, observe } from "mobx"
+
+const person = observable({
+ firstName: "Maarten",
+ lastName: "Luther"
+})
+
+// Observe all fields.
+const disposer = observe(person, change => {
+ console.log(change.type, change.name, "from", change.oldValue, "to", change.object[change.name])
+})
+
+person.firstName = "Martin"
+// Prints: 'update firstName from Maarten to Martin'
+
+// Ignore any future updates.
+disposer()
+
+// Observe a single field.
+const disposer2 = observe(person, "lastName", change => {
+ console.log("LastName changed to ", change.newValue)
+})
+```
+
+Related blog: [Object.observe is dead. Long live mobx.observe](https://medium.com/@mweststrate/object-observe-is-dead-long-live-mobservable-observe-ad96930140c5)
+
+## Event overview
+
+The callbacks of `intercept` and `observe` will receive an event object which has at least the following properties:
+
+- `object`: the observable triggering the event.
+- `debugObjectName`: the name of the observable triggering the event (for debugging).
+- `observableKind`: the type of the observable (value, set, array, object, map, computed).
+- `type` (string): the type of the current event.
+
+These are the additional fields that are available per type:
+
+| Observable type | Event type | Property | Description | Available during intercept | Can be modified by intercept |
+| ---------------------------- | ---------- | ------------ | ------------------------------------------------------------------------------------------------- | -------------------------- | ---------------------------- |
+| Object | add | name | Name of the property being added. | √ | |
+| | | newValue | The new value being assigned. | √ | √ |
+| | update\* | name | Name of the property being updated. | √ | |
+| | | newValue | The new value being assigned. | √ | √ |
+| | | oldValue | The value that is replaced. | | |
+| Array | splice | index | Starting index of the splice. Splices are also fired by `push`, `unshift`, `replace`, etc. | √ | |
+| | | removedCount | Amount of items being removed. | √ | √ |
+| | | added | Array with items being added. | √ | √ |
+| | | removed | Array with items that were removed. | | |
+| | | addedCount | Amount of items that were added. | | |
+| | update | index | Index of the single entry being updated. | √ | |
+| | | newValue | The newValue that is / will be assigned. | √ | √ |
+| | | oldValue | The old value that was replaced. | | |
+| Map | add | name | The name of the entry that was added. | √ | |
+| | | newValue | The new value that is being assigned. | √ | √ |
+| | update | name | The name of the entry being updated. | √ | |
+| | | newValue | The new value that is being assigned. | √ | √ |
+| | | oldValue | The value that has been replaced. | | |
+| | delete | name | The name of the entry being removed. | √ | |
+| | | oldValue | The value of the entry that was removed. | | |
+| Boxed & computed observables | create | newValue | The value that was assigned during creation. Only available as `spy` event for boxed observables. | | |
+| | update | newValue | The new value being assigned. | √ | √ |
+| | | oldValue | The previous value of the observable. | | |
+
+**Note:** object `update` events won't fire for updated computed values (as those aren't mutations). But it is possible to observe them by explicitly subscribing to the specific property using `observe(object, 'computedPropertyName', listener)`.
diff --git a/docs/intro/concepts.md b/docs/intro/concepts.md
new file mode 100644
index 0000000000..c7ccd24567
--- /dev/null
+++ b/docs/intro/concepts.md
@@ -0,0 +1,10 @@
+---
+title: The gist of MobX
+hide_title: true
+---
+
+
+
+# This document has been updated and moved
+
+[Please click on this link to open the updated version.](../the-gist-of-mobx.md)
diff --git a/docs/intro/how-to-read.md b/docs/intro/how-to-read.md
new file mode 100644
index 0000000000..127ec348e3
--- /dev/null
+++ b/docs/intro/how-to-read.md
@@ -0,0 +1,10 @@
+---
+title: About this documentation
+hide_title: true
+---
+
+
+
+# This document has been updated and moved
+
+[Please click on this link to open the updated version.](../about-this-documentation.md)
diff --git a/docs/intro/installation.md b/docs/intro/installation.md
new file mode 100644
index 0000000000..bdce28b0ba
--- /dev/null
+++ b/docs/intro/installation.md
@@ -0,0 +1,10 @@
+---
+title: Installation
+hide_title: true
+---
+
+
+
+# This document has been updated and moved
+
+[Please click on this link to open the updated version.](../installation.md)
diff --git a/docs/lazy-observables.md b/docs/lazy-observables.md
new file mode 100644
index 0000000000..fb33130c42
--- /dev/null
+++ b/docs/lazy-observables.md
@@ -0,0 +1,52 @@
+---
+title: Creating lazy observables
+sidebar_label: Lazy observables {🚀}
+hide_title: true
+---
+
+
+
+# Creating lazy observables {🚀}
+
+Usage:
+
+- `onBecomeObserved(observable, property?, listener: () => void): (() => void)`
+- `onBecomeUnobserved(observable, property?, listener: () => void): (() => void)`
+
+Functions `onBecomeObserved` and `onBecomeUnobserved` can be used to attach lazy behavior or side effects to existing observables. They hook into the observability system of MobX and get notified when an observable _starts_ and _stops_ becoming observed. They both return a _disposer_ function that detaches the _listener_.
+
+In the example below we use them to perform network fetches only when the observed value is actually in use.
+
+```javascript
+export class City {
+ location
+ temperature
+ interval
+
+ constructor(location) {
+ makeAutoObservable(this, {
+ resume: false,
+ suspend: false
+ })
+ this.location = location
+ // Only start data fetching if temperature is actually used!
+ onBecomeObserved(this, "temperature", this.resume)
+ onBecomeUnobserved(this, "temperature", this.suspend)
+ }
+
+ resume = () => {
+ log(`Resuming ${this.location}`)
+ this.interval = setInterval(() => this.fetchTemperature(), 5000)
+ }
+
+ suspend = () => {
+ log(`Suspending ${this.location}`)
+ this.temperature = undefined
+ clearInterval(this.interval)
+ }
+
+ fetchTemperature = flow(function* () {
+ // Data fetching logic...
+ })
+}
+```
diff --git a/docs/migrating-from-4-or-5.md b/docs/migrating-from-4-or-5.md
new file mode 100644
index 0000000000..f7bf7a4d7a
--- /dev/null
+++ b/docs/migrating-from-4-or-5.md
@@ -0,0 +1,80 @@
+---
+title: Migrating from MobX 4/5
+sidebar_label: Migrating from MobX 4/5 {🚀}
+hide_title: true
+---
+
+
+
+# Migrating from MobX 4/5 {🚀}
+
+MobX 6 is quite different from MobX 5. This pages covers a migration guide from MobX 4 and 5 to 6, and an extensive list of all the changes.
+
+For a better understanding, check out the MobX 6.0 [CHANGELOG](https://github.com/mobxjs/mobx/blob/main/packages/mobx/CHANGELOG.md#600).
+
+_⚠️ **Warning**: Depending on factors like the size and complexity of your code base, your MobX usage patterns, and the quality of your automated tests, this migration guide might take you anywhere between an hour and a couple of days. Please refrain from upgrading if you don't trust your Continuous Integration or QA / test procedures enough to pick up any unexpected breakages. Unexpected behavioral changes might be caused by changes in MobX itself or the changes needed to your Babel / TypeScript build configuration. ⚠️_
+
+## Getting started
+
+1. Update `mobx` to the latest version of MobX 4/5 and solve any deprecation messages.
+2. Update `mobx` to version 6.
+3. If you are upgrading from MobX 4, and you will need to support Internet Explorer / React Native without proxies, call `import { configure } from "mobx"; configure({ useProxies: "never" })` at the initialization of your application, to back-out of the Proxy implementation. Check out the [Proxy Support](configuration.md#proxy-support) section for more details.
+4. For babel users:
+ - If you are using Babel and have class-properties enabled, disable the legacy loose field support: `["@babel/plugin-proposal-class-properties", { "loose": false }]`
+ - (Optional) In MobX 6 decorators have become opt-in. If you no longer wish to use decorators, remove `plugin-proposal-decorators` from your babel configuration and dependencies. Check out the [Enabling decorators {🚀}](enabling-decorators.md) section for more details.
+5. For TypeScript users:
+ - Add the flag `"useDefineForClassFields": true` to your compiler config.
+ - (Optional) In MobX 6 decorators have become opt-in. If you no longer wish to use decorators, remove / disable the `experimentalDecorators` configuration from your TypeScript config. Check out the [Enabling decorators {🚀}](enabling-decorators.md) section for more details.
+6. The MobX default configuration has become more strict. We recommend to adopt the new defaults after completing the upgrade, check out the [Configuration {🚀}](configuration.md) section. During migration, we recommend to configure MobX in the same way as it would be in v4/v5 out of the box: `import {configure} from "mobx"; configure({ enforceActions: "never" });`. After finishing the entire migration process and validating that your project works as expected, consider enabling the flags `computedRequiresReaction`, `reactionRequiresObservable` and `observableRequiresReaction` and `enforceActions: "observed"` to write more idiomatic MobX code.
+
+## Upgrading classes to use `makeObservable`
+
+Due to standardized JavaScript limitations in how class fields are constructed, it is no longer possible for MobX to alter the behavior of class fields by means of decorators or the `decorate` utility. Instead, fields have to be made observable by the `constructor`. This can be done in three different ways:
+
+1. Remove all decorators and call `makeObservable` in the `constructor` and explicitly define which field should be made observable using which decorator. For example: `makeObservable(this, { count: observable, tick: action, elapsedTime: computed })` (note that the second argument corresponds to what would be passed to `decorate`). This is the recommended approach if you want to drop decorators in your code base, and the project isn't yet too big.
+2. Leave all the decorators and call `makeObservable(this)` in the `constructor`. This will pick up the metadata generated by the decorators. This is the recommended way if you want to limit the impact of a MobX 6 migration.
+3. Remove decorators and use `makeAutoObservable(this)` in the class `constructor`'s.
+
+Check out [makeObservable / makeAutoObservable](observable-state.md) for more details.
+
+Some specifics to note:
+
+1. Using `makeObservable` / `makeAutoObservable` needs to be done in every class definition that declares MobX based members. So if a sub-class and super-class both introduce observable members, they will both have to call `makeObservable`.
+2. `makeAutoObservable` will mark methods using a new decorator [`autoAction`](observable-state.md#autoAction), that will apply `action` only if it is not in a derivation context. This makes it safe to call automatically decorated methods also from computed properties.
+
+Migrating a large code base with lots of classes might be daunting. But no worries, there is a code-mod available that will automate the above process!!
+
+## Upgrading your code with the `mobx-undecorate` codemod
+
+If you are an existing MobX user you have code that uses a lot of decorators, or the equivalent calls to `decorate`.
+
+The [`mobx-undecorate`](https://www.npmjs.com/package/mobx-undecorate) package provides a codemod that can automatically update your code to be conformant to MobX 6. There is no need to install it; instead you download and execute it using the [`npx`](https://www.npmjs.com/package/npx) tool which you do need to install if you haven't already.
+
+To get rid of all uses of MobX decorators and replace them with the equivalent `makeObservable` calls, go to the directory that contains your source code and run:
+
+```shell
+npx mobx-undecorate
+```
+
+MobX will continue to support decorators -- so if you want to retain them
+and only introduce `makeObservable(this)` where required, you can use the `--keepDecorators` option:
+
+```shell
+npx mobx-undecorate --keepDecorators
+```
+
+See [documentation](https://www.npmjs.com/package/mobx-undecorate) for more options.
+
+### Limitations of `mobx-undecorate`
+
+The `mobx-undecorate` command has to introduce a constructor in classes that do not yet have one. If base class of the constructor expects arguments, the codemod cannot introduce these arguments for the subclass being upgraded, and the `super` call won't pass them either. You have to fix these manually.
+The tool will generate a `// TODO: [mobx-undecorate]` comment in these cases.
+
+We do have a special case for React class components to do the right thing and
+pass along `props` to the superclass.
+
+## Functions are auto-converted
+
+Functions that become part of a deep observable structure are automatically converted to [`autoAction`](observable-state.md#autoAction) or to [`flow`](actions.md#using-flow-instead-of-async--await-) if it's a generator function. See [inference rules](observable-state.md#makeautoobservable) for details.
+This means that the original function reference is not preserved - in the same spirit as the original array/object/set/map reference is lost when converted to observable. [This can be surprising in some situations](https://github.com/mobxjs/mobx/issues/3616).
+If this behavior is not desired use [`observable.shallow`](observable-state.md#available-annotations) / [`observable.ref`](observable-state.md#available-annotations) / [`false`](observable-state.md#available-annotations) / [`deep: false`](observable-state.md#options-) to prevent the conversion process or make sure the function is already an `action` as shown in the issue.
diff --git a/docs/mobx-utils.md b/docs/mobx-utils.md
new file mode 100644
index 0000000000..b91c7f1e3e
--- /dev/null
+++ b/docs/mobx-utils.md
@@ -0,0 +1,11 @@
+---
+title: MobX-utils
+sidebar_label: MobX-utils {🚀}
+hide_title: true
+---
+
+
+
+# MobX-utils {🚀}
+
+[MobX-utils](https://github.com/mobxjs/mobx-utils) provides an extensive series of additional utility functions, observables and common patterns for MobX.
diff --git a/docs/observable-state.md b/docs/observable-state.md
new file mode 100644
index 0000000000..c7311ad984
--- /dev/null
+++ b/docs/observable-state.md
@@ -0,0 +1,367 @@
+---
+title: Creating observable state
+sidebar_label: Observable state
+hide_title: true
+---
+
+
+
+# Creating observable state
+
+Properties, entire objects, arrays, Maps and Sets can all be made observable.
+The basics of making objects observable is specifying an annotation per property using `makeObservable`.
+The most important annotations are:
+
+- [`observable`](#observable) defines a trackable field that stores the state.
+- [`action`](actions.md) marks a method as an action that will modify the state.
+- [`computed`](computeds.md) marks a getter that will derive new facts from the state and cache its output.
+
+## `makeObservable`
+
+Usage:
+
+- `makeObservable(target, annotations?, options?)`
+
+This function can be used to make _existing_ object properties observable. Any JavaScript object (including class instances) can be passed into `target`.
+Typically `makeObservable` is used in the constructor of a class, and its first argument is `this`.
+The `annotations` argument maps [annotations](#available-annotations) to each member. Only annotated members are affected.
+
+Alternatively, decorators like `@observable` can be used on class members instead of calling `makeObservable` in the constructor.
+
+Methods that derive information and take arguments (for example `findUsersOlderThan(age: number): User[]`) can not be annotated as `computed` – their read operations will still be tracked when they are called from a reaction, but their output won't be memoized to avoid memory leaks. To memoize such methods you can use [MobX-utils computedFn {🚀}](https://github.com/mobxjs/mobx-utils#computedfn) instead.
+
+[Subclassing is supported with some limitations](subclassing.md) by using the `override` annotation (see the example [here](subclassing.md)).
+
+
+
+
+```javascript
+import { makeObservable, observable, computed, action, flow } from "mobx"
+
+class Doubler {
+ value
+
+ constructor(value) {
+ makeObservable(this, {
+ value: observable,
+ double: computed,
+ increment: action,
+ fetch: flow
+ })
+ this.value = value
+ }
+
+ get double() {
+ return this.value * 2
+ }
+
+ increment() {
+ this.value++
+ }
+
+ *fetch() {
+ const response = yield fetch("/api/value")
+ this.value = response.json()
+ }
+}
+```
+
+**All annotated** fields are **non-configurable**.
+**All non-observable** (stateless) fields (`action`, `flow`) are **non-writable**.
+
+
+
+When using modern decorators, there is no need to call `makeObservable`, below is what a decorator based class looks like.
+Note that the `@observable` annotation should always be used in combination with the `accessor` keyword.
+
+```javascript
+import { observable, computed, action, flow } from "mobx"
+
+class Doubler {
+ @observable accessor value
+
+ constructor(value) {
+ this.value = value
+ }
+
+ @computed
+ get double() {
+ return this.value * 2
+ }
+
+ @action
+ increment() {
+ this.value++
+ }
+
+ @flow
+ *fetch() {
+ const response = yield fetch("/api/value")
+ this.value = response.json()
+ }
+}
+```
+
+
+
+```javascript
+import { makeAutoObservable } from "mobx"
+
+function createDoubler(value) {
+ return makeAutoObservable({
+ value,
+ get double() {
+ return this.value * 2
+ },
+ increment() {
+ this.value++
+ }
+ })
+}
+```
+
+Note that classes can leverage `makeAutoObservable` as well.
+The difference in the examples just demonstrates how MobX can be applied to different programming styles.
+
+
+
+```javascript
+import { observable } from "mobx"
+
+const todosById = observable({
+ "TODO-123": {
+ title: "find a decent task management system",
+ done: false
+ }
+})
+
+todosById["TODO-456"] = {
+ title: "close all tickets older than two weeks",
+ done: true
+}
+
+const tags = observable(["high prio", "medium prio", "low prio"])
+tags.push("prio: for fun")
+```
+
+In contrast to the first example with `makeObservable`, `observable` supports adding (and removing) _fields_ to an object.
+This makes `observable` great for collections like dynamically keyed objects, arrays, Maps and Sets.
+
+
+
+To use legacy decorators, `makeObservable(this)` should be called in the constructor to make sure decorators work.
+
+```javascript
+import { observable, computed, action, flow } from "mobx"
+
+class Doubler {
+ @observable value
+
+ constructor(value) {
+ makeObservable(this)
+ this.value = value
+ }
+
+ @computed
+ get double() {
+ return this.value * 2
+ }
+
+ @action
+ increment() {
+ this.value++
+ }
+
+ @flow
+ *fetch() {
+ const response = yield fetch("/api/value")
+ this.value = response.json()
+ }
+}
+```
+
+
+
+## `makeAutoObservable`
+
+Usage:
+
+- `makeAutoObservable(target, overrides?, options?)`
+
+`makeAutoObservable` is like `makeObservable` on steroids, as it infers all the properties by default. You can however use the `overrides` parameter to override the default behavior with specific annotations —
+in particular `false` can be used to exclude a property or method from being processed entirely.
+Check out the code above for an example.
+
+The `makeAutoObservable` function can be more compact and easier to maintain than using `makeObservable`, since new members don't have to be mentioned explicitly.
+However, `makeAutoObservable` cannot be used on classes that have super or are [subclassed](subclassing.md).
+
+Inference rules:
+
+- All _own_ properties become `observable`.
+- All `getters` become `computed`.
+- All `setters` become `action`.
+- All _functions_ become [`autoAction`](#autoAction).
+- All _generator_ functions become `flow`. (Note that generator functions are not detectable in some transpiler configurations, if flow doesn't work as expected, make sure to specify `flow` explicitly.)
+- Members marked with `false` in the `overrides` argument will not be annotated. For example, using it for read only fields such as identifiers.
+
+## `observable`
+
+Usage:
+
+- `observable(source, overrides?, options?)`
+- `@observable accessor` _(field decorator)_
+
+The `observable` annotation can also be called as a function to make an entire object observable at once.
+The `source` object will be cloned and all members will be made observable, similar to how it would be done by `makeAutoObservable`.
+Likewise, an `overrides` map can be provided to specify the annotations of specific members.
+Check out the above code block for an example.
+
+The object returned by `observable` will be a Proxy, which means that properties that are added later to the object will be picked up and made observable as well (except when [proxy usage](configuration.md#proxy-support) is disabled).
+
+The `observable` method can also be called with collections types like [arrays](api.md#observablearray), [Maps](api.md#observablemap) and [Sets](api.md#observableset). Those will be cloned as well and converted into their observable counterparts.
+
+> Tip: as holds for JavaScript in general, don't use observable plain objects to create a keyed collection (for example to store a mapping from a user's UUID to user object), use maps instead. Object descriptors are aggressively cached by MobX, so if property names are unstable, this might result in memory leaks.
+
+**Example:** observable array
+
+The following example creates an observable and observes it using [`autorun`](reactions.md#autorun).
+Working with Map and Set collections works similarly.
+
+```javascript
+import { observable, autorun } from "mobx"
+
+const todos = observable([
+ { title: "Spoil tea", completed: true },
+ { title: "Make coffee", completed: false }
+])
+
+autorun(() => {
+ console.log(
+ "Remaining:",
+ todos
+ .filter(todo => !todo.completed)
+ .map(todo => todo.title)
+ .join(", ")
+ )
+})
+// Prints: 'Remaining: Make coffee'
+
+todos[0].completed = false
+// Prints: 'Remaining: Spoil tea, Make coffee'
+
+todos[2] = { title: "Take a nap", completed: false }
+// Prints: 'Remaining: Spoil tea, Make coffee, Take a nap'
+
+todos.shift()
+// Prints: 'Remaining: Make coffee, Take a nap'
+```
+
+Observable arrays have some additional nifty utility functions:
+
+- `clear()` removes all current entries from the array.
+- `replace(newItems)` replaces all existing entries in the array with new ones.
+- `remove(value)` removes a single item by value from the array. Returns `true` if the item was found and removed.
+
+
+
+**Note:** primitives and class instances are never converted to observables
+
+Primitive values cannot be made observable by MobX since they are immutable in JavaScript (but they can be [boxed](api.md#observablebox)).
+Although there is typically no use for this mechanism outside libraries.
+
+Class instances will never be made observable automatically by passing them to `observable` or assigning them to an `observable` property.
+Making class members observable is considered the responsibility of the class constructor.
+
+
+
+{🚀} **Tip:** observable (proxied) versus makeObservable (unproxied)
+
+The primary difference between `make(Auto)Observable` and `observable` is that the first one modifies the object you are passing in as first argument, while `observable` creates a _clone_ that is made observable.
+
+The second difference is that `observable` creates a [`Proxy`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy) object, to be able to trap future property additions in case you use the object as a dynamic lookup map.
+If the object you want to make observable has a regular structure where all members are known up-front, we recommend to use `makeObservable` as non proxied objects are a little faster, and they are easier to inspect in the debugger and `console.log`.
+
+Because of that, `make(Auto)Observable` is the recommended API to use in factory functions.
+Note that it is possible to pass `{ proxy: false }` as an option to `observable` to get a non proxied clone.
+
+
+
+## Available annotations
+
+| Annotation | Description |
+| ------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| `observable` `observable.deep` | Defines a trackable field that stores state. If possible, any value assigned to `observable` is automatically converted to (deep) `observable`, [`autoAction`](#autoAction) or `flow` based on its type. Only `plain object`, `array`, `Map`, `Set`, `function`, `generator function` are convertible. Class instances and others are untouched. |
+| `observable.ref` | Like `observable`, but only reassignments will be tracked. The assigned values are completely ignored and will NOT be automatically converted to `observable`/[`autoAction`](#autoAction)/`flow`. For example, use this if you intend to store immutable data in an observable field. |
+| `observable.shallow` | Like `observable.ref` but for collections. Any collection assigned will be made observable, but the contents of the collection itself won't become observable. |
+| `observable.struct` | Like `observable`, except that any assigned value that is structurally equal to the current value will be ignored. |
+| `action` | Mark a method as an action that will modify the state. Check out [actions](actions.md) for more details. Non-writable. |
+| `action.bound` | Like action, but will also bind the action to the instance so that `this` will always be set. Non-writable. |
+| `computed` | Can be used on a [getter](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/get) to declare it as a derived value that can be cached. Check out [computeds](computeds.md) for more details. |
+| `computed.struct` | Like `computed`, except that if after recomputing the result is structurally equal to the previous result, no observers will be notified. |
+| `true` | Infer the best annotation. Check out [makeAutoObservable](#makeautoobservable) for more details. |
+| `false` | Explicitly do not annotate this property. |
+| `flow` | Creates a `flow` to manage asynchronous processes. Check out [flow](actions.md#using-flow-instead-of-async--await-) for more details. Note that the inferred return type in TypeScript might be off. Non-writable. |
+| `flow.bound` | Like flow, but will also bind the flow to the instance so that `this` will always be set. Non-writable. |
+| `override` | [Applicable to inherited `action`, `flow`, `computed`, `action.bound` overridden by subclass](subclassing.md). |
+| `autoAction` | Should not be used explicitly, but is used under the hood by `makeAutoObservable` to mark methods that can act as action or derivation, based on their calling context. It will be determined at runtime if the function is a derivation or action. |
+
+## Limitations
+
+1. `make(Auto)Observable` only supports properties that are already defined. Make sure your [**compiler configuration** is correct](installation.md#use-spec-compliant-transpilation-for-class-properties), or as work-around, that a value is assigned to all properties before using `make(Auto)Observable`. Without correct configuration, fields that are declared but not initialized (like in `class X { y; }`) will not be picked up correctly.
+1. `makeObservable` can only annotate properties declared by its own class definition. If a sub- or superclass introduces observable fields, it will have to call `makeObservable` for those properties itself.
+1. `options` argument can be provided only once. Passed `options` are _"sticky"_ and can NOT be changed later (eg. in [subclass](subclassing.md)).
+1. **Every field can be annotated only once** (except for `override`). The field annotation or configuration can't change in [subclass](subclassing.md).
+1. **All annotated** fields of non-plain objects (**classes**) are **non-configurable**.
+ [Can be disabled with `configure({ safeDescriptors: false })` {🚀☣️} ](configuration.md#safedescriptors-boolean).
+1. **All non-observable** (stateless) fields (`action`, `flow`) are **non-writable**.
+ [Can be disabled with `configure({ safeDescriptors: false })` {🚀☣️} ](configuration.md#safedescriptors-boolean).
+1. [Only **`action`, `computed`, `flow`, `action.bound`** defined **on prototype** can be **overridden** by subclass](subclassing.md).
+1. By default _TypeScript_ will not allow you to annotate **private** fields. This can be overcome by explicitly passing the relevant private fields as generic argument, like this: `makeObservable(this, { privateField: observable, privateField2: observable })`
+1. **Calling `make(Auto)Observable`** and providing annotations must be done **unconditionally**, as this makes it possible to cache the inference results.
+1. **Modifying prototypes** after **`make(Auto)Observable`** has been called is **not supported**.
+1. _EcmaScript_ **private** fields (**`#field`**) are **not supported** by `make(Auto)Observable`. Use auto-accessor + Stage-3 decorators (`@observable accessor #field`) syntax instead. Otherwise, when using _TypeScript_, it is recommended to use the `private` modifier.
+1. **Mixing annotations and decorators** within single inheritance chain is **not supported** - eg. you can't use decorators for superclass and annotations for subclass.
+1. `makeObservable`,`extendObservable` cannot be used on other builtin observable types (`ObservableMap`, `ObservableSet`, `ObservableArray`, etc)
+1. `makeObservable(Object.create(prototype))` copies properties from `prototype` to created object and makes them `observable`. This behavior is wrong, unexpected and therefore **deprecated** and will likely change in future versions. Don't rely on it.
+
+## Options {🚀}
+
+The above APIs take an optional `options` argument which is an object that supports the following options:
+
+- **`autoBind: true`** uses `action.bound`/`flow.bound` by default, rather than `action`/`flow`. Does not affect explicitly annotated members.
+- **`deep: false`** uses `observable.ref` by default, rather than `observable`. Does not affect explicitly annotated members.
+- **`name: `** gives the object a debug name that is printed in error messages and reflection APIs.
+- **`proxy: false`** forces `observable(thing)` to use non-[**proxy**](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy) implementation. This is a good option if the shape of the object will not change over time, as non-proxied objects are easier to debug and faster. This option is **not** available for `make(Auto)Observable`, see [avoiding proxies](#avoid-proxies).
+
+**Note:** options are *sticky* and can be provided only once
+`options` argument can be provided only for `target` that is NOT observable yet.
+It is NOT possible to change options once the observable object was initialized.
+Options are stored on target and respected by subsequent `makeObservable`/`extendObservable` calls.
+You can't pass different options in [subclass](subclassing.md).
+
+
+## Converting observables back to vanilla JavaScript collections
+
+Sometimes it is necessary to convert observable data structures back to their vanilla counterparts.
+For example when passing observable objects to a React component that can't track observables, or to obtain a clone that should not be further mutated.
+
+To convert a collection shallowly, the usual JavaScript mechanisms work:
+
+```javascript
+const plainObject = { ...observableObject }
+const plainArray = observableArray.slice()
+const plainMap = new Map(observableMap)
+```
+
+To convert a data tree recursively to plain objects, the [`toJS`](api.md#tojs) utility can be used.
+For classes, it is recommended to implement a `toJSON()` method, as it will be picked up by `JSON.stringify`.
+
+## A short note on classes
+
+So far most examples above have been leaning towards the class syntax.
+MobX is in principle unopinionated about this, and there are probably just as many MobX users that use plain objects.
+However, a slight benefit of classes is that they have more easily discoverable APIs, e.g. TypeScript.
+Also, `instanceof` checks are really powerful for type inference, and class instances aren't wrapped in `Proxy` objects, giving them a better experience in debuggers.
+Finally, classes benefit from a lot of engine optimizations, since their shape is predictable, and methods are shared on the prototype.
+But heavy inheritance patterns can easily become foot-guns, so if you use classes, keep them simple.
+So, even though there is a slight preference to use classes, we definitely want to encourage you to deviate from this style if that suits you better.
diff --git a/docs/react-integration.md b/docs/react-integration.md
new file mode 100644
index 0000000000..2d25d92962
--- /dev/null
+++ b/docs/react-integration.md
@@ -0,0 +1,527 @@
+---
+title: React integration
+sidebar_label: React integration
+hide_title: true
+---
+
+
+
+# React integration
+
+This documentation outlines how to manually apply observation to React components. However, by using the [mobx-react-observer](https://github.com/christianalfoni/mobx-react-observer) Babel/SWC plugin, you can automatically handle observation without manual intervention. Still, understanding how MobX observation integrates with React components remains valuable, even when leveraging automated solutions.
+
+```javascript
+import { observer } from "mobx-react-lite" // Or "mobx-react".
+
+const MyComponent = observer(props => ReactElement)
+```
+
+While MobX works independently from React, they are most commonly used together. In [The gist of MobX](the-gist-of-mobx.md) you have already seen the most important part of this integration: the `observer` [HoC](https://reactjs.org/docs/higher-order-components.html) that you can wrap around a React component.
+
+`observer` is provided by a separate React bindings package you choose [during installation](installation.md#installation). In this example, we're going to use the more lightweight [`mobx-react-lite` package](https://github.com/mobxjs/mobx/tree/main/packages/mobx-react-lite).
+
+```javascript
+import React from "react"
+import ReactDOM from "react-dom"
+import { makeAutoObservable } from "mobx"
+import { observer } from "mobx-react-lite"
+
+class Timer {
+ secondsPassed = 0
+
+ constructor() {
+ makeAutoObservable(this)
+ }
+
+ increaseTimer() {
+ this.secondsPassed += 1
+ }
+}
+
+const myTimer = new Timer()
+
+// A function component wrapped with `observer` will react
+// to any future change in an observable it used before.
+const TimerView = observer(({ timer }) => Seconds passed: {timer.secondsPassed})
+
+ReactDOM.render(, document.body)
+
+setInterval(() => {
+ myTimer.increaseTimer()
+}, 1000)
+```
+
+**Hint:** you can play with the above example yourself on [CodeSandbox](https://codesandbox.io/s/minimal-observer-p9ti4?file=/src/index.tsx).
+
+The `observer` HoC automatically subscribes React components to _any observables_ that are used _during rendering_.
+As a result, components will automatically re-render when relevant observables change.
+It also makes sure that components don't re-render when there are _no relevant_ changes.
+So, observables that are accessible by the component, but not actually read, won't ever cause a re-render.
+
+In practice this makes MobX applications very well optimized out of the box and they typically don't need any additional code to prevent excessive rendering.
+
+For `observer` to work, it doesn't matter _how_ the observables arrive in the component, only that they are read.
+Reading observables deeply is fine, complex expression like `todos[0].author.displayName` work out of the box.
+This makes the subscription mechanism much more precise and efficient compared to other frameworks in which data dependencies have to be declared explicitly or be pre-computed (e.g. selectors).
+
+## Local and external state
+
+There is great flexibility in how state is organized, since it doesn't matter (technically that is) which observables we read or where observables originated from.
+The examples below demonstrate different patterns on how external and local observable state can be used in components wrapped with `observer`.
+
+### Using external state in `observer` components
+
+
+
+
+Observables can be passed into components as props (as in the example above):
+
+```javascript
+import { observer } from "mobx-react-lite"
+
+const myTimer = new Timer() // See the Timer definition above.
+
+const TimerView = observer(({ timer }) => Seconds passed: {timer.secondsPassed})
+
+// Pass myTimer as a prop.
+ReactDOM.render(, document.body)
+```
+
+
+
+Since it doesn't matter _how_ we got the reference to an observable, we can consume
+observables from outer scopes directly (including from imports, etc.):
+
+```javascript
+const myTimer = new Timer() // See the Timer definition above.
+
+// No props, `myTimer` is directly consumed from the closure.
+const TimerView = observer(() => Seconds passed: {myTimer.secondsPassed})
+
+ReactDOM.render(, document.body)
+```
+
+Using observables directly works very well, but since this typically introduces module state, this pattern might complicate unit testing. Instead, we recommend using React Context instead.
+
+
+
+[React Context](https://reactjs.org/docs/context.html) is a great mechanism to share observables with an entire subtree:
+
+```javascript
+import {observer} from 'mobx-react-lite'
+import {createContext, useContext} from "react"
+
+const TimerContext = createContext()
+
+const TimerView = observer(() => {
+ // Grab the timer from the context.
+ const timer = useContext(TimerContext) // See the Timer definition above.
+ return (
+ Seconds passed: {timer.secondsPassed}
+ )
+})
+
+ReactDOM.render(
+
+
+ ,
+ document.body
+)
+```
+
+Note that we don't recommend ever replacing the `value` of a `Provider` with a different one. Using MobX, there should be no need for that, since the observable that is shared can be updated itself.
+
+
+
+### Using local observable state in `observer` components
+
+Since observables used by `observer` can come from anywhere, they can be local state as well.
+Again, different options are available for us.
+
+
+
+
+The simplest way to use local observable state is to store a reference to an observable class with `useState`.
+Note that, since we typically don't want to replace the reference, we totally ignore the updater function returned by `useState`:
+
+```javascript
+import { observer } from "mobx-react-lite"
+import { useState } from "react"
+
+const TimerView = observer(() => {
+ const [timer] = useState(() => new Timer()) // See the Timer definition above.
+ return Seconds passed: {timer.secondsPassed}
+})
+
+ReactDOM.render(, document.body)
+```
+
+If you want to automatically update the timer like we did in the original example,
+`useEffect` could be used in typical React fashion:
+
+```javascript
+useEffect(() => {
+ const handle = setInterval(() => {
+ timer.increaseTimer()
+ }, 1000)
+ return () => {
+ clearInterval(handle)
+ }
+}, [timer])
+```
+
+
+
+As stated before, instead of using classes, it is possible to directly create observable objects.
+We can leverage [observable](observable-state.md#observable) for that:
+
+```javascript
+import { observer } from "mobx-react-lite"
+import { observable } from "mobx"
+import { useState } from "react"
+
+const TimerView = observer(() => {
+ const [timer] = useState(() =>
+ observable({
+ secondsPassed: 0,
+ increaseTimer() {
+ this.secondsPassed++
+ }
+ })
+ )
+ return Seconds passed: {timer.secondsPassed}
+})
+
+ReactDOM.render(, document.body)
+```
+
+
+
+The combination `const [store] = useState(() => observable({ /* something */}))` is
+quite common. To make this pattern simpler the [`useLocalObservable`](https://github.com/mobxjs/mobx-react#uselocalobservable-hook) hook is exposed from `mobx-react-lite` package, making it possible to simplify the earlier example to:
+
+```javascript
+import { observer, useLocalObservable } from "mobx-react-lite"
+
+const TimerView = observer(() => {
+ const timer = useLocalObservable(() => ({
+ secondsPassed: 0,
+ increaseTimer() {
+ this.secondsPassed++
+ }
+ }))
+ return Seconds passed: {timer.secondsPassed}
+})
+
+ReactDOM.render(, document.body)
+```
+
+
+
+### You might not need locally observable state
+
+In general, we recommend to not resort to MobX observables for local component state too quickly, as this can theoretically lock you out of some features of React's Suspense mechanism.
+As a rule of thumb, use MobX observables when the state captures domain data that is shared among components (including children). Such as todo items, users, bookings, etc.
+
+State that only captures UI state, like loading state, selections, etc, might be better served by the [`useState` hook](https://reactjs.org/docs/hooks-state.html), since this will allow you to leverage React suspense features in the future.
+
+Using observables inside React components adds value as soon as they are either 1) deep, 2) have computed values or 3) are shared with other `observer` components.
+
+## Always read observables inside `observer` components
+
+You might be wondering, when do I apply `observer`? The rule of thumb is: _apply `observer` to all components that read observable data_.
+
+`observer` only enhances the component you are decorating, not the components called by it. So usually all your components should be wrapped by `observer`. Don't worry, this is not inefficient. On the contrary, more `observer` components make rendering more efficient as updates become more fine-grained.
+
+### Tip: Grab values from objects as late as possible
+
+`observer` works best if you pass object references around as long as possible, and only read their properties inside the `observer` based components that are going to render them into the DOM / low-level components.
+In other words, `observer` reacts to the fact that you 'dereference' a value from an object.
+
+In the above example, the `TimerView` component would **not** react to future changes if it was defined
+as follows, because the `.secondsPassed` is not read inside the `observer` component, but outside, and is hence _not_ tracked:
+
+```javascript
+const TimerView = observer(({ secondsPassed }) => Seconds passed: {secondsPassed})
+
+React.render(, document.body)
+```
+
+Note that this is a different mindset from other libraries like `react-redux`, where it is a good practice to dereference early and pass primitives down, to better leverage memoization.
+If the problem is not entirely clear, make sure to check out the [Understanding reactivity](understanding-reactivity.md) section.
+
+### Don't pass observables into components that aren't `observer`
+
+Components wrapped with `observer` _only_ subscribe to observables used during their _own_ rendering of the component. So if observable objects / arrays / maps are passed to child components, those have to be wrapped with `observer` as well.
+This is also true for any callback based components.
+
+If you want to pass observables to a component that isn't an `observer`, either because it is a third-party component, or because you want to keep that component MobX agnostic, you will have to [convert the observables to plain JavaScript values or structures](observable-state.md#converting-observables-back-to-vanilla-javascript-collections) before passing them on.
+
+To elaborate on the above,
+take the following example observable `todo` object, a `TodoView` component (observer) and an imaginary `GridRow` component that takes a column / value mapping, but which isn't an `observer`:
+
+```javascript
+class Todo {
+ title = "test"
+ done = true
+
+ constructor() {
+ makeAutoObservable(this)
+ }
+}
+
+const TodoView = observer(({ todo }: { todo: Todo }) =>
+ // WRONG: GridRow won't pick up changes in todo.title / todo.done
+ // since it isn't an observer.
+ return
+
+ // CORRECT: let `TodoView` detect relevant changes in `todo`,
+ // and pass plain data down.
+ return
+
+ // CORRECT: using `toJS` works as well, but being explicit is typically better.
+ return
+)
+```
+
+### Callback components might require ``
+
+Imagine the same example, where `GridRow` takes an `onRender` callback instead.
+Since `onRender` is part of the rendering cycle of `GridRow`, rather than `TodoView`'s render (even though that is where it syntactically appears), we have to make sure that the callback component uses an `observer` component.
+Or, we can create an in-line anonymous observer using [``](https://github.com/mobxjs/mobx-react#observer):
+
+```javascript
+const TodoView = observer(({ todo }: { todo: Todo }) => {
+ // WRONG: GridRow.onRender won't pick up changes in todo.title / todo.done
+ // since it isn't an observer.
+ return
{todo.title}
} />
+
+ // CORRECT: wrap the callback rendering in Observer to be able to detect changes.
+ return {() =>
{todo.title}
}} />
+})
+```
+
+## Tips
+
+Server Side Rendering (SSR)
+If `observer` is used in server side rendering context; make sure to call `enableStaticRendering(true)`, so that `observer` won't subscribe to any observables used, and no GC problems are introduced.
+
+
+**Note:** mobx-react vs. mobx-react-lite
+In this documentation we used `mobx-react-lite` as default.
+[mobx-react](https://github.com/mobxjs/mobx-react/) is its big brother, which uses `mobx-react-lite` under the hood.
+It offers a few more features which are typically not needed anymore in greenfield projects. The additional things offered by mobx-react:
+
+1. Support for React class components.
+1. `Provider` and `inject`. MobX's own React.createContext predecessor which is not needed anymore.
+1. Observable specific `propTypes`.
+
+Note that `mobx-react` fully repackages and re-exports `mobx-react-lite`, including functional component support.
+If you use `mobx-react`, there is no need to add `mobx-react-lite` as a dependency or import from it anywhere.
+
+
+
+**Note:** `observer` or `React.memo`?
+`observer` automatically applies `memo`, so `observer` components never need to be wrapped in `memo`.
+`memo` can be applied safely to observer components because mutations (deeply) inside the props will be picked up by `observer` anyway if relevant.
+
+
+**Tip:** `observer` for class based React components
+
+As stated above, class based components are only supported through `mobx-react`, and not `mobx-react-lite`.
+Briefly, you can wrap class-based components in `observer` just like
+you can wrap function components:
+
+```javascript
+import React from "react"
+
+const TimerView = observer(
+ class TimerView extends React.Component {
+ render() {
+ const { timer } = this.props
+ return Seconds passed: {timer.secondsPassed}
+ }
+ }
+)
+```
+
+Check out [mobx-react docs](https://github.com/mobxjs/mobx/tree/main/packages/mobx-react#class-components) for more information.
+
+
+
+**Tip:** nice component names in React DevTools
+
+[React DevTools](https://reactjs.org/blog/2019/08/15/new-react-devtools.html) uses the display name information of components to properly display the component hierarchy.
+
+If you use:
+
+```javascript
+export const MyComponent = observer(props =>
hi
)
+```
+
+then no display name will be visible in the DevTools.
+
+
+
+The following approaches can be used to fix this:
+
+- use `function` with a name instead of an arrow function. `mobx-react` infers component name from the function name:
+
+ ```javascript
+ export const MyComponent = observer(function MyComponent(props) {
+ return
hi
+ })
+ ```
+
+- Transpilers (like Babel or TypeScript) infer component name from the variable name:
+
+ ```javascript
+ const _MyComponent = props =>
hi
+ export const MyComponent = observer(_MyComponent)
+ ```
+
+- Infer from the variable name again, using default export:
+
+ ```javascript
+ const MyComponent = props =>
)
+ MyComponent.displayName = "MyComponent"
+ ```
+
+ This is broken in React 16 at the time of writing; mobx-react `observer` uses a React.memo and runs into this bug: https://github.com/facebook/react/issues/18026, but it will be fixed in React 17.
+
+Now you can see component names:
+
+
+
+
+
+{🚀} **Tip:** when combining `observer` with other higher-order-components, apply `observer` first
+
+When `observer` needs to be combined with other decorators or higher-order-components, make sure that `observer` is the innermost (first applied) decorator;
+otherwise it might do nothing at all.
+
+
+
+{🚀} **Tip:** deriving computeds from props
+In some cases the computed values of your local observables might depend on some of the props your component receives.
+However, the set of props that a React component receives is in itself not observable, so changes to the props won't be reflected in any computed values. You have to manually update local observable state in order to properly derive computed values from latest data.
+
+```javascript
+import { observer, useLocalObservable } from "mobx-react-lite"
+import { useEffect } from "react"
+
+const TimerView = observer(({ offset = 0 }) => {
+ const timer = useLocalObservable(() => ({
+ offset, // The initial offset value
+ secondsPassed: 0,
+ increaseTimer() {
+ this.secondsPassed++
+ },
+ get offsetTime() {
+ return this.secondsPassed - this.offset // Not 'offset' from 'props'!
+ }
+ }))
+
+ useEffect(() => {
+ // Sync the offset from 'props' into the observable 'timer'
+ timer.offset = offset
+ }, [offset])
+
+ // Effect to set up a timer, only for demo purposes.
+ useEffect(() => {
+ const handle = setInterval(timer.increaseTimer, 1000)
+ return () => {
+ clearInterval(handle)
+ }
+ }, [])
+
+ return Seconds passed: {timer.offsetTime}
+})
+
+ReactDOM.render(, document.body)
+```
+
+In practice you will rarely need this pattern, since
+`return Seconds passed: {timer.secondsPassed - offset}`
+is a much simpler, albeit slightly less efficient solution.
+
+
+
+{🚀} **Tip:** useEffect and observables
+
+`useEffect` can be used to set up side effects that need to happen, and which are bound to the life-cycle of the React component.
+Using `useEffect` requires specifying dependencies.
+With MobX that isn't really needed, since MobX has already a way to automatically determine the dependencies of an effect, `autorun`.
+Combining `autorun` and coupling it to the life-cycle of the component using `useEffect` is luckily straightforward:
+
+```javascript
+import { observer, useLocalObservable, useAsObservableSource } from "mobx-react-lite"
+import { useState } from "react"
+
+const TimerView = observer(() => {
+ const timer = useLocalObservable(() => ({
+ secondsPassed: 0,
+ increaseTimer() {
+ this.secondsPassed++
+ }
+ }))
+
+ // Effect that triggers upon observable changes.
+ useEffect(
+ () =>
+ autorun(() => {
+ if (timer.secondsPassed > 60) alert("Still there. It's a minute already?!!")
+ }),
+ []
+ )
+
+ // Effect to set up a timer, only for demo purposes.
+ useEffect(() => {
+ const handle = setInterval(timer.increaseTimer, 1000)
+ return () => {
+ clearInterval(handle)
+ }
+ }, [])
+
+ return Seconds passed: {timer.secondsPassed}
+})
+
+ReactDOM.render(, document.body)
+```
+
+Note that we return the disposer created by `autorun` from our effect function.
+This is important, since it makes sure the `autorun` gets cleaned up once the component unmounts!
+
+The dependency array can typically be left empty, unless a non-observable value should trigger a re-run of the autorun, in which case you will need to add it there.
+To make your linter happy, you can define `timer` (in the above example) as a dependency.
+That is safe and has no further effect, since the reference will never actually change.
+
+If you'd rather explicitly define which observables should trigger the effect, use `reaction` instead of `autorun`, beyond that the pattern remains identical.
+
+
+
+### How can I further optimize my React components?
+
+Check out the [React optimizations {🚀}](react-optimizations.md) section.
+
+## Troubleshooting
+
+Help! My component isn't re-rendering...
+
+1. Make sure you didn't forget `observer` (yes, this is the most common mistake).
+1. Verify that the thing you intend to react to is indeed observable. Use utilities like [`isObservable`](api.md#isobservable), [`isObservableProp`](api.md#isobservableprop) if needed to verify this at runtime.
+1. Check the console logs in the browsers for any warnings or errors.
+1. Make sure you grok how tracking works in general. Check out the [Understanding reactivity](understanding-reactivity.md) section.
+1. Read the common pitfalls as described above.
+1. [Configure](configuration.md#linting-options) MobX to warn you of unsound usage of mechanisms and check the console logs.
+1. Use [trace](analyzing-reactivity.md) to verify that you are subscribing to the right things or check what MobX is doing in general using [spy](analyzing-reactivity.md#spy) / the [mobx-log](https://github.com/kubk/mobx-log) package.
diff --git a/docs/react-optimizations.md b/docs/react-optimizations.md
new file mode 100644
index 0000000000..7b03332153
--- /dev/null
+++ b/docs/react-optimizations.md
@@ -0,0 +1,122 @@
+---
+title: Optimizing React component rendering
+sidebar_label: React optimizations {🚀}
+hide_title: true
+---
+
+
+
+# Optimizing React component rendering {🚀}
+
+MobX is very fast, [often even faster than Redux](https://twitter.com/mweststrate/status/718444275239882753), but here are some tips to get most out of React and MobX. Most apply to React in general and are not specific to MobX.
+Note that while it's good to be aware of these patterns, usually your application
+will be fast enough even if you don't worry about them at all.
+
+Prioritize performance only when it's an actual issue!
+
+## Use many small components
+
+`observer` components will track all values they use and re-render if any of them changes.
+So the smaller your components are, the smaller the change they have to re-render. It means that more parts of your user interface have the possibility to render independently of each other.
+
+## Render lists in dedicated components
+
+The above is especially true when rendering big collections.
+React is notoriously bad at rendering large collections as the reconciler has to evaluate the components produced by a collection on each collection change.
+It is therefore recommended to have components that just map over a collection and render it, and render nothing else.
+
+Bad:
+
+```javascript
+const MyComponent = observer(({ todos, user }) => (
+
+ {user.name}
+
+ {todos.map(todo => (
+
+ ))}
+
+
+))
+```
+
+In the above listing React will unnecessarily need to reconcile all `TodoView` components when the `user.name` changes. They won't re-render, but the reconcile process is expensive in itself.
+
+Good:
+
+```javascript
+const MyComponent = observer(({ todos, user }) => (
+
+))
+```
+
+## Don't use array indexes as keys
+
+Don't use array indexes or any value that might change in the future as key. Generate ids for your objects if needed.
+Check out this [blog post](https://medium.com/@robinpokorny/index-as-a-key-is-an-anti-pattern-e0349aece318).
+
+## Dereference values late
+
+When using `mobx-react` it is recommended to dereference values as late as possible.
+This is because MobX will re-render components that dereference observable values automatically.
+If this happens deeper in your component tree, less components have to re-render.
+
+Slower:
+
+```javascript
+
+```
+
+Faster:
+
+```javascript
+
+```
+
+In the faster example, a change in the `name` property triggers only `DisplayName` to re-render, while in the slower one the owner of the component has to re-render as well. There is nothing wrong with that, and if rendering of the owning component is fast enough (usually it is!), then this approach works well.
+
+### Function props {🚀}
+
+You may notice that to dereference values late, you have to create lots of small observer components where each is customized to render a different part of data, for example:
+
+```javascript
+const PersonNameDisplayer = observer(({ person }) => )
+
+const CarNameDisplayer = observer(({ car }) => )
+
+const ManufacturerNameDisplayer = observer(({ car }) =>
+
+)
+```
+
+This quickly becomes tedious if you have lots of data of different shape. An alternative is to use a function that returns the data that you want your `*Displayer` to render:
+
+```javascript
+const GenericNameDisplayer = observer(({ getName }) => )
+```
+
+Then, you can use the component like this:
+
+```javascript
+const MyComponent = ({ person, car }) => (
+ <>
+ person.name} />
+ car.model} />
+ car.manufacturer.name} />
+ >
+)
+```
+
+This approach will allow `GenericNameDisplayer` to be reused throughout your application to render any name, and you still keep component re-rendering
+to a minimum.
diff --git a/docs/react/react-integration.md b/docs/react/react-integration.md
new file mode 100644
index 0000000000..56a2fcca1c
--- /dev/null
+++ b/docs/react/react-integration.md
@@ -0,0 +1,10 @@
+---
+title: React integration
+hide_title: true
+---
+
+
+
+# This document has been updated and moved
+
+[Please click on this link to open the updated version.](../react-integration.md)
diff --git a/docs/react/react-performance.md b/docs/react/react-performance.md
new file mode 100644
index 0000000000..0a1ae7ac0c
--- /dev/null
+++ b/docs/react/react-performance.md
@@ -0,0 +1,10 @@
+---
+title: Optimizing React component rendering
+hide_title: true
+---
+
+
+
+# This document has been updated and moved
+
+[Please click on this link to open the updated version.](../react-optimizations.md)
diff --git a/docs/reactions.md b/docs/reactions.md
new file mode 100644
index 0000000000..994ec57c5c
--- /dev/null
+++ b/docs/reactions.md
@@ -0,0 +1,432 @@
+---
+title: Running side effects with reactions
+sidebar_label: Reactions {🚀}
+hide_title: true
+---
+
+
+
+# Running side effects with reactions {🚀}
+
+Reactions are an important concept to understand, as it is where everything in MobX comes together.
+The goal of reactions is to model side effects that happen automatically.
+Their significance is in creating consumers for your observable state and _automatically_ running side effects whenever something _relevant_ changes.
+
+However, with that in mind, it is important to realize that the APIs discussed here should rarely be used.
+They are often abstracted away in other libraries (like mobx-react) or abstractions specific to your application.
+
+But, to grok MobX, let's take a look at how reactions can be created.
+The simplest way is to use the [`autorun`](#autorun) utility.
+Beyond that, there are also [`reaction`](#reaction) and [`when`](#when).
+
+## Autorun
+
+Usage:
+
+- `autorun(effect: (reaction) => void, options?)`
+
+The `autorun` function accepts one function that should run every time anything it observes changes.
+It also runs once when you create the `autorun` itself. It only responds to changes in observable state, things you have annotated `observable` or `computed`.
+
+### How tracking works
+
+Autorun works by running the `effect` in a _reactive context_. During the execution of the provided function, MobX keeps track of all observable and computed values that are directly or indirectly _read_ by the effect.
+Once the function finishes, MobX will collect and subscribe to all observables that were read and wait until any of them changes again.
+Once they do, the `autorun` will trigger again, repeating the entire process.
+
+
+
+This is how the example below works like.
+
+### Example
+
+```javascript
+import { makeAutoObservable, autorun } from "mobx"
+
+class Animal {
+ name
+ energyLevel
+
+ constructor(name) {
+ this.name = name
+ this.energyLevel = 100
+ makeAutoObservable(this)
+ }
+
+ reduceEnergy() {
+ this.energyLevel -= 10
+ }
+
+ get isHungry() {
+ return this.energyLevel < 50
+ }
+}
+
+const giraffe = new Animal("Gary")
+
+autorun(() => {
+ console.log("Energy level:", giraffe.energyLevel)
+})
+
+autorun(() => {
+ if (giraffe.isHungry) {
+ console.log("Now I'm hungry!")
+ } else {
+ console.log("I'm not hungry!")
+ }
+})
+
+console.log("Now let's change state!")
+for (let i = 0; i < 10; i++) {
+ giraffe.reduceEnergy()
+}
+```
+
+Running this code, you will get the following output:
+
+```
+Energy level: 100
+I'm not hungry!
+Now let's change state!
+Energy level: 90
+Energy level: 80
+Energy level: 70
+Energy level: 60
+Energy level: 50
+Energy level: 40
+Now I'm hungry!
+Energy level: 30
+Energy level: 20
+Energy level: 10
+Energy level: 0
+```
+
+As you can see in the first two lines of the output above, both `autorun` functions run once when they are initialized. This is all you would see without the `for` loop.
+
+Once we run the `for` loop to change the `energyLevel` with the `reduceEnergy`
+action, we see a new log entry every time an `autorun` function observes a
+change in its observable state:
+
+1. For the _"Energy level"_ function, this is every time the `energyLevel` observable changes, 10 times in total.
+
+2. For the _"Now I'm hungry"_ function, this is every time the `isHungry` computed
+ changes, only one time.
+
+## Reaction
+
+Usage:
+
+- `reaction(() => value, (value, previousValue, reaction) => { sideEffect }, options?)`.
+
+`reaction` is like `autorun`, but gives more fine grained control on which observables will be tracked.
+It takes two functions: the first, _data_ function, is tracked and returns the data that is used as input for the second, _effect_ function.
+It is important to note that the side effect _only_ reacts to data that was _accessed_ in the data function, which might be less than the data that is actually used in the effect function.
+
+The typical pattern is that you produce the things you need in your side effect
+in the _data_ function, and in that way control more precisely when the effect triggers.
+By default, the result of the _data_ function has to change in order for the _effect_ function to be triggered.
+Unlike `autorun`, the side effect won't run once when initialized, but only after the data expression returns a new value for the first time.
+
+**Example:** the data and effect functions
+
+In the example below, the reaction is only triggered once, when `isHungry` changes.
+Changes to `giraffe.energyLevel`, which is used by the _effect_ function, do not cause the _effect_ function to be executed. If you wanted `reaction` to respond to this
+as well, you would have to also access it in the _data_ function and return it.
+
+```javascript
+import { makeAutoObservable, reaction } from "mobx"
+
+class Animal {
+ name
+ energyLevel
+
+ constructor(name) {
+ this.name = name
+ this.energyLevel = 100
+ makeAutoObservable(this)
+ }
+
+ reduceEnergy() {
+ this.energyLevel -= 10
+ }
+
+ get isHungry() {
+ return this.energyLevel < 50
+ }
+}
+
+const giraffe = new Animal("Gary")
+
+reaction(
+ () => giraffe.isHungry,
+ isHungry => {
+ if (isHungry) {
+ console.log("Now I'm hungry!")
+ } else {
+ console.log("I'm not hungry!")
+ }
+ console.log("Energy level:", giraffe.energyLevel)
+ }
+)
+
+console.log("Now let's change state!")
+for (let i = 0; i < 10; i++) {
+ giraffe.reduceEnergy()
+}
+```
+
+Output:
+
+```
+Now let's change state!
+Now I'm hungry!
+Energy level: 40
+```
+
+
+
+## When
+
+Usage:
+
+- `when(predicate: () => boolean, effect?: () => void, options?)`
+- `when(predicate: () => boolean, options?): Promise`
+
+`when` observes and runs the given _predicate_ function until it returns `true`.
+Once that happens, the given _effect_ function is executed and the autorunner is disposed.
+
+The `when` function returns a disposer, allowing you to cancel it manually, unless you don't pass in a second `effect` function, in which case it returns a `Promise`.
+
+
+ **Example:** dispose of things in a reactive way
+
+`when` is really useful for disposing or canceling of things in a reactive way.
+For example:
+
+```javascript
+import { when, makeAutoObservable } from "mobx"
+
+class MyResource {
+ constructor() {
+ makeAutoObservable(this, { dispose: false })
+ when(
+ // Once...
+ () => !this.isVisible,
+ // ... then.
+ () => this.dispose()
+ )
+ }
+
+ get isVisible() {
+ // Indicate whether this item is visible.
+ }
+
+ dispose() {
+ // Clean up some resources.
+ }
+}
+```
+
+As soon as `isVisible` becomes `false`, the `dispose` method is called that
+then does some cleanup for `MyResource`.
+
+
+
+### `await when(...)`
+
+If no `effect` function is provided, `when` returns a `Promise`. This combines nicely with `async / await` to let you wait for changes in observable state.
+
+```javascript
+async function() {
+ await when(() => that.isVisible)
+ // etc...
+}
+```
+
+To cancel `when` prematurely, it is possible to call `.cancel()` on the promise returned by itself.
+
+## Rules
+
+There are a few rules that apply to any reactive context:
+
+1. Affected reactions run by default immediately (synchronously) if an observable is changed. However, they won't run before the end of the current outermost (trans)action.
+2. Autorun tracks only the observables that are read during the synchronous execution of the provided function, but it won't track anything that happens asynchronously.
+3. Autorun won't track observables that are read by an action invoked by the autorun, as actions are always _untracked_.
+
+For more examples on what precisely MobX will and will not react to, check out the [Understanding reactivity](understanding-reactivity.md) section.
+For a more detailed technical breakdown on how tracking works, read the blog post [Becoming fully reactive: an in-depth explanation of MobX](https://hackernoon.com/becoming-fully-reactive-an-in-depth-explanation-of-mobservable-55995262a254).
+
+## Always dispose of reactions
+
+The functions passed to `autorun`, `reaction` and `when` are only garbage collected if all objects they observe are garbage collected themselves. In principle, they keep waiting forever for new changes to happen in the observables they use.
+To be able to stop them from waiting until forever has passed, they all return a disposer function that can be used to stop them and unsubscribe from any observables they used.
+
+```javascript
+const counter = observable({ count: 0 })
+
+// Sets up the autorun and prints 0.
+const disposer = autorun(() => {
+ console.log(counter.count)
+})
+
+// Prints: 1
+counter.count++
+
+// Stops the autorun.
+disposer()
+
+// Will not print.
+counter.count++
+```
+
+We strongly recommend to always use the disposer function that is returned from these methods as soon as their side effect is no longer needed.
+Failing to do so can lead to memory leaks.
+
+The `reaction` argument that is passed as second argument to the effect functions of `reaction` and `autorun`, can be used to prematurely clean up the reaction as well by calling `reaction.dispose()`.
+
+**Example:** memory leak
+
+```javascript
+class Vat {
+ value = 1.2
+
+ constructor() {
+ makeAutoObservable(this)
+ }
+}
+
+const vat = new Vat()
+
+class OrderLine {
+ price = 10
+ amount = 1
+ constructor() {
+ makeAutoObservable(this)
+
+ // This autorun will be GC-ed together with the current orderline
+ // instance as it only uses observables from `this`. It's not strictly
+ // necessary to dispose of it once an OrderLine instance is deleted.
+ this.disposer1 = autorun(() => {
+ doSomethingWith(this.price * this.amount)
+ })
+
+ // This autorun won't be GC-ed together with the current orderline
+ // instance, since vat keeps a reference to notify this autorun, which
+ // in turn keeps 'this' in scope.
+ this.disposer2 = autorun(() => {
+ doSomethingWith(this.price * this.amount * vat.value)
+ })
+ }
+
+ dispose() {
+ // So, to avoid subtle memory issues, always call the
+ // disposers when the reactions are no longer needed.
+ this.disposer1()
+ this.disposer2()
+ }
+}
+```
+
+
+
+In environments that support [Explicit Resource Management](https://github.com/tc39/proposal-explicit-resource-management),
+the disposer function includes a `[Symbol.dispose]` method that can be used to
+dispose of the reaction. This can be useful when disposing of several reactions
+simultaneously or when disposing of reactions alongside other Disposables.
+
+**Example:** using DisposableStack
+
+```javascript
+function createSomeDisposableResource() {}
+
+class Vat {
+ value = 1.2
+
+ constructor() {
+ makeAutoObservable(this)
+ }
+}
+
+const vat = new Vat()
+
+class OrderLine {
+ price = 10
+ amount = 1
+ disposableStack = new DisposableStack()
+ someDisposableResource
+
+ constructor() {
+ makeAutoObservable(this)
+
+ this.disposableStack.use(autorun(() => {
+ doSomethingWith(this.price * this.amount)
+ }))
+
+ this.disposableStack.use(autorun(() => {
+ doSomethingWith(this.price * this.amount * vat.value)
+ }))
+
+ this.someDisposableResource = this.disposableStack.use(createSomeDisposableResource())
+ }
+
+ [Symbol.dispose]() {
+ this.disposableStack[Symbol.dispose]();
+ }
+}
+```
+
+
+
+## Use reactions sparingly!
+
+As it was already said, you won't create reactions very often.
+It might very well be that your application doesn't use any of these APIs directly, and the only way reactions are constructed is indirectly, through for example `observer` from the mobx-react bindings.
+
+Before you set up a reaction, it is good to first check if it conforms to the following principles:
+
+1. **Only use Reactions if there is no direct relation between cause and effect**: If a side effect should happen in response to a very limited set of events / actions, it will often be clearer to directly trigger the effect from those specific actions. For example, if pressing a form submit button should lead to a network request to be posted, it is clearer to trigger this effect directly in response of the `onClick` event, rather than indirectly through a reaction. In contrast, if any change you make to the form state should automatically end up in local storage, then a reaction can be very useful, so that you don't have to trigger this effect from every individual `onChange` event.
+1. **Reactions shouldn't update other observables**: Is the reaction going to modify other observables? If the answer is yes, typically the observable you want to update should be annotated as a [`computed`](computeds.md) value instead. For example, if a collection of todos is altered, don't use a reaction to compute the amount of `remainingTodos`, but annotate `remainingTodos` as a computed value. That will lead to much clearer and easier to debug code. Reactions should not compute new data, but only cause effects.
+1. **Reactions should be independent**: Does your code rely on some other reaction having to run first? If that is the case, you probably
+ either violated the first rule, or the new reaction you are about to create should be merged into the one it is depending upon. MobX does not guarantee the order in which reactions will be run.
+
+There are real-life scenarios that do not fit in the above principles. That is why they are _principles_, not _laws_.
+But, the exceptions are rare so only violate them as a last resort.
+
+## Options {🚀}
+
+The behavior of `autorun`, `reaction` and `when` can be further fine-tuned by passing in an `options` argument as shown in the usages above.
+
+### `name`
+
+This string is used as a debug name for this reaction in the [Spy event listeners](analyzing-reactivity.md#spy) and [MobX developer tools](https://github.com/mobxjs/mobx-devtools).
+
+### `fireImmediately` _(reaction)_
+
+Boolean indicating that the _effect_ function should immediately be triggered after the first run of the _data_ function. `false` by default.
+
+### `delay` _(autorun, reaction)_
+
+Number of milliseconds that can be used to throttle the effect function. If zero (default), no throttling happens.
+
+### `timeout` _(when)_
+
+Set a limited amount of time that `when` will wait for. If the deadline passes, `when` will reject / throw.
+
+### `signal`
+
+An AbortSignal object instance; can be used as an alternative method for disposal.
+When used with promise version of `when`, the promise rejects with the "WHEN_ABORTED" error.
+
+### `onError`
+
+By default, any exception thrown inside an reaction will be logged, but not further thrown. This is to make sure that an exception in one reaction does not prevent the scheduled execution of other, possibly unrelated reactions. This also allows reactions to recover from exceptions. Throwing an exception does not break the tracking done by MobX, so subsequent runs of the reaction might complete normally again if the cause for the exception is removed. This option allows overriding that behavior. It is possible to set a global error handler or to disable catching errors completely using [configure](configuration.md#disableerrorboundaries-boolean).
+
+### `scheduler` _(autorun, reaction)_
+
+Set a custom scheduler to determine how re-running the autorun function should be scheduled. It takes a function that should be invoked at some point in the future, for example: `{ scheduler: run => { setTimeout(run, 1000) }}`
+
+### `equals`: (reaction)
+
+Set to `comparer.default` by default. If specified, this comparer function is used to compare the previous and next values produced by the _data_ function. The _effect_ function is only invoked if this function returns false.
+
+Check out the [Built-in comparers](computeds.md#built-in-comparers) section.
diff --git a/docs/refguide/action.md b/docs/refguide/action.md
new file mode 100644
index 0000000000..b1eaee680d
--- /dev/null
+++ b/docs/refguide/action.md
@@ -0,0 +1,10 @@
+---
+title: Updating state using actions
+hide_title: true
+---
+
+
+
+# This document has been updated and moved
+
+[Please click on this link to open the updated version.](../actions.md)
diff --git a/docs/refguide/api.md b/docs/refguide/api.md
new file mode 100644
index 0000000000..72e45fdbe8
--- /dev/null
+++ b/docs/refguide/api.md
@@ -0,0 +1,10 @@
+---
+title: MobX API overview
+hide_title: true
+---
+
+
+
+# This document has been updated and moved
+
+[Please click on this link to open the updated version.](../api.md)
diff --git a/docs/refguide/autorun.md b/docs/refguide/autorun.md
new file mode 100644
index 0000000000..999e336a13
--- /dev/null
+++ b/docs/refguide/autorun.md
@@ -0,0 +1,10 @@
+---
+title: Running side effects with reactions
+hide_title: true
+---
+
+
+
+# This document has been updated and moved
+
+[Please click on this link to open the updated version.](../reactions.md)
diff --git a/docs/refguide/computed-with-args.md b/docs/refguide/computed-with-args.md
new file mode 100644
index 0000000000..6daf7dae43
--- /dev/null
+++ b/docs/refguide/computed-with-args.md
@@ -0,0 +1,10 @@
+---
+title: Computeds with arguments
+hide_title: true
+---
+
+
+
+# This document has been updated and moved
+
+[Please click on this link to open the updated version.](../computeds-with-args.md)
diff --git a/docs/refguide/computed.md b/docs/refguide/computed.md
new file mode 100644
index 0000000000..67726c786d
--- /dev/null
+++ b/docs/refguide/computed.md
@@ -0,0 +1,10 @@
+---
+title: Deriving information with computeds
+hide_title: true
+---
+
+
+
+# This document has been updated and moved
+
+[Please click on this link to open the updated version.](../computeds.md)
diff --git a/docs/refguide/configure.md b/docs/refguide/configure.md
new file mode 100644
index 0000000000..40d1e56118
--- /dev/null
+++ b/docs/refguide/configure.md
@@ -0,0 +1,10 @@
+---
+title: Configuration
+hide_title: true
+---
+
+
+
+# This document has been updated and moved
+
+[Please click on this link to open the updated version.](../configuration.md)
diff --git a/docs/refguide/extending.md b/docs/refguide/extending.md
new file mode 100644
index 0000000000..d416ab6157
--- /dev/null
+++ b/docs/refguide/extending.md
@@ -0,0 +1,10 @@
+---
+title: Creating custom observables
+hide_title: true
+---
+
+
+
+# This document has been updated and moved
+
+[Please click on this link to open the updated version.](../custom-observables.md)
diff --git a/docs/refguide/mobx-utils.md b/docs/refguide/mobx-utils.md
new file mode 100644
index 0000000000..b0520b734d
--- /dev/null
+++ b/docs/refguide/mobx-utils.md
@@ -0,0 +1,10 @@
+---
+title: MobX-utils
+hide_title: true
+---
+
+
+
+# This document has been updated and moved
+
+[Please click on this link to open the updated version.](../mobx-utils.md)
diff --git a/docs/refguide/modifiers.md b/docs/refguide/modifiers.md
new file mode 100644
index 0000000000..2f3be6b4b8
--- /dev/null
+++ b/docs/refguide/modifiers.md
@@ -0,0 +1,10 @@
+---
+title: Observable modifiers
+hide_title: true
+---
+
+
+
+# This document has been updated and moved
+
+[Please click on this link to open the updated version.](../observable-state.md#available-annotations)
diff --git a/docs/refguide/object-api.md b/docs/refguide/object-api.md
new file mode 100644
index 0000000000..3ccb533466
--- /dev/null
+++ b/docs/refguide/object-api.md
@@ -0,0 +1,10 @@
+---
+title: Collection utilities
+hide_title: true
+---
+
+
+
+# This document has been updated and moved
+
+[Please click on this link to open the updated version.](../collection-utilities.md)
diff --git a/docs/refguide/object.md b/docs/refguide/object.md
new file mode 100644
index 0000000000..4300d70464
--- /dev/null
+++ b/docs/refguide/object.md
@@ -0,0 +1,10 @@
+---
+title: Observable Objects
+hide_title: true
+---
+
+
+
+# This document has been updated and moved
+
+[Please click on this link to open the updated version.](../api.md#observableobject)
diff --git a/docs/refguide/observable.md b/docs/refguide/observable.md
new file mode 100644
index 0000000000..29a9ca2288
--- /dev/null
+++ b/docs/refguide/observable.md
@@ -0,0 +1,10 @@
+---
+title: Creating observable state
+hide_title: true
+---
+
+
+
+# This document has been updated and moved
+
+[Please click on this link to open the updated version.](../observable-state.md)
diff --git a/docs/refguide/observe.md b/docs/refguide/observe.md
new file mode 100644
index 0000000000..2ca4c53497
--- /dev/null
+++ b/docs/refguide/observe.md
@@ -0,0 +1,10 @@
+---
+title: Intercept & Observe
+hide_title: true
+---
+
+
+
+# This document has been updated and moved
+
+[Please click on this link to open the updated version.](../intercept-and-observe.md)
diff --git a/docs/refguide/on-become-observed.md b/docs/refguide/on-become-observed.md
new file mode 100644
index 0000000000..678a3918e3
--- /dev/null
+++ b/docs/refguide/on-become-observed.md
@@ -0,0 +1,10 @@
+---
+title: Creating lazy observables
+hide_title: true
+---
+
+
+
+# This document has been updated and moved
+
+[Please click on this link to open the updated version.](../lazy-observables.md)
diff --git a/docs/refguide/set.md b/docs/refguide/set.md
new file mode 100644
index 0000000000..4b5f82e9cd
--- /dev/null
+++ b/docs/refguide/set.md
@@ -0,0 +1,10 @@
+---
+title: Observable Sets
+hide_title: true
+---
+
+
+
+# This document has been updated and moved
+
+[Please click on this link to open the updated version.](../api.md#observableset)
diff --git a/docs/styles/website.css b/docs/styles/website.css
new file mode 100644
index 0000000000..c2d63981a7
--- /dev/null
+++ b/docs/styles/website.css
@@ -0,0 +1,7 @@
+h1 {
+ display: -ms-flexbox;
+ display: flex;
+ width: 100%;
+ -ms-flex-pack: start;
+ justify-content: flex-start;
+}
diff --git a/docs/subclassing.md b/docs/subclassing.md
new file mode 100644
index 0000000000..57610e3935
--- /dev/null
+++ b/docs/subclassing.md
@@ -0,0 +1,142 @@
+---
+title: Subclassing
+sidebar_label: Subclassing
+hide_title: true
+---
+
+
+
+# Subclassing
+
+Subclassing is supported with [limitations](#limitations). Most notably you can only **override actions/flows/computeds on prototype** - you cannot override _[field declarations](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes#field_declarations)_. Use the `override` annotation for methods/getters overridden in a subclass - see example below. Try to keep things simple and prefer composition over inheritance.
+
+```javascript
+import { makeObservable, observable, computed, action, override } from "mobx"
+
+class Parent {
+ // Annotated instance fields are NOT overridable
+ observable = 0
+ arrowAction = () => {}
+
+ // Non-annotated instance fields are overridable
+ overridableArrowAction = action(() => {})
+
+ // Annotated prototype methods/getters are overridable
+ action() {}
+ actionBound() {}
+ get computed() {}
+
+ constructor(value) {
+ makeObservable(this, {
+ observable: observable,
+ arrowAction: action
+ action: action,
+ actionBound: action.bound,
+ computed: computed,
+ })
+ }
+}
+
+class Child extends Parent {
+ /* --- INHERITED --- */
+ // THROWS - TypeError: Cannot redefine property
+ // observable = 5
+ // arrowAction = () = {}
+
+ // OK - not annotated
+ overridableArrowAction = action(() => {})
+
+ // OK - prototype
+ action() {}
+ actionBound() {}
+ get computed() {}
+
+ /* --- NEW --- */
+ childObservable = 0;
+ childArrowAction = () => {}
+ childAction() {}
+ childActionBound() {}
+ get childComputed() {}
+
+ constructor(value) {
+ super()
+ makeObservable(this, {
+ // inherited
+ action: override,
+ actionBound: override,
+ computed: override,
+ // new
+ childObservable: observable,
+ childArrowAction: action,
+ childAction: action,
+ childActionBound: action.bound,
+ childComputed: computed,
+ })
+ }
+}
+```
+
+## Limitations
+
+1. Only `action`, `computed`, `flow`, `action.bound` defined **on prototype** can be **overridden** by subclass.
+1. Field can't be re-annotated in subclass, except with `override`.
+1. `makeAutoObservable` does not support subclassing.
+1. Extending builtins (`ObservableMap`, `ObservableArray`, etc) is not supported.
+1. You can't provide different options to `makeObservable` in subclass.
+1. You can't mix annotations/decorators in single inheritance chain.
+1. [All other limitations apply as well](observable-state.md#limitations)
+
+### `TypeError: Cannot redefine property`
+
+If you see this, you're probably trying to **override arrow function** in subclass `x = () => {}`. That's not possible because **all annotated** fields of classes are **non-configurable** ([see limitations](observable-state.md#limitations)). You have two options:
+
+1. Move function to prototype and use `action.bound` annotation instead
+
+```javascript
+class Parent {
+ // action = () => {};
+ // =>
+ action() {}
+
+ constructor() {
+ makeObservable(this, {
+ action: action.bound
+ })
+ }
+}
+class Child {
+ action() {}
+
+ constructor() {
+ super()
+ makeObservable(this, {
+ action: override
+ })
+ }
+}
+```
+
+
+2. Remove `action` annotation and wrap the function in action manually: `x = action(() => {})`
+
+```javascript
+class Parent {
+ // action = () => {};
+ // =>
+ action = action(() => {})
+
+ constructor() {
+ makeObservable(this, {}) // <-- annotation removed
+ }
+}
+class Child {
+ action = action(() => {})
+
+ constructor() {
+ super()
+ makeObservable(this, {}) // <-- annotation removed
+ }
+}
+```
+
+
diff --git a/docs/the-gist-of-mobx.md b/docs/the-gist-of-mobx.md
new file mode 100644
index 0000000000..aba00ddc0c
--- /dev/null
+++ b/docs/the-gist-of-mobx.md
@@ -0,0 +1,220 @@
+---
+title: The gist of MobX
+sidebar_label: The gist of MobX
+hide_title: true
+---
+
+
+
+# The gist of MobX
+
+## Concepts
+
+MobX distinguishes between the following three concepts in your application:
+
+1. State
+2. Actions
+3. Derivations
+
+Let's take a closer look at these concepts below, or alternatively, in the [10 minute introduction to MobX and React](https://mobx.js.org/getting-started), where you can interactively dive deeper into these concepts step by step and build a simple Todo list app.
+
+Some might recognise the concept of "Signals" in the concepts described below.
+This is correct, MobX is a signal based state management library avant la lettre.
+
+### 1. Define state and make it observable
+
+_State_ is the data that drives your application.
+Usually, there is _domain specific state_ like a list of todo items, and there is _view state_, such as the currently selected element.
+State is like spreadsheet cells that hold a value.
+
+Store state in any data structure you like: plain objects, arrays, classes, cyclic data structures or references. It doesn't matter for the workings of MobX.
+Just make sure that all properties you want to change over time are marked as `observable` so MobX can track them.
+
+Here is a simple example:
+
+```javascript
+import { makeObservable, observable, action } from "mobx"
+
+class Todo {
+ id = Math.random()
+ title = ""
+ finished = false
+
+ constructor(title) {
+ makeObservable(this, {
+ title: observable,
+ finished: observable,
+ toggle: action
+ })
+ this.title = title
+ }
+
+ toggle() {
+ this.finished = !this.finished
+ }
+}
+```
+
+Using `observable` is like turning a property of an object into a spreadsheet cell.
+But unlike spreadsheets, these values can not only be primitive values, but also references, objects and arrays.
+
+Tip: Prefer classes, plain objects or decorators? MobX supports many styles.
+
+This example can be shortened using [`makeAutoObservable`](observable-state.md), but by being explicit we can showcase the different concepts in greater detail.
+Note that MobX doesn't dictate an object style, plain objects instead can be used as well, as can decorators for even more concise classes. See the page for more details.
+
+
+
+But what about `toggle`, which we marked as `action`?
+
+### 2. Update state using actions
+
+An _action_ is any piece of code that changes the _state_. User events, backend data pushes, scheduled events, etc.
+An action is like a user that enters a new value into a spreadsheet cell.
+
+In the `Todo` model above you can see that we have a `toggle` method that changes the value of `finished`. `finished` is marked as `observable`. It is recommended that you mark any piece of code that changes `observable`'s as an [`action`](actions.md). That way MobX can automatically apply transactions for effortless optimal performance.
+
+Using actions helps you structure your code and prevents you from inadvertently changing state when you don't intend to.
+Methods that modify state are called _actions_ in MobX terminology. In contrast to _views_, which compute new information based on the current state.
+Every method should serve at most one of those two goals.
+
+### 3. Create derivations that automatically respond to state changes
+
+_Anything_ that can be derived from the _state_ without any further interaction is a derivation.
+Derivations exist in many forms:
+
+- The _user interface_
+- _Derived data_, such as the number of remaining `todos`
+- _Backend integrations_, e.g. sending changes to the server
+
+MobX distinguishes between two kinds of derivations:
+
+- _Computed values_, which can always be derived from the current observable state using a pure function
+- _Reactions_, side effects that need to happen automatically when the state changes (bridge between imperative and reactive programming)
+
+When starting with MobX, people tend to overuse reactions.
+The golden rule is, always use `computed` if you want to create a value based on the current state.
+
+#### 3.1. Model derived values using computed
+
+To create a _computed_ value, define a property using a JS getter function `get` and mark it as `computed` with `makeObservable`.
+
+```javascript
+import { makeObservable, observable, computed } from "mobx"
+
+class TodoList {
+ todos = []
+ get unfinishedTodoCount() {
+ return this.todos.filter(todo => !todo.finished).length
+ }
+ constructor(todos) {
+ makeObservable(this, {
+ todos: observable,
+ unfinishedTodoCount: computed
+ })
+ this.todos = todos
+ }
+}
+```
+
+MobX will ensure that `unfinishedTodoCount` is updated automatically when a todo is added or when one of the `finished` properties is modified.
+
+These computations resemble formulas in spreadsheet programs like MS Excel. They update automatically, but only when required. That is, if something is interested in their outcome.
+
+#### 3.2. Model side effects using reactions
+
+For you as a user to be able to see a change in state or computed values on the screen, a _reaction_ that repaints a part of the GUI is needed.
+
+Reactions are similar to computed values, but instead of producing information, they produce side effects like printing to the console, making network requests, incrementally updating React component tree to patch the DOM, etc.
+
+In short, reactions bridge the worlds of [reactive](https://en.wikipedia.org/wiki/Reactive_programming) and [imperative](https://en.wikipedia.org/wiki/Imperative_programming) programming.
+
+By far the most used form of reactions are UI components.
+Note that it is possible to trigger side effects from both actions and reactions.
+Side effects that have a clear, explicit origin from which they can be triggered, such
+as making a network request when submitting a form, should be triggered explicitly from the relevant event handler.
+
+#### 3.3. Reactive React components
+
+If you are using React, you can make your components reactive by wrapping them with the [`observer`](react-integration.md) function from the bindings package you've [chosen during installation](installation.md#installation). In this example, we're going to use the more lightweight `mobx-react-lite` package.
+
+```javascript
+import * as React from "react"
+import { render } from "react-dom"
+import { observer } from "mobx-react-lite"
+
+const TodoListView = observer(({ todoList }) => (
+
+))
+
+const store = new TodoList([new Todo("Get Coffee"), new Todo("Write simpler code")])
+render(, document.getElementById("root"))
+```
+
+`observer` converts React components into derivations of the data they render.
+When using MobX there are no smart or dumb components.
+All components render smartly, but are defined in a dumb manner. MobX will simply make sure the components are always re-rendered whenever needed, and never more than that.
+
+So the `onClick` handler in the above example will force the proper `TodoView` component to re-render as it uses the `toggle` action, but will only cause the `TodoListView` component to re-render if the number of unfinished tasks has changed.
+And if you would remove the `Tasks left` line (or put it into a separate component), the `TodoListView` component would no longer re-render when ticking a task.
+
+To learn more about how React works with MobX, check out the [React integration](react-integration.md) section.
+
+#### 3.4. Custom reactions
+
+You will need them rarely, but they can be created using the [`autorun`](reactions.md#autorun),
+[`reaction`](reactions.md#reaction) or [`when`](reactions.md#when) functions to fit your specific situations.
+For example, the following `autorun` prints a log message every time the amount of `unfinishedTodoCount` changes:
+
+```javascript
+// A function that automatically observes the state.
+autorun(() => {
+ console.log("Tasks left: " + todos.unfinishedTodoCount)
+})
+```
+
+Why does a new message get printed every time the `unfinishedTodoCount` is changed? The answer is this rule of thumb:
+
+_MobX reacts to any existing observable property that is read during the execution of a tracked function._
+
+To learn more about how MobX determines which observables need to be reacted to, check out the [Understanding reactivity](understanding-reactivity.md) section.
+
+## Principles
+
+MobX uses a uni-directional data flow where _actions_ change the _state_, which in turn updates all affected _views_.
+
+
+
+
+1. All _derivations_ are updated **automatically** and **atomically** when the _state_ changes. As a result, it is never possible to observe intermediate values.
+
+2. All _derivations_ are updated **synchronously** by default. This means that, for example, _actions_ can safely inspect a computed value directly after altering the _state_.
+
+3. _Computed values_ are updated **lazily**. Any computed value that is not actively in use will not be updated until it is needed for a side effect (I/O).
+ If a view is no longer in use it will be garbage collected automatically.
+
+4. All _computed values_ should be **pure**. They are not supposed to change _state_.
+
+To learn more about the background context, check out [the fundamental principles behind MobX](https://hackernoon.com/the-fundamental-principles-behind-mobx-7a725f71f3e8).
+
+## Try it out!
+
+You can play with the above examples yourself on [CodeSandbox](https://codesandbox.io/s/concepts-principles-il8lt?file=/src/index.js:1161-1252).
+
+## Linting
+
+If you find it hard to adopt the mental model of MobX, configure it to be very strict and warn you at runtime whenever you deviate from these patterns. Check out the [linting MobX](configuration.md#linting-options) section.
diff --git a/docs/understanding-reactivity.md b/docs/understanding-reactivity.md
new file mode 100644
index 0000000000..4bdb0ed036
--- /dev/null
+++ b/docs/understanding-reactivity.md
@@ -0,0 +1,387 @@
+---
+title: Understanding reactivity
+sidebar_label: Understanding reactivity
+hide_title: true
+---
+
+
+
+# Understanding reactivity
+
+MobX usually reacts to exactly the things you expect it to, which means that in 90% of your use cases MobX should "just work".
+However, at some point you will encounter a case where it does not do what you expected.
+At that point it is invaluable to understand how MobX determines what to react to.
+
+> MobX reacts to any _existing_ **observable** _property_ that is read during the execution of a tracked function.
+
+- _"reading"_ is dereferencing an object's property, which can be done through "dotting into" it (eg. `user.name`) or using the bracket notation (eg. `user['name']`, `todos[3]`) or destructuring (eg. `const {name} = user`).
+- _"tracked functions"_ are the expression of `computed`, the _rendering_ of an `observer` React function component, the `render()` method of an `observer` based React class component, and the functions that are passed as the first param to `autorun`, `reaction` and `when`.
+- _"during"_ means that only those observables that are read while the function is executing are tracked. It doesn't matter whether these values are used directly or indirectly by the tracked function. But things that have been 'spawned' from the function won't be tracked (e.g. `setTimeout`, `promise.then`, `await` etc).
+
+In other words, MobX will not react to:
+
+- Values that are obtained from observables, but outside a tracked function
+- Observables that are read in an asynchronously invoked code block
+
+## MobX tracks property access, not values
+
+To elaborate on the above rules with an example, suppose that you have the following observable instance:
+
+```javascript
+class Message {
+ title
+ author
+ likes
+ constructor(title, author, likes) {
+ makeAutoObservable(this)
+ this.title = title
+ this.author = author
+ this.likes = likes
+ }
+
+ updateTitle(title) {
+ this.title = title
+ }
+}
+
+let message = new Message("Foo", { name: "Michel" }, ["Joe", "Sara"])
+```
+
+In memory this looks as follows. The green boxes indicate _observable_ properties. Note that the _values_ themselves are not observable!
+
+
+
+What MobX basically does is recording which _arrows_ you use in your function. After that, it will re-run whenever one of these _arrows_ changes; when they start to refer to something else.
+
+## Examples
+
+Let's show that with a bunch of examples (based on the `message` variable defined above):
+
+#### Correct: dereference inside the tracked function
+
+```javascript
+autorun(() => {
+ console.log(message.title)
+})
+message.updateTitle("Bar")
+```
+
+This will react as expected. The `.title` property was dereferenced by the autorun, and changed afterwards, so this change is detected.
+
+You can verify what MobX will track by calling [`trace()`](analyzing-reactivity.md) inside the tracked function. In the case of the above function it outputs the following:
+
+```javascript
+import { trace } from "mobx"
+
+const disposer = autorun(() => {
+ console.log(message.title)
+ trace()
+})
+// Outputs:
+// [mobx.trace] 'Autorun@2' tracing enabled
+
+message.updateTitle("Hello")
+// Outputs:
+// [mobx.trace] 'Autorun@2' is invalidated due to a change in: 'Message@1.title'
+Hello
+```
+
+It is also possible to get the internal dependency (or observer) tree by using `getDependencyTree`:
+
+```javascript
+import { getDependencyTree } from "mobx"
+
+// Prints the dependency tree of the reaction coupled to the disposer.
+console.log(getDependencyTree(disposer))
+// Outputs:
+// { name: 'Autorun@2', dependencies: [ { name: 'Message@1.title' } ] }
+```
+
+#### Incorrect: changing a non-observable reference
+
+```javascript
+autorun(() => {
+ console.log(message.title)
+})
+message = new Message("Bar", { name: "Martijn" }, ["Felicia", "Marcus"])
+```
+
+This will **not** react. `message` was changed, but `message` is not an observable, just a variable which _refers to_ an observable, but the variable (reference) itself is not observable.
+
+#### Incorrect: dereference outside of a tracked function
+
+```javascript
+let title = message.title
+autorun(() => {
+ console.log(title)
+})
+message.updateMessage("Bar")
+```
+
+This will **not** react. `message.title` was dereferenced outside of `autorun`, and just contains the value of `message.title` at the moment of dereferencing (the string `"Foo"`). `title` is not an observable so `autorun` will never react.
+
+#### Correct: dereference inside the tracked function
+
+```javascript
+autorun(() => {
+ console.log(message.author.name)
+})
+
+runInAction(() => {
+ message.author.name = "Sara"
+})
+runInAction(() => {
+ message.author = { name: "Joe" }
+})
+```
+
+This reacts to both changes. Both `author` and `author.name` are dotted into, allowing MobX to track these references.
+
+Note that we had to use `runInAction` here to be allowed to make changes outside
+of an `action`.
+
+#### Incorrect: store a local reference to an observable object without tracking
+
+```javascript
+const author = message.author
+autorun(() => {
+ console.log(author.name)
+})
+
+runInAction(() => {
+ message.author.name = "Sara"
+})
+runInAction(() => {
+ message.author = { name: "Joe" }
+})
+```
+
+The first change will be picked up, `message.author` and `author` are the same object, and the `.name` property is dereferenced in the autorun.
+However, the second change is **not** picked up, because the `message.author` relation is not tracked by the `autorun`. Autorun is still using the "old" `author`.
+
+#### Common pitfall: console.log
+
+```javascript
+autorun(() => {
+ console.log(message)
+})
+
+// Won't trigger a re-run.
+message.updateTitle("Hello world")
+```
+
+In the above example, the updated message title won't be printed, because it is not used inside the autorun.
+The autorun only depends on `message`, which is not an observable, but a variable. In other words, as far as MobX is concerned, `title` is not used in the `autorun`.
+
+If you use this in a web browser debugging tool, you may be able to find the
+updated value of `title` after all, but this is misleading -- autorun run after all has run once when it was first called. This happens because `console.log` is an asynchronous function and the object is only formatted later in time. This means that if you follow the title in the debugging toolbar, you can find the updated value. But the `autorun` does not track any updates.
+
+The way to make this work is to make sure to always pass immutable data or defensive copies to `console.log`. So the following solutions all react to changes in `message.title`:
+
+```javascript
+autorun(() => {
+ console.log(message.title) // Clearly, the `.title` observable is used.
+})
+
+autorun(() => {
+ console.log(mobx.toJS(message)) // toJS creates a deep clone, and thus will read the message.
+})
+
+autorun(() => {
+ console.log({ ...message }) // Creates a shallow clone, also using `.title` in the process.
+})
+
+autorun(() => {
+ console.log(JSON.stringify(message)) // Also reads the entire structure.
+})
+```
+
+#### Correct: access array properties in tracked function
+
+```javascript
+autorun(() => {
+ console.log(message.likes.length)
+})
+message.likes.push("Jennifer")
+```
+
+This will react as expected. `.length` counts towards a property.
+Note that this will react to _any_ change in the array.
+Arrays are not tracked per index / property (like observable objects and maps), but as a whole.
+
+#### Incorrect: access out-of-bounds indices in tracked function
+
+```javascript
+autorun(() => {
+ console.log(message.likes[0])
+})
+message.likes.push("Jennifer")
+```
+
+This will react with the above sample data because array indexes count as property access. But **only** if the provided `index < length`.
+MobX does not track not-yet-existing array indices.
+So always guard your array index based access with a `.length` check.
+
+#### Correct: access array functions in tracked function
+
+```javascript
+autorun(() => {
+ console.log(message.likes.join(", "))
+})
+message.likes.push("Jennifer")
+```
+
+This will react as expected. All array functions that do not mutate the array are tracked automatically.
+
+---
+
+```javascript
+autorun(() => {
+ console.log(message.likes.join(", "))
+})
+message.likes[2] = "Jennifer"
+```
+
+This will react as expected. All array index assignments are detected, but only if `index <= length`.
+
+#### Incorrect: "use" an observable but without accessing any of its properties
+
+```javascript
+autorun(() => {
+ message.likes
+})
+message.likes.push("Jennifer")
+```
+
+This will **not** react. Simply because the `likes` array itself is not being used by the `autorun`, only the reference to the array.
+So in contrast, `message.likes = ["Jennifer"]` would be picked up; that statement does not modify the array, but the `likes` property itself.
+
+#### Correct: using not yet existing map entries
+
+```javascript
+const twitterUrls = observable.map({
+ Joe: "twitter.com/joey"
+})
+
+autorun(() => {
+ console.log(twitterUrls.get("Sara"))
+})
+
+runInAction(() => {
+ twitterUrls.set("Sara", "twitter.com/horsejs")
+})
+```
+
+This **will** react. Observable maps support observing entries that may not exist.
+Note that this will initially print `undefined`.
+You can check for the existence of an entry first by using `twitterUrls.has("Sara")`.
+So in an environment without Proxy support for dynamically keyed collections always use observable maps. If you do have Proxy support you can use observable maps as well,
+but you also have the option to use plain objects.
+
+#### MobX does not track asynchronously accessed data
+
+```javascript
+function upperCaseAuthorName(author) {
+ const baseName = author.name
+ return baseName.toUpperCase()
+}
+autorun(() => {
+ console.log(upperCaseAuthorName(message.author))
+})
+
+runInAction(() => {
+ message.author.name = "Chesterton"
+})
+```
+
+This will react. Even though `author.name` is not dereferenced by the function passed to `autorun` itself, MobX will still track the dereferencing that happens in `upperCaseAuthorName`, because it happens _during_ the execution of the autorun.
+
+---
+
+```javascript
+autorun(() => {
+ setTimeout(() => console.log(message.likes.join(", ")), 10)
+})
+
+runInAction(() => {
+ message.likes.push("Jennifer")
+})
+```
+
+This will **not** react because during the execution of the `autorun` no observables were accessed, only during the `setTimeout`, which is an asynchronous function.
+
+Check out the [Asynchronous actions](actions.md#asynchronous-actions) section as well.
+
+#### Using non-observable object properties
+
+```javascript
+autorun(() => {
+ console.log(message.author.age)
+})
+
+runInAction(() => {
+ message.author.age = 10
+})
+```
+
+This **will** react if you run React in an environment that supports Proxy.
+Note that this is only done for objects created with `observable` or `observable.object`. New properties on class instances will not be made observable automatically.
+
+_Environments without Proxy support_
+
+This will **not** react. MobX can only track observable properties, and 'age' has not been defined as observable property above.
+
+However, it is possible to use the `get` and `set` methods as exposed by MobX to work around this:
+
+```javascript
+import { get, set } from "mobx"
+
+autorun(() => {
+ console.log(get(message.author, "age"))
+})
+set(message.author, "age", 10)
+```
+
+#### [Without Proxy support] Incorrect: using not yet existing observable object properties
+
+```javascript
+autorun(() => {
+ console.log(message.author.age)
+})
+extendObservable(message.author, {
+ age: 10
+})
+```
+
+This will **not** react. MobX will not react to observable properties that did not exist when tracking started.
+If the two statements are swapped, or if any other observable causes the `autorun` to re-run, the `autorun` will start tracking the `age` as well.
+
+#### [Without Proxy support] Correct: using MobX utilities to read / write to objects
+
+If you are in an environment without proxy support and still want to use observable
+objects as a dynamic collection, you can handle them using the MobX `get` and `set`
+API.
+
+The following will react as well:
+
+```javascript
+import { get, set, observable } from "mobx"
+
+const twitterUrls = observable.object({
+ Joe: "twitter.com/joey"
+})
+
+autorun(() => {
+ console.log(get(twitterUrls, "Sara")) // `get` can track not yet existing properties.
+})
+
+runInAction(() => {
+ set(twitterUrls, { Sara: "twitter.com/horsejs" })
+})
+```
+
+Check out the [Collection utilities API](api.md#collection-utilities-) for more details.
+
+#### TL;DR
+
+> MobX reacts to any _existing_ **observable** _property_ that is read during the execution of a tracked function.
diff --git a/jest.base.config.js b/jest.base.config.js
new file mode 100644
index 0000000000..acd82f6c56
--- /dev/null
+++ b/jest.base.config.js
@@ -0,0 +1,34 @@
+const fs = require("fs")
+const path = require("path")
+
+module.exports = function buildConfig(
+ packageDirectory,
+ pkgConfig,
+ tsConfig = "tsconfig.test.json"
+) {
+ const packageName = require(`${packageDirectory}/package.json`).name
+ const packageTsconfig = path.resolve(packageDirectory, tsConfig)
+ return {
+ preset: "ts-jest/presets/js-with-ts",
+ testEnvironment: "jsdom",
+ globals: {
+ __DEV__: true
+ },
+ transform: {
+ "^.+\\.[jt]sx?$": [
+ "ts-jest",
+ {
+ tsconfig: fs.existsSync(packageTsconfig)
+ ? packageTsconfig
+ : path.resolve(__dirname, tsConfig)
+ }
+ ]
+ },
+ testRegex: "__tests__/.*\\.(j|t)sx?$",
+ coverageDirectory: "/coverage/",
+ coverageReporters: ["lcov", "text"],
+ collectCoverageFrom: ["/src/**/*.{ts,tsx}", "!**/node_modules/**"],
+ displayName: packageName,
+ ...pkgConfig
+ }
+}
diff --git a/jest.config.js b/jest.config.js
new file mode 100644
index 0000000000..8f8d6d15a6
--- /dev/null
+++ b/jest.config.js
@@ -0,0 +1,5 @@
+module.exports = {
+ coverageDirectory: "/coverage/",
+ coverageReporters: ["lcov", "text"],
+ projects: ["/packages/*/jest.config.js", "/packages/*/jest.config-*.js"]
+}
diff --git a/lerna.json b/lerna.json
new file mode 100644
index 0000000000..49ca831eb9
--- /dev/null
+++ b/lerna.json
@@ -0,0 +1,6 @@
+{
+ "packages": ["packages/*"],
+ "version": "independent",
+ "npmClient": "yarn",
+ "useWorkspaces": true
+}
diff --git a/package.json b/package.json
index 0f542dd0c3..20dbf72c8e 100644
--- a/package.json
+++ b/package.json
@@ -1,125 +1,79 @@
{
- "name": "mobx",
- "version": "5.8.0",
- "description": "Simple, scalable state management.",
- "main": "lib/mobx.js",
- "umd:main": "lib/mobx.umd.js",
- "module": "lib/mobx.module.js",
- "browser": {
- "./lib/mobx.js": "./lib/mobx.js",
- "./lib/mobx.module.js": "./lib/mobx.module.js"
- },
- "unpkg": "lib/mobx.umd.min.js",
- "jsnext:main": "lib/mobx.module.js",
- "react-native": "lib/mobx.module.js",
- "typings": "lib/mobx.d.ts",
- "scripts": {
- "test": "jest",
- "watch": "jest --watch",
- "test:mixed-versions": "jest --testRegex mixed-versions",
- "test:all": "yarn small-build && yarn jest -i && yarn test:flow && yarn test:mixed-versions",
- "test:webpack": "node scripts/webpack-regression-tests.js",
- "test:flow": "node_modules/.bin/flow check",
- "test:performance": "npm run small-build && PERSIST=true time node --expose-gc test/perf/index.js",
- "test:travis": "yarn test:all && yarn test:performance && yarn test -i --coverage && yarn test:webpack && yarn size",
- "prettier": "prettier --write --print-width 100 --tab-width 4 --no-semi \"**/*.js\" \"**/*.jsx\" \"**/*.tsx\" \"**/*.ts\"",
- "_prepublish": "npm run small-build",
- "quick-build": "tsc --pretty",
- "small-build": "node scripts/build.js",
- "lint": "tslint -c tslint.json src/*.ts src/types/*.ts src/api/*.ts src/core/*.ts src/utils/*.ts",
- "size": "size-limit --babili 20KB lib/mobx.js",
- "precommit": "lint-staged",
- "publish-script": "node scripts/publish.js"
- },
- "repository": {
- "type": "git",
- "url": "https://github.com/mobxjs/mobx.git"
- },
- "author": "Michel Weststrate",
- "license": "MIT",
- "bugs": {
- "url": "https://github.com/mobxjs/mobx/issues"
- },
- "files": [
- "lib",
- "LICENSE"
- ],
- "homepage": "https://mobx.js.org/",
- "devDependencies": {
- "@babel/core": "^7.1.0",
- "@babel/plugin-proposal-class-properties": "^7.1.0",
- "@babel/plugin-proposal-decorators": "^7.1.0",
- "@babel/preset-env": "^7.1.0",
- "@types/jest": "^21.1.9",
- "@types/node": "^7.0.22",
- "babel-core": "^7.0.0-bridge.0",
- "babel-jest": "^23.6.0",
- "browserify": "^12.0.1",
- "chalk": "^1.1.3",
- "coveralls": "^2.11.4",
- "envify": "^4.1.0",
- "flow-bin": "^0.59.0",
- "fs-extra": "^3.0.1",
- "husky": "^0.14.3",
- "iterall": "^1.0.2",
- "jest": "^23.6.0",
- "lint-staged": "^3.6.1",
- "lodash.intersection": "^3.2.0",
- "ncp": "^2.0.0",
- "prettier": "^1.4.4",
- "regenerator-runtime": "^0.11.1",
- "rollup": "^0.41.6",
- "rollup-plugin-filesize": "^1.3.2",
- "rollup-plugin-node-resolve": "^3.0.0",
- "serializr": "^1.3.0",
- "shelljs": "^0.8.2",
- "size-limit": "^0.2.0",
- "tape": "^4.2.2",
- "ts-jest": "^22.0.0",
- "tslib": "^1.7.1",
- "typescript": "^3.2.1",
- "uglify-es": "^3.3.9"
- },
- "dependencies": {},
- "keywords": [
- "mobx",
- "mobservable",
- "observable",
- "react-component",
- "react",
- "reactjs",
- "reactive",
- "model",
- "frp",
- "functional-reactive-programming",
- "state management",
- "data flow"
- ],
- "lint-staged": {
- "*.{ts,tsx,js,jsx}": [
- "prettier --write --print-width 100 --tab-width 4 --no-semi",
- "git add"
- ]
- },
- "jest": {
- "transform": {
- "^.+\\.tsx?$": "ts-jest",
- "^.+\\.jsx?$": "babel-jest"
- },
- "testRegex": "test/base/.*\\.(t|j)sx?$",
- "moduleFileExtensions": [
- "ts",
- "tsx",
- "js",
- "jsx",
- "json"
- ],
- "testPathIgnorePatterns": [
- "/node_modules/",
- "/\\./"
+ "name": "mobx-root",
+ "private": true,
+ "workspaces": [
+ "packages/*"
],
- "watchPathIgnorePatterns": [
- "/node_modules/"
- ]
- }
-}
\ No newline at end of file
+ "resolutions": {
+ "jest": "^30.3.0",
+ "typescript": "^5.9.2",
+ "recast": "^0.23.1"
+ },
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/mobxjs/mobx.git"
+ },
+ "scripts": {
+ "test": "jest",
+ "coverage": "jest --coverage",
+ "lint": "eslint packages/*/src/**/* --ext .js,.ts,.tsx",
+ "prettier": "prettier --write **/*.{js,ts,md}",
+ "release": "yarn lerna run prepublishOnly && yarn changeset publish",
+ "mobx": "yarn workspace mobx",
+ "mobx-react": "yarn workspace mobx-react",
+ "mobx-react-lite": "yarn workspace mobx-react-lite",
+ "mobx-undecorate": "yarn workspace mobx-undecorate",
+ "eslint-plugin-mobx": "yarn workspace eslint-plugin-mobx",
+ "docs:build": "yarn --cwd website build",
+ "docs:start": "yarn --cwd website start",
+ "docs:publish": "yarn --cwd website publish-gh-pages",
+ "prepare": "yarn dedup && yarn --cwd website install",
+ "dedup": "npx yarn-deduplicate --strategy fewer yarn.lock"
+ },
+ "devDependencies": {
+ "@changesets/changelog-github": "^0.2.7",
+ "@changesets/cli": "^2.11.0",
+ "@testing-library/dom": "^10.4.0",
+ "@testing-library/jest-dom": "^5.16.4",
+ "@testing-library/react": "^16.1.0",
+ "@types/jest": "^30.0.0",
+ "@types/node": "18",
+ "@types/prop-types": "^15.5.2",
+ "@types/react": "^18.0.0",
+ "@types/react-dom": "^18.0.0",
+ "@typescript-eslint/eslint-plugin": "^5.0.0",
+ "@typescript-eslint/parser": "^5.0.0",
+ "coveralls": "^3.1.0",
+ "eslint": "^6.8.0",
+ "execa": "^4.1.0",
+ "fs-extra": "9.0.1",
+ "husky": "^4.2.5",
+ "import-size": "^1.0.2",
+ "iterall": "^1.3.0",
+ "jest": "^30.3.0",
+ "jest-environment-jsdom": "^30.3.0",
+ "jest-mock-console": "^1.0.1",
+ "lerna": "^3.22.1",
+ "lint-staged": "^10.1.7",
+ "lodash": "^4.17.4",
+ "minimist": "^1.2.5",
+ "mkdirp": "1.0.4",
+ "prettier": "^2.8.4",
+ "pretty-quick": "3.1.0",
+ "prop-types": "15.6.2",
+ "react": "^19.0.0",
+ "react-dom": "^19.0.0",
+ "react-test-renderer": "^18.0.0",
+ "serializr": "^2.0.3",
+ "tape": "^5.0.1",
+ "ts-jest": "^29.4.6",
+ "tsdx": "^0.14.1",
+ "typescript": "^5.9.2"
+ },
+ "husky": {
+ "hooks": {
+ "pre-commit": "pretty-quick --staged"
+ }
+ },
+ "packageManager": "yarn@1.22.21+sha1.1959a18351b811cdeedbd484a8f86c3cc3bbaf72"
+}
diff --git a/packages/eslint-plugin-mobx/.babelrc.js b/packages/eslint-plugin-mobx/.babelrc.js
new file mode 100644
index 0000000000..818f677552
--- /dev/null
+++ b/packages/eslint-plugin-mobx/.babelrc.js
@@ -0,0 +1,8 @@
+module.exports = {
+ "presets": [
+ ["@babel/preset-env"],
+ ],
+ "plugins": [
+ //"@babel/plugin-transform-runtime"
+ ]
+};
\ No newline at end of file
diff --git a/packages/eslint-plugin-mobx/CHANGELOG.md b/packages/eslint-plugin-mobx/CHANGELOG.md
new file mode 100644
index 0000000000..26cca38976
--- /dev/null
+++ b/packages/eslint-plugin-mobx/CHANGELOG.md
@@ -0,0 +1,76 @@
+# eslint-plugin-mobx
+
+## 0.0.14
+
+### Patch Changes
+
+- [`f65bc0700461846b05294f857beb211f45eead6f`](https://github.com/mobxjs/mobx/commit/f65bc0700461846b05294f857beb211f45eead6f) [#4613](https://github.com/mobxjs/mobx/pull/4613) Thanks [@roli-lpci](https://github.com/roli-lpci)! - Replace deprecated context.getSourceCode() with context.sourceCode for ESLint 10 compatibility
+
+## 0.0.13
+
+### Patch Changes
+
+- [`88aa10828eabdf9edf2d0e523e0388c854c79dea`](https://github.com/mobxjs/mobx/commit/88aa10828eabdf9edf2d0e523e0388c854c79dea) [#3947](https://github.com/mobxjs/mobx/pull/3947) Thanks [@dartess](https://github.com/dartess)! - fix config name for recommended flat config
+
+## 0.0.12
+
+### Patch Changes
+
+- [`218ebde877712775054e027cfda812210d2aa7d6`](https://github.com/mobxjs/mobx/commit/218ebde877712775054e027cfda812210d2aa7d6) [#3942](https://github.com/mobxjs/mobx/pull/3942) Thanks [@dartess](https://github.com/dartess)! - add eslint@9 support and flat config
+
+## 0.0.11
+
+### Patch Changes
+
+- [`638533e592f4fda7663fa351447380f9d0917d14`](https://github.com/mobxjs/mobx/commit/638533e592f4fda7663fa351447380f9d0917d14) [#3909](https://github.com/mobxjs/mobx/pull/3909) Thanks [@urugator](https://github.com/urugator)! - mobx/missing-observer rule false positive with forwardRef #3908
+
+## 0.0.10
+
+### Patch Changes
+
+- [`44a5fe07`](https://github.com/mobxjs/mobx/commit/44a5fe07fb95c2ba24d8df19f18b57ee92abb1a9) [#3881](https://github.com/mobxjs/mobx/pull/3881) Thanks [@kade-robertson](https://github.com/kade-robertson)! - Adds an option for the `mobx/exhaustive-make-observable` eslint rule to configure whether fields are annotated with `true` or `false` with the autofixer.
+
+ This option defaults to `true` if not present or an invalid value is received to maintain existing behavior.
+
+## 0.0.9
+
+### Patch Changes
+
+- [`e63c2df0`](https://github.com/mobxjs/mobx/commit/e63c2df0ef166868675bce21892cd686a46db953) [#3443](https://github.com/mobxjs/mobx/pull/3443) Thanks [@urugator](https://github.com/urugator)! - changeset for PR #3423: deprecate no-anonymous-observer
+
+## 0.0.8
+
+### Patch Changes
+
+- [`aafda613`](https://github.com/mobxjs/mobx/commit/aafda6136afd107c6cbdd73d9bab57e45a2eb9f5) [#3256](https://github.com/mobxjs/mobx/pull/3256) Thanks [@urugator](https://github.com/urugator)! - fix `no-anonymous-observer` autofix for arrow functions without BlockStatement body
+
+## 0.0.7
+
+### Patch Changes
+
+- [`0aaf1831`](https://github.com/mobxjs/mobx/commit/0aaf183131f3eadd40c05ccc94139282bd8d7d56) [#3231](https://github.com/mobxjs/mobx/pull/3231) Thanks [@ahoisl](https://github.com/ahoisl)! - fix(lint): fix 'missing-make-observable' rule with constructor overloads
+
+## 0.0.6
+
+### Patch Changes
+
+- [`4b1337ec`](https://github.com/mobxjs/mobx/commit/4b1337ecd64c7bfc904a04063bd1b07e62e392f1) [#3228](https://github.com/mobxjs/mobx/pull/3228) Thanks [@ahoisl](https://github.com/ahoisl)! - fix name for missing-observer rule in recommended
+
+## 0.0.5
+
+### Patch Changes
+
+- [`021f34ec`](https://github.com/mobxjs/mobx/commit/021f34ec81daed9e5b5ed8425b2f3e0fa85dfe5b) [#3219](https://github.com/mobxjs/mobx/pull/3219) Thanks [@urugator](https://github.com/urugator)! - Add [`mobx/missing-observer`](https://github.com/mobxjs/mobx/tree/main/packages/eslint-plugin-mobx#mobxmissing-observer),
+ [`mobx/no-anonymous-observer`](https://github.com/mobxjs/mobx/tree/main/packages/eslint-plugin-mobx#mobxno-anonymous-observer) rules,
+
+## 0.0.4
+
+### Patch Changes
+
+- [`5b6f3001`](https://github.com/mobxjs/mobx/commit/5b6f30017939a2082f7d767a857e0189210a91a7) [#3204](https://github.com/mobxjs/mobx/pull/3204) Thanks [@urugator](https://github.com/urugator)! - changed build process
+
+## 0.0.3
+
+### Patch Changes
+
+- [`cd6a6a68`](https://github.com/mobxjs/mobx/commit/cd6a6a68245f082bdc35a3109214a5449ef9818d) [#3200](https://github.com/mobxjs/mobx/pull/3200) Thanks [@urugator](https://github.com/urugator)! - fix package.json
diff --git a/packages/eslint-plugin-mobx/LICENSE b/packages/eslint-plugin-mobx/LICENSE
new file mode 100644
index 0000000000..b58becae8c
--- /dev/null
+++ b/packages/eslint-plugin-mobx/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2015 Michel Weststrate
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/packages/eslint-plugin-mobx/README.md b/packages/eslint-plugin-mobx/README.md
new file mode 100644
index 0000000000..03aec4c4a3
--- /dev/null
+++ b/packages/eslint-plugin-mobx/README.md
@@ -0,0 +1,146 @@
+# eslint-plugin-mobx
+
+Mobx specific linting rules for `eslint`.
+
+## Installation
+
+```
+npm install --save-dev eslint @typescript-eslint/parser eslint-plugin-mobx
+```
+
+## Configuration
+
+### Legacy Config
+
+```javascript
+// .eslintrc.js
+module.exports = {
+ parser: "@typescript-eslint/parser",
+ // Include "mobx" in plugins array:
+ plugins: ["mobx"],
+ // Either extend our recommended configuration:
+ extends: "plugin:mobx/recommended",
+ // ...or specify and customize individual rules:
+ rules: {
+ // these values are the same as recommended
+ "mobx/exhaustive-make-observable": "warn",
+ "mobx/unconditional-make-observable": "error",
+ "mobx/missing-make-observable": "error",
+ "mobx/missing-observer": "warn"
+ }
+}
+```
+
+### Flat Config
+
+```javascript
+// eslint.config.js
+import pluginMobx from "eslint-plugin-mobx"
+
+export default [
+ // ...
+
+ // Either extend our recommended configuration:
+ pluginMobx.flatConfigs.recommended,
+
+ // ...or specify and customize individual rules:
+ {
+ plugins: { mobx: pluginMobx },
+ rules: {
+ // these values are the same as recommended
+ "mobx/exhaustive-make-observable": "warn",
+ "mobx/unconditional-make-observable": "error",
+ "mobx/missing-make-observable": "error",
+ "mobx/missing-observer": "warn"
+ }
+ }
+]
+```
+
+## Rules
+
+### mobx/exhaustive-make-observable
+
+Makes sure that `makeObservable` annotates all fields defined on class or object literal.
+To exclude a field, annotate it using `field: false`.
+Does not support fields introduced by constructor (`this.foo = 5`).
+Does not warn about annotated non-existing fields (there is a runtime check, but the autofix removing the field could be handy...).
+**Autofix** adds `field: true` for each missing field by default. You can change this behaviour by specifying options in your eslint config:
+
+```json
+{
+ "rules": {
+ "mobx/exhaustive-make-observable": ["error", { "autofixAnnotation": false }]
+ }
+}
+```
+
+This is a boolean value that controls if the field is annotated with `true` or `false`.
+If you are migrating an existing project using `makeObservable` and do not want this rule to override
+your current usage (even if it may be wrong), you should run the autofix with the annotation set to `false` to maintain existing behaviour: `eslint --no-eslintrc --fix --rule='mobx/exhaustive-make-observable: [2, { "autofixAnnotation": false }]' .`
+
+### mobx/missing-make-observable
+
+_When using decorators (eg `@observable foo = 5`)_, makes sure that `makeObservable(this)` is called in a constructor.
+**Autofix** creates a constructor if necessary and adds `makeObservable(this)` at it's end.
+
+### mobx/unconditional-make-observable
+
+Makes sure the `make(Auto)Observable(this)` is called unconditionally inside a constructor.
+
+### mobx/missing-observer
+
+Makes sure every React component is wrapped with `observer`. A React component is considered to be any _class_ extending from `Component` or `React.Component` and any _function_ which name has the first letter capitalized (for anonymous functions the name is inferred from variable). These are all considered components:
+
+```javascript
+class Cmp extends React.Component { }
+class Cmp extends Component { }
+const Cmp = class extends React.Component { }
+const Cmp = class extends Component { }
+class extends Component { }
+class extends React.Component { }
+
+function Named() { }
+const foo = function Named() { }
+const Anonym = function () { };
+const Arrow = () => { };
+```
+
+**Autofix** wraps the component with `observer` and if necessary declares a constant of the same name: `const Name = observer(function Name() {})`.
+It's a bit opinionated and can lead to a lot of false positives depending on your conventions. You will probably want to combine this rule with `overrides` option, eg:
+
+```javascript
+// .eslintrc.js
+"overrides": [
+ {
+ "files": ["*.jsx"],
+ "rules": {
+ "mobx/missing-observer": "error"
+ }
+ }
+]
+```
+
+### mobx/no-anonymous-observer (deprecated)
+
+_Deprecated in favor of [react/display-name](https://github.com/jsx-eslint/eslint-plugin-react/blob/master/docs/rules/display-name.md) + [componentWrapperFunctions](https://github.com/jsx-eslint/eslint-plugin-react). Example of **.eslintrc**:_
+
+```
+{
+ "rules": {
+ "react/display-name": "warn"
+ },
+ "settings": {
+ "componentWrapperFunctions": [
+ "observer"
+ ]
+ }
+}
+```
+
+---
+
+Forbids anonymous functions or classes as `observer` components.
+Improves debugging experience and [avoids problem with inability to customize `displayName`](https://github.com/mobxjs/mobx/issues/2721).
+Plays nice with `eslint-plugin-react-hooks` and `mobx/missing-observer` as both of these don't recognize anonymous function as component.
+**Autofix** infers the name from variable if possible.
diff --git a/packages/eslint-plugin-mobx/__tests__/exhaustive-make-observable.js b/packages/eslint-plugin-mobx/__tests__/exhaustive-make-observable.js
new file mode 100644
index 0000000000..ab2433ddc1
--- /dev/null
+++ b/packages/eslint-plugin-mobx/__tests__/exhaustive-make-observable.js
@@ -0,0 +1,251 @@
+import { getRuleTester } from "./utils/get-rule-tester";
+
+import rule from "../src/exhaustive-make-observable.js";
+
+const tester = getRuleTester();
+
+const decoratedFields = [
+ '@observable o = 5',
+ '@observable.ref or = []',
+ '@observable.shallow os = []',
+ '@observable.deep od = {}',
+ '@computed get c() {}',
+ '@computed.struct get cs() {}',
+ '@computed({ equals }) get co() {}',
+ '@action a() {}',
+ '@action.bound ab() {}',
+ '@flow *f() {}',
+ '@flow.bound *fb() {}',
+];
+
+const valid1 = decoratedFields.map(field => `
+class C {
+ ${field}
+
+ constructor() {
+ makeObservable(this)
+ }
+}
+`).map(code => ({ code }))
+
+const valid2 = {
+ code: `
+class C {
+ o = 5
+ get c() {}
+ a() {}
+ *f() {}
+
+ constructor() {
+ makeObservable(this, {
+ o: true,
+ a: true,
+ c: true,
+ f: true,
+ })
+ }
+}
+`
+}
+
+const valid3 = {
+ code: `
+class C {
+ o = 5
+ get c() {}
+ a() {}
+ *f() {}
+
+ constructor() {
+ makeObservable({})
+ }
+}
+`
+}
+
+const invalid1 = {
+ code: `
+class C {
+ o = 5
+ get c() {}
+ a() {}
+ *f() {}
+
+ constructor() {
+ makeObservable(this, {
+ a: true,
+ c: true,
+ f: true,
+ })
+ }
+}
+`,
+ errors: [{ messageId: 'missingAnnotation' }],
+ output: `
+class C {
+ o = 5
+ get c() {}
+ a() {}
+ *f() {}
+
+ constructor() {
+ makeObservable(this, { o: true,
+ a: true,
+ c: true,
+ f: true,
+ })
+ }
+}
+`
+}
+
+const invalid2 = {
+ code: `
+class C {
+ o = 5
+ get c() {}
+ a() {}
+ *f() {}
+
+ constructor() {
+ makeObservable(this, {
+ o: true,
+ c: true,
+ f: true,
+ })
+ }
+}
+`,
+ errors: [{ messageId: 'missingAnnotation' }],
+ output: `
+class C {
+ o = 5
+ get c() {}
+ a() {}
+ *f() {}
+
+ constructor() {
+ makeObservable(this, { a: true,
+ o: true,
+ c: true,
+ f: true,
+ })
+ }
+}
+`
+}
+
+const invalid3 = {
+ code: `
+class C {
+ o = 5
+ get c() {}
+ a() {}
+ *f() {}
+
+ constructor() {
+ makeObservable(this, {
+ o: true,
+ a: true,
+ f: true,
+ })
+ }
+}
+`,
+ errors: [{ messageId: 'missingAnnotation' }],
+ output: `
+class C {
+ o = 5
+ get c() {}
+ a() {}
+ *f() {}
+
+ constructor() {
+ makeObservable(this, { c: true,
+ o: true,
+ a: true,
+ f: true,
+ })
+ }
+}
+`
+}
+
+const invalid4 = {
+ code: `
+class C {
+ o = 5
+ get c() {}
+ a() {}
+ *f() {}
+
+ constructor() {
+ makeObservable(this, {
+ o: true,
+ a: true,
+ c: true,
+ })
+ }
+}
+`,
+ errors: [{ messageId: 'missingAnnotation' }],
+ output: `
+class C {
+ o = 5
+ get c() {}
+ a() {}
+ *f() {}
+
+ constructor() {
+ makeObservable(this, { f: true,
+ o: true,
+ a: true,
+ c: true,
+ })
+ }
+}
+`
+}
+
+const invalid5 = {
+ code: `
+class C {
+ o = 5
+ get c() {}
+ a() {}
+ *f() {}
+
+ constructor() {
+ makeObservable(this)
+ }
+}
+`,
+ errors: [{ messageId: 'missingAnnotation' }],
+ output: `
+class C {
+ o = 5
+ get c() {}
+ a() {}
+ *f() {}
+
+ constructor() {
+ makeObservable(this, { o: true, c: true, a: true, f: true, })
+ }
+}
+`
+}
+
+tester.run("exhaustive-make-observable", rule, {
+ valid: [
+ ...valid1,
+ valid2,
+ valid3,
+ ],
+ invalid: [
+ invalid1,
+ invalid2,
+ invalid3,
+ invalid4,
+ invalid5,
+ ],
+});
\ No newline at end of file
diff --git a/packages/eslint-plugin-mobx/__tests__/missing-make-observable.js b/packages/eslint-plugin-mobx/__tests__/missing-make-observable.js
new file mode 100644
index 0000000000..161ef8502d
--- /dev/null
+++ b/packages/eslint-plugin-mobx/__tests__/missing-make-observable.js
@@ -0,0 +1,173 @@
+import { getRuleTester } from "./utils/get-rule-tester";
+
+import rule from "../src/missing-make-observable.js";
+
+const tester = getRuleTester();
+
+const fields = [
+ '@observable o = 5',
+ '@observable.ref or = []',
+ '@observable.shallow os = []',
+ '@observable.deep od = {}',
+ '@computed get c() {}',
+ '@computed.struct get cs() {}',
+ '@computed({ equals }) get co() {}',
+ '@action a() {}',
+ '@action.bound ab() {}',
+ '@flow *f() {}',
+ '@flow.bound *fb() {}',
+];
+
+const valid1 = fields.map(field => `
+class C {
+ ${field}
+
+ constructor() {
+ makeObservable(this)
+ }
+}
+`).map(code => ({ code }))
+
+const valid2 = {
+ code: `
+class C {
+ o = 5;
+ get c() {};
+ a() {};
+ *f() {};
+
+ constructor() {
+ makeObservable(this, {})
+ }
+}
+`
+}
+
+const valid3 = fields.map(field => `
+class C {
+ ${field}
+
+ constructor() {
+ makeObservable(this, null, { name: 'foo' })
+ }
+}
+`).map(code => ({ code }))
+
+const valid4 = fields.map(field => `
+class C {
+ ${field}
+
+ constructor(aString: string);
+ constructor(aNum: number);
+ constructor(stringOrNum: string | number) {
+ makeObservable(this, null, { name: 'foo' })
+ }
+}
+`).map(code => ({ code }))
+
+const invalid1 = fields.map(field => ({
+ code: `
+class C {
+ ${field}
+}
+`,
+ errors: [
+ { messageId: 'missingMakeObservable' },
+ ],
+ output: `
+class C {
+constructor() { makeObservable(this); }
+ ${field}
+}
+`
+}))
+
+const invalid2 = fields.map(field => ({
+ code: `
+class C {
+ ${field}
+ constructor() {}
+}
+`,
+ errors: [
+ { messageId: 'missingMakeObservable' },
+ ],
+ output: `
+class C {
+ ${field}
+ constructor() {;makeObservable(this);}
+}
+`,
+}))
+
+const invalid3 = fields.map(field => ({
+ code: `
+class C {
+ ${field}
+ constructor() {
+ makeObservable({ a: 5 });
+ }
+}
+`,
+ errors: [
+ { messageId: 'missingMakeObservable' },
+ ],
+ output: `
+class C {
+ ${field}
+ constructor() {
+ makeObservable({ a: 5 });
+ ;makeObservable(this);}
+}
+`,
+}))
+
+const invalid4 = fields.map(field => ({
+ code: `
+class C {
+ ${field}
+ constructor()
+}
+`,
+ errors: [
+ { messageId: 'missingMakeObservable' },
+ ],
+ output: `
+class C {
+ ${field}
+ constructor() { makeObservable(this); }
+}
+`,
+}))
+
+
+const invalid5 = fields.map(field => ({
+ code: `
+class C {
+ ${field}
+ constructor() {
+ makeObservable(this, { o: observable.ref });
+ }
+}
+`,
+ errors: [
+ { messageId: 'secondArgMustBeNullish' },
+ ],
+}))
+
+
+tester.run("missing-make-observable", rule, {
+ valid: [
+ ...valid1,
+ valid2,
+ ...valid3,
+ ...valid4,
+ ],
+ invalid: [
+ ...invalid1,
+ ...invalid2,
+ ...invalid3,
+ ...invalid4,
+ ...invalid5,
+ ],
+});
\ No newline at end of file
diff --git a/packages/eslint-plugin-mobx/__tests__/missing-observer.js b/packages/eslint-plugin-mobx/__tests__/missing-observer.js
new file mode 100644
index 0000000000..aa27542216
--- /dev/null
+++ b/packages/eslint-plugin-mobx/__tests__/missing-observer.js
@@ -0,0 +1,54 @@
+import { getRuleTester } from "./utils/get-rule-tester";
+
+import rule from "../src/missing-observer.js"
+
+const tester = getRuleTester();
+
+const valids = [
+ "observer(function Named() { });",
+ "const foo = observer(function Named() { })",
+ "const Anonym = observer(function () { });",
+ "const Arrow = observer(() => { });",
+ "function notCmp() { }",
+ "const notCmp = function notCmp() { }",
+ "const notCmp = function () { }",
+ "const notCmp = () => { }",
+ "class NotCmp { }",
+ "class NotCmp extends Foo { }",
+ "class NotCmp extends React.Foo { }",
+ "const Cmp = observer(class Cmp extends React.Component { })",
+ "const Cmp = observer(class Cmp extends Component { })",
+ "const Cmp = observer(class extends React.Component { })",
+ "const Cmp = observer(class extends Component { })"
+]
+
+const invalids = [
+ ["function Named() { }", "const Named = observer(function Named() { })"],
+ ["const foo = function Named() { }", "const foo = observer(function Named() { })"],
+ ["const Anonym = function () { };", "const Anonym = observer(function () { });"],
+ ["const Arrow = () => { };", "const Arrow = observer(() => { });"],
+ [
+ "class Cmp extends React.Component { }",
+ "const Cmp = observer(class Cmp extends React.Component { })"
+ ],
+ ["class Cmp extends Component { }", "const Cmp = observer(class Cmp extends Component { })"],
+ [
+ "const Cmp = class extends React.Component { }",
+ "const Cmp = observer(class extends React.Component { })"
+ ],
+ [
+ "const Cmp = class extends Component { }",
+ "const Cmp = observer(class extends Component { })"
+ ],
+ ["class extends Component { }", "observer(class extends Component { })"],
+ ["class extends React.Component { }", "observer(class extends React.Component { })"]
+]
+
+tester.run("missing-observer", rule, {
+ valid: valids.map(code => ({ code })),
+ invalid: invalids.map(([code, output]) => ({
+ code,
+ output,
+ errors: [{ messageId: "missingObserver" }]
+ }))
+})
diff --git a/packages/eslint-plugin-mobx/__tests__/no-anonymous-observer.js b/packages/eslint-plugin-mobx/__tests__/no-anonymous-observer.js
new file mode 100644
index 0000000000..8d171fcf4b
--- /dev/null
+++ b/packages/eslint-plugin-mobx/__tests__/no-anonymous-observer.js
@@ -0,0 +1,39 @@
+import { getRuleTester } from "./utils/get-rule-tester";
+
+import rule from "../src/no-anonymous-observer.js"
+
+const tester = getRuleTester();
+
+const valids = ["observer(function Name() {})", "observer(class Name {})"]
+
+const invalidsNotFixed = ["observer(() => {})", "observer(function () {})", "observer(class {})"]
+
+const invalidsFixed = [
+ ["const Cmp = observer(() => {})", "const Cmp = observer(function Cmp() {})"],
+ ['const Cmp = observer(() => "")', 'const Cmp = observer(function Cmp() { return "" })'],
+ [
+ "const Cmp = observer(() => expr())",
+ "const Cmp = observer(function Cmp() { return expr() })"
+ ],
+ [
+ "const Cmp = observer(() => literal)",
+ "const Cmp = observer(function Cmp() { return literal })"
+ ],
+ ["const Cmp = observer(function () {})", "const Cmp = observer(function Cmp () {})"],
+ ["const Cmp = observer(class {})", "const Cmp = observer(class Cmp {})"]
+]
+
+tester.run("no-anonymous-observer", rule, {
+ valid: valids.map(code => ({ code })),
+ invalid: [
+ ...invalidsNotFixed.map(code => ({
+ code,
+ errors: [{ messageId: "observerComponentMustHaveName" }]
+ })),
+ ...invalidsFixed.map(([code, output]) => ({
+ code,
+ output,
+ errors: [{ messageId: "observerComponentMustHaveName" }]
+ }))
+ ]
+})
diff --git a/packages/eslint-plugin-mobx/__tests__/unconditional-make-observable.js b/packages/eslint-plugin-mobx/__tests__/unconditional-make-observable.js
new file mode 100644
index 0000000000..d7887a268f
--- /dev/null
+++ b/packages/eslint-plugin-mobx/__tests__/unconditional-make-observable.js
@@ -0,0 +1,65 @@
+import { getRuleTester } from "./utils/get-rule-tester";
+
+import rule from "../src/unconditional-make-observable.js";
+
+const tester = getRuleTester();
+
+const valid1 = {
+ code: `
+class C {
+ constructor() {
+ makeObservable()
+ makeObservable({})
+ makeObservable(this)
+ function f() {
+ makeObservable(this, {});
+ }
+ const ff = function () {
+ makeObservable(this, {});
+ }
+ }
+}
+`
+}
+
+const invalid1 = {
+ code: `
+class C {
+ constructor() {
+ if (true) {
+ makeObservable(this, {});
+ makeAutoObservable(this, {});
+ }
+ for (let i = 0; i < 1; i++) {
+ makeObservable(this, {});
+ makeAutoObservable(this, {});
+ }
+ while (Math.random() > 1) {
+ makeObservable(this, {});
+ makeAutoObservable(this, {});
+ }
+ const a = () => {
+ makeObservable(this, {});
+ }
+ }
+}
+`,
+ errors: [
+ { messageId: 'mustCallUnconditionally' },
+ { messageId: 'mustCallUnconditionally' },
+ { messageId: 'mustCallUnconditionally' },
+ { messageId: 'mustCallUnconditionally' },
+ { messageId: 'mustCallUnconditionally' },
+ { messageId: 'mustCallUnconditionally' },
+ { messageId: 'mustCallUnconditionally' },
+ ],
+}
+
+tester.run("unconditional-make-observable", rule, {
+ valid: [
+ valid1,
+ ],
+ invalid: [
+ invalid1
+ ],
+});
\ No newline at end of file
diff --git a/packages/eslint-plugin-mobx/__tests__/utils/get-rule-tester.js b/packages/eslint-plugin-mobx/__tests__/utils/get-rule-tester.js
new file mode 100644
index 0000000000..ed44d0f62a
--- /dev/null
+++ b/packages/eslint-plugin-mobx/__tests__/utils/get-rule-tester.js
@@ -0,0 +1,29 @@
+const version = global.ESLINT_V;
+
+const { RuleTester } = require(`eslint-${version}`);
+const typescriptEslintParser = require("@typescript-eslint/parser");
+
+function getRuleTesterConfig() {
+ switch (version) {
+ case 7:
+ return {
+ parser: require.resolve("@typescript-eslint/parser"),
+ parserOptions: {},
+ };
+ case 9:
+ return {
+ languageOptions: {
+ parser: typescriptEslintParser,
+ parserOptions: {},
+ },
+ };
+ default:
+ throw new Error(`Unknown or unspecified ESLINT_V (${String(version)})`);
+ }
+}
+
+function getRuleTester() {
+ return new RuleTester(getRuleTesterConfig());
+}
+
+export { getRuleTester }
\ No newline at end of file
diff --git a/packages/eslint-plugin-mobx/jest.config-eslint-7.js b/packages/eslint-plugin-mobx/jest.config-eslint-7.js
new file mode 100644
index 0000000000..5adb809c02
--- /dev/null
+++ b/packages/eslint-plugin-mobx/jest.config-eslint-7.js
@@ -0,0 +1,10 @@
+const buildConfig = require("../../jest.base.config")
+
+module.exports = buildConfig(__dirname, {
+ displayName: 'eslint-plugin-mobx with eslint@7',
+ setupFilesAfterEnv: ["/jest.setup.js"],
+ testRegex: "__tests__/[^/]+\\.(t|j)sx?$",
+ globals: {
+ ESLINT_V: 7
+ }
+})
diff --git a/packages/eslint-plugin-mobx/jest.config-eslint-9.js b/packages/eslint-plugin-mobx/jest.config-eslint-9.js
new file mode 100644
index 0000000000..85d5fabf65
--- /dev/null
+++ b/packages/eslint-plugin-mobx/jest.config-eslint-9.js
@@ -0,0 +1,10 @@
+const buildConfig = require("../../jest.base.config")
+
+module.exports = buildConfig(__dirname, {
+ displayName: 'eslint-plugin-mobx with eslint@9',
+ setupFilesAfterEnv: ["/jest.setup.js"],
+ testRegex: "__tests__/[^/]+\\.(t|j)sx?$",
+ globals: {
+ ESLINT_V: 9
+ }
+})
diff --git a/packages/eslint-plugin-mobx/jest.setup.js b/packages/eslint-plugin-mobx/jest.setup.js
new file mode 100644
index 0000000000..a570cedd11
--- /dev/null
+++ b/packages/eslint-plugin-mobx/jest.setup.js
@@ -0,0 +1,4 @@
+/** @see https://github.com/jsdom/jsdom/issues/3363 */
+global.structuredClone = val => {
+ return JSON.parse(JSON.stringify(val))
+}
diff --git a/packages/eslint-plugin-mobx/package-lock.json b/packages/eslint-plugin-mobx/package-lock.json
new file mode 100644
index 0000000000..c20df8d186
--- /dev/null
+++ b/packages/eslint-plugin-mobx/package-lock.json
@@ -0,0 +1,2261 @@
+{
+ "name": "eslint-plugin-mobx",
+ "version": "0.0.3",
+ "lockfileVersion": 2,
+ "requires": true,
+ "packages": {
+ "": {
+ "version": "0.0.3",
+ "license": "MIT",
+ "devDependencies": {
+ "@rollup/plugin-commonjs": "^21.0.1",
+ "@typescript-eslint/eslint-plugin": "^4.29.3",
+ "@typescript-eslint/parser": "^4.0.0",
+ "eslint": "^7.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/mobx"
+ },
+ "peerDependencies": {
+ "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0"
+ }
+ },
+ "node_modules/@rollup/plugin-commonjs": {
+ "version": "21.0.1",
+ "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-21.0.1.tgz",
+ "integrity": "sha512-EA+g22lbNJ8p5kuZJUYyhhDK7WgJckW5g4pNN7n4mAFUM96VuwUnNT3xr2Db2iCZPI1pJPbGyfT5mS9T1dHfMg==",
+ "dev": true,
+ "dependencies": {
+ "@rollup/pluginutils": "^3.1.0",
+ "commondir": "^1.0.1",
+ "estree-walker": "^2.0.1",
+ "glob": "^7.1.6",
+ "is-reference": "^1.2.1",
+ "magic-string": "^0.25.7",
+ "resolve": "^1.17.0"
+ },
+ "engines": {
+ "node": ">= 8.0.0"
+ },
+ "peerDependencies": {
+ "rollup": "^2.38.3"
+ }
+ },
+ "node_modules/@rollup/pluginutils": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.1.0.tgz",
+ "integrity": "sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==",
+ "dev": true,
+ "dependencies": {
+ "@types/estree": "0.0.39",
+ "estree-walker": "^1.0.1",
+ "picomatch": "^2.2.2"
+ },
+ "engines": {
+ "node": ">= 8.0.0"
+ },
+ "peerDependencies": {
+ "rollup": "^1.20.0||^2.0.0"
+ }
+ },
+ "node_modules/@rollup/pluginutils/node_modules/estree-walker": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-1.0.1.tgz",
+ "integrity": "sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==",
+ "dev": true
+ },
+ "node_modules/@types/estree": {
+ "version": "0.0.39",
+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz",
+ "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==",
+ "dev": true
+ },
+ "node_modules/@types/json-schema": {
+ "version": "7.0.9",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@typescript-eslint/eslint-plugin": {
+ "version": "4.31.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/experimental-utils": "4.31.0",
+ "@typescript-eslint/scope-manager": "4.31.0",
+ "debug": "^4.3.1",
+ "functional-red-black-tree": "^1.0.1",
+ "regexpp": "^3.1.0",
+ "semver": "^7.3.5",
+ "tsutils": "^3.21.0"
+ },
+ "engines": {
+ "node": "^10.12.0 || >=12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "@typescript-eslint/parser": "^4.0.0",
+ "eslint": "^5.0.0 || ^6.0.0 || ^7.0.0"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/scope-manager": {
+ "version": "4.31.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "4.31.0",
+ "@typescript-eslint/visitor-keys": "4.31.0"
+ },
+ "engines": {
+ "node": "^8.10.0 || ^10.13.0 || >=11.10.1"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/types": {
+ "version": "4.31.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^8.10.0 || ^10.13.0 || >=11.10.1"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/visitor-keys": {
+ "version": "4.31.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "4.31.0",
+ "eslint-visitor-keys": "^2.0.0"
+ },
+ "engines": {
+ "node": "^8.10.0 || ^10.13.0 || >=11.10.1"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/eslint-plugin/node_modules/debug": {
+ "version": "4.3.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "2.1.2"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@typescript-eslint/eslint-plugin/node_modules/eslint-visitor-keys": {
+ "version": "2.0.0",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@typescript-eslint/eslint-plugin/node_modules/regexpp": {
+ "version": "3.2.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/mysticatea"
+ }
+ },
+ "node_modules/@typescript-eslint/eslint-plugin/node_modules/semver": {
+ "version": "7.3.5",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "lru-cache": "^6.0.0"
+ },
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@typescript-eslint/experimental-utils": {
+ "version": "4.31.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/json-schema": "^7.0.7",
+ "@typescript-eslint/scope-manager": "4.31.0",
+ "@typescript-eslint/types": "4.31.0",
+ "@typescript-eslint/typescript-estree": "4.31.0",
+ "eslint-scope": "^5.1.1",
+ "eslint-utils": "^3.0.0"
+ },
+ "engines": {
+ "node": "^10.12.0 || >=12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "*"
+ }
+ },
+ "node_modules/@typescript-eslint/experimental-utils/node_modules/@typescript-eslint/scope-manager": {
+ "version": "4.31.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "4.31.0",
+ "@typescript-eslint/visitor-keys": "4.31.0"
+ },
+ "engines": {
+ "node": "^8.10.0 || ^10.13.0 || >=11.10.1"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/experimental-utils/node_modules/@typescript-eslint/types": {
+ "version": "4.31.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^8.10.0 || ^10.13.0 || >=11.10.1"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/experimental-utils/node_modules/@typescript-eslint/typescript-estree": {
+ "version": "4.31.0",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "@typescript-eslint/types": "4.31.0",
+ "@typescript-eslint/visitor-keys": "4.31.0",
+ "debug": "^4.3.1",
+ "globby": "^11.0.3",
+ "is-glob": "^4.0.1",
+ "semver": "^7.3.5",
+ "tsutils": "^3.21.0"
+ },
+ "engines": {
+ "node": "^10.12.0 || >=12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@typescript-eslint/experimental-utils/node_modules/@typescript-eslint/visitor-keys": {
+ "version": "4.31.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "4.31.0",
+ "eslint-visitor-keys": "^2.0.0"
+ },
+ "engines": {
+ "node": "^8.10.0 || ^10.13.0 || >=11.10.1"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/experimental-utils/node_modules/debug": {
+ "version": "4.3.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "2.1.2"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@typescript-eslint/experimental-utils/node_modules/eslint-utils": {
+ "version": "3.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "eslint-visitor-keys": "^2.0.0"
+ },
+ "engines": {
+ "node": "^10.0.0 || ^12.0.0 || >= 14.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/mysticatea"
+ },
+ "peerDependencies": {
+ "eslint": ">=5"
+ }
+ },
+ "node_modules/@typescript-eslint/experimental-utils/node_modules/eslint-visitor-keys": {
+ "version": "2.0.0",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@typescript-eslint/experimental-utils/node_modules/semver": {
+ "version": "7.3.5",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "lru-cache": "^6.0.0"
+ },
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@typescript-eslint/parser": {
+ "version": "4.29.3",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "@typescript-eslint/scope-manager": "4.29.3",
+ "@typescript-eslint/types": "4.29.3",
+ "@typescript-eslint/typescript-estree": "4.29.3",
+ "debug": "^4.3.1"
+ },
+ "engines": {
+ "node": "^10.12.0 || >=12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^5.0.0 || ^6.0.0 || ^7.0.0"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@typescript-eslint/parser/node_modules/debug": {
+ "version": "4.3.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "2.1.2"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@typescript-eslint/scope-manager": {
+ "version": "4.29.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "4.29.3",
+ "@typescript-eslint/visitor-keys": "4.29.3"
+ },
+ "engines": {
+ "node": "^8.10.0 || ^10.13.0 || >=11.10.1"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/types": {
+ "version": "4.29.3",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^8.10.0 || ^10.13.0 || >=11.10.1"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/typescript-estree": {
+ "version": "4.29.3",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "@typescript-eslint/types": "4.29.3",
+ "@typescript-eslint/visitor-keys": "4.29.3",
+ "debug": "^4.3.1",
+ "globby": "^11.0.3",
+ "is-glob": "^4.0.1",
+ "semver": "^7.3.5",
+ "tsutils": "^3.21.0"
+ },
+ "engines": {
+ "node": "^10.12.0 || >=12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@typescript-eslint/typescript-estree/node_modules/debug": {
+ "version": "4.3.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "2.1.2"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": {
+ "version": "7.3.5",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "lru-cache": "^6.0.0"
+ },
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/balanced-match": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+ "dev": true
+ },
+ "node_modules/brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "dev": true,
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/commondir": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz",
+ "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=",
+ "dev": true
+ },
+ "node_modules/concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
+ "dev": true
+ },
+ "node_modules/escape-string-regexp": {
+ "version": "4.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/eslint": {
+ "version": "7.32.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "7.12.11",
+ "@eslint/eslintrc": "^0.4.3",
+ "@humanwhocodes/config-array": "^0.5.0",
+ "ajv": "^6.10.0",
+ "cross-spawn": "^7.0.2",
+ "debug": "^4.0.1",
+ "doctrine": "^3.0.0",
+ "enquirer": "^2.3.5",
+ "escape-string-regexp": "^4.0.0",
+ "eslint-scope": "^5.1.1",
+ "eslint-utils": "^2.1.0",
+ "eslint-visitor-keys": "^2.0.0",
+ "espree": "^7.3.1",
+ "esquery": "^1.4.0",
+ "esutils": "^2.0.2",
+ "fast-deep-equal": "^3.1.3",
+ "file-entry-cache": "^6.0.1",
+ "functional-red-black-tree": "^1.0.1",
+ "glob-parent": "^5.1.2",
+ "globals": "^13.6.0",
+ "chalk": "^4.0.0",
+ "ignore": "^4.0.6",
+ "import-fresh": "^3.0.0",
+ "imurmurhash": "^0.1.4",
+ "is-glob": "^4.0.0",
+ "js-yaml": "^3.13.1",
+ "json-stable-stringify-without-jsonify": "^1.0.1",
+ "levn": "^0.4.1",
+ "lodash.merge": "^4.6.2",
+ "minimatch": "^3.0.4",
+ "natural-compare": "^1.4.0",
+ "optionator": "^0.9.1",
+ "progress": "^2.0.0",
+ "regexpp": "^3.1.0",
+ "semver": "^7.2.1",
+ "strip-ansi": "^6.0.0",
+ "strip-json-comments": "^3.1.0",
+ "table": "^6.0.9",
+ "text-table": "^0.2.0",
+ "v8-compile-cache": "^2.0.3"
+ },
+ "bin": {
+ "eslint": "bin/eslint.js"
+ },
+ "engines": {
+ "node": "^10.12.0 || >=12.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/eslint/node_modules/@babel/code-frame": {
+ "version": "7.10.4",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/highlight": "^7.10.4"
+ }
+ },
+ "node_modules/eslint/node_modules/cross-spawn": {
+ "version": "7.0.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "path-key": "^3.1.0",
+ "shebang-command": "^2.0.0",
+ "which": "^2.0.1"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/eslint/node_modules/cross-spawn/node_modules/path-key": {
+ "version": "3.1.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/eslint/node_modules/cross-spawn/node_modules/shebang-command": {
+ "version": "2.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "shebang-regex": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/eslint/node_modules/cross-spawn/node_modules/shebang-command/node_modules/shebang-regex": {
+ "version": "3.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/eslint/node_modules/cross-spawn/node_modules/which": {
+ "version": "2.0.2",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "isexe": "^2.0.0"
+ },
+ "bin": {
+ "node-which": "bin/node-which"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/eslint/node_modules/eslint-utils": {
+ "version": "2.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "eslint-visitor-keys": "^1.1.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/mysticatea"
+ }
+ },
+ "node_modules/eslint/node_modules/eslint-utils/node_modules/eslint-visitor-keys": {
+ "version": "1.3.0",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/eslint/node_modules/eslint-visitor-keys": {
+ "version": "2.0.0",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/eslint/node_modules/espree": {
+ "version": "7.3.1",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "acorn": "^7.4.0",
+ "acorn-jsx": "^5.3.1",
+ "eslint-visitor-keys": "^1.3.0"
+ },
+ "engines": {
+ "node": "^10.12.0 || >=12.0.0"
+ }
+ },
+ "node_modules/eslint/node_modules/espree/node_modules/acorn-jsx": {
+ "version": "5.3.2",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
+ }
+ },
+ "node_modules/eslint/node_modules/espree/node_modules/eslint-visitor-keys": {
+ "version": "1.3.0",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/eslint/node_modules/esquery": {
+ "version": "1.4.0",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "estraverse": "^5.1.0"
+ },
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/eslint/node_modules/file-entry-cache": {
+ "version": "6.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "flat-cache": "^3.0.4"
+ },
+ "engines": {
+ "node": "^10.12.0 || >=12.0.0"
+ }
+ },
+ "node_modules/eslint/node_modules/file-entry-cache/node_modules/flat-cache": {
+ "version": "3.0.4",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "flatted": "^3.1.0",
+ "rimraf": "^3.0.2"
+ },
+ "engines": {
+ "node": "^10.12.0 || >=12.0.0"
+ }
+ },
+ "node_modules/eslint/node_modules/file-entry-cache/node_modules/flat-cache/node_modules/flatted": {
+ "version": "3.2.2",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/eslint/node_modules/file-entry-cache/node_modules/flat-cache/node_modules/rimraf": {
+ "version": "3.0.2",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "glob": "^7.1.3"
+ },
+ "bin": {
+ "rimraf": "bin.js"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/eslint/node_modules/glob-parent": {
+ "version": "5.1.2",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "is-glob": "^4.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/eslint/node_modules/globals": {
+ "version": "13.11.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "type-fest": "^0.20.2"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/eslint/node_modules/globals/node_modules/type-fest": {
+ "version": "0.8.1",
+ "dev": true,
+ "license": "(MIT OR CC0-1.0)",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/eslint/node_modules/chalk": {
+ "version": "4.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/eslint/node_modules/chalk/node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/eslint/node_modules/chalk/node_modules/ansi-styles/node_modules/color-convert": {
+ "version": "2.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/eslint/node_modules/chalk/node_modules/ansi-styles/node_modules/color-convert/node_modules/color-name": {
+ "version": "1.1.4",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/eslint/node_modules/chalk/node_modules/supports-color": {
+ "version": "7.2.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/eslint/node_modules/chalk/node_modules/supports-color/node_modules/has-flag": {
+ "version": "4.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/eslint/node_modules/levn": {
+ "version": "0.4.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "prelude-ls": "^1.2.1",
+ "type-check": "~0.4.0"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/eslint/node_modules/levn/node_modules/prelude-ls": {
+ "version": "1.1.2",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/eslint/node_modules/optionator": {
+ "version": "0.9.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "deep-is": "^0.1.3",
+ "fast-levenshtein": "^2.0.6",
+ "levn": "^0.4.1",
+ "prelude-ls": "^1.2.1",
+ "type-check": "^0.4.0",
+ "word-wrap": "^1.2.3"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/eslint/node_modules/optionator/node_modules/levn": {
+ "version": "0.3.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "prelude-ls": "~1.1.2",
+ "type-check": "~0.3.2"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/eslint/node_modules/optionator/node_modules/levn/node_modules/type-check": {
+ "version": "0.4.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "prelude-ls": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/eslint/node_modules/optionator/node_modules/levn/node_modules/type-check/node_modules/prelude-ls": {
+ "version": "1.1.2",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/eslint/node_modules/optionator/node_modules/prelude-ls": {
+ "version": "1.1.2",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/eslint/node_modules/optionator/node_modules/type-check": {
+ "version": "0.3.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "prelude-ls": "~1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/eslint/node_modules/regexpp": {
+ "version": "3.2.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/mysticatea"
+ }
+ },
+ "node_modules/eslint/node_modules/semver": {
+ "version": "7.3.5",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "lru-cache": "^6.0.0"
+ },
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/eslint/node_modules/strip-ansi": {
+ "version": "6.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/eslint/node_modules/strip-ansi/node_modules/ansi-regex": {
+ "version": "5.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/eslint/node_modules/table": {
+ "version": "6.7.1",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "ajv": "^8.0.1",
+ "lodash.clonedeep": "^4.5.0",
+ "lodash.truncate": "^4.4.2",
+ "slice-ansi": "^4.0.0",
+ "string-width": "^4.2.0",
+ "strip-ansi": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=10.0.0"
+ }
+ },
+ "node_modules/eslint/node_modules/table/node_modules/ajv": {
+ "version": "8.6.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fast-deep-equal": "^3.1.1",
+ "json-schema-traverse": "^1.0.0",
+ "require-from-string": "^2.0.2",
+ "uri-js": "^4.2.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/epoberezkin"
+ }
+ },
+ "node_modules/eslint/node_modules/table/node_modules/ajv/node_modules/json-schema-traverse": {
+ "version": "1.0.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/eslint/node_modules/table/node_modules/slice-ansi": {
+ "version": "4.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^4.0.0",
+ "astral-regex": "^2.0.0",
+ "is-fullwidth-code-point": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/slice-ansi?sponsor=1"
+ }
+ },
+ "node_modules/eslint/node_modules/table/node_modules/slice-ansi/node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/eslint/node_modules/table/node_modules/slice-ansi/node_modules/ansi-styles/node_modules/color-convert": {
+ "version": "2.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/eslint/node_modules/table/node_modules/slice-ansi/node_modules/ansi-styles/node_modules/color-convert/node_modules/color-name": {
+ "version": "1.1.4",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/eslint/node_modules/table/node_modules/slice-ansi/node_modules/astral-regex": {
+ "version": "2.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/eslint/node_modules/table/node_modules/slice-ansi/node_modules/is-fullwidth-code-point": {
+ "version": "3.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/eslint/node_modules/table/node_modules/string-width": {
+ "version": "4.2.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/eslint/node_modules/table/node_modules/string-width/node_modules/emoji-regex": {
+ "version": "8.0.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/eslint/node_modules/table/node_modules/string-width/node_modules/is-fullwidth-code-point": {
+ "version": "3.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/estraverse": {
+ "version": "5.2.0",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/estree-walker": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
+ "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
+ "dev": true
+ },
+ "node_modules/fs.realpath": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
+ "dev": true
+ },
+ "node_modules/fsevents": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
+ "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
+ "dev": true,
+ "hasInstallScript": true,
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "peer": true,
+ "engines": {
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+ }
+ },
+ "node_modules/function-bind": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
+ "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
+ "dev": true
+ },
+ "node_modules/glob": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz",
+ "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==",
+ "dev": true,
+ "dependencies": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.0.4",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ },
+ "engines": {
+ "node": "*"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/globby": {
+ "version": "11.0.4",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "array-union": "^2.1.0",
+ "dir-glob": "^3.0.1",
+ "fast-glob": "^3.1.1",
+ "ignore": "^5.1.4",
+ "merge2": "^1.3.0",
+ "slash": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/globby/node_modules/ignore": {
+ "version": "5.1.8",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "node_modules/has": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
+ "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
+ "dev": true,
+ "dependencies": {
+ "function-bind": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4.0"
+ }
+ },
+ "node_modules/inflight": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+ "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
+ "dev": true,
+ "dependencies": {
+ "once": "^1.3.0",
+ "wrappy": "1"
+ }
+ },
+ "node_modules/inherits": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+ "dev": true
+ },
+ "node_modules/is-core-module": {
+ "version": "2.8.0",
+ "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.0.tgz",
+ "integrity": "sha512-vd15qHsaqrRL7dtH6QNuy0ndJmRDrS9HAM1CAiSifNUFv4x1a0CCVsj18hJ1mShxIG6T2i1sO78MkP56r0nYRw==",
+ "dev": true,
+ "dependencies": {
+ "has": "^1.0.3"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-reference": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.2.1.tgz",
+ "integrity": "sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==",
+ "dev": true,
+ "dependencies": {
+ "@types/estree": "*"
+ }
+ },
+ "node_modules/lru-cache": {
+ "version": "6.0.0",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "yallist": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/magic-string": {
+ "version": "0.25.7",
+ "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.7.tgz",
+ "integrity": "sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA==",
+ "dev": true,
+ "dependencies": {
+ "sourcemap-codec": "^1.4.4"
+ }
+ },
+ "node_modules/minimatch": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
+ "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
+ "dev": true,
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/once": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+ "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
+ "dev": true,
+ "dependencies": {
+ "wrappy": "1"
+ }
+ },
+ "node_modules/path-is-absolute": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+ "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/path-parse": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
+ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
+ "dev": true
+ },
+ "node_modules/picomatch": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz",
+ "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==",
+ "dev": true,
+ "engines": {
+ "node": ">=8.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/prelude-ls": {
+ "version": "1.2.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/resolve": {
+ "version": "1.20.0",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz",
+ "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==",
+ "dev": true,
+ "dependencies": {
+ "is-core-module": "^2.2.0",
+ "path-parse": "^1.0.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/rollup": {
+ "version": "2.60.1",
+ "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.60.1.tgz",
+ "integrity": "sha512-akwfnpjY0rXEDSn1UTVfKXJhPsEBu+imi1gqBA1ZkHGydUnkV/fWCC90P7rDaLEW8KTwBcS1G3N4893Ndz+jwg==",
+ "dev": true,
+ "peer": true,
+ "bin": {
+ "rollup": "dist/bin/rollup"
+ },
+ "engines": {
+ "node": ">=10.0.0"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.2"
+ }
+ },
+ "node_modules/sourcemap-codec": {
+ "version": "1.4.8",
+ "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz",
+ "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==",
+ "dev": true
+ },
+ "node_modules/tsutils": {
+ "version": "3.21.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "tslib": "^1.8.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ },
+ "peerDependencies": {
+ "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta"
+ }
+ },
+ "node_modules/type-check": {
+ "version": "0.4.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "prelude-ls": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/wrappy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
+ "dev": true
+ },
+ "node_modules/yallist": {
+ "version": "4.0.0",
+ "dev": true,
+ "license": "ISC"
+ }
+ },
+ "dependencies": {
+ "@rollup/plugin-commonjs": {
+ "version": "21.0.1",
+ "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-21.0.1.tgz",
+ "integrity": "sha512-EA+g22lbNJ8p5kuZJUYyhhDK7WgJckW5g4pNN7n4mAFUM96VuwUnNT3xr2Db2iCZPI1pJPbGyfT5mS9T1dHfMg==",
+ "dev": true,
+ "requires": {
+ "@rollup/pluginutils": "^3.1.0",
+ "commondir": "^1.0.1",
+ "estree-walker": "^2.0.1",
+ "glob": "^7.1.6",
+ "is-reference": "^1.2.1",
+ "magic-string": "^0.25.7",
+ "resolve": "^1.17.0"
+ }
+ },
+ "@rollup/pluginutils": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.1.0.tgz",
+ "integrity": "sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==",
+ "dev": true,
+ "requires": {
+ "@types/estree": "0.0.39",
+ "estree-walker": "^1.0.1",
+ "picomatch": "^2.2.2"
+ },
+ "dependencies": {
+ "estree-walker": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-1.0.1.tgz",
+ "integrity": "sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==",
+ "dev": true
+ }
+ }
+ },
+ "@types/estree": {
+ "version": "0.0.39",
+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz",
+ "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==",
+ "dev": true
+ },
+ "@types/json-schema": {
+ "version": "7.0.9",
+ "dev": true
+ },
+ "@typescript-eslint/eslint-plugin": {
+ "version": "4.31.0",
+ "dev": true,
+ "requires": {
+ "@typescript-eslint/experimental-utils": "4.31.0",
+ "@typescript-eslint/scope-manager": "4.31.0",
+ "debug": "^4.3.1",
+ "functional-red-black-tree": "^1.0.1",
+ "regexpp": "^3.1.0",
+ "semver": "^7.3.5",
+ "tsutils": "^3.21.0"
+ },
+ "dependencies": {
+ "@typescript-eslint/scope-manager": {
+ "version": "4.31.0",
+ "dev": true,
+ "requires": {
+ "@typescript-eslint/types": "4.31.0",
+ "@typescript-eslint/visitor-keys": "4.31.0"
+ }
+ },
+ "@typescript-eslint/types": {
+ "version": "4.31.0",
+ "dev": true
+ },
+ "@typescript-eslint/visitor-keys": {
+ "version": "4.31.0",
+ "dev": true,
+ "requires": {
+ "@typescript-eslint/types": "4.31.0",
+ "eslint-visitor-keys": "^2.0.0"
+ }
+ },
+ "debug": {
+ "version": "4.3.2",
+ "dev": true,
+ "requires": {
+ "ms": "2.1.2"
+ }
+ },
+ "eslint-visitor-keys": {
+ "version": "2.0.0",
+ "dev": true
+ },
+ "regexpp": {
+ "version": "3.2.0",
+ "dev": true
+ },
+ "semver": {
+ "version": "7.3.5",
+ "dev": true,
+ "requires": {
+ "lru-cache": "^6.0.0"
+ }
+ }
+ }
+ },
+ "@typescript-eslint/experimental-utils": {
+ "version": "4.31.0",
+ "dev": true,
+ "requires": {
+ "@types/json-schema": "^7.0.7",
+ "@typescript-eslint/scope-manager": "4.31.0",
+ "@typescript-eslint/types": "4.31.0",
+ "@typescript-eslint/typescript-estree": "4.31.0",
+ "eslint-scope": "^5.1.1",
+ "eslint-utils": "^3.0.0"
+ },
+ "dependencies": {
+ "@typescript-eslint/scope-manager": {
+ "version": "4.31.0",
+ "dev": true,
+ "requires": {
+ "@typescript-eslint/types": "4.31.0",
+ "@typescript-eslint/visitor-keys": "4.31.0"
+ }
+ },
+ "@typescript-eslint/types": {
+ "version": "4.31.0",
+ "dev": true
+ },
+ "@typescript-eslint/typescript-estree": {
+ "version": "4.31.0",
+ "dev": true,
+ "requires": {
+ "@typescript-eslint/types": "4.31.0",
+ "@typescript-eslint/visitor-keys": "4.31.0",
+ "debug": "^4.3.1",
+ "globby": "^11.0.3",
+ "is-glob": "^4.0.1",
+ "semver": "^7.3.5",
+ "tsutils": "^3.21.0"
+ }
+ },
+ "@typescript-eslint/visitor-keys": {
+ "version": "4.31.0",
+ "dev": true,
+ "requires": {
+ "@typescript-eslint/types": "4.31.0",
+ "eslint-visitor-keys": "^2.0.0"
+ }
+ },
+ "debug": {
+ "version": "4.3.2",
+ "dev": true,
+ "requires": {
+ "ms": "2.1.2"
+ }
+ },
+ "eslint-utils": {
+ "version": "3.0.0",
+ "dev": true,
+ "requires": {
+ "eslint-visitor-keys": "^2.0.0"
+ }
+ },
+ "eslint-visitor-keys": {
+ "version": "2.0.0",
+ "dev": true
+ },
+ "semver": {
+ "version": "7.3.5",
+ "dev": true,
+ "requires": {
+ "lru-cache": "^6.0.0"
+ }
+ }
+ }
+ },
+ "@typescript-eslint/parser": {
+ "version": "4.29.3",
+ "dev": true,
+ "requires": {
+ "@typescript-eslint/scope-manager": "4.29.3",
+ "@typescript-eslint/types": "4.29.3",
+ "@typescript-eslint/typescript-estree": "4.29.3",
+ "debug": "^4.3.1"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "4.3.2",
+ "dev": true,
+ "requires": {
+ "ms": "2.1.2"
+ }
+ }
+ }
+ },
+ "@typescript-eslint/scope-manager": {
+ "version": "4.29.3",
+ "dev": true,
+ "requires": {
+ "@typescript-eslint/types": "4.29.3",
+ "@typescript-eslint/visitor-keys": "4.29.3"
+ }
+ },
+ "@typescript-eslint/types": {
+ "version": "4.29.3",
+ "dev": true
+ },
+ "@typescript-eslint/typescript-estree": {
+ "version": "4.29.3",
+ "dev": true,
+ "requires": {
+ "@typescript-eslint/types": "4.29.3",
+ "@typescript-eslint/visitor-keys": "4.29.3",
+ "debug": "^4.3.1",
+ "globby": "^11.0.3",
+ "is-glob": "^4.0.1",
+ "semver": "^7.3.5",
+ "tsutils": "^3.21.0"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "4.3.2",
+ "dev": true,
+ "requires": {
+ "ms": "2.1.2"
+ }
+ },
+ "semver": {
+ "version": "7.3.5",
+ "dev": true,
+ "requires": {
+ "lru-cache": "^6.0.0"
+ }
+ }
+ }
+ },
+ "balanced-match": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+ "dev": true
+ },
+ "brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "dev": true,
+ "requires": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "commondir": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz",
+ "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=",
+ "dev": true
+ },
+ "concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
+ "dev": true
+ },
+ "escape-string-regexp": {
+ "version": "4.0.0",
+ "dev": true
+ },
+ "eslint": {
+ "version": "7.32.0",
+ "dev": true,
+ "requires": {
+ "@babel/code-frame": "7.12.11",
+ "@eslint/eslintrc": "^0.4.3",
+ "@humanwhocodes/config-array": "^0.5.0",
+ "ajv": "^6.10.0",
+ "cross-spawn": "^7.0.2",
+ "debug": "^4.0.1",
+ "doctrine": "^3.0.0",
+ "enquirer": "^2.3.5",
+ "escape-string-regexp": "^4.0.0",
+ "eslint-scope": "^5.1.1",
+ "eslint-utils": "^2.1.0",
+ "eslint-visitor-keys": "^2.0.0",
+ "espree": "^7.3.1",
+ "esquery": "^1.4.0",
+ "esutils": "^2.0.2",
+ "fast-deep-equal": "^3.1.3",
+ "file-entry-cache": "^6.0.1",
+ "functional-red-black-tree": "^1.0.1",
+ "glob-parent": "^5.1.2",
+ "globals": "^13.6.0",
+ "chalk": "^4.0.0",
+ "ignore": "^4.0.6",
+ "import-fresh": "^3.0.0",
+ "imurmurhash": "^0.1.4",
+ "is-glob": "^4.0.0",
+ "js-yaml": "^3.13.1",
+ "json-stable-stringify-without-jsonify": "^1.0.1",
+ "levn": "^0.4.1",
+ "lodash.merge": "^4.6.2",
+ "minimatch": "^3.0.4",
+ "natural-compare": "^1.4.0",
+ "optionator": "^0.9.1",
+ "progress": "^2.0.0",
+ "regexpp": "^3.1.0",
+ "semver": "^7.2.1",
+ "strip-ansi": "^6.0.0",
+ "strip-json-comments": "^3.1.0",
+ "table": "^6.0.9",
+ "text-table": "^0.2.0",
+ "v8-compile-cache": "^2.0.3"
+ },
+ "dependencies": {
+ "@babel/code-frame": {
+ "version": "7.10.4",
+ "dev": true,
+ "requires": {
+ "@babel/highlight": "^7.10.4"
+ }
+ },
+ "cross-spawn": {
+ "version": "7.0.3",
+ "dev": true,
+ "requires": {
+ "path-key": "^3.1.0",
+ "shebang-command": "^2.0.0",
+ "which": "^2.0.1"
+ },
+ "dependencies": {
+ "path-key": {
+ "version": "3.1.1",
+ "dev": true
+ },
+ "shebang-command": {
+ "version": "2.0.0",
+ "dev": true,
+ "requires": {
+ "shebang-regex": "^3.0.0"
+ },
+ "dependencies": {
+ "shebang-regex": {
+ "version": "3.0.0",
+ "dev": true
+ }
+ }
+ },
+ "which": {
+ "version": "2.0.2",
+ "dev": true,
+ "requires": {
+ "isexe": "^2.0.0"
+ }
+ }
+ }
+ },
+ "eslint-utils": {
+ "version": "2.1.0",
+ "dev": true,
+ "requires": {
+ "eslint-visitor-keys": "^1.1.0"
+ },
+ "dependencies": {
+ "eslint-visitor-keys": {
+ "version": "1.3.0",
+ "dev": true
+ }
+ }
+ },
+ "eslint-visitor-keys": {
+ "version": "2.0.0",
+ "dev": true
+ },
+ "espree": {
+ "version": "7.3.1",
+ "dev": true,
+ "requires": {
+ "acorn": "^7.4.0",
+ "acorn-jsx": "^5.3.1",
+ "eslint-visitor-keys": "^1.3.0"
+ },
+ "dependencies": {
+ "acorn-jsx": {
+ "version": "5.3.2",
+ "dev": true,
+ "requires": {}
+ },
+ "eslint-visitor-keys": {
+ "version": "1.3.0",
+ "dev": true
+ }
+ }
+ },
+ "esquery": {
+ "version": "1.4.0",
+ "dev": true,
+ "requires": {
+ "estraverse": "^5.1.0"
+ }
+ },
+ "file-entry-cache": {
+ "version": "6.0.1",
+ "dev": true,
+ "requires": {
+ "flat-cache": "^3.0.4"
+ },
+ "dependencies": {
+ "flat-cache": {
+ "version": "3.0.4",
+ "dev": true,
+ "requires": {
+ "flatted": "^3.1.0",
+ "rimraf": "^3.0.2"
+ },
+ "dependencies": {
+ "flatted": {
+ "version": "3.2.2",
+ "dev": true
+ },
+ "rimraf": {
+ "version": "3.0.2",
+ "dev": true,
+ "requires": {
+ "glob": "^7.1.3"
+ }
+ }
+ }
+ }
+ }
+ },
+ "glob-parent": {
+ "version": "5.1.2",
+ "dev": true,
+ "requires": {
+ "is-glob": "^4.0.1"
+ }
+ },
+ "globals": {
+ "version": "13.11.0",
+ "dev": true,
+ "requires": {
+ "type-fest": "^0.20.2"
+ },
+ "dependencies": {
+ "type-fest": {
+ "version": "0.8.1",
+ "dev": true
+ }
+ }
+ },
+ "chalk": {
+ "version": "4.1.0",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "dependencies": {
+ "ansi-styles": {
+ "version": "4.3.0",
+ "dev": true,
+ "requires": {
+ "color-convert": "^2.0.1"
+ },
+ "dependencies": {
+ "color-convert": {
+ "version": "2.0.1",
+ "dev": true,
+ "requires": {
+ "color-name": "~1.1.4"
+ },
+ "dependencies": {
+ "color-name": {
+ "version": "1.1.4",
+ "dev": true
+ }
+ }
+ }
+ }
+ },
+ "supports-color": {
+ "version": "7.2.0",
+ "dev": true,
+ "requires": {
+ "has-flag": "^4.0.0"
+ },
+ "dependencies": {
+ "has-flag": {
+ "version": "4.0.0",
+ "dev": true
+ }
+ }
+ }
+ }
+ },
+ "levn": {
+ "version": "0.4.1",
+ "dev": true,
+ "requires": {
+ "prelude-ls": "^1.2.1",
+ "type-check": "~0.4.0"
+ },
+ "dependencies": {
+ "prelude-ls": {
+ "version": "1.1.2",
+ "dev": true
+ }
+ }
+ },
+ "optionator": {
+ "version": "0.9.1",
+ "dev": true,
+ "requires": {
+ "deep-is": "^0.1.3",
+ "fast-levenshtein": "^2.0.6",
+ "levn": "^0.4.1",
+ "prelude-ls": "^1.2.1",
+ "type-check": "^0.4.0",
+ "word-wrap": "^1.2.3"
+ },
+ "dependencies": {
+ "levn": {
+ "version": "0.3.0",
+ "dev": true,
+ "requires": {
+ "prelude-ls": "~1.1.2",
+ "type-check": "~0.3.2"
+ },
+ "dependencies": {
+ "type-check": {
+ "version": "0.4.0",
+ "dev": true,
+ "requires": {
+ "prelude-ls": "^1.2.1"
+ },
+ "dependencies": {
+ "prelude-ls": {
+ "version": "1.1.2",
+ "dev": true
+ }
+ }
+ }
+ }
+ },
+ "prelude-ls": {
+ "version": "1.1.2",
+ "dev": true
+ },
+ "type-check": {
+ "version": "0.3.2",
+ "dev": true,
+ "requires": {
+ "prelude-ls": "~1.1.2"
+ }
+ }
+ }
+ },
+ "regexpp": {
+ "version": "3.2.0",
+ "dev": true
+ },
+ "semver": {
+ "version": "7.3.5",
+ "dev": true,
+ "requires": {
+ "lru-cache": "^6.0.0"
+ }
+ },
+ "strip-ansi": {
+ "version": "6.0.0",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^5.0.0"
+ },
+ "dependencies": {
+ "ansi-regex": {
+ "version": "5.0.0",
+ "dev": true
+ }
+ }
+ },
+ "table": {
+ "version": "6.7.1",
+ "dev": true,
+ "requires": {
+ "ajv": "^8.0.1",
+ "lodash.clonedeep": "^4.5.0",
+ "lodash.truncate": "^4.4.2",
+ "slice-ansi": "^4.0.0",
+ "string-width": "^4.2.0",
+ "strip-ansi": "^6.0.0"
+ },
+ "dependencies": {
+ "ajv": {
+ "version": "8.6.2",
+ "dev": true,
+ "requires": {
+ "fast-deep-equal": "^3.1.1",
+ "json-schema-traverse": "^1.0.0",
+ "require-from-string": "^2.0.2",
+ "uri-js": "^4.2.2"
+ },
+ "dependencies": {
+ "json-schema-traverse": {
+ "version": "1.0.0",
+ "dev": true
+ }
+ }
+ },
+ "slice-ansi": {
+ "version": "4.0.0",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^4.0.0",
+ "astral-regex": "^2.0.0",
+ "is-fullwidth-code-point": "^3.0.0"
+ },
+ "dependencies": {
+ "ansi-styles": {
+ "version": "4.3.0",
+ "dev": true,
+ "requires": {
+ "color-convert": "^2.0.1"
+ },
+ "dependencies": {
+ "color-convert": {
+ "version": "2.0.1",
+ "dev": true,
+ "requires": {
+ "color-name": "~1.1.4"
+ },
+ "dependencies": {
+ "color-name": {
+ "version": "1.1.4",
+ "dev": true
+ }
+ }
+ }
+ }
+ },
+ "astral-regex": {
+ "version": "2.0.0",
+ "dev": true
+ },
+ "is-fullwidth-code-point": {
+ "version": "3.0.0",
+ "dev": true
+ }
+ }
+ },
+ "string-width": {
+ "version": "4.2.0",
+ "dev": true,
+ "requires": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.0"
+ },
+ "dependencies": {
+ "emoji-regex": {
+ "version": "8.0.0",
+ "dev": true
+ },
+ "is-fullwidth-code-point": {
+ "version": "3.0.0",
+ "dev": true
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "estraverse": {
+ "version": "5.2.0",
+ "dev": true
+ },
+ "estree-walker": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
+ "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
+ "dev": true
+ },
+ "fs.realpath": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
+ "dev": true
+ },
+ "fsevents": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
+ "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
+ "dev": true,
+ "optional": true,
+ "peer": true
+ },
+ "function-bind": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
+ "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
+ "dev": true
+ },
+ "glob": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz",
+ "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==",
+ "dev": true,
+ "requires": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.0.4",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ }
+ },
+ "globby": {
+ "version": "11.0.4",
+ "dev": true,
+ "requires": {
+ "array-union": "^2.1.0",
+ "dir-glob": "^3.0.1",
+ "fast-glob": "^3.1.1",
+ "ignore": "^5.1.4",
+ "merge2": "^1.3.0",
+ "slash": "^3.0.0"
+ },
+ "dependencies": {
+ "ignore": {
+ "version": "5.1.8",
+ "dev": true
+ }
+ }
+ },
+ "has": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
+ "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
+ "dev": true,
+ "requires": {
+ "function-bind": "^1.1.1"
+ }
+ },
+ "inflight": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+ "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
+ "dev": true,
+ "requires": {
+ "once": "^1.3.0",
+ "wrappy": "1"
+ }
+ },
+ "inherits": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+ "dev": true
+ },
+ "is-core-module": {
+ "version": "2.8.0",
+ "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.0.tgz",
+ "integrity": "sha512-vd15qHsaqrRL7dtH6QNuy0ndJmRDrS9HAM1CAiSifNUFv4x1a0CCVsj18hJ1mShxIG6T2i1sO78MkP56r0nYRw==",
+ "dev": true,
+ "requires": {
+ "has": "^1.0.3"
+ }
+ },
+ "is-reference": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.2.1.tgz",
+ "integrity": "sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==",
+ "dev": true,
+ "requires": {
+ "@types/estree": "*"
+ }
+ },
+ "lru-cache": {
+ "version": "6.0.0",
+ "dev": true,
+ "requires": {
+ "yallist": "^4.0.0"
+ }
+ },
+ "magic-string": {
+ "version": "0.25.7",
+ "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.7.tgz",
+ "integrity": "sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA==",
+ "dev": true,
+ "requires": {
+ "sourcemap-codec": "^1.4.4"
+ }
+ },
+ "minimatch": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
+ "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
+ "dev": true,
+ "requires": {
+ "brace-expansion": "^1.1.7"
+ }
+ },
+ "once": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+ "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
+ "dev": true,
+ "requires": {
+ "wrappy": "1"
+ }
+ },
+ "path-is-absolute": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+ "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
+ "dev": true
+ },
+ "path-parse": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
+ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
+ "dev": true
+ },
+ "picomatch": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz",
+ "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==",
+ "dev": true
+ },
+ "prelude-ls": {
+ "version": "1.2.1",
+ "dev": true
+ },
+ "resolve": {
+ "version": "1.20.0",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz",
+ "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==",
+ "dev": true,
+ "requires": {
+ "is-core-module": "^2.2.0",
+ "path-parse": "^1.0.6"
+ }
+ },
+ "rollup": {
+ "version": "2.60.1",
+ "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.60.1.tgz",
+ "integrity": "sha512-akwfnpjY0rXEDSn1UTVfKXJhPsEBu+imi1gqBA1ZkHGydUnkV/fWCC90P7rDaLEW8KTwBcS1G3N4893Ndz+jwg==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "fsevents": "~2.3.2"
+ }
+ },
+ "sourcemap-codec": {
+ "version": "1.4.8",
+ "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz",
+ "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==",
+ "dev": true
+ },
+ "tsutils": {
+ "version": "3.21.0",
+ "dev": true,
+ "requires": {
+ "tslib": "^1.8.1"
+ }
+ },
+ "type-check": {
+ "version": "0.4.0",
+ "dev": true,
+ "requires": {
+ "prelude-ls": "^1.2.1"
+ }
+ },
+ "wrappy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
+ "dev": true
+ },
+ "yallist": {
+ "version": "4.0.0",
+ "dev": true
+ }
+ }
+}
diff --git a/packages/eslint-plugin-mobx/package.json b/packages/eslint-plugin-mobx/package.json
new file mode 100644
index 0000000000..2ceabd5504
--- /dev/null
+++ b/packages/eslint-plugin-mobx/package.json
@@ -0,0 +1,56 @@
+{
+ "name": "eslint-plugin-mobx",
+ "version": "0.0.14",
+ "description": "ESLint rules for MobX",
+ "main": "dist/index.js",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/mobxjs/mobx.git",
+ "directory": "packages/eslint-plugin-mobx"
+ },
+ "license": "MIT",
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/mobx"
+ },
+ "bugs": {
+ "url": "https://github.com/mobxjs/mobx/issues"
+ },
+ "files": [
+ "src",
+ "dist",
+ "LICENSE",
+ "CHANGELOG.md",
+ "README.md"
+ ],
+ "homepage": "https://mobx.js.org/",
+ "peerDependencies": {
+ "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0"
+ },
+ "devDependencies": {
+ "@babel/core": "^7.16.0",
+ "@babel/preset-env": "^7.16.4",
+ "@rollup/plugin-babel": "^5.3.0",
+ "@rollup/plugin-commonjs": "^21.0.1",
+ "@rollup/plugin-node-resolve": "13.0.6",
+ "@typescript-eslint/eslint-plugin": "^5.0.0",
+ "@typescript-eslint/parser": "^5.0.0",
+ "eslint": "^7.0.0",
+ "eslint-7": "npm:eslint@^7.0.0",
+ "eslint-9": "npm:eslint@^9.0.0",
+ "rollup": "^2.60.2"
+ },
+ "keywords": [
+ "eslint",
+ "eslint-plugin",
+ "eslintplugin",
+ "mobx"
+ ],
+ "scripts": {
+ "test:7": "jest --config jest.config-eslint-7.js",
+ "test:9": "jest --config jest.config-eslint-9.js",
+ "test": "npm run test:7 && npm run test:9",
+ "build": "yarn rollup --config",
+ "prepublishOnly": "yarn build"
+ }
+}
diff --git a/packages/eslint-plugin-mobx/preview/.eslintrc.js b/packages/eslint-plugin-mobx/preview/.eslintrc.js
new file mode 100644
index 0000000000..4390247d3d
--- /dev/null
+++ b/packages/eslint-plugin-mobx/preview/.eslintrc.js
@@ -0,0 +1,24 @@
+module.exports = {
+ "extends": [
+ "plugin:mobx/recommended"
+ ],
+ "env": {
+ "browser": true,
+ "es6": true,
+ "node": true
+ },
+ "parser": "@typescript-eslint/parser",
+ "parserOptions": {
+ "ecmaVersion": 2018,
+ "sourceType": "module"
+ },
+ "plugins": [
+ "mobx",
+ //"@typescript-eslint"
+ ],
+ "rules": {
+ //'mobx/exhaustive-make-observable': 'off',
+ //'mobx/unconditional-make-observable': 'off',
+ //'mobx/missing-observer': 'off',
+ }
+};
\ No newline at end of file
diff --git a/packages/eslint-plugin-mobx/preview/.gitignore b/packages/eslint-plugin-mobx/preview/.gitignore
new file mode 100644
index 0000000000..cf4bab9ddd
--- /dev/null
+++ b/packages/eslint-plugin-mobx/preview/.gitignore
@@ -0,0 +1 @@
+!node_modules
diff --git a/packages/eslint-plugin-mobx/preview/make-observable.js b/packages/eslint-plugin-mobx/preview/make-observable.js
new file mode 100644
index 0000000000..4c4af058e8
--- /dev/null
+++ b/packages/eslint-plugin-mobx/preview/make-observable.js
@@ -0,0 +1,164 @@
+/* eslint mobx/exhaustive-make-observable: "error" */
+/* eslint mobx/missing-make-observable: "error" */
+/* eslint mobx/unconditional-make-observable: "error" */
+
+makeObservable();
+makeObservable(foo, {});
+makeObservable(this, {});
+makeAutoObservable();
+makeAutoObservable(foo, {});
+makeAutoObservable(this, {});
+
+makeObservable({
+ o: 5,
+ a() { },
+ get c() { },
+ set c() { },
+ "lit": 1,
+ [cmp()]() { },
+});
+
+// ok
+makeObservable({
+ o: 5,
+ a() { },
+ get c() { },
+ set c() { },
+ "lit": 1,
+}, {
+ o: observable,
+ a: action,
+ c: computed,
+ "lit": 1,
+ [cmp()]() { },
+});
+
+class Exhaustive1 {
+ o = 5;
+ a() { };
+ get c() { };
+ set c() { };
+ "lit" = 1;
+ [cmp()]() { };
+
+ constructor() {
+ makeObservable(this, {})
+ }
+}
+
+class Exhaustive2 {
+ o = 5;
+ a() { };
+ get c() { };
+ set c() { };
+ "lit" = 1;
+ [cmp()]() { };
+
+ constructor() {
+ // ok
+ makeObservable(this, {
+ o: observable,
+ a: action,
+ c: computed,
+ "lit": 1,
+ [cmp()]() { },
+ })
+ }
+}
+
+class Exhaustive3 {
+ o = 5;
+ o2 = 5;
+ a() { };
+ a2() { };
+ get c() { };
+ get c2() { };
+
+ constructor() {
+ makeObservable(this, {
+ o: observable,
+ a: action,
+ c: computed,
+ })
+ }
+}
+
+class Exhaustive4 {
+ o = 5;
+ a() { };
+ get c() { };
+
+ constructor() {
+ function a() {
+ makeObservable(this) // ok - `this` doesn't refer to class instance
+ }
+ }
+}
+
+class Exhaustive4 {
+ o = 5;
+ a() { };
+ get c() { };
+
+ constructor() {
+ makeObservable({}); // ok - not making `this` observable
+ }
+}
+
+class Unconditional2 {
+ constructor() {
+ makeObservable(this, {}); // ok - no condition
+ makeObservable() // ok - no `this`
+ makeObservable({}) // ok - no `this`
+ if (true) {
+ makeObservable(this, {});
+ makeAutoObservable(this, {});
+ }
+ for (let i = 0; i < 1; i++) {
+ makeObservable(this, {});
+ makeAutoObservable(this, {});
+ }
+ while (Math.random() > 1) {
+ makeObservable(this, {});
+ makeAutoObservable(this, {});
+ }
+ const a = () => {
+ makeObservable(this, {});
+ }
+ function f() {
+ makeObservable(this, {}); // ok - `this` doesn't refer to class instance
+ }
+ const ff = function () {
+ makeObservable(this, {}); // ok - `this` doesn't refer to class instance
+ }
+ }
+}
+
+class MissingMakeObservable1 {
+ @observable o = 5;
+}
+
+class MissingMakeObservable2 {
+ @observable o = 5;
+ constructor() { }
+}
+
+class MissingMakeObservable3 {
+ @observable o = 5;
+ constructor() {
+ makeObservable({})
+ }
+}
+
+class MissingMakeObservable4 {
+ @observable o = 5;
+ constructor()
+}
+
+// ok
+class MissingMakeObservable5 {
+ @observable o = 5;
+ constructor() {
+ makeObservable(this)
+ }
+}
\ No newline at end of file
diff --git a/packages/eslint-plugin-mobx/preview/missing-observer.js b/packages/eslint-plugin-mobx/preview/missing-observer.js
new file mode 100644
index 0000000000..96f0fbd52a
--- /dev/null
+++ b/packages/eslint-plugin-mobx/preview/missing-observer.js
@@ -0,0 +1,40 @@
+/* eslint mobx/missing-observer: "error" */
+
+function Named() {}
+const named = function Named() {}
+const namedRef = forwardRef(function Named() {})
+const Anonym = function () {}
+const AnonymRef = forwardRef(function () {})
+const Arrow = () => {}
+const ArrowRef = forwardRef(() => {})
+
+observer(function Named() {})
+observer(forwardRef(function Named() {}))
+const namedObs = observer(function Named() {})
+const namedRefObs = observer(forwardRef(function Named() {}))
+const AnonymObs = observer(function () {})
+const AnonymRefObs = observer(forwardRef(function () {}))
+const ArrowObs = observer(() => {})
+const ArrowRefObs = observer(forwardRef(() => {}))
+
+function notCmp() {}
+const notCmp = function notCmp() {}
+const notCmp = function () {}
+const notCmp = () => {}
+const notCmp = forwardRef(() => {})
+
+class Cmp extends React.Component {}
+class Cmp extends Component {}
+const Cmp = class extends React.Component {}
+const Cmp = class extends Component {}
+;(class extends Component {})
+;(class extends React.Component {})
+
+class NotCmp {}
+class NotCmp extends Foo {}
+class NotCmp extends React.Foo {}
+
+const Cmp = observer(class Cmp extends React.Component {})
+const Cmp = observer(class Cmp extends Component {})
+const Cmp = observer(class extends React.Component {})
+const Cmp = observer(class extends Component {})
diff --git a/packages/eslint-plugin-mobx/preview/no-anonymous-observer.js b/packages/eslint-plugin-mobx/preview/no-anonymous-observer.js
new file mode 100644
index 0000000000..866148e392
--- /dev/null
+++ b/packages/eslint-plugin-mobx/preview/no-anonymous-observer.js
@@ -0,0 +1,15 @@
+/* eslint mobx/no-anonymous-observer: "error" */
+
+observer(() => {})
+observer(function () {})
+observer(class {})
+
+const Cmp = observer(() => {})
+const Cmp = observer(() => "") // different autofix
+const Cmp = observer(() => expr()) // different autofix
+const Cmp = observer(() => literal) // different autofix
+const Cmp = observer(function () {})
+const Cmp = observer(class {})
+
+observer(function Name() {})
+observer(class Name {})
diff --git a/packages/eslint-plugin-mobx/preview/node_modules/eslint-plugin-mobx/index.js b/packages/eslint-plugin-mobx/preview/node_modules/eslint-plugin-mobx/index.js
new file mode 100644
index 0000000000..4330567c06
--- /dev/null
+++ b/packages/eslint-plugin-mobx/preview/node_modules/eslint-plugin-mobx/index.js
@@ -0,0 +1,3 @@
+// At the time of writing, eslint config does not allow filenames as plugins.
+// This is a fake module used as workaround.
+module.exports = require('../../../src/index.js');
\ No newline at end of file
diff --git a/packages/eslint-plugin-mobx/rollup.config.js b/packages/eslint-plugin-mobx/rollup.config.js
new file mode 100644
index 0000000000..6851d5a3e8
--- /dev/null
+++ b/packages/eslint-plugin-mobx/rollup.config.js
@@ -0,0 +1,21 @@
+import { nodeResolve } from "@rollup/plugin-node-resolve";
+import commonjs from "@rollup/plugin-commonjs";
+import babel from "@rollup/plugin-babel";
+import pkg from "./package.json";
+
+export default [
+ {
+ input: "src/index.js",
+ plugins: [
+ nodeResolve(),
+ commonjs(),
+ babel({
+ babelHelpers: "bundled",
+ exclude: "**/node_modules/**",
+ }),
+ ],
+ output: [
+ { file: pkg.main, format: "cjs", exports: "auto" },
+ ],
+ },
+];
\ No newline at end of file
diff --git a/packages/eslint-plugin-mobx/src/exhaustive-make-observable.js b/packages/eslint-plugin-mobx/src/exhaustive-make-observable.js
new file mode 100644
index 0000000000..ee5bad59a3
--- /dev/null
+++ b/packages/eslint-plugin-mobx/src/exhaustive-make-observable.js
@@ -0,0 +1,131 @@
+"use strict"
+
+const { findAncestor, isMobxDecorator } = require("./utils.js")
+
+// TODO support this.foo = 5; in constructor
+// TODO? report on field as well
+function create(context) {
+ const sourceCode = context.sourceCode ?? context.getSourceCode()
+ const autofixAnnotation = context.options[0]?.autofixAnnotation ?? true
+
+ function fieldToKey(field) {
+ // TODO cache on field?
+ const key = sourceCode.getText(field.key)
+ return field.computed ? `[${key}]` : key
+ }
+
+ return {
+ 'CallExpression[callee.name="makeObservable"]': makeObservable => {
+ // Only interested about makeObservable(this, ...) in constructor or makeObservable({}, ...)
+ // ClassDeclaration
+ // ClassBody
+ // MethodDefinition[kind="constructor"]
+ // FunctionExpression
+ // BlockStatement
+ // ExpressionStatement
+ // CallExpression[callee.name="makeObservable"]
+ const [firstArg, secondArg] = makeObservable.arguments
+ if (!firstArg) return
+ let members
+ if (firstArg.type === "ThisExpression") {
+ const closestFunction = findAncestor(
+ makeObservable,
+ node =>
+ node.type === "FunctionExpression" || node.type === "FunctionDeclaration"
+ )
+ if (closestFunction?.parent?.kind !== "constructor") return
+ members = closestFunction.parent.parent.parent.body.body
+ } else if (firstArg.type === "ObjectExpression") {
+ members = firstArg.properties
+ } else {
+ return
+ }
+
+ const annotationProps = secondArg?.properties || []
+ const nonAnnotatedMembers = []
+ let hasAnyDecorator = false
+
+ members.forEach(member => {
+ if (member.static) return
+ if (member.kind === "constructor") return
+ //if (member.type !== 'MethodDefinition' && member.type !== 'ClassProperty') return;
+ hasAnyDecorator =
+ hasAnyDecorator || member.decorators?.some(isMobxDecorator) || false
+ if (!annotationProps.some(prop => fieldToKey(prop) === fieldToKey(member))) {
+ // TODO optimize?
+ nonAnnotatedMembers.push(member)
+ }
+ })
+ /*
+ // With decorators, second arg must be null/undefined or not provided
+ if (hasAnyDecorator && secondArg && secondArg.name !== "undefined" && secondArg.value !== null) {
+ context.report({
+ node: makeObservable,
+ message: 'When using decorators, second arg must be `null`, `undefined` or not provided.',
+ })
+ }
+ // Without decorators, in constructor, second arg must be object literal
+ if (!hasAnyDecorator && firstArg.type === 'ThisExpression' && (!secondArg || secondArg.type !== 'ObjectExpression')) {
+ context.report({
+ node: makeObservable,
+ message: 'Second argument must be object in form of `{ key: annotation }`.',
+ })
+ }
+ */
+
+ if (!hasAnyDecorator && nonAnnotatedMembers.length) {
+ // Set avoids reporting twice for setter+getter pair or actual duplicates
+ const keys = [...new Set(nonAnnotatedMembers.map(fieldToKey))]
+ const keyList = keys.map(key => `\`${key}\``).join(", ")
+
+ const fix = fixer => {
+ const annotationList =
+ keys.map(key => `${key}: ${autofixAnnotation}`).join(", ") + ","
+ if (!secondArg) {
+ return fixer.insertTextAfter(firstArg, `, { ${annotationList} }`)
+ } else if (secondArg.type !== "ObjectExpression") {
+ return fixer.replaceText(secondArg, `{ ${annotationList} }`)
+ } else {
+ const openingBracket = sourceCode.getFirstToken(secondArg)
+ return fixer.insertTextAfter(openingBracket, ` ${annotationList} `)
+ }
+ }
+
+ context.report({
+ node: makeObservable,
+ messageId: "missingAnnotation",
+ data: { keyList },
+ fix
+ })
+ }
+ }
+ }
+}
+
+module.exports = {
+ meta: {
+ type: "suggestion",
+ fixable: "code",
+ schema: [
+ {
+ type: "object",
+ properties: {
+ autofixAnnotation: {
+ type: "boolean"
+ }
+ },
+ additionalProperties: false
+ }
+ ],
+ docs: {
+ description: "enforce all fields being listen in `makeObservable`",
+ recommended: true,
+ suggestion: false
+ },
+ messages: {
+ missingAnnotation:
+ "Missing annotation for {{ keyList }}. To exclude a field, use `false` as annotation."
+ }
+ },
+ create
+}
diff --git a/packages/eslint-plugin-mobx/src/index.js b/packages/eslint-plugin-mobx/src/index.js
new file mode 100644
index 0000000000..96fdf39637
--- /dev/null
+++ b/packages/eslint-plugin-mobx/src/index.js
@@ -0,0 +1,50 @@
+"use strict"
+
+const fs = require("fs")
+const path = require("path")
+
+const exhaustiveMakeObservable = require("./exhaustive-make-observable.js")
+const unconditionalMakeObservable = require("./unconditional-make-observable.js")
+const missingMakeObservable = require("./missing-make-observable.js")
+const missingObserver = require("./missing-observer")
+const noAnonymousObserver = require("./no-anonymous-observer.js")
+
+const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, "..", "package.json"), "utf8"))
+
+const pluginMobx = {
+ meta: {
+ name: pkg.name,
+ version: pkg.version
+ },
+ rules: {
+ "exhaustive-make-observable": exhaustiveMakeObservable,
+ "unconditional-make-observable": unconditionalMakeObservable,
+ "missing-make-observable": missingMakeObservable,
+ "missing-observer": missingObserver,
+ "no-anonymous-observer": noAnonymousObserver
+ }
+}
+
+const recommendedRules = {
+ "mobx/exhaustive-make-observable": "warn",
+ "mobx/unconditional-make-observable": "error",
+ "mobx/missing-make-observable": "error",
+ "mobx/missing-observer": "warn"
+}
+
+module.exports = {
+ ...pluginMobx,
+ configs: {
+ recommended: {
+ plugins: ["mobx"],
+ rules: recommendedRules
+ }
+ },
+ flatConfigs: {
+ recommended: {
+ name: "mobx/recommended",
+ plugins: { mobx: pluginMobx },
+ rules: recommendedRules
+ }
+ }
+}
diff --git a/packages/eslint-plugin-mobx/src/missing-make-observable.js b/packages/eslint-plugin-mobx/src/missing-make-observable.js
new file mode 100644
index 0000000000..0747d30c11
--- /dev/null
+++ b/packages/eslint-plugin-mobx/src/missing-make-observable.js
@@ -0,0 +1,71 @@
+'use strict';
+
+const { findAncestor, isMobxDecorator } = require('./utils.js');
+
+function create(context) {
+ const sourceCode = context.sourceCode ?? context.getSourceCode();
+
+ return {
+ 'Decorator': decorator => {
+ if (!isMobxDecorator(decorator)) return;
+ const clazz = findAncestor(decorator, node => node.type === 'ClassDeclaration' || node.type === 'ClassExpression');
+ if (!clazz) return;
+ // ClassDeclaration > ClassBody > []
+ const constructor = clazz.body.body.find(node => node.kind === 'constructor' && node.value.type === 'FunctionExpression') ??
+ clazz.body.body.find(node => node.kind === 'constructor');
+ // MethodDefinition > FunctionExpression > BlockStatement > []
+ const isMakeObservable = node => node.expression?.callee?.name === 'makeObservable' && node.expression?.arguments[0]?.type === 'ThisExpression';
+ const makeObservable = constructor?.value.body?.body.find(isMakeObservable)?.expression;
+
+ if (makeObservable) {
+ // make sure second arg is nullish
+ const secondArg = makeObservable.arguments[1];
+ if (secondArg && secondArg.value !== null && secondArg.name !== 'undefined') {
+ context.report({
+ node: makeObservable,
+ messageId: 'secondArgMustBeNullish',
+ })
+ }
+ } else {
+ const fix = fixer => {
+ if (constructor?.value.type === 'TSEmptyBodyFunctionExpression') {
+ // constructor() - yes this a thing
+ const closingBracket = sourceCode.getLastToken(constructor.value);
+ return fixer.insertTextAfter(closingBracket, ' { makeObservable(this); }')
+ } else if (constructor) {
+ // constructor() {}
+ const closingBracket = sourceCode.getLastToken(constructor.value.body);
+ return fixer.insertTextBefore(closingBracket, ';makeObservable(this);')
+ } else {
+ // class C {}
+ const openingBracket = sourceCode.getFirstToken(clazz.body);
+ return fixer.insertTextAfter(openingBracket, '\nconstructor() { makeObservable(this); }')
+ }
+ };
+
+ context.report({
+ node: clazz,
+ messageId: 'missingMakeObservable',
+ fix,
+ })
+ }
+ },
+ };
+}
+
+module.exports = {
+ meta: {
+ type: 'problem',
+ fixable: 'code',
+ docs: {
+ description: 'prevents missing `makeObservable(this)` when using decorators',
+ recommended: true,
+ suggestion: false,
+ },
+ messages: {
+ missingMakeObservable: "Constructor is missing `makeObservable(this)`.",
+ secondArgMustBeNullish: "`makeObservable`'s second argument must be nullish or not provided when using decorators."
+ },
+ },
+ create,
+};
\ No newline at end of file
diff --git a/packages/eslint-plugin-mobx/src/missing-observer.js b/packages/eslint-plugin-mobx/src/missing-observer.js
new file mode 100644
index 0000000000..11b925a11f
--- /dev/null
+++ b/packages/eslint-plugin-mobx/src/missing-observer.js
@@ -0,0 +1,94 @@
+"use strict"
+
+function create(context) {
+ const sourceCode = context.sourceCode ?? context.getSourceCode()
+
+ return {
+ "FunctionDeclaration,FunctionExpression,ArrowFunctionExpression,ClassDeclaration,ClassExpression":
+ cmp => {
+ if (
+ cmp.parent &&
+ cmp.parent.type === "CallExpression" &&
+ cmp.parent.callee.name === "observer"
+ ) {
+ // observer(...)
+ return
+ }
+ let forwardRef =
+ cmp.parent &&
+ cmp.parent.type === "CallExpression" &&
+ cmp.parent.callee.name === "forwardRef"
+ ? cmp.parent
+ : undefined
+ if (
+ forwardRef &&
+ forwardRef.parent &&
+ forwardRef.parent.type === "CallExpression" &&
+ forwardRef.parent.callee.name === "observer"
+ ) {
+ // forwardRef(observer(...))
+ return
+ }
+
+ const cmpOrForwardRef = forwardRef || cmp
+ let name = cmp.id?.name
+ // If anonymous try to infer name from variable declaration
+ if (!name && cmpOrForwardRef.parent?.type === "VariableDeclarator") {
+ name = cmpOrForwardRef.parent.id.name
+ }
+ if (cmp.type.startsWith("Class")) {
+ // Must extend Component or React.Component
+ const { superClass } = cmp
+ if (!superClass) {
+ // not a component
+ return
+ }
+ const superClassText = sourceCode.getText(superClass)
+ if (superClassText !== "Component" && superClassText !== "React.Component") {
+ // not a component
+ return
+ }
+ } else {
+ // Name must start with uppercase letter
+ if (!name?.charAt(0).match(/^[A-Z]$/)) {
+ // not a component
+ return
+ }
+ }
+
+ const fix = fixer => {
+ return [
+ fixer.insertTextBefore(
+ sourceCode.getFirstToken(cmpOrForwardRef),
+ (name && cmp.type.endsWith("Declaration") ? `const ${name} = ` : "") +
+ "observer("
+ ),
+ fixer.insertTextAfter(sourceCode.getLastToken(cmpOrForwardRef), ")")
+ ]
+ }
+ context.report({
+ node: cmp,
+ messageId: "missingObserver",
+ data: {
+ name: name || ""
+ },
+ fix
+ })
+ }
+ }
+}
+
+module.exports = {
+ meta: {
+ type: "problem",
+ fixable: "code",
+ docs: {
+ description: "prevents missing `observer` on react component",
+ recommended: true
+ },
+ messages: {
+ missingObserver: "Component `{{ name }}` is missing `observer`."
+ }
+ },
+ create
+}
diff --git a/packages/eslint-plugin-mobx/src/no-anonymous-observer.js b/packages/eslint-plugin-mobx/src/no-anonymous-observer.js
new file mode 100644
index 0000000000..5ee9b4956b
--- /dev/null
+++ b/packages/eslint-plugin-mobx/src/no-anonymous-observer.js
@@ -0,0 +1,65 @@
+"use strict"
+
+function create(context) {
+ const sourceCode = context.sourceCode ?? context.getSourceCode()
+
+ return {
+ 'CallExpression[callee.name="observer"]': observer => {
+ const cmp = observer.arguments[0]
+ if (!cmp) return
+ if (cmp?.id?.name) return
+
+ const fix = fixer => {
+ // Use name from variable for autofix
+ const name =
+ observer.parent?.type === "VariableDeclarator"
+ ? observer.parent.id.name
+ : undefined
+
+ if (!name) return
+ if (cmp.type === "ArrowFunctionExpression") {
+ const arrowToken = sourceCode.getTokenBefore(cmp.body)
+ const fixes = [
+ fixer.replaceText(arrowToken, ""),
+ fixer.insertTextBefore(cmp, `function ${name}`)
+ ]
+ if (cmp.body.type !== "BlockStatement") {
+ fixes.push(
+ fixer.insertTextBefore(cmp.body, `{ return `),
+ fixer.insertTextAfter(cmp.body, ` }`)
+ )
+ }
+ return fixes
+ }
+ if (cmp.type === "FunctionExpression") {
+ const functionToken = sourceCode.getFirstToken(cmp)
+ return fixer.replaceText(functionToken, `function ${name}`)
+ }
+ if (cmp.type === "ClassExpression") {
+ const classToken = sourceCode.getFirstToken(cmp)
+ return fixer.replaceText(classToken, `class ${name}`)
+ }
+ }
+ context.report({
+ node: cmp,
+ messageId: "observerComponentMustHaveName",
+ fix
+ })
+ }
+ }
+}
+
+module.exports = {
+ meta: {
+ type: "problem",
+ fixable: "code",
+ docs: {
+ description: "forbids anonymous functions or classes as `observer` components",
+ recommended: true
+ },
+ messages: {
+ observerComponentMustHaveName: "`observer` component must have a name."
+ }
+ },
+ create
+}
diff --git a/packages/eslint-plugin-mobx/src/unconditional-make-observable.js b/packages/eslint-plugin-mobx/src/unconditional-make-observable.js
new file mode 100644
index 0000000000..1cb9654bc4
--- /dev/null
+++ b/packages/eslint-plugin-mobx/src/unconditional-make-observable.js
@@ -0,0 +1,44 @@
+'use strict';
+
+const { findAncestor } = require('./utils.js');
+
+function create(context) {
+ return {
+ 'CallExpression[callee.name=/(makeObservable|makeAutoObservable)/]': makeObservable => {
+ // Only iterested about makeObservable(this, ...) inside constructor and not inside nested bindable function
+ const [firstArg] = makeObservable.arguments;
+ if (!firstArg) return;
+ if (firstArg.type !== 'ThisExpression') return;
+ // MethodDefinition[key.name="constructor"][kind="constructor"]
+ // FunctionExpression
+ // BlockStatement
+ // ExpressionStatement
+ // CallExpression[callee.name="makeObservable"]
+ const closestFunction = findAncestor(makeObservable, node => node.type === 'FunctionExpression' || node.type === 'FunctionDeclaration');
+ if (closestFunction?.parent?.kind !== 'constructor') return;
+ if (makeObservable.parent.parent.parent !== closestFunction) {
+ context.report({
+ node: makeObservable,
+ messageId: 'mustCallUnconditionally',
+ data: {
+ name: makeObservable.callee.name,
+ }
+ });
+ }
+ },
+ };
+}
+
+module.exports = {
+ meta: {
+ type: 'problem',
+ docs: {
+ description: 'disallows calling `makeObservable(this)` conditionally inside constructors',
+ recommended: true,
+ },
+ messages: {
+ mustCallUnconditionally: '`{{ name }}` must be called unconditionally inside constructor.',
+ }
+ },
+ create,
+}
\ No newline at end of file
diff --git a/packages/eslint-plugin-mobx/src/utils.js b/packages/eslint-plugin-mobx/src/utils.js
new file mode 100644
index 0000000000..ffc23ef684
--- /dev/null
+++ b/packages/eslint-plugin-mobx/src/utils.js
@@ -0,0 +1,29 @@
+'use strict';
+
+const mobxDecorators = new Set(['observable', 'computed', 'action', 'flow', 'override']);
+
+function isMobxDecorator(decorator) {
+ return mobxDecorators.has(decorator.expression.name) // @foo
+ || mobxDecorators.has(decorator.expression.callee?.name) // @foo()
+ || mobxDecorators.has(decorator.expression.object?.name) // @foo.bar
+}
+
+function findAncestor(node, match) {
+ const { parent } = node;
+ if (!parent) return;
+ if (match(parent)) return parent;
+ return findAncestor(parent, match);
+}
+
+function assert(expr, error) {
+ if (!expr) {
+ error ??= 'Assertion failed';
+ error = error instanceof Error ? error : new Error(error)
+ throw error;
+ }
+}
+
+module.exports = {
+ findAncestor,
+ isMobxDecorator,
+}
\ No newline at end of file
diff --git a/packages/mobx-react-lite/.eslintignore b/packages/mobx-react-lite/.eslintignore
new file mode 100644
index 0000000000..26e0cb464f
--- /dev/null
+++ b/packages/mobx-react-lite/.eslintignore
@@ -0,0 +1,3 @@
+dist/
+lib/
+es/
diff --git a/packages/mobx-react-lite/.gitignore b/packages/mobx-react-lite/.gitignore
new file mode 100644
index 0000000000..8c5a1afa25
--- /dev/null
+++ b/packages/mobx-react-lite/.gitignore
@@ -0,0 +1,12 @@
+/node_modules/
+
+es/
+lib/
+dist/
+coverage/
+
+.DS_Store
+
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
diff --git a/packages/mobx-react-lite/CHANGELOG.md b/packages/mobx-react-lite/CHANGELOG.md
new file mode 100644
index 0000000000..f86b625aa1
--- /dev/null
+++ b/packages/mobx-react-lite/CHANGELOG.md
@@ -0,0 +1,205 @@
+# mobx-react-lite
+
+## 4.1.1
+
+### Patch Changes
+
+- [`2e703388eda4ba3eefed2bf1f5ca3958980978c3`](https://github.com/mobxjs/mobx/commit/2e703388eda4ba3eefed2bf1f5ca3958980978c3) [#4584](https://github.com/mobxjs/mobx/pull/4584) Thanks [@mweststrate](https://github.com/mweststrate)! - Fix type definition of `observer`, when used in combination with a Higher Order Component. By @mbest in #4576
+
+## 4.1.0
+
+### Minor Changes
+
+- [`2587df31a1a967a6b385b7ab2d9f0d42fc94e4b0`](https://github.com/mobxjs/mobx/commit/2587df31a1a967a6b385b7ab2d9f0d42fc94e4b0) [#3985](https://github.com/mobxjs/mobx/pull/3985) Thanks [@imjordanxd](https://github.com/imjordanxd)! - \* Added React 19 support, fixes #3986
+
+## 4.0.7
+
+### Patch Changes
+
+- [`61abc53f`](https://github.com/mobxjs/mobx/commit/61abc53ff10554d1d5ce3e85466f6beda4d63fa2) [#3852](https://github.com/mobxjs/mobx/pull/3852) Thanks [@mweststrate](https://github.com/mweststrate)! - Patched the release process, forcing release to get everything in pristine state.
+
+* [`7bbb523a`](https://github.com/mobxjs/mobx/commit/7bbb523a7b81229570e0e2a176b989bfc74c4634) [#3842](https://github.com/mobxjs/mobx/pull/3842) Thanks [@r0b1n](https://github.com/r0b1n)! - Prevent warnings when using `mobx-react-lite` with Rollup
+
+## 4.0.6
+
+### Patch Changes
+
+- [`b970cbb4`](https://github.com/mobxjs/mobx/commit/b970cbb4dd2e43516d37f3f01c956cab3540d4d3) [#3830](https://github.com/mobxjs/mobx/pull/3830) Thanks [@dmitrytavern](https://github.com/dmitrytavern)! - fix #3826: components make two renders because of the different state of the snapshots
+
+* [`1b8ab199`](https://github.com/mobxjs/mobx/commit/1b8ab199df5e73d384cc40ec2d13915a690c14f3) [#3831](https://github.com/mobxjs/mobx/pull/3831) Thanks [@kitsuned](https://github.com/kitsuned)! - fix: ensure observer component name is only set when configurable
+
+## 4.0.5
+
+### Patch Changes
+
+- [`87e5dfb5`](https://github.com/mobxjs/mobx/commit/87e5dfb52a84869d62b6343887a1c8659aee595d) [#3763](https://github.com/mobxjs/mobx/pull/3763) Thanks [@mweststrate](https://github.com/mweststrate)! - Switched observer implementation from using global to local state version. Fixes #3728
+
+## 4.0.4
+
+### Patch Changes
+
+- [`3ceeb865`](https://github.com/mobxjs/mobx/commit/3ceeb8651e328c4c7211c875696b3f5269fea834) [#3732](https://github.com/mobxjs/mobx/pull/3732) Thanks [@urugator](https://github.com/urugator)! - fix: #3734: `isolateGlobalState: true` makes observer stop to react to store changes
+
+## 4.0.3
+
+### Patch Changes
+
+- [`58bb052c`](https://github.com/mobxjs/mobx/commit/58bb052ca41b8592e5bd5c3003b68ec52da53f33) [#3670](https://github.com/mobxjs/mobx/pull/3670) Thanks [@urugator](https://github.com/urugator)! - fix #3669: SSR: `useSyncExternalStore` throws due to missing `getServerSnapshot`
+
+* [`473cb3f5`](https://github.com/mobxjs/mobx/commit/473cb3f5fc8bf43abdd1c9c7857fe2820d2291fe) [#3718](https://github.com/mobxjs/mobx/pull/3718) Thanks [@mweststrate](https://github.com/mweststrate)! - - Fixed `observer` in `StrictMode` #3671
+ - **[BREAKING CHANGE]** Class component's `props`/`state`/`context` are no longer observable. Attempt to use these in any derivation other than component's `render` throws and error. For details see https://github.com/mobxjs/mobx/blob/main/packages/mobx-react/README.md#note-on-using-props-and-state-in-derivations
+ - Extending or applying `observer` classes is now explicitly forbidden
+
+## 4.0.2
+
+### Patch Changes
+
+- [`f86df867`](https://github.com/mobxjs/mobx/commit/f86df86784fa92d793ca4d1b97a3dd954355f7dd) [#3667](https://github.com/mobxjs/mobx/pull/3667) Thanks [@tony](https://github.com/tony)! - Fix package dependency for use-sync-external-store.
+
+## 4.0.1
+
+### Patch Changes
+
+- [`8e58fa95`](https://github.com/mobxjs/mobx/commit/8e58fa958908f632a2c49d22c259fda135781455) [#3664](https://github.com/mobxjs/mobx/pull/3664) Thanks [@mweststrate](https://github.com/mweststrate)! - (Hopefully) fixed release process for mobx-react-lite
+
+## 4.0.0
+
+### Major Changes
+
+- [`44a2cf42`](https://github.com/mobxjs/mobx/commit/44a2cf42dec7635f639ddbfb19202ebc710bac54) [#3590](https://github.com/mobxjs/mobx/pull/3590) Thanks [@urugator](https://github.com/urugator)! - Components now use `useSyncExternalStore`, which should prevent tearing - you have to update mobx, otherwise it should behave as previously.
+ Improved displayName/name handling as discussed in #3438.
+
+## 3.4.3
+
+### Patch Changes
+
+- [`dfeb1f5d`](https://github.com/mobxjs/mobx/commit/dfeb1f5d18acb8a995d4fa78374173d419fec16e) [#3651](https://github.com/mobxjs/mobx/pull/3651) Thanks [@urugator](https://github.com/urugator)! - fix #3650 regression clearTimers -> clearTimes
+
+## 3.4.2
+
+### Patch Changes
+
+- [`7acaf305`](https://github.com/mobxjs/mobx/commit/7acaf305f81ac5457a8de272e42dd5634a97eb88) [#3645](https://github.com/mobxjs/mobx/pull/3645) Thanks [@urugator](https://github.com/urugator)! - fix FinalizationRegistry support check #3643
+
+## 3.4.1
+
+### Patch Changes
+
+- [`4ef8ff3f`](https://github.com/mobxjs/mobx/commit/4ef8ff3f84ec8ae893d8c84031664ea388d78091) [#3598](https://github.com/mobxjs/mobx/pull/3598) Thanks [@urugator](https://github.com/urugator)! - refactor reaction tracking
+
+## 3.4.0
+
+### Minor Changes
+
+- [`4c5e75cd`](https://github.com/mobxjs/mobx/commit/4c5e75cdfec08c04ad774c70dca0629bd2c77016) [#3382](https://github.com/mobxjs/mobx/pull/3382) Thanks [@iChenLei](https://github.com/iChenLei)! - replace the deprecated react type definition with recommended type definition
+
+* [`bd4b70d8`](https://github.com/mobxjs/mobx/commit/bd4b70d8ded29673af8161aa42fb88dc4ad4420e) [#3387](https://github.com/mobxjs/mobx/pull/3387) Thanks [@mweststrate](https://github.com/mweststrate)! - Added experimental / poor man's support for React 18. Fixes #3363, #2526. Supersedes #3005
+
+ - Updated tests, test / build infra, peerDependencies to React 18
+ - **[breaking icmw upgrading to React 18]** Already deprecated hooks like `useMutableSource` will trigger warnings in React 18, which is correct and those shouldn't be used anymore.
+ - **[breaking icmw upgrading to React 18]** When using React 18, it is important that `act` is used in **unit tests** around every programmatic mutation. Without it, changes won't propagate!
+ - The React 18 support is poor man's support; that is, we don't do anything yet to play nicely with Suspense features. Although e.g. [startTransition](https://github.com/mweststrate/platform-app/commit/bdd995773ddc6551235a4d2b0a4c9bd57d30510e) basically works, MobX as is doesn't respect the Suspense model and will always reflect the latest state that is being rendered with, so tearing might occur. I think this is in theoretically addressable by using `useSyncExternalStore` and capturing the current values together with the dependency tree of every component instance. However that isn't included in this pull request 1) it would be a breaking change, whereas the current change is still compatible with React 16 and 17. 2) I want to collect use cases where the tearing leads to problems first to build a better problem understanding. If you run into the problem, please submit an issue describing your scenario, and a PR with a unit tests demonstrating the problem in simplified form. For further discussion see #2526, #3005
+
+## 3.3.0
+
+### Minor Changes
+
+- [`59b42c28`](https://github.com/mobxjs/mobx/commit/59b42c2826208435353ce6bf154ae59077edcc05) [#3282](https://github.com/mobxjs/mobx/pull/3282) Thanks [@urugator](https://github.com/urugator)! - support `observable(forwardRef(fn))`, deprecate `observable(fn, { forwardRef: true })`, solve #2527, #3267
+
+## 3.2.3
+
+### Patch Changes
+
+- [`4887d200`](https://github.com/mobxjs/mobx/commit/4887d200ba5e1563717a0b4f55e09b9984437990) [#3192](https://github.com/mobxjs/mobx/pull/3192) Thanks [@urugator](https://github.com/urugator)! - Support customizing `displayName` on anonymous components [#2721](https://github.com/mobxjs/mobx/issues/2721).
+
+## 3.2.2
+
+### Patch Changes
+
+- [`2dcfec09`](https://github.com/mobxjs/mobx/commit/2dcfec093533bd12bb564580b14ce6037ee1ebac) [#3172](https://github.com/mobxjs/mobx/pull/3172) Thanks [@urugator](https://github.com/urugator)! - support legacy context
+
+## 3.2.1
+
+### Patch Changes
+
+- [`320544a5`](https://github.com/mobxjs/mobx/commit/320544a5d0defb1a1524c83c7a5d0a9dee9de001) [#2983](https://github.com/mobxjs/mobx/pull/2983) Thanks [@urugator](https://github.com/urugator)! - Allow force update to be called infinitely times
+
+* [`10c762cc`](https://github.com/mobxjs/mobx/commit/10c762cce4871f3599bac6acc2c56776e0b4badd) [#2995](https://github.com/mobxjs/mobx/pull/2995) Thanks [@Bnaya](https://github.com/Bnaya)! - Reduce useObserver gc pressure
+
+## 3.2.0
+
+### Patch Changes
+
+- Updated dependencies [[`28f8a11d`](https://github.com/mobxjs/mobx/commit/28f8a11d8b94f1aca2eec4ae9c5f45c5ea2f4362)]:
+ - mobx@6.1.0
+
+## 3.1.7
+
+### Patch Changes
+
+- [`592e6e99`](https://github.com/mobxjs/mobx/commit/592e6e996c2d5264e162cfb0921a071c1d815c92) [#2743](https://github.com/mobxjs/mobx/pull/2743) Thanks [@vkrol](https://github.com/vkrol)! - Remove `sideEffects` section in `mobx-react-lite` `package.json`
+
+- Updated dependencies [[`6b304232`](https://github.com/mobxjs/mobx/commit/6b30423266e5418a3f20389d0bd0eae31f3384d2), [`83b84fd3`](https://github.com/mobxjs/mobx/commit/83b84fd354f2253fdd8ea556e217a92fc2633c00), [`65c7b73b`](https://github.com/mobxjs/mobx/commit/65c7b73b7f0b1a69a1a2786b5f484419d129d10b), [`989390d4`](https://github.com/mobxjs/mobx/commit/989390d46bbe9941b61ac6c6d1292f96445e7cc3), [`dea1cf18`](https://github.com/mobxjs/mobx/commit/dea1cf189b0f43929f4f626229d34a80bd10212e), [`592e6e99`](https://github.com/mobxjs/mobx/commit/592e6e996c2d5264e162cfb0921a071c1d815c92)]:
+ - mobx@6.0.5
+
+## 3.1.6
+
+### Patch Changes
+
+- [`2f3dcb27`](https://github.com/mobxjs/mobx/commit/2f3dcb274f795ffca4ae724b6b4795958620838d) Thanks [@FredyC](https://github.com/FredyC)! - Fix names of UMD exports [#2517](https://github.com/mobxjs/mobx/issues/2617)
+
+- Updated dependencies [[`79a09f49`](https://github.com/mobxjs/mobx/commit/79a09f49a9f2baddbab8d89e9a7ac07cffadf624)]:
+ - mobx@6.0.4
+
+## 3.1.5
+
+### Patch Changes
+
+- [`01a050f7`](https://github.com/mobxjs/mobx/commit/01a050f7603183e6833b7fd948adb4fbe1437f5a) Thanks [@FredyC](https://github.com/FredyC)! - Fix use of react-dom vs react-native
+
+ The `es` folder content is compiled only without transpilation to keep `utils/reactBatchedUpdates` which exists in DOM and RN versions. The bundled `esm` is still kept around too, especially the prod/dev ones that should be utilized in modern browser environments.
+
+## 3.1.4
+
+### Patch Changes
+
+- [`8bbbc7c0`](https://github.com/mobxjs/mobx/commit/8bbbc7c0df77cd79530add5db2d6a04cfe6d84b1) Thanks [@FredyC](https://github.com/FredyC)! - Fix names of dist files (for real now). Third time is the charm 😅
+
+## 3.1.3
+
+### Patch Changes
+
+- [`b7aa9d35`](https://github.com/mobxjs/mobx/commit/b7aa9d35432888ee5dd80a6c9dcbc18b04a0346c) Thanks [@FredyC](https://github.com/FredyC)! - Fixed wrong package name for dist files
+
+## 3.1.2
+
+### Patch Changes
+
+- [`5239db80`](https://github.com/mobxjs/mobx/commit/5239db80cf000026906c28a035725933d4dd6823) Thanks [@FredyC](https://github.com/FredyC)! - Fixed release with missing dist files
+
+## 3.1.1
+
+### Patch Changes
+
+- [`81a2f865`](https://github.com/mobxjs/mobx/commit/81a2f8654d9656e2e831176e45cbf926fbc364e0) Thanks [@FredyC](https://github.com/FredyC)! - ESM bundles without NODE_ENV present are available in dist folder. This useful for consumption in browser environment that supports ESM Choose either `esm.production.min.js` or `esm.development.js` from `dist` folder.
+
+## 3.1.0
+
+### Minor Changes
+
+- [`a0e5fea`](https://github.com/mobxjs/mobx-react-lite/commit/a0e5feaeede68b0bac035f60bf2a7edff3fa1269) [#329](https://github.com/mobxjs/mobx-react-lite/pull/329) Thanks [@RoystonS](https://github.com/RoystonS)! - expose `clearTimers` function to tidy up background timers, allowing test frameworks such as Jest to exit immediately
+
+### Patch Changes
+
+- [`fafb136`](https://github.com/mobxjs/mobx-react-lite/commit/fafb136cce2847b83174cbd15af803442a9a0023) [#332](https://github.com/mobxjs/mobx-react-lite/pull/332) Thanks [@Bnaya](https://github.com/Bnaya)! - Introduce alternative way for managing cleanup of reactions.
+ This is internal change and shouldn't affect functionality of the library.
+
+## 3.0.1
+
+### Patch Changes
+
+- [`570e8d5`](https://github.com/mobxjs/mobx-react-lite/commit/570e8d594bac415cf9a6c6771080fec043161d0b) [#328](https://github.com/mobxjs/mobx-react-lite/pull/328) Thanks [@mweststrate](https://github.com/mweststrate)! - If observable data changed between mount and useEffect, the render reaction would incorrectly be disposed causing incorrect suspension of upstream computed values
+
+* [`1d6f0a8`](https://github.com/mobxjs/mobx-react-lite/commit/1d6f0a8dd0ff34d7e7cc71946ed670c31193572d) [#326](https://github.com/mobxjs/mobx-react-lite/pull/326) Thanks [@FredyC](https://github.com/FredyC)! - No important changes, just checking new setup for releases.
+
+> Prior 3.0.0 see GitHub releases for changelog
diff --git a/packages/mobx-react-lite/LICENSE b/packages/mobx-react-lite/LICENSE
new file mode 100644
index 0000000000..b58becae8c
--- /dev/null
+++ b/packages/mobx-react-lite/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2015 Michel Weststrate
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/packages/mobx-react-lite/README.md b/packages/mobx-react-lite/README.md
new file mode 100644
index 0000000000..30ad4b190f
--- /dev/null
+++ b/packages/mobx-react-lite/README.md
@@ -0,0 +1,173 @@
+# mobx-react-lite
+
+[](https://circleci.com/gh/mobxjs/mobx-react-lite)
+[](https://coveralls.io/github/mobxjs/mobx-react-lite)
+[](https://npmjs.com/package/mobx-react-lite)[](https://bundlephobia.com/result?p=mobx-react-lite)
+[](https://github.com/mobxjs/mobx/discussions)
+[](https://changelogs.xyz/mobx-react-lite)
+
+This is a lighter version of [mobx-react](https://github.com/mobxjs/mobx-react) which supports React **functional components only** and as such makes the library slightly faster and smaller (_only 1.5kB gzipped_). Note however that it is possible to use `` inside the render of class components.
+Unlike `mobx-react`, it doesn't `Provider`/`inject`, as `useContext` can be used instead.
+
+## Compatibility table (major versions)
+
+| mobx | mobx-react-lite | Browser |
+| ---- | --------------- | ---------------------------------------------- |
+| 6 | 3 | Modern browsers (IE 11+ in compatibility mode) |
+| 5 | 2 | Modern browsers |
+| 4 | 2 | IE 11+, RN w/o Proxy support |
+
+`mobx-react-lite` requires React 16.8 or higher.
+
+## User Guide 👉 https://mobx.js.org/react-integration.html
+
+---
+
+## API reference ⚒
+
+### **`observer
(baseComponent: FunctionComponent
): FunctionComponent
`**
+
+The observer converts a component into a reactive component, which tracks which observables are used automatically and re-renders the component when one of these values changes.
+Can only be used for function components. For class component support see the `mobx-react` package.
+
+### **`{renderFn}`**
+
+Is a React component, which applies observer to an anonymous region in your component. `` can be used both inside class and function components.
+
+### **`useLocalObservable(initializer: () => T, annotations?: AnnotationsMap): T`**
+
+Creates an observable object with the given properties, methods and computed values.
+
+Note that computed values cannot directly depend on non-observable values, but only on observable values, so it might be needed to sync properties into the observable using `useEffect` (see the example below at `useAsObservableSource`).
+
+`useLocalObservable` is a short-hand for:
+
+`const [state] = useState(() => observable(initializer(), annotations, { autoBind: true }))`
+
+### **`enableStaticRendering(enable: true)`**
+
+Call `enableStaticRendering(true)` when running in an SSR environment, in which `observer` wrapped components should never re-render, but cleanup after the first rendering automatically. Use `isUsingStaticRendering()` to inspect the current setting.
+
+---
+
+## Deprecated APIs
+
+### **`useObserver(fn: () => T, baseComponentName = "observed", options?: IUseObserverOptions): T`** (deprecated)
+
+_This API is deprecated in 3.\*. It is often used wrong (e.g. to select data rather than for rendering, and `` better decouples the rendering from the component updates_
+
+```ts
+interface IUseObserverOptions {
+ // optional custom hook that should make a component re-render (or not) upon changes
+ // Supported in 2.x only
+ useForceUpdate: () => () => void
+}
+```
+
+It allows you to use an observer like behaviour, but still allowing you to optimize the component in any way you want (e.g. using memo with a custom areEqual, using forwardRef, etc.) and to declare exactly the part that is observed (the render phase).
+
+### **`useLocalStore(initializer: () => T, source?: S): T`** (deprecated)
+
+_This API is deprecated in 3.\*. Use `useLocalObservable` instead. They do roughly the same, but `useLocalObservable` accepts an set of annotations as second argument, rather than a `source` object. Using `source` is not recommended, see the deprecation message at `useAsObservableSource` for details_
+
+Local observable state can be introduced by using the useLocalStore hook, that runs its initializer function once to create an observable store and keeps it around for a lifetime of a component.
+
+The annotations are similar to the annotations that are passed in to MobX's [`observable`](https://mobx.js.org/observable.html#available-annotations) API, and can be used to override the automatic member inference of specific fields.
+
+### **`useAsObservableSource(source: T): T`** (deprecated)
+
+The useAsObservableSource hook can be used to turn any set of values into an observable object that has a stable reference (the same object is returned every time from the hook).
+
+_This API is deprecated in 3.\* as it relies on observables to be updated during rendering which is an anti-pattern. Instead, use `useEffect` to synchronize non-observable values with values. Example:_
+
+```javascript
+// Before:
+function Measurement({ unit }) {
+ const observableProps = useAsObservableSource({ unit })
+ const state = useLocalStore(() => ({
+ length: 0,
+ get lengthWithUnit() {
+ // lengthWithUnit can only depend on observables, hence the above conversion with `useAsObservableSource`
+ return observableProps.unit === "inch"
+ ? `${this.length / 2.54} inch`
+ : `${this.length} cm`
+ }
+ }))
+
+ return
{state.lengthWithUnit}
+}
+
+// After:
+function Measurement({ unit }) {
+ const state = useLocalObservable(() => ({
+ unit, // the initial unit
+ length: 0,
+ get lengthWithUnit() {
+ // lengthWithUnit can only depend on observables, hence the above conversion with `useAsObservableSource`
+ return this.unit === "inch" ? `${this.length / 2.54} inch` : `${this.length} cm`
+ }
+ }))
+
+ useEffect(() => {
+ // sync the unit from 'props' into the observable 'state'
+ state.unit = unit
+ }, [unit])
+
+ return
{state.lengthWithUnit}
+}
+```
+
+Note that, at your own risk, it is also possible to not use `useEffect`, but do `state.unit = unit` instead in the rendering.
+This is closer to the old behavior, but React will warn correctly about this if this would affect the rendering of other components.
+
+## Observer batching (deprecated)
+
+_Note: configuring observer batching is only needed when using `mobx-react-lite` 2.0.* or 2.1.*. From 2.2 onward it will be configured automatically based on the availability of react-dom / react-native packages_
+
+[Check out the elaborate explanation](https://github.com/mobxjs/mobx-react/pull/787#issuecomment-573599793).
+
+In short without observer batching the React doesn't guarantee the order component rendering in some cases. We highly recommend that you configure batching to avoid these random surprises.
+
+Import one of these before any React rendering is happening, typically `index.js/ts`. For Jest tests you can utilize [setupFilesAfterEnv](https://jestjs.io/docs/en/configuration#setupfilesafterenv-array).
+
+**React DOM:**
+
+> import 'mobx-react-lite/batchingForReactDom'
+
+**React Native:**
+
+> import 'mobx-react-lite/batchingForReactNative'
+
+### Opt-out
+
+To opt-out from batching in some specific cases, simply import the following to silence the warning.
+
+> import 'mobx-react-lite/batchingOptOut'
+
+### Custom batched updates
+
+Above imports are for a convenience to utilize standard versions of batching. If you for some reason have customized version of batched updates, you can do the following instead.
+
+```js
+import { observerBatching } from "mobx-react-lite"
+observerBatching(customBatchedUpdates)
+```
+
+## Testing
+
+Running the full test suite now requires node 14+
+But the library itself does not have this limitation
+
+In order to avoid memory leaks due to aborted renders from React
+fiber handling or React `StrictMode`, on environments that does not support [FinalizationRegistry](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/FinalizationRegistry), this library needs to
+run timers to tidy up the remains of the aborted renders.
+
+This can cause issues with test frameworks such as Jest
+which require that timers be cleaned up before the tests
+can exit.
+
+### **`clearTimers()`**
+
+Call `clearTimers()` in the `afterEach` of your tests to ensure
+that `mobx-react-lite` cleans up immediately and allows tests
+to exit.
diff --git a/packages/mobx-react-lite/__tests__/.eslintrc.yaml b/packages/mobx-react-lite/__tests__/.eslintrc.yaml
new file mode 100644
index 0000000000..111b92a95a
--- /dev/null
+++ b/packages/mobx-react-lite/__tests__/.eslintrc.yaml
@@ -0,0 +1,5 @@
+env:
+ jest: true
+rules:
+ "react/display-name": "off"
+ "react/prop-types": "off"
diff --git a/packages/mobx-react-lite/__tests__/ObserverComponent.test.tsx b/packages/mobx-react-lite/__tests__/ObserverComponent.test.tsx
new file mode 100644
index 0000000000..b533ed756e
--- /dev/null
+++ b/packages/mobx-react-lite/__tests__/ObserverComponent.test.tsx
@@ -0,0 +1,57 @@
+import mockConsole from "jest-mock-console"
+import * as mobx from "mobx"
+import * as React from "react"
+import { act, cleanup, render } from "@testing-library/react"
+
+import { Observer } from "../src"
+
+afterEach(cleanup)
+
+describe("regions should rerender component", () => {
+ const execute = () => {
+ const data = mobx.observable.box("hi")
+ const Comp = () => (
+
+ {() => {data.get()}}
+
{data.get()}
+
+ )
+ return { ...render(), data }
+ }
+
+ test("init state is correct", () => {
+ const { container } = execute()
+ expect(container.querySelector("span")!.innerHTML).toBe("hi")
+ expect(container.querySelector("li")!.innerHTML).toBe("hi")
+ })
+
+ test("set the data to hello", async () => {
+ const { container, data } = execute()
+ act(() => {
+ data.set("hello")
+ })
+ expect(container.querySelector("span")!.innerHTML).toBe("hello")
+ expect(container.querySelector("li")!.innerHTML).toBe("hi")
+ })
+})
+
+it("renders null if no children/render prop is supplied a function", () => {
+ const restoreConsole = mockConsole()
+ const Comp = () =>
+ const { container } = render()
+ expect(container).toMatchInlineSnapshot(``)
+ restoreConsole()
+})
+
+it.skip("prop types checks for children/render usage", () => {
+ const Comp = () => (
+ children}>{() => children}
+ )
+ const restoreConsole = mockConsole("error")
+ render()
+ // tslint:disable-next-line:no-console
+ expect(console.error).toHaveBeenCalledWith(
+ expect.stringContaining("Do not use children and render in the same time")
+ )
+ restoreConsole()
+})
diff --git a/packages/mobx-react-lite/__tests__/__snapshots__/observer.test.tsx.snap b/packages/mobx-react-lite/__tests__/__snapshots__/observer.test.tsx.snap
new file mode 100644
index 0000000000..aa7385fc12
--- /dev/null
+++ b/packages/mobx-react-lite/__tests__/__snapshots__/observer.test.tsx.snap
@@ -0,0 +1,115 @@
+// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
+
+exports[`changing state in render should fail 1`] = `
+
+
+ 4
+
+
+`;
+
+exports[`changing state in render should fail 2`] = `
+
+
+ 4
+
+
+`;
+
+exports[`issue 12 init state is correct 1`] = `
+
+
+
+ coffee
+ !
+
+
+ tea
+
+
+
+`;
+
+exports[`issue 12 init state is correct 2`] = `
+
+
+
+ coffee
+ !
+
+
+ tea
+
+
+
+`;
+
+exports[`issue 12 run transaction 1`] = `
+
+
+
+ soup
+
+
+
+`;
+
+exports[`issue 12 run transaction 2`] = `
+
+
+
+ soup
+
+
+
+`;
+
+exports[`issue 309 isObserverBatched is still defined and yields true by default 1`] = `
+[MockFunction] {
+ "calls": [
+ [
+ "[MobX] Deprecated",
+ ],
+ ],
+ "results": [
+ {
+ "type": "return",
+ "value": undefined,
+ },
+ ],
+}
+`;
+
+exports[`issue 309 isObserverBatched is still defined and yields true by default 2`] = `
+[MockFunction] {
+ "calls": [
+ [
+ "[MobX] Deprecated",
+ ],
+ ],
+ "results": [
+ {
+ "type": "return",
+ "value": undefined,
+ },
+ ],
+}
+`;
+
+exports[`observer(cmp, { forwardRef: true }) + useImperativeHandle 1`] = `
+[MockFunction] {
+ "calls": [
+ [
+ "[mobx-react-lite] \`observer(fn, { forwardRef: true })\` is deprecated, use \`observer(React.forwardRef(fn))\`",
+ ],
+ ],
+ "results": [
+ {
+ "type": "return",
+ "value": undefined,
+ },
+ ],
+}
+`;
+
+exports[`useImperativeHandle and forwardRef should work with useObserver 1`] = `[MockFunction]`;
diff --git a/packages/mobx-react-lite/__tests__/__snapshots__/printDebugValue.test.ts.snap b/packages/mobx-react-lite/__tests__/__snapshots__/printDebugValue.test.ts.snap
new file mode 100644
index 0000000000..5cd288b65e
--- /dev/null
+++ b/packages/mobx-react-lite/__tests__/__snapshots__/printDebugValue.test.ts.snap
@@ -0,0 +1,26 @@
+// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
+
+exports[`printDebugValue 1`] = `
+{
+ "dependencies": [
+ {
+ "name": "ObservableObject@1.euro",
+ },
+ {
+ "dependencies": [
+ {
+ "name": "ObservableObject@1.euro",
+ },
+ ],
+ "name": "ObservableObject@1.pound",
+ },
+ ],
+ "name": "Autorun@2",
+}
+`;
+
+exports[`printDebugValue 2`] = `
+{
+ "name": "Autorun@2",
+}
+`;
diff --git a/packages/mobx-react-lite/__tests__/__snapshots__/useAsObservableSource.deprecated.test.tsx.snap b/packages/mobx-react-lite/__tests__/__snapshots__/useAsObservableSource.deprecated.test.tsx.snap
new file mode 100644
index 0000000000..88ba3f79b2
--- /dev/null
+++ b/packages/mobx-react-lite/__tests__/__snapshots__/useAsObservableSource.deprecated.test.tsx.snap
@@ -0,0 +1,24 @@
+// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
+
+exports[`base useAsObservableSource should work with 1`] = `
+[MockFunction] {
+ "calls": [
+ [
+ "[mobx-react-lite] 'useAsObservableSource' is deprecated, please store the values directly in an observable, for example by using 'useLocalObservable', and sync future updates using 'useEffect' when needed. See the README for examples.",
+ ],
+ [
+ "[mobx-react-lite] 'useLocalStore' is deprecated, use 'useLocalObservable' instead.",
+ ],
+ ],
+ "results": [
+ {
+ "type": "return",
+ "value": undefined,
+ },
+ {
+ "type": "return",
+ "value": undefined,
+ },
+ ],
+}
+`;
diff --git a/packages/mobx-react-lite/__tests__/__snapshots__/useLocalStore.deprecated.test.tsx.snap b/packages/mobx-react-lite/__tests__/__snapshots__/useLocalStore.deprecated.test.tsx.snap
new file mode 100644
index 0000000000..5e9338971b
--- /dev/null
+++ b/packages/mobx-react-lite/__tests__/__snapshots__/useLocalStore.deprecated.test.tsx.snap
@@ -0,0 +1,33 @@
+// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
+
+exports[`base useLocalStore should work 1`] = `
+[MockFunction] {
+ "calls": [
+ [
+ "[mobx-react-lite] 'useLocalStore' is deprecated, use 'useLocalObservable' instead.",
+ ],
+ ],
+ "results": [
+ {
+ "type": "return",
+ "value": undefined,
+ },
+ ],
+}
+`;
+
+exports[`is used to keep observable within component body with props and useObserver 1`] = `
+[MockFunction] {
+ "calls": [
+ [
+ "[mobx-react-lite] 'useAsObservableSource' is deprecated, please store the values directly in an observable, for example by using 'useLocalObservable', and sync future updates using 'useEffect' when needed. See the README for examples.",
+ ],
+ ],
+ "results": [
+ {
+ "type": "return",
+ "value": undefined,
+ },
+ ],
+}
+`;
diff --git a/packages/mobx-react-lite/__tests__/api.test.ts b/packages/mobx-react-lite/__tests__/api.test.ts
new file mode 100644
index 0000000000..a2f7cf57e6
--- /dev/null
+++ b/packages/mobx-react-lite/__tests__/api.test.ts
@@ -0,0 +1,25 @@
+const api = require("../src/index.ts")
+
+test("correct api should be exposed", function () {
+ expect(
+ Object.keys(api)
+ .filter(key => api[key] !== undefined)
+ .sort()
+ ).toEqual(
+ [
+ "isUsingStaticRendering",
+ "enableStaticRendering",
+ "observer",
+ "Observer",
+ "useLocalObservable",
+ "useLocalStore",
+ "useAsObservableSource",
+ "clearTimers",
+ "useObserver",
+ "isObserverBatched",
+ "observerBatching",
+ "useStaticRendering",
+ "_observerFinalizationRegistry"
+ ].sort()
+ )
+})
diff --git a/packages/mobx-react-lite/__tests__/assertEnvironment.test.ts b/packages/mobx-react-lite/__tests__/assertEnvironment.test.ts
new file mode 100644
index 0000000000..33c414e844
--- /dev/null
+++ b/packages/mobx-react-lite/__tests__/assertEnvironment.test.ts
@@ -0,0 +1,21 @@
+afterEach(() => {
+ jest.resetModules()
+ jest.resetAllMocks()
+})
+
+it("throws if react is not installed", () => {
+ jest.mock("react", () => ({}))
+ expect(() => require("../src/utils/assertEnvironment.ts")).toThrowErrorMatchingInlineSnapshot(
+ `"mobx-react-lite requires React with Hooks support"`
+ )
+})
+
+it("throws if mobx is not installed", () => {
+ jest.mock("react", () => ({ useState: true }))
+ jest.mock("mobx", () => ({}))
+ expect(() => require("../src/utils/assertEnvironment.ts")).toThrowErrorMatchingInlineSnapshot(
+ `"mobx-react-lite@3 requires mobx at least version 6 to be available"`
+ )
+})
+
+export default "Cannot use import statement outside a module"
diff --git a/packages/mobx-react-lite/__tests__/enforceActions.test.tsx b/packages/mobx-react-lite/__tests__/enforceActions.test.tsx
new file mode 100644
index 0000000000..c26c51b1f0
--- /dev/null
+++ b/packages/mobx-react-lite/__tests__/enforceActions.test.tsx
@@ -0,0 +1,74 @@
+import * as mobx from "mobx"
+import { _resetGlobalState } from "mobx"
+import * as React from "react"
+import { useEffect } from "react"
+import { observer, useLocalObservable } from "mobx-react"
+import { render } from "@testing-library/react"
+
+let consoleWarnMock: jest.SpyInstance | undefined
+afterEach(() => {
+ consoleWarnMock?.mockRestore()
+})
+
+afterEach(() => {
+ _resetGlobalState()
+})
+
+describe("enforcing actions", () => {
+ it("'never' should work", () => {
+ consoleWarnMock = jest.spyOn(console, "warn").mockImplementation(() => {})
+ mobx.configure({ enforceActions: "never" })
+
+ const Parent = observer(() => {
+ const obs = useLocalObservable(() => ({
+ x: 1
+ }))
+ useEffect(() => {
+ obs.x++
+ }, [])
+
+ return
)
+
+ // Render our observing component wrapped in StrictMode
+ const rendering = render(
+
+
+
+ )
+
+ // That will have caused our component to have been rendered
+ // more than once, but when we unmount it'll only unmount once.
+ rendering.unmount()
+
+ // Trigger a change to the observable. If the reactions were
+ // not disposed correctly, we'll see some console errors from
+ // React StrictMode because we're calling state mutators to
+ // trigger an update.
+ const restoreConsole = mockConsole()
+ try {
+ act(() => {
+ store.count++
+ })
+
+ // Check to see if any console errors were reported.
+ // tslint:disable-next-line: no-console
+ expect(console.error).not.toHaveBeenCalled()
+ } finally {
+ restoreConsole()
+ }
+})
+
+test(`observable changes before first commit are not lost`, async () => {
+ const store = mobx.observable({ value: "initial" })
+
+ const TestComponent = () =>
+ useObserver(() => {
+ const res =
{store.value}
+ // Change our observable. This is happening between the initial render of
+ // our component and its initial commit, so it isn't fully mounted yet.
+ // We want to ensure that the change isn't lost.
+ store.value = "changed"
+ return res
+ })
+
+ const rootNode = document.createElement("div")
+ document.body.appendChild(rootNode)
+
+ const rendering = render(
+
+
+
+ )
+
+ expect(rendering.baseElement.textContent).toBe("changed")
+})
diff --git a/packages/mobx-react-lite/__tests__/strictAndConcurrentModeUsingFinalizationRegistry.test.tsx b/packages/mobx-react-lite/__tests__/strictAndConcurrentModeUsingFinalizationRegistry.test.tsx
new file mode 100644
index 0000000000..7bfac4c0ce
--- /dev/null
+++ b/packages/mobx-react-lite/__tests__/strictAndConcurrentModeUsingFinalizationRegistry.test.tsx
@@ -0,0 +1,70 @@
+import { cleanup, render, waitFor } from "@testing-library/react"
+import * as mobx from "mobx"
+import * as React from "react"
+import { useObserver } from "../src/useObserver"
+import gc from "expose-gc/function"
+import { observerFinalizationRegistry } from "../src/utils/observerFinalizationRegistry"
+
+if (typeof globalThis.FinalizationRegistry !== "function") {
+ throw new Error("This test must run with node >= 14")
+}
+
+expect(observerFinalizationRegistry).toBeInstanceOf(globalThis.FinalizationRegistry)
+
+afterEach(cleanup)
+
+function nextFrame() {
+ return new Promise(accept => setTimeout(accept, 1))
+}
+
+async function gc_cycle() {
+ await nextFrame()
+ gc()
+ await nextFrame()
+}
+
+test("uncommitted components should not leak observations", async () => {
+ jest.setTimeout(30_000)
+ const store = mobx.observable({ count1: 0, count2: 0 })
+
+ // Track whether counts are observed
+ let count1IsObserved = false
+ let count2IsObserved = false
+ mobx.onBecomeObserved(store, "count1", () => (count1IsObserved = true))
+ mobx.onBecomeUnobserved(store, "count1", () => (count1IsObserved = false))
+ mobx.onBecomeObserved(store, "count2", () => (count2IsObserved = true))
+ mobx.onBecomeUnobserved(store, "count2", () => (count2IsObserved = false))
+
+ const TestComponent1 = () => useObserver(() =>
)
+
+ // Render, then remove only #2
+ const rendering = render(
+
+
+
+
+ )
+ rendering.rerender(
+
+
+
+ )
+
+ // Allow any reaction-disposal cleanup timers to run
+ const skip = Math.max(REGISTRY_FINALIZE_AFTER, REGISTRY_SWEEP_INTERVAL)
+ fakeNow += skip
+ jest.advanceTimersByTime(skip)
+
+ // count1 should still be being observed by Component1,
+ // but count2 should have had its reaction cleaned up.
+ expect(count1IsObserved).toBeTruthy()
+ expect(count2IsObserved).toBeFalsy()
+})
+
+test("cleanup timer should not clean up recently-pended reactions", () => {
+ // If we're not careful with timings, it's possible to get the
+ // following scenario:
+ // 1. Component instance A is being created; it renders, we put its reaction R1 into the cleanup list
+ // 2. Strict/Concurrent mode causes that render to be thrown away
+ // 3. Component instance A is being created; it renders, we put its reaction R2 into the cleanup list
+ // 4. The MobX reaction timer from 5 seconds ago kicks in and cleans up all reactions from uncommitted
+ // components, including R1 and R2
+ // 5. The commit phase runs for component A, but reaction R2 has already been disposed. Game over.
+
+ // This unit test attempts to replicate that scenario:
+ registry.finalizeAllImmediately()
+
+ // Unfortunately, Jest fake timers don't mock out Date.now, so we fake
+ // that out in parallel to Jest useFakeTimers
+ const fakeNow = Date.now()
+ jest.useFakeTimers()
+ jest.spyOn(Date, "now").mockImplementation(() => fakeNow)
+
+ const store = mobx.observable({ count: 0 })
+
+ // Track whether the count is observed
+ let countIsObserved = false
+ mobx.onBecomeObserved(store, "count", () => (countIsObserved = true))
+ mobx.onBecomeUnobserved(store, "count", () => (countIsObserved = false))
+
+ const TestComponent1 = () => useObserver(() =>
{store.count}
)
+
+ const rendering = render(
+ // We use StrictMode here, but it would be helpful to switch this to use real
+ // concurrent mode: we don't have a true async render right now so this test
+ // isn't as thorough as it could be.
+
+
+
+ )
+
+ // We need to trigger our cleanup timer to run. We can't do this simply
+ // by running all jest's faked timers as that would allow the scheduled
+ // `useEffect` calls to run, and we want to simulate our cleanup timer
+ // getting in between those stages.
+
+ // We force our cleanup loop to run even though enough time hasn't _really_
+ // elapsed. In theory, it won't do anything because not enough time has
+ // elapsed since the reactions were queued, and so they won't be disposed.
+ registry.sweep()
+
+ // Advance time enough to allow any timer-queued effects to run
+ jest.advanceTimersByTime(500)
+
+ // Now allow the useEffect calls to run to completion.
+ act(() => {
+ // no-op, but triggers effect flushing
+ })
+
+ // count should still be observed
+ expect(countIsObserved).toBeTruthy()
+})
+
+// TODO: MWE: disabled during React 18 migration, not sure how to express it icmw with testing-lib,
+// and using new React renderRoot will fail icmw JSDOM
+test.skip("component should recreate reaction if necessary", () => {
+ // There _may_ be very strange cases where the reaction gets tidied up
+ // but is actually still needed. This _really_ shouldn't happen.
+ // e.g. if we're using Suspense and the component starts to render,
+ // but then gets paused for 60 seconds, and then comes back to life.
+ // With the implementation of React at the time of writing this, React
+ // will actually fully re-render that component (discarding previous
+ // hook slots) before going ahead with a commit, but it's unwise
+ // to depend on such an implementation detail. So we must cope with
+ // the component having had its reaction tidied and still going on to
+ // be committed. In that case we recreate the reaction and force
+ // an update.
+
+ // This unit test attempts to replicate that scenario:
+
+ registry.finalizeAllImmediately()
+
+ // Unfortunately, Jest fake timers don't mock out Date.now, so we fake
+ // that out in parallel to Jest useFakeTimers
+ let fakeNow = Date.now()
+ jest.useFakeTimers()
+ jest.spyOn(Date, "now").mockImplementation(() => fakeNow)
+
+ const store = mobx.observable({ count: 0 })
+
+ // Track whether the count is observed
+ let countIsObserved = false
+ mobx.onBecomeObserved(store, "count", () => (countIsObserved = true))
+ mobx.onBecomeUnobserved(store, "count", () => (countIsObserved = false))
+
+ const TestComponent1 = () => useObserver(() =>
{store.count}
)
+
+ const rendering = render(
+
+
+
+ )
+
+ // We need to trigger our cleanup timer to run. We don't want
+ // to allow Jest's effects to run, however: we want to simulate the
+ // case where the component is rendered, then the reaction gets cleaned up,
+ // and _then_ the component commits.
+
+ // Force everything to be disposed.
+ const skip = Math.max(REGISTRY_FINALIZE_AFTER, REGISTRY_SWEEP_INTERVAL)
+ fakeNow += skip
+ registry.sweep()
+
+ // The reaction should have been cleaned up.
+ expect(countIsObserved).toBeFalsy()
+
+ // Whilst nobody's looking, change the observable value
+ store.count = 42
+
+ // Now allow the useEffect calls to run to completion,
+ // re-awakening the component.
+ jest.advanceTimersByTime(500)
+ act(() => {
+ // no-op, but triggers effect flushing
+ })
+
+ // count should be observed once more.
+ expect(countIsObserved).toBeTruthy()
+ // and the component should have rendered enough to
+ // show the latest value, which was set whilst it
+ // wasn't even looking.
+ expect(rendering.baseElement.textContent).toContain("42")
+})
diff --git a/packages/mobx-react-lite/__tests__/transactions.test.tsx b/packages/mobx-react-lite/__tests__/transactions.test.tsx
new file mode 100644
index 0000000000..8fede3c854
--- /dev/null
+++ b/packages/mobx-react-lite/__tests__/transactions.test.tsx
@@ -0,0 +1,68 @@
+import * as mobx from "mobx"
+import * as React from "react"
+import { act, render } from "@testing-library/react"
+
+import { observer } from "../src"
+
+test("mobx issue 50", done => {
+ const foo = {
+ a: mobx.observable.box(true),
+ b: mobx.observable.box(false),
+ c: mobx.computed((): boolean => {
+ // console.log("evaluate c")
+ return foo.b.get()
+ })
+ }
+ function flipStuff() {
+ mobx.transaction(() => {
+ foo.a.set(!foo.a.get())
+ foo.b.set(!foo.b.get())
+ })
+ }
+ let asText = ""
+ let willReactCount = 0
+ mobx.autorun(() => (asText = [foo.a.get(), foo.b.get(), foo.c.get()].join(":")))
+ const Test = observer(() => {
+ willReactCount++
+ return
& React.RefAttributes
+ >
+ >
+ : never /* forwardRef set for a non forwarding component */
+ : C & { displayName: string }
+
+// n.b. base case is not used for actual typings or exported in the typing files
+export function observer
+ | React.ForwardRefExoticComponent & React.RefAttributes>,
+ // TODO remove in next major
+ options?: IObserverOptions
+) {
+ if (process.env.NODE_ENV !== "production" && warnObserverOptionsDeprecated && options) {
+ warnObserverOptionsDeprecated = false
+ console.warn(
+ `[mobx-react-lite] \`observer(fn, { forwardRef: true })\` is deprecated, use \`observer(React.forwardRef(fn))\``
+ )
+ }
+
+ if (ReactMemoSymbol && baseComponent["$$typeof"] === ReactMemoSymbol) {
+ throw new Error(
+ `[mobx-react-lite] You are trying to use \`observer\` on a function component wrapped in either another \`observer\` or \`React.memo\`. The observer already applies 'React.memo' for you.`
+ )
+ }
+
+ // The working of observer is explained step by step in this talk: https://www.youtube.com/watch?v=cPF4iBedoF0&feature=youtu.be&t=1307
+ if (isUsingStaticRendering()) {
+ return baseComponent
+ }
+
+ let useForwardRef = options?.forwardRef ?? false
+ let render = baseComponent
+
+ const baseComponentName = baseComponent.displayName || baseComponent.name
+
+ // If already wrapped with forwardRef, unwrap,
+ // so we can patch render and apply memo
+ if (ReactForwardRefSymbol && baseComponent["$$typeof"] === ReactForwardRefSymbol) {
+ useForwardRef = true
+ render = baseComponent["render"]
+ if (typeof render !== "function") {
+ throw new Error(
+ `[mobx-react-lite] \`render\` property of ForwardRef was not a function`
+ )
+ }
+ }
+
+ let observerComponent = (props: any, ref: React.Ref) => {
+ return useObserver(() => render(props, ref), baseComponentName)
+ }
+
+ // Inherit original name and displayName, see #3438
+ ;(observerComponent as React.FunctionComponent).displayName = baseComponent.displayName
+
+ if (isFunctionNameConfigurable) {
+ Object.defineProperty(observerComponent, "name", {
+ value: baseComponent.name,
+ writable: true,
+ configurable: true
+ })
+ }
+
+ // Support legacy context: `contextTypes` must be applied before `memo`
+ if ((baseComponent as any).contextTypes) {
+ ;(observerComponent as React.FunctionComponent).contextTypes = (
+ baseComponent as any
+ ).contextTypes
+
+ if (process.env.NODE_ENV !== "production" && warnLegacyContextTypes) {
+ warnLegacyContextTypes = false
+ console.warn(
+ `[mobx-react-lite] Support for Legacy Context in function components will be removed in the next major release.`
+ )
+ }
+ }
+
+ if (useForwardRef) {
+ // `forwardRef` must be applied prior `memo`
+ // `forwardRef(observer(cmp))` throws:
+ // "forwardRef requires a render function but received a `memo` component. Instead of forwardRef(memo(...)), use memo(forwardRef(...))"
+ observerComponent = forwardRef(observerComponent)
+ }
+
+ // memo; we are not interested in deep updates
+ // in props; we assume that if deep objects are changed,
+ // this is in observables, which would have been tracked anyway
+ observerComponent = memo(observerComponent)
+
+ copyStaticProperties(baseComponent, observerComponent)
+
+ if ("production" !== process.env.NODE_ENV) {
+ Object.defineProperty(observerComponent, "contextTypes", {
+ set() {
+ throw new Error(
+ `[mobx-react-lite] \`${
+ this.displayName || this.type?.displayName || this.type?.name || "Component"
+ }.contextTypes\` must be set before applying \`observer\`.`
+ )
+ }
+ })
+ }
+
+ return observerComponent
+}
+
+// based on https://github.com/mridgway/hoist-non-react-statics/blob/master/src/index.js
+const hoistBlackList: any = {
+ $$typeof: true,
+ render: true,
+ compare: true,
+ type: true,
+ // Don't redefine `displayName`,
+ // it's defined as getter-setter pair on `memo` (see #3192).
+ displayName: true
+}
+
+function copyStaticProperties(base: any, target: any) {
+ Object.keys(base).forEach(key => {
+ if (!hoistBlackList[key]) {
+ Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(base, key)!)
+ }
+ })
+}
diff --git a/packages/mobx-react-lite/src/staticRendering.ts b/packages/mobx-react-lite/src/staticRendering.ts
new file mode 100644
index 0000000000..525bdbb733
--- /dev/null
+++ b/packages/mobx-react-lite/src/staticRendering.ts
@@ -0,0 +1,9 @@
+let globalIsUsingStaticRendering = false
+
+export function enableStaticRendering(enable: boolean) {
+ globalIsUsingStaticRendering = enable
+}
+
+export function isUsingStaticRendering(): boolean {
+ return globalIsUsingStaticRendering
+}
diff --git a/packages/mobx-react-lite/src/useAsObservableSource.ts b/packages/mobx-react-lite/src/useAsObservableSource.ts
new file mode 100644
index 0000000000..ffda077f68
--- /dev/null
+++ b/packages/mobx-react-lite/src/useAsObservableSource.ts
@@ -0,0 +1,18 @@
+import { useDeprecated } from "./utils/utils"
+import { observable, runInAction } from "mobx"
+import { useState } from "react"
+
+export function useAsObservableSource(current: TSource): TSource {
+ if ("production" !== process.env.NODE_ENV)
+ useDeprecated(
+ "[mobx-react-lite] 'useAsObservableSource' is deprecated, please store the values directly in an observable, for example by using 'useLocalObservable', and sync future updates using 'useEffect' when needed. See the README for examples."
+ )
+ // We're deliberately not using idiomatic destructuring for the hook here.
+ // Accessing the state value as an array element prevents TypeScript from generating unnecessary helpers in the resulting code.
+ // For further details, please refer to mobxjs/mobx#3842.
+ const res = useState(() => observable(current, {}, { deep: false }))[0]
+ runInAction(() => {
+ Object.assign(res, current)
+ })
+ return res
+}
diff --git a/packages/mobx-react-lite/src/useLocalObservable.ts b/packages/mobx-react-lite/src/useLocalObservable.ts
new file mode 100644
index 0000000000..fde49eac61
--- /dev/null
+++ b/packages/mobx-react-lite/src/useLocalObservable.ts
@@ -0,0 +1,9 @@
+import { observable, AnnotationsMap } from "mobx"
+import { useState } from "react"
+
+export function useLocalObservable>(
+ initializer: () => TStore,
+ annotations?: AnnotationsMap
+): TStore {
+ return useState(() => observable(initializer(), annotations, { autoBind: true }))[0]
+}
diff --git a/packages/mobx-react-lite/src/useLocalStore.ts b/packages/mobx-react-lite/src/useLocalStore.ts
new file mode 100644
index 0000000000..9060ee1110
--- /dev/null
+++ b/packages/mobx-react-lite/src/useLocalStore.ts
@@ -0,0 +1,23 @@
+import { observable } from "mobx"
+import { useState } from "react"
+
+import { useDeprecated } from "./utils/utils"
+import { useAsObservableSource } from "./useAsObservableSource"
+
+export function useLocalStore>(initializer: () => TStore): TStore
+export function useLocalStore, TSource extends object>(
+ initializer: (source: TSource) => TStore,
+ current: TSource
+): TStore
+export function useLocalStore, TSource extends object>(
+ initializer: (source?: TSource) => TStore,
+ current?: TSource
+): TStore {
+ if ("production" !== process.env.NODE_ENV) {
+ useDeprecated(
+ "[mobx-react-lite] 'useLocalStore' is deprecated, use 'useLocalObservable' instead."
+ )
+ }
+ const source = current && useAsObservableSource(current)
+ return useState(() => observable(initializer(source), undefined, { autoBind: true }))[0]
+}
diff --git a/packages/mobx-react-lite/src/useObserver.ts b/packages/mobx-react-lite/src/useObserver.ts
new file mode 100644
index 0000000000..886073467f
--- /dev/null
+++ b/packages/mobx-react-lite/src/useObserver.ts
@@ -0,0 +1,119 @@
+import { Reaction } from "mobx"
+import React from "react"
+import { printDebugValue } from "./utils/printDebugValue"
+import { isUsingStaticRendering } from "./staticRendering"
+import { observerFinalizationRegistry } from "./utils/observerFinalizationRegistry"
+import { useSyncExternalStore } from "use-sync-external-store/shim"
+
+// Do not store `admRef` (even as part of a closure!) on this object,
+// otherwise it will prevent GC and therefore reaction disposal via FinalizationRegistry.
+type ObserverAdministration = {
+ reaction: Reaction | null // also serves as disposed flag
+ onStoreChange: Function | null // also serves as mounted flag
+ // stateVersion that 'ticks' for every time the reaction fires
+ // tearing is still present,
+ // because there is no cross component synchronization,
+ // but we can use `useSyncExternalStore` API.
+ // TODO: optimize to use number?
+ stateVersion: any
+ name: string
+ // These don't depend on state/props, therefore we can keep them here instead of `useCallback`
+ subscribe: Parameters[0]
+ getSnapshot: Parameters[1]
+}
+
+function createReaction(adm: ObserverAdministration) {
+ adm.reaction = new Reaction(`observer${adm.name}`, () => {
+ adm.stateVersion = Symbol()
+ // onStoreChange won't be available until the component "mounts".
+ // If state changes in between initial render and mount,
+ // `useSyncExternalStore` should handle that by checking the state version and issuing update.
+ adm.onStoreChange?.()
+ })
+}
+
+export function useObserver(render: () => T, baseComponentName: string = "observed"): T {
+ if (isUsingStaticRendering()) {
+ return render()
+ }
+
+ const admRef = React.useRef(null)
+
+ if (!admRef.current) {
+ // First render
+ const adm: ObserverAdministration = {
+ reaction: null,
+ onStoreChange: null,
+ stateVersion: Symbol(),
+ name: baseComponentName,
+ subscribe(onStoreChange: () => void) {
+ // Do NOT access admRef here!
+ observerFinalizationRegistry.unregister(adm)
+ adm.onStoreChange = onStoreChange
+ if (!adm.reaction) {
+ // We've lost our reaction and therefore all subscriptions, occurs when:
+ // 1. Timer based finalization registry disposed reaction before component mounted.
+ // 2. React "re-mounts" same component without calling render in between (typically ).
+ // We have to recreate reaction and schedule re-render to recreate subscriptions,
+ // even if state did not change.
+ createReaction(adm)
+ // `onStoreChange` won't force update if subsequent `getSnapshot` returns same value.
+ // So we make sure that is not the case
+ adm.stateVersion = Symbol()
+ }
+
+ return () => {
+ // Do NOT access admRef here!
+ adm.onStoreChange = null
+ adm.reaction?.dispose()
+ adm.reaction = null
+ }
+ },
+ getSnapshot() {
+ // Do NOT access admRef here!
+ return adm.stateVersion
+ }
+ }
+
+ admRef.current = adm
+ }
+
+ const adm = admRef.current!
+
+ if (!adm.reaction) {
+ // First render or reaction was disposed by registry before subscribe
+ createReaction(adm)
+ // StrictMode/ConcurrentMode/Suspense may mean that our component is
+ // rendered and abandoned multiple times, so we need to track leaked
+ // Reactions.
+ observerFinalizationRegistry.register(admRef, adm, adm)
+ }
+
+ React.useDebugValue(adm.reaction!, printDebugValue)
+
+ useSyncExternalStore(
+ // Both of these must be stable, otherwise it would keep resubscribing every render.
+ adm.subscribe,
+ adm.getSnapshot,
+ adm.getSnapshot
+ )
+
+ // render the original component, but have the
+ // reaction track the observables, so that rendering
+ // can be invalidated (see above) once a dependency changes
+ let renderResult!: T
+ let exception
+ adm.reaction!.track(() => {
+ try {
+ renderResult = render()
+ } catch (e) {
+ exception = e
+ }
+ })
+
+ if (exception) {
+ throw exception // re-throw any exceptions caught during rendering
+ }
+
+ return renderResult
+}
diff --git a/packages/mobx-react-lite/src/utils/UniversalFinalizationRegistry.ts b/packages/mobx-react-lite/src/utils/UniversalFinalizationRegistry.ts
new file mode 100644
index 0000000000..3d463733e9
--- /dev/null
+++ b/packages/mobx-react-lite/src/utils/UniversalFinalizationRegistry.ts
@@ -0,0 +1,65 @@
+export declare class FinalizationRegistryType {
+ constructor(finalize: (value: T) => void)
+ register(target: object, value: T, token?: object): void
+ unregister(token: object): void
+}
+
+declare const FinalizationRegistry: typeof FinalizationRegistryType | undefined
+
+export const REGISTRY_FINALIZE_AFTER = 10_000
+export const REGISTRY_SWEEP_INTERVAL = 10_000
+
+export class TimerBasedFinalizationRegistry implements FinalizationRegistryType {
+ private registrations: Map = new Map()
+ private sweepTimeout: ReturnType | undefined
+
+ constructor(private readonly finalize: (value: T) => void) {}
+
+ // Token is actually required with this impl
+ register(target: object, value: T, token?: object) {
+ this.registrations.set(token, {
+ value,
+ registeredAt: Date.now()
+ })
+ this.scheduleSweep()
+ }
+
+ unregister(token: unknown) {
+ this.registrations.delete(token)
+ }
+
+ // Bound so it can be used directly as setTimeout callback.
+ sweep = (maxAge = REGISTRY_FINALIZE_AFTER) => {
+ // cancel timeout so we can force sweep anytime
+ clearTimeout(this.sweepTimeout)
+ this.sweepTimeout = undefined
+
+ const now = Date.now()
+ this.registrations.forEach((registration, token) => {
+ if (now - registration.registeredAt >= maxAge) {
+ this.finalize(registration.value)
+ this.registrations.delete(token)
+ }
+ })
+
+ if (this.registrations.size > 0) {
+ this.scheduleSweep()
+ }
+ }
+
+ // Bound so it can be exported directly as clearTimers test utility.
+ finalizeAllImmediately = () => {
+ this.sweep(0)
+ }
+
+ private scheduleSweep() {
+ if (this.sweepTimeout === undefined) {
+ this.sweepTimeout = setTimeout(this.sweep, REGISTRY_SWEEP_INTERVAL)
+ }
+ }
+}
+
+export const UniversalFinalizationRegistry =
+ typeof FinalizationRegistry !== "undefined"
+ ? FinalizationRegistry
+ : TimerBasedFinalizationRegistry
diff --git a/packages/mobx-react-lite/src/utils/assertEnvironment.ts b/packages/mobx-react-lite/src/utils/assertEnvironment.ts
new file mode 100644
index 0000000000..339dfb29d2
--- /dev/null
+++ b/packages/mobx-react-lite/src/utils/assertEnvironment.ts
@@ -0,0 +1,9 @@
+import { makeObservable } from "mobx"
+import { useState } from "react"
+
+if (!useState) {
+ throw new Error("mobx-react-lite requires React with Hooks support")
+}
+if (!makeObservable) {
+ throw new Error("mobx-react-lite@3 requires mobx at least version 6 to be available")
+}
diff --git a/packages/mobx-react-lite/src/utils/observerBatching.ts b/packages/mobx-react-lite/src/utils/observerBatching.ts
new file mode 100644
index 0000000000..42ce876251
--- /dev/null
+++ b/packages/mobx-react-lite/src/utils/observerBatching.ts
@@ -0,0 +1,25 @@
+import { configure } from "mobx"
+
+export function defaultNoopBatch(callback: () => void) {
+ callback()
+}
+
+export function observerBatching(reactionScheduler: any) {
+ if (!reactionScheduler) {
+ reactionScheduler = defaultNoopBatch
+ if ("production" !== process.env.NODE_ENV) {
+ console.warn(
+ "[MobX] Failed to get unstable_batched updates from react-dom / react-native"
+ )
+ }
+ }
+ configure({ reactionScheduler })
+}
+
+export const isObserverBatched = () => {
+ if ("production" !== process.env.NODE_ENV) {
+ console.warn("[MobX] Deprecated")
+ }
+
+ return true
+}
diff --git a/packages/mobx-react-lite/src/utils/observerFinalizationRegistry.ts b/packages/mobx-react-lite/src/utils/observerFinalizationRegistry.ts
new file mode 100644
index 0000000000..e139ac2105
--- /dev/null
+++ b/packages/mobx-react-lite/src/utils/observerFinalizationRegistry.ts
@@ -0,0 +1,9 @@
+import { Reaction } from "mobx"
+import { UniversalFinalizationRegistry } from "./UniversalFinalizationRegistry"
+
+export const observerFinalizationRegistry = new UniversalFinalizationRegistry(
+ (adm: { reaction: Reaction | null }) => {
+ adm.reaction?.dispose()
+ adm.reaction = null
+ }
+)
diff --git a/packages/mobx-react-lite/src/utils/printDebugValue.ts b/packages/mobx-react-lite/src/utils/printDebugValue.ts
new file mode 100644
index 0000000000..8ef487fd64
--- /dev/null
+++ b/packages/mobx-react-lite/src/utils/printDebugValue.ts
@@ -0,0 +1,5 @@
+import { getDependencyTree, Reaction } from "mobx"
+
+export function printDebugValue(v: Reaction) {
+ return getDependencyTree(v)
+}
diff --git a/packages/mobx-react-lite/src/utils/reactBatchedUpdates.native.ts b/packages/mobx-react-lite/src/utils/reactBatchedUpdates.native.ts
new file mode 100644
index 0000000000..a8e25fbcf7
--- /dev/null
+++ b/packages/mobx-react-lite/src/utils/reactBatchedUpdates.native.ts
@@ -0,0 +1,2 @@
+// @ts-ignore
+export { unstable_batchedUpdates } from "react-native"
diff --git a/packages/mobx-react-lite/src/utils/reactBatchedUpdates.ts b/packages/mobx-react-lite/src/utils/reactBatchedUpdates.ts
new file mode 100644
index 0000000000..8c64462b07
--- /dev/null
+++ b/packages/mobx-react-lite/src/utils/reactBatchedUpdates.ts
@@ -0,0 +1 @@
+export { unstable_batchedUpdates } from "react-dom"
diff --git a/packages/mobx-react-lite/src/utils/utils.ts b/packages/mobx-react-lite/src/utils/utils.ts
new file mode 100644
index 0000000000..50e4173ab8
--- /dev/null
+++ b/packages/mobx-react-lite/src/utils/utils.ts
@@ -0,0 +1,8 @@
+const deprecatedMessages: string[] = []
+
+export function useDeprecated(msg: string) {
+ if (!deprecatedMessages.includes(msg)) {
+ deprecatedMessages.push(msg)
+ console.warn(msg)
+ }
+}
diff --git a/packages/mobx-react-lite/tsconfig.build.cjs.json b/packages/mobx-react-lite/tsconfig.build.cjs.json
new file mode 100644
index 0000000000..1bceb718ed
--- /dev/null
+++ b/packages/mobx-react-lite/tsconfig.build.cjs.json
@@ -0,0 +1,7 @@
+{
+ "extends": "./tsconfig.build.json",
+ "compilerOptions": {
+ "outDir": "lib",
+ "module": "CommonJS"
+ }
+}
diff --git a/packages/mobx-react-lite/tsconfig.build.es.json b/packages/mobx-react-lite/tsconfig.build.es.json
new file mode 100644
index 0000000000..1561876960
--- /dev/null
+++ b/packages/mobx-react-lite/tsconfig.build.es.json
@@ -0,0 +1,7 @@
+{
+ "extends": "./tsconfig.build.json",
+ "compilerOptions": {
+ "outDir": "es",
+ "module": "ESNext"
+ }
+}
diff --git a/packages/mobx-react-lite/tsconfig.build.json b/packages/mobx-react-lite/tsconfig.build.json
new file mode 100644
index 0000000000..5687524039
--- /dev/null
+++ b/packages/mobx-react-lite/tsconfig.build.json
@@ -0,0 +1,9 @@
+{
+ "extends": "./tsconfig.json",
+ "compilerOptions": {
+ "esModuleInterop": true,
+ "target": "ES5",
+ "noEmit": false,
+ "declaration": false
+ }
+}
diff --git a/packages/mobx-react-lite/tsconfig.json b/packages/mobx-react-lite/tsconfig.json
new file mode 100644
index 0000000000..1bbccaf728
--- /dev/null
+++ b/packages/mobx-react-lite/tsconfig.json
@@ -0,0 +1,8 @@
+{
+ "extends": "../../tsconfig.json",
+ "compilerOptions": {
+ "rootDir": "src",
+ "lib": ["ESNext", "DOM"]
+ },
+ "include": ["src"]
+}
diff --git a/packages/mobx-react-lite/tsconfig.test.json b/packages/mobx-react-lite/tsconfig.test.json
new file mode 100644
index 0000000000..7bf229ec27
--- /dev/null
+++ b/packages/mobx-react-lite/tsconfig.test.json
@@ -0,0 +1,6 @@
+{
+ "extends": "../../tsconfig.test.json",
+ "compilerOptions": {
+ "lib": ["esnext", "dom"]
+ }
+}
diff --git a/packages/mobx-react-lite/tsdx.config.js b/packages/mobx-react-lite/tsdx.config.js
new file mode 100644
index 0000000000..f2cf8dd75a
--- /dev/null
+++ b/packages/mobx-react-lite/tsdx.config.js
@@ -0,0 +1,15 @@
+module.exports = {
+ rollup(config) {
+ return {
+ ...config,
+ output: {
+ ...config.output,
+ globals: {
+ react: "React",
+ mobx: "mobx",
+ "react-dom": "ReactDOM"
+ }
+ }
+ }
+ }
+}
diff --git a/packages/mobx-react/.browserlistrc b/packages/mobx-react/.browserlistrc
new file mode 100644
index 0000000000..915b680508
--- /dev/null
+++ b/packages/mobx-react/.browserlistrc
@@ -0,0 +1,6 @@
+{
+ "targets": {
+ "chrome": "58",
+ "ie": "9"
+ }
+}
diff --git a/packages/mobx-react/CHANGELOG.md b/packages/mobx-react/CHANGELOG.md
new file mode 100644
index 0000000000..9eaa5ff14c
--- /dev/null
+++ b/packages/mobx-react/CHANGELOG.md
@@ -0,0 +1,851 @@
+# mobx-react
+
+## 9.2.1
+
+### Patch Changes
+
+- [`2e703388eda4ba3eefed2bf1f5ca3958980978c3`](https://github.com/mobxjs/mobx/commit/2e703388eda4ba3eefed2bf1f5ca3958980978c3) [#4584](https://github.com/mobxjs/mobx/pull/4584) Thanks [@mweststrate](https://github.com/mweststrate)! - Fix type definition of `observer`, when used in combination with a Higher Order Component. By @mbest in #4576
+
+- Updated dependencies [[`2e703388eda4ba3eefed2bf1f5ca3958980978c3`](https://github.com/mobxjs/mobx/commit/2e703388eda4ba3eefed2bf1f5ca3958980978c3)]:
+ - mobx-react-lite@4.1.1
+
+## 9.2.0
+
+### Minor Changes
+
+- [`2587df31a1a967a6b385b7ab2d9f0d42fc94e4b0`](https://github.com/mobxjs/mobx/commit/2587df31a1a967a6b385b7ab2d9f0d42fc94e4b0) [#3985](https://github.com/mobxjs/mobx/pull/3985) Thanks [@imjordanxd](https://github.com/imjordanxd)! - \* Added React 19 support, fixes #3986
+
+### Patch Changes
+
+- Updated dependencies [[`2587df31a1a967a6b385b7ab2d9f0d42fc94e4b0`](https://github.com/mobxjs/mobx/commit/2587df31a1a967a6b385b7ab2d9f0d42fc94e4b0)]:
+ - mobx-react-lite@4.1.0
+
+## 9.1.1
+
+### Patch Changes
+
+- [`61abc53f`](https://github.com/mobxjs/mobx/commit/61abc53ff10554d1d5ce3e85466f6beda4d63fa2) [#3852](https://github.com/mobxjs/mobx/pull/3852) Thanks [@mweststrate](https://github.com/mweststrate)! - Patched the release process, forcing release to get everything in pristine state.
+
+- Updated dependencies [[`61abc53f`](https://github.com/mobxjs/mobx/commit/61abc53ff10554d1d5ce3e85466f6beda4d63fa2), [`7bbb523a`](https://github.com/mobxjs/mobx/commit/7bbb523a7b81229570e0e2a176b989bfc74c4634)]:
+ - mobx-react-lite@4.0.7
+
+## 9.1.0
+
+### Minor Changes
+
+- [`c9260974`](https://github.com/mobxjs/mobx/commit/c9260974f726f58de0fd4974ea024c644d9b7c6f) [#3790](https://github.com/mobxjs/mobx/pull/3790) Thanks [@mweststrate](https://github.com/mweststrate)! - Added support for modern 2022.3 Decorators. [#3790](https://github.com/mobxjs/mobx/pull/3790)
+ - [Installation / usage instruction](https://mobx.js.org/enabling-decorators.html).
+ - [Introduction announcement](https://michel.codes/blogs/mobx-decorators)
+ - Original PR by [@Matchlighter](https://github.com/Matchlighter) in [#3638](https://github.com/mobxjs/mobx/pull/3638),
+
+## 9.0.2
+
+### Patch Changes
+
+- [`5063c38e`](https://github.com/mobxjs/mobx/commit/5063c38ead557624321e2bbeb1aff905438564b0) [#3776](https://github.com/mobxjs/mobx/pull/3776) Thanks [@wbercx](https://github.com/wbercx)! - Fixed premature disposal of class component observers.
+
+## 9.0.1
+
+### Patch Changes
+
+- [`d813746c`](https://github.com/mobxjs/mobx/commit/d813746cfaa18d80daddee3724562fed6b307c0a) [#3731](https://github.com/mobxjs/mobx/pull/3731) Thanks [@urugator](https://github.com/urugator)! - fix #3730: class component does not react to state changes performed before mount
+
+- Updated dependencies [[`3ceeb865`](https://github.com/mobxjs/mobx/commit/3ceeb8651e328c4c7211c875696b3f5269fea834)]:
+ - mobx-react-lite@4.0.4
+
+## 9.0.0
+
+### Major Changes
+
+- [`473cb3f5`](https://github.com/mobxjs/mobx/commit/473cb3f5fc8bf43abdd1c9c7857fe2820d2291fe) [#3718](https://github.com/mobxjs/mobx/pull/3718) Thanks [@mweststrate](https://github.com/mweststrate)! - - Fixed `observer` in `StrictMode` #3671
+ - **[BREAKING CHANGE]** Class component's `props`/`state`/`context` are no longer observable. Attempt to use these in any derivation other than component's `render` throws and error. For details see https://github.com/mobxjs/mobx/blob/main/packages/mobx-react/README.md#note-on-using-props-and-state-in-derivations
+ - Extending or applying `observer` classes is now explicitly forbidden
+
+### Patch Changes
+
+- Updated dependencies [[`58bb052c`](https://github.com/mobxjs/mobx/commit/58bb052ca41b8592e5bd5c3003b68ec52da53f33), [`473cb3f5`](https://github.com/mobxjs/mobx/commit/473cb3f5fc8bf43abdd1c9c7857fe2820d2291fe)]:
+ - mobx-react-lite@4.0.3
+
+## 8.0.0
+
+### Major Changes
+
+- [`44a2cf42`](https://github.com/mobxjs/mobx/commit/44a2cf42dec7635f639ddbfb19202ebc710bac54) [#3590](https://github.com/mobxjs/mobx/pull/3590) Thanks [@urugator](https://github.com/urugator)! - Functional components now use `useSyncExternalStore`, which should prevent tearing - you have to update mobx, otherwise it should behave as previously.
+ Improved displayName/name handling of functional components as discussed in #3438.
+ Reactions of uncommited class components are now correctly disposed, fixes #3492.
+ Reactions don't notify uncommited class components, avoiding the warning, fixes #3492.
+ Removed symbol "polyfill" and replaced with actual Symbols.
+ Removed `this.render` replacement detection + warning. `this.render` is no longer configurable/writable (possibly BC).
+ Class component instance is no longer exposed as `component[$mobx]["reactcomponent"]` (possibly BC).
+ Deprecated `disposeOnUnmount`, it's not compatible with remounting.
+
+### Patch Changes
+
+- Updated dependencies [[`44a2cf42`](https://github.com/mobxjs/mobx/commit/44a2cf42dec7635f639ddbfb19202ebc710bac54)]:
+ - mobx-react-lite@4.0.0
+
+## 7.6.0
+
+### Minor Changes
+
+- [`7aab223e`](https://github.com/mobxjs/mobx/commit/7aab223e99bdd453365103782dba2047e77e41d0) [#3565](https://github.com/mobxjs/mobx/pull/3565) Thanks [@kubk](https://github.com/kubk)! - Make mobx-react compatible with TS 4.8+
+
+## 7.5.3
+
+### Patch Changes
+
+- [`78d1aa23`](https://github.com/mobxjs/mobx/commit/78d1aa2362b4dc5d521518688d6ac7e2d4f7ad3a) [#3458](https://github.com/mobxjs/mobx/pull/3458) Thanks [@egilll](https://github.com/egilll)! - A slight revamp of the README, wording, and clearer links
+
+## 7.5.2
+
+### Patch Changes
+
+- [`a23aaf3f`](https://github.com/mobxjs/mobx/commit/a23aaf3ff50217c40729e0d58c85767911323ebe) [#3452](https://github.com/mobxjs/mobx/pull/3452) Thanks [@urugator](https://github.com/urugator)! - fix #3448 regression: static rendering + class component
+
+## 7.5.1
+
+### Patch Changes
+
+- [`bbcb12dc`](https://github.com/mobxjs/mobx/commit/bbcb12dc754524552181b177a52ffdbe80ecb953) [#3434](https://github.com/mobxjs/mobx/pull/3434) Thanks [@urugator](https://github.com/urugator)! - Support re-mounting of class components. Fixes #3395: observer not working with React@18 <StrictMode>.
+
+## 7.5.0
+
+### Minor Changes
+
+- [`1470b8e4`](https://github.com/mobxjs/mobx/commit/1470b8e4273d6b4046f3107b7f6c30fcffc70eeb) [#3404](https://github.com/mobxjs/mobx/pull/3404) Thanks [@pixelkritzel](https://github.com/pixelkritzel)! - `this.context` is observable if static contextType is set
+
+## 7.4.0
+
+### Minor Changes
+
+- [`4c5e75cd`](https://github.com/mobxjs/mobx/commit/4c5e75cdfec08c04ad774c70dca0629bd2c77016) [#3382](https://github.com/mobxjs/mobx/pull/3382) Thanks [@iChenLei](https://github.com/iChenLei)! - replace the deprecated react type definition with recommended type definition
+
+* [`bd4b70d8`](https://github.com/mobxjs/mobx/commit/bd4b70d8ded29673af8161aa42fb88dc4ad4420e) [#3387](https://github.com/mobxjs/mobx/pull/3387) Thanks [@mweststrate](https://github.com/mweststrate)! - Added experimental / poor man's support for React 18. Fixes #3363, #2526. Supersedes #3005
+
+ - Updated tests, test / build infra, peerDependencies to React 18
+ - **[breaking icmw upgrading to React 18]** Already deprecated hooks like `useMutableSource` will trigger warnings in React 18, which is correct and those shouldn't be used anymore.
+ - **[breaking icmw upgrading to React 18]** When using React 18, it is important that `act` is used in **unit tests** around every programmatic mutation. Without it, changes won't propagate!
+ - The React 18 support is poor man's support; that is, we don't do anything yet to play nicely with Suspense features. Although e.g. [startTransition](https://github.com/mweststrate/platform-app/commit/bdd995773ddc6551235a4d2b0a4c9bd57d30510e) basically works, MobX as is doesn't respect the Suspense model and will always reflect the latest state that is being rendered with, so tearing might occur. I think this is in theoretically addressable by using `useSyncExternalStore` and capturing the current values together with the dependency tree of every component instance. However that isn't included in this pull request 1) it would be a breaking change, whereas the current change is still compatible with React 16 and 17. 2) I want to collect use cases where the tearing leads to problems first to build a better problem understanding. If you run into the problem, please submit an issue describing your scenario, and a PR with a unit tests demonstrating the problem in simplified form. For further discussion see #2526, #3005
+
+### Patch Changes
+
+- Updated dependencies [[`4c5e75cd`](https://github.com/mobxjs/mobx/commit/4c5e75cdfec08c04ad774c70dca0629bd2c77016), [`bd4b70d8`](https://github.com/mobxjs/mobx/commit/bd4b70d8ded29673af8161aa42fb88dc4ad4420e)]:
+ - mobx-react-lite@3.4.0
+
+## 7.3.0
+
+### Minor Changes
+
+- [`59b42c28`](https://github.com/mobxjs/mobx/commit/59b42c2826208435353ce6bf154ae59077edcc05) [#3282](https://github.com/mobxjs/mobx/pull/3282) Thanks [@urugator](https://github.com/urugator)! - `observer(forwardRef(fn))` no longer generates extra `` element and applies `memo` correctly
+
+### Patch Changes
+
+- Updated dependencies [[`59b42c28`](https://github.com/mobxjs/mobx/commit/59b42c2826208435353ce6bf154ae59077edcc05)]:
+ - mobx-react-lite@3.3.0
+
+## 7.2.1
+
+### Patch Changes
+
+- [`8a1ff856`](https://github.com/mobxjs/mobx/commit/8a1ff856043d59396f623f8ca209822b1331d85f) [#3103](https://github.com/mobxjs/mobx/pull/3103) Thanks [@urugator](https://github.com/urugator)! - Missing `render` on component prototype throws.
+
+## 7.2.0
+
+### Minor Changes
+
+- [`87b3e1de`](https://github.com/mobxjs/mobx/commit/87b3e1de58069617a39552d71a4d5c5c134cbbaf) [#2930](https://github.com/mobxjs/mobx/pull/2930) Thanks [@vkrol](https://github.com/vkrol)! - inject shouldn't change original displayName of component that uses forwardRef
+
+## 7.1.0
+
+### Patch Changes
+
+- Updated dependencies [[`28f8a11d`](https://github.com/mobxjs/mobx/commit/28f8a11d8b94f1aca2eec4ae9c5f45c5ea2f4362)]:
+ - mobx@6.1.0
+ - mobx-react-lite@4.0.0
+
+## 7.0.6
+
+### Patch Changes
+
+- [`592e6e99`](https://github.com/mobxjs/mobx/commit/592e6e996c2d5264e162cfb0921a071c1d815c92) [#2743](https://github.com/mobxjs/mobx/pull/2743) Thanks [@vkrol](https://github.com/vkrol)! - Remove `sideEffects` section in `mobx-react-lite` `package.json`
+
+- Updated dependencies [[`6b304232`](https://github.com/mobxjs/mobx/commit/6b30423266e5418a3f20389d0bd0eae31f3384d2), [`83b84fd3`](https://github.com/mobxjs/mobx/commit/83b84fd354f2253fdd8ea556e217a92fc2633c00), [`65c7b73b`](https://github.com/mobxjs/mobx/commit/65c7b73b7f0b1a69a1a2786b5f484419d129d10b), [`989390d4`](https://github.com/mobxjs/mobx/commit/989390d46bbe9941b61ac6c6d1292f96445e7cc3), [`dea1cf18`](https://github.com/mobxjs/mobx/commit/dea1cf189b0f43929f4f626229d34a80bd10212e), [`592e6e99`](https://github.com/mobxjs/mobx/commit/592e6e996c2d5264e162cfb0921a071c1d815c92)]:
+ - mobx@6.0.5
+ - mobx-react-lite@3.1.7
+
+## 7.0.5
+
+### Patch Changes
+
+- [`2f3dcb27`](https://github.com/mobxjs/mobx/commit/2f3dcb274f795ffca4ae724b6b4795958620838d) Thanks [@FredyC](https://github.com/FredyC)! - Fix names of UMD exports [#2517](https://github.com/mobxjs/mobx/issues/2617)
+
+- Updated dependencies [[`2f3dcb27`](https://github.com/mobxjs/mobx/commit/2f3dcb274f795ffca4ae724b6b4795958620838d), [`79a09f49`](https://github.com/mobxjs/mobx/commit/79a09f49a9f2baddbab8d89e9a7ac07cffadf624)]:
+ - mobx-react-lite@3.1.6
+ - mobx@6.0.4
+
+## 7.0.4
+
+### Patch Changes
+
+- [`8bbbc7c0`](https://github.com/mobxjs/mobx/commit/8bbbc7c0df77cd79530add5db2d6a04cfe6d84b1) Thanks [@FredyC](https://github.com/FredyC)! - Fix names of dist files (for real now). Third time is the charm 😅
+
+- Updated dependencies [[`8bbbc7c0`](https://github.com/mobxjs/mobx/commit/8bbbc7c0df77cd79530add5db2d6a04cfe6d84b1)]:
+ - mobx-react-lite@3.1.4
+
+## 7.0.3
+
+### Patch Changes
+
+- [`b7aa9d35`](https://github.com/mobxjs/mobx/commit/b7aa9d35432888ee5dd80a6c9dcbc18b04a0346c) Thanks [@FredyC](https://github.com/FredyC)! - Fixed wrong package name for dist files
+
+- Updated dependencies [[`b7aa9d35`](https://github.com/mobxjs/mobx/commit/b7aa9d35432888ee5dd80a6c9dcbc18b04a0346c)]:
+ - mobx-react-lite@3.1.3
+
+## 7.0.2
+
+### Patch Changes
+
+- [`5239db80`](https://github.com/mobxjs/mobx/commit/5239db80cf000026906c28a035725933d4dd6823) Thanks [@FredyC](https://github.com/FredyC)! - Fixed release with missing dist files
+
+- Updated dependencies [[`5239db80`](https://github.com/mobxjs/mobx/commit/5239db80cf000026906c28a035725933d4dd6823)]:
+ - mobx-react-lite@3.1.2
+
+## 7.0.1
+
+### Patch Changes
+
+- [`81a2f865`](https://github.com/mobxjs/mobx/commit/81a2f8654d9656e2e831176e45cbf926fbc364e0) Thanks [@FredyC](https://github.com/FredyC)! - ESM bundles without NODE_ENV present are available in dist folder. This useful for consumption in browser environment that supports ESM Choose either `esm.production.min.js` or `esm.development.js` from `dist` folder.
+
+- Updated dependencies [[`81a2f865`](https://github.com/mobxjs/mobx/commit/81a2f8654d9656e2e831176e45cbf926fbc364e0)]:
+ - mobx-react-lite@3.1.1
+
+## 7.0.0
+
+Release for compatibility with MobX v6
+
+## 6.3.1
+
+### Patch Changes
+
+- [`aa780c0`](https://github.com/mobxjs/mobx-react/commit/aa780c07162be99e198e7bbdbd6465c1f451f1d6) [#908](https://github.com/mobxjs/mobx-react/pull/908) Thanks [@FredyC](https://github.com/FredyC)! - Initial setup of [changesets](https://github.com/atlassian/changesets). No code changes present.
+
+## 6.3.0
+
+- Updated mobx-react-lite to 2.2.0 which removes the need to manually configure batching. Fixes [#859](https://github.com/mobxjs/mobx-react/issues/859)
+
+## 6.2.4
+
+- Fix error thrown in the already defined observer class component warning message when attempting to get the components display name. [#887](https://github.com/mobxjs/mobx-react/issues/887)
+
+## 6.2.3
+
+- Log warning if class component is already an observer to prevent memory leaks. [#839](https://github.com/mobxjs/mobx-react/issues/839)
+- Fix disposeOnUnmount when using react-hot-loader. [#725](https://github.com/mobxjs/mobx-react/issues/725)
+
+## 6.2.2
+
+- Observer batching imports are kept in production builds as side effects ([see issue](https://github.com/mobxjs/mobx-react-lite/issues/273))
+
+## 6.2.1
+
+- Remove auto configured observer batching using react-dom. Fixes: [#852](https://github.com/mobxjs/mobx-react/issues/852).
+
+## 6.2.0
+
+- Updated to latest mobx-react-lite V2 for compatibility with `React.StrictMode`.
+- Observer batching (see more [in the docs](https://github.com/mobxjs/mobx-react-lite/#observer-batching)).
+- Possibly breaking change, the `dist/mobxreact.rn.module.js` is no longer available, use `dist/mobxreact.esm.js` instead.
+
+## 6.1.6 / 6.1.7
+
+- Fix an issue with class components & observableRequiresReaction. [#806](https://github.com/mobxjs/mobx-react/issues/806) through [#829](https://github.com/mobxjs/mobx-react/pull/829)
+- Use TSDX for building to mitigate issues with accessing `process.env` [#821](https://github.com/mobxjs/mobx-react/pull/821)
+
+## 6.1.5
+
+- Added check if `process.env` is available, fixes [#801](https://github.com/mobxjs/mobx-react/issues/801) through [#812](https://github.com/mobxjs/mobx-react/pull/812) by [@ynejati](https://github.com/ynejati)
+- Added warning if component's `render` method is accidentally overwritten. [#799](https://github.com/mobxjs/mobx-react/pull/799) by [@Venryx](https://github.com/Venryx). Helps prevent memory leaks as in: [#797](https://github.com/mobxjs/mobx-react/issues/797)
+
+## 6.1.4
+
+- Update dependency mobx-react-lite@1.4.2 which includes fix for [RN Fast Refresh](https://github.com/mobxjs/mobx-react-lite/issues/226)
+
+## 6.1.2 / 6.1.3
+
+- Add reexport of `useObserver` from `mobx-react-lite` [#734](https://github.com/mobxjs/mobx-react/issues/734)
+- Add the ability to pass multiple children to Provider
+- Fixed [#717](https://github.com/mobxjs/mobx-react/issues/717). Now `inject` works correctly with components that use `React.forwardRef`
+- Observer checks for use of React.memo [#720](https://github.com/mobxjs/mobx-react/issues/720)
+- Get rid of the redundant Injector wrapper [#716](https://github.com/mobxjs/mobx-react/pull/716)
+
+## 6.1.1
+
+- Fixed issue where combining `@disposeOnUnmount` with `disposeOnUnmount` didn't clean up everything. Fixes [#666](https://github.com/mobxjs/mobx-react/issues/666) trough [#671](https://github.com/mobxjs/mobx-react/pull/671) by [@JabX](https://github.com/JabX)
+
+## 6.1.0
+
+- Restored the classic implementation of `observer`: class based components are patched again, rather than wrapping them in ``, see [#703](https://github.com/mobxjs/mobx-react/pull/703). Fixes:
+ - `componentDidUpdate` not being triggered after a reactive render [#692](https://github.com/mobxjs/mobx-react/issues/692)
+ - The appearance of an additional `` component in the component tree, which complicates shallow testing [#699](https://github.com/mobxjs/mobx-react/issues/699)
+ - Some regressions in `disposeOnUnmount` [#702](https://github.com/mobxjs/mobx-react/issues/702)
+ - Note that dev tool support, and other constraints mentioned in the 6.0.0 release notes have not been restored.
+- The function `useStaticRendering(value: boolean): void` from mobx-react-lite is now exposed
+
+## 6.0.4
+
+- Fixed IE 11 compatibility which was accidentally broken. Fixes [#698](https://github.com/mobxjs/mobx-react/issues/698)
+
+## 6.0.3
+
+- `disposeOnUnmount` now supports initializing it with an array of disposers. Fixes [#637](https://github.com/mobxjs/mobx-react/pull/637) through [#641](https://github.com/mobxjs/mobx-react/pull/641) by [@Amareis](https://github.com/Amareis)
+- Fixed hoisting of statically declared members. Fixes [#678](https://github.com/mobxjs/mobx-react/issues/678) through [#682](https://github.com/mobxjs/mobx-react/pull/682) by [@meabed](https://github.com/meabed)
+
+## 6.0.2
+
+- Added missing types for `MobXProviderContext`, `useLocalStore` and `useAsObservableSource`. Fixes #679.
+
+## 6.0.0
+
+**Breaking changes**
+
+- The minimal supported version of React is 16.8.0
+- Killed the possibility to directly pass store names to `observer`. Always use `inject` instead. (This was deprecated for a long time already). `observer(["a", "b"], component)` should now be written as `inject("a", "b")(component)`.
+- `observer` components no longer automatically recover from errors (to prevent potential memory leaks). Instead, this is the responsibility of error boundaries.
+- `inject` now supports ref forwarding. As such, the `.wrappedInstance` property has been removed since refs can be used instead. (Fixes [#616](https://github.com/mobxjs/mobx-react/issues/616) (See also [#619](https://github.com/mobxjs/mobx-react/pull/619) by [42shadow42](https://github.com/42shadow42))
+- Changing the set of stores in `Provider` is no longer supported and while throw a hard error (this was a warning before), as the model of `Provider` / `inject` has always been designed to inject final values into the tree. (That is, constanted references, the injected objects themselves can be stateful without problem). If you want to dynamically swap what is provided into the tree, use `React.createContext` instead of `Provider` / `inject`. The suppressChangedStoreWarning`flag for`Provider` has been dropped.
+- The third argument of custom `storesToProps` functions passed to `inject` is no longer available.
+- `` no longer supports the deprecated `inject` property.
+- Defining `shouldComponentUpdate` on `observer` based components is no longer supported
+- `propTypes` is no longer exposed, use `PropTypes` instead
+- `disposeOnUnmount` now only supports direct subclasses of `React.Component` and `React.PureComponent`. This prevents several unreliable edge cases that silently leaked memory before. Either only extend React.(Pure)Component when using `disposeOnUnmount`, or manually clean up stuff in `componentWillUnmount`.
+- The `onError` global error handler has been removed. Use error boundaries instead.
+- Improved dev tool names for `inject` wrapped components, see [#472](https://github.com/mobxjs/mobx-react/pull/472) by [SimeonC](https://github.com/SimeonC). Fixes [#466](https://github.com/mobxjs/mobx-react/issues/466)
+- Dropped support for a build of mobx-react that doesn't target either `react-dom` or `react-native`. mobx-react doesn't need `react-dom` to be present, but to make sure your build tools don't fail, you might want to stub `react-dom` as an empty module.
+- The `componentWillReact` has been dropped
+- The MobX-react devtools (either as package or browser plugin) are no longer supported. Instead, the following tools can be analyzed to analyze your mobx-react application:
+ - Visualizing re-rendering of components is now part of the standard React devtools
+ - The dependency tree of a compent tree can be inspected by showing the state of the `useObserver` hook in the React devtools (at the time of this release it displays as just `Object`, but the next iteration of the React devtools will support those properly)
+ - Spying on events can still be done with the [MobX-react browser plugin](https://github.com/mobxjs/mobx-devtools), through the [mobx-logger](https://github.com/winterbe/mobx-logger) package or manually by using the `spy` or `trace` utility from the mobx package.
+
+**Improvements**
+
+- Hook based components are now supported by mobx-react (in fact, the package is now implemented using hooks)
+- Class based `observer` components are now _recommended_ to extend `React.PureComponent`. Functional `observer` components are now automatically wrapped in `React.memo` internally. See section in [README](https://mobx.js.org/README.html#observercomponentclass) for more details.
+- For `observer` based components, there will now be an additional `Observer` component in the tree.
+- Two new hooks have been exposed, in case you want to manage local state in observable: `useLocalStore` and `useAsObservableSource`.
+- `MobXProviderContext` is now exposed from the package, in case you want to consume the context used by `Provider` with a `useContext` hook.
+
+## 5.4.3
+
+- Fixed [#612](https://github.com/mobxjs/mobx-react/issues/612), `contextType` was hoisted by `inject`, which shouldn't the case.
+
+## 5.4.1 / 5.4.2
+
+- Fixed issue where `react-is` wasn't properly rolled-up into the package. Fixes [#608](https://github.com/mobxjs/mobx-react/issues/608)
+
+## 5.4.0
+
+- Added support for forward refs, fixes [#602](https://github.com/mobxjs/mobx-react/issues/602)
+
+## 5.3.6
+
+- Fixed some additional issues around life-cycle patching, take 3. See [#536](https://github.com/mobxjs/mobx-react/pull/586) by [@xaviergonz](https://github.com/xaviergonz). Fixed [#579](https://github.com/mobxjs/mobx-react/issues/579)
+
+## 5.3.5
+
+- Fixed some additional issues around life-cycle patching, see [#583](https://github.com/mobxjs/mobx-react/pull/583) by [@xaviergonz](https://github.com/xaviergonz). Fixed [#581](https://github.com/mobxjs/mobx-react/issues/581)
+
+## 5.3.4
+
+- Fixed unending recursing as a result of lifecylce patching. Fixes [#579](https://github.com/mobxjs/mobx-react/issues/579) through [#582](https://github.com/mobxjs/mobx-react/pull/582) by [@xaviergonz](https://github.com/xaviergonz)
+
+## 5.3.3
+
+- Fixed `Cannot read property 'forEach' of undefined` exception if `disposeOnUnmount` was called conditionally. [#578](https://github.com/mobxjs/mobx-react/pull/578) by [Jef Hellemans](https://github.com/JefHellemans)
+
+## 5.3.2
+
+- Fixed: "process not defined", [#574](https://github.com/mobxjs/mobx-react/pull/574/) through [#576](https://github.com/mobxjs/mobx-react/pull/576/) by [@xaviergonz](https://github.com/xaviergonz)
+
+## 5.3.0 / 5.3.1
+
+_5.3.0 was retracted as files were not generated correctly during publish_
+
+- Added `disposeOnUnmount` utility / decorator to call disposable properties (reaction, autorun, etc) automatically on `componentWillUnmount`
+- Introduced new method to patch lifecycle methods which should be more compatible with for example arrow functions.
+
+## 5.2.8
+
+- Make sure `mobx-react` doesn't require `Object.assign` polyfill
+
+## 5.2.7
+
+- Fixed issue where React 16.5 printed a warning when using `Provider`, fixes [#545](https://github.com/mobxjs/mobx-react/issues/545)
+
+## 5.2.6
+
+- Fixed bug in defining properties (although the bug had no known observable effect). Fixes [#540](https://github.com/mobxjs/mobx-react/issues/540)
+
+## 5.2.4 / 5.2.5
+
+- Improved compatibility with React-Hot-Loader, see [#522](https://github.com/mobxjs/mobx-react/pull/522) by [theKashey](https://github.com/theKashey). Fixes [#500](https://github.com/mobxjs/mobx-react/issues/500)
+
+## 5.2.3
+
+- Fixed problem with `Symbol` feature detection. By [@Strate](https://github.com/Strate) through [#501](https://github.com/mobxjs/mobx-react/pull/501). Fixes [#498](https://github.com/mobxjs/mobx-react/issues/498) and [#503](https://github.com/mobxjs/mobx-react/issues/503).
+
+## 5.2.2
+
+- Polyfill `Symbol` if it doesn't exist. By [@Strate](https://github.com/Strate) through [#499](https://github.com/mobxjs/mobx-react/pull/499).
+
+## 5.2.1
+
+- Component `props` and `state` properties are now made observable during the instance creation. This restores the behavior from before 5.1.0 where `props` and `state` could safely be observed during mount. Actually it is now possible to do similar things in constructors as well. Fixes [#478](https://github.com/mobxjs/mobx-react/issues/478). Thanks [@Strate](https://github.com/Strate) for the idea and PR! [#496](https://github.com/mobxjs/mobx-react/pull/496).
+
+## 5.2.0
+
+- Added backward compatible support for MobX 5.
+- Fixed components sometimes being displayed as `undefined` in mobx-devtools. See [#470](https://github.com/mobxjs/mobx-react/pull/470) by [@MauricioAndrades](https://github.com/MauricioAndrades)
+- Removed unnecessary warning `@observer` was used both on a sub and super class. See [#492](https://github.com/mobxjs/mobx-react/pull/476) by [@skiritsis](https://github.com/skiritsis). _N.B. putting `@observer` on a super and subclass is still not an supported pattern, use @observer on subclasses only!_
+
+## 5.1.2
+
+- Fixed regression bug in integration with devtools. Fixed through [#465](https://github.com/mobxjs/mobx-react/pull/465) by @le0nik
+
+## 5.1.0
+
+- Added support for React 16.3, including support for the `getDerivedStateFromProps` life-cycle hook. MobX will no longer use `componentWillMount` hook internally, so that it can be used in `StrictMode` react as well. Fixes [#447](https://github.com/mobxjs/mobx-react/issues/447)
+- Static properties of a function component are now automatically hoisted when the component is wrapped by `observer`. Implements [#427](https://github.com/mobxjs/mobx-react/pull/427)
+- Misspelled export `componentByNodeRegistery` is now properly export as `componentByNodeRegistry` as well, please update consumers, the mispelled version will be dropped in the next major. Fixes [#421](https://github.com/mobxjs/mobx-react/issues/421)
+- Deprecated the support for the `inject` property on `Observer`, it is fundamentally broken and should not be used. Use `inject` on the enclosing component instead and grab the necessary stores from the closure. Fixes [#423](https://github.com/mobxjs/mobx-react/issues/423)
+- Added warning about using `observer` on a React.PureComponent, this will become an exception in the next major. Fixes [#309](https://github.com/mobxjs/mobx-react/issues/309)
+- Mobx-react will now print a warning when combining `observer` with a custom `shouldComponentUpdate` implementation. Fixes [#417](https://github.com/mobxjs/mobx-react/issues/417)
+
+## 5.0.0
+
+- Added compatibility with MobX 4.x. This version is not compatible with older Mobx versions
+
+## 4.4.3
+
+- The exposed React Native build now uses commonjs, to prevent the need of further transpilation. Fixes [#428](https://github.com/mobxjs/mobx-react/issues/428)
+
+## 4.4.2
+
+- Fixed issue with mobx-react not compiling on react-native due to the presence of a `.babelrc` file. Fixes [#415](https://github.com/mobxjs/mobx-react/issues/415) by [Ryan Rampersad](https://github.com/ryanmr) through [#416](https://github.com/mobxjs/mobx-react/pull/416)
+
+## 4.4.1
+
+- Fixed syntax error in 4.4.0 that escaped
+
+## 4.4.0
+
+- `Observer` now supports render props, `render` and `inject`. See the updated readme. By [ZiYingMai](https://github.com/Sunshine168) through [#403](https://github.com/mobxjs/mobx-react/pull/403)
+- Fixed: `NaN` is now considered to be equal to `NaN` when doing reconciliation. Fixes [#363](https://github.com/mobxjs/mobx-react/issues/363), by [Andrew Branch](https://github.com/andrewbranch) through [#402](https://github.com/mobxjs/mobx-react/pull/402)
+- Improved typings of `Observer` component, by [Rafał Filipek](https://github.com/RafalFilipek) through [#376](https://github.com/mobxjs/mobx-react/pull/376)
+- Fixed incorrect generation of component name, by [Andy Kogut](https://github.com/andykog) through [#368](https://github.com/mobxjs/mobx-react/pull/368)
+- Lot of internal repo upgrades: Test suite is now in Jest, Prettier is used etc.
+
+## 4.3.5
+
+Fixed some issues with the typescript typings. See for example #353
+
+## 4.3.4
+
+Improved typescript typings, including support for `strict` mode in TS 2.6. Fixes
+
+## 4.3.3
+
+Added support for React 16. (No changes)
+
+## 4.3.2
+
+Killed accidentally exposed default exports.
+
+If you are still using `import mobxReact from "mobx-react"`, use `import * as mobxReact from "mobx-react"`, or better `import { whatYouNeed } from "mobx-react"` instead.
+
+## 4.3.1
+
+## 4.3.0 (unpublished)
+
+Improved module rollup setup, enabling better tree shaking. See #324 / #328
+
+## 4.2.2
+
+- Fixed check for stateless components, by @leader22, see #280
+
+## 4.2.1
+
+_Note: Due to pull / rebase issue the release commit is incorrect. This is the released [commit](https://github.com/mobxjs/mobx-react/commit/f1b3eefc5239cb451b317204fa8aad94b4dcfc2f)_
+
+- Reduced module size by 31% (switched to rollup.js). See #244 by @rossipedia
+- Skip creation of `.wrappedInstance` reference for stateless components. See #254 by @farwayer
+- Introduced global `onError` handler hook to be notified on errors thrown by `@observer` components. See #262 by @andykog
+- Improved typescript typings of the exposed `propTypes`, See #263 by @panjiesw
+
+## 4.2.0
+
+- Same as 4.2.1, but contained build issue and is unpublished
+
+## 4.1.8
+
+- Undid change introduced in 4.1.4 where the lifecycle hooks were protected, as this breaks react-hot-loader.... Fixes #231
+
+## 4.1.7
+
+- Added support for React 15.5 (no deprecation warnings) and 16.0 (no proptypes / createClass), by @andykog, see #238. Fixes #233, #237
+
+## 4.1.5
+
+- Improved typescript typings, fixes #223
+
+## 4.1.4
+
+- Made lifecycle hooks used by mobx-react read-only to make sure they are not accidentally overwritten in component instances. Fixes, #195, #202. Note that they can still be defined, just make sure to define them on the prototype (`componentWillMount() {}`) instead of the instance (`componentWillMount = () => {}`). Which is best practice anyway.
+
+## 4.1.3
+
+- Fixed `ReactDOM.findDOMNode` exception when using react-test-runner, #216
+
+## 4.1.2
+
+- Exceptions caught during render are now rethrown with proper stack, fixes #206
+
+## 4.1.1
+
+- Exposed `wrappedInstance` and `wrappedComponent` in typings
+- Fixed accidental use of `default` import from `mobx` package.
+
+## 4.1.0
+
+- Added support for MobX3. Note that using MobX3 changes the error semantics. If an `observer` component throws, it will no longer crash the app, but just log the exceptions instead.
+
+## 4.0.4
+
+- Introduced `suppressChangedStoreWarning` to optionally supresss change store warnings, by @dropfen, see #182, #183
+
+## 4.0.3
+
+- Fixed issue where userland componentWilMount was run before observer componentWillMount
+
+## 4.0.2
+
+- Fixed order of `inject` overloads, see #169
+- Fixed import of `mobx` when using Webpack without commonjs plugin, see: #168
+
+## 4.0.1
+
+- Improved typings, by @timmolendijk, fixes #164, #166
+- Fixed `inject` signature in readme, by @farwayer
+
+## 4.0.0
+
+### `observer` now uses shallow comparision for all props _(Breaking change)_
+
+`observer` used to compare all properties shallow in the built-in _shouldComponentUpdate_, except when it received
+non-observable data structures.
+Because mobx-react cannot know whether a non observable has been deeply modified, it took no chances and just re-renders.
+
+However, the downside of this when an unchanged, non-observable object is passed in to an observer component again, it would still cause a re-render.
+Objects such as styling etc. To fix this mobx-react will now always compare all properties in a pure manner.
+In general this should cause no trouble, as typically mutable data in mobx based objects is captured in observable objects, which will still cause components to re-render if needed.
+
+If you need to pass in a deeply modified object and still want to make sure to cause a re-render, either
+
+- make sure the object / array is an observable
+- do not decorate your component with `observer`, but use `Observer` regions instead (see below)
+
+See [#160](https://github.com/mobxjs/mobx-react/issues/160) for more details.
+
+### `inject(fn)(component)` will now track `fn` as well
+
+`inject(func)` is now reactive as well, that means that transformations in the selector function will be tracked, see [#111](https://github.com/mobxjs/mobx-react/issues/111)
+
+```javascript
+const NameDisplayer = ({ name }) =>
{name}
+
+const UserNameDisplayer = inject(stores => ({
+ name: stores.userStore.name
+}))(NameDisplayer)
+
+const user = mobx.observable({
+ name: "Noa"
+})
+
+const App = () => (
+
+
+
+)
+
+ReactDOM.render(, document.body)
+```
+
+_N.B. note that in this specific case NameDisplayer doesn't have to be an `observer`, as it doesn't receive observables, but just plain data from the transformer function._
+
+### `this.props` and `this.state` in React components are now observables as well
+
+A common cause of confusion were cases like:
+
+```javascript
+@observer class MyComponent() {
+ @computed upperCaseName() {
+ return this.props.user.name.toUpperCase()
+ }
+
+ render() {
+ return
{this.upperCaseName}
+ }
+}
+```
+
+This component would re-render if `user.name` was modified, but it would still render the previous user's name if a complete new user was received!
+The reason for that is that in the above example the only observable tracked by the computed value is `user.name`, but not `this.props.user`.
+So a change to the first would be picked up, but a change in `props` itself, assigning a new user, not.
+
+Although this is technically correct, it was a source of confusion.
+For that reason `this.state` and `this.props` are now automatically converted to observables in any `observer` based react component.
+For more details, see [#136](https://github.com/mobxjs/mobx-react/pull/136) by @Strate
+
+### Better support for Server Side Rendering
+
+Introduced `useStaticRendering(boolean)` to better support server-side rendering scenarios. See [#140](https://github.com/mobxjs/mobx-react/issues/140)
+
+### Introduced `Observer` as alternative syntax to the `observer` decorator.
+
+_This feature is still experimental and might change in the next minor release, or be deprecated_
+
+Introduced `Observer`. Can be used as alternative to the `observer` decorator. Marks a component region as reactive.
+See the Readme / [#138](https://github.com/mobxjs/mobx-react/issues/138)
+Example:
+
+```javascript
+const UserNameDisplayer = ({ user }) => {() =>
{user.name}
}
+```
+
+### Using `observer` to inject stores is deprecated
+
+The fact that `observer` could inject stores as well caused quite some confusion.
+Because in some cases `observer` would return the original component (when not inject), but it would return a HoC when injecting.
+To make this more consistent, you should always use `inject` to inject stores into a component. So use:
+
+```
+@inject("store1", "store2") @observer
+class MyComponent extends React.Component {
+```
+
+or:
+
+```
+const MyComponent = inject("store1", "store2")(observer(props => rendering))
+```
+
+For more info see the related [discussion](https://github.com/mobxjs/mobx-react/commit/666577b41b7af8209839e7b243064a31c9951632#commitcomment-19773706)
+
+### Other improvements
+
+- If `mobx` and `mobx-react` are used in combination, all reactions are run as part of React's batched updates. This minimizes the work of the reconciler, guarantees optimal rendering order of components (if the rendering was not triggered from within a React event). Tnx @gkaemmer for the suggestion.
+- It is now possible to directly define `propTypes` and `defaultProps` on components wrapped with `inject` (or `observer(["stores"])`) again, see #120, #142. Removed the warnings for this, and instead improved the docs.
+- Clean up data subscriptions if an error is thrown by an `observer` component, see [#134](https://github.com/mobxjs/mobx-react/pull/134) by @andykog
+- export `PropTypes` as well in typescript typings, fixes #153
+- Add react as a peer dependency
+- Added minified browser build: `index.min.js`, fixes #147
+- Generate better component names when using `inject`
+
+---
+
+## 3.5.9
+
+- Print warning when `inject` and `observer` are used in the wrong order, see #146, by @delaetthomas
+
+## 3.5.8
+
+- Fixed issue where `props` where not passed properly to components in very rare cases. Also fixed #115
+
+## 3.5.7
+
+- Bundles are no longer minified, fixes #127
+
+## 3.5.6
+
+- Export `propTypes` as `PropTypes`, like React (@andykog, ##117)
+
+## 3.5.5
+
+- Removed `experimental` status of `inject` / `Provider`. Official feature now.
+- Fixed hot-reloading issue, #101
+
+## 3.5.4
+
+- Introduced `wrappedInstance` by @rossipedia on `inject` decorated HOC's, see https://github.com/mobxjs/mobx-react/pull/90/
+- print warnings when assign values to `propTypes`, `defaultProps`, or `contextTypes` of a HOC. (by @jtraub, see https://github.com/mobxjs/mobx-react/pull/88/)
+- Static properties are now hoisted to HoC components when, #92
+- If `inject` is used incombination with a function, the object return from the function will now be merged into the `nextProps` instead of replacing them, #80
+- Always do propType checking untracked, partially fixes #56, #305
+
+## 3.5.3
+
+- Fixed error `Cannot read property 'renderReporter' of undefined` (#96)
+
+## 3.5.2
+
+- Added propTypes.observableArrayOf and propTypes.arrayOrObservableArrayOf (#91)
+
+## 3.5.1
+
+- Fixed regression #85, changes caused by the constructor results in inconsistent rendering (N.B.: that is un-idiomatic React usage and React will warn about this!)
+
+## 3.5.0
+
+- Introduced `inject("store1", "store2")(component)` as alternative syntax to inject stores. Should address #77, #70
+- Introduced the `wrappedComponent` property on injected higher order components, addresses #70, #72
+- Fixed #76: error when no stores are provided through context
+- Added typings for devTools related features (@benjamingr).
+- Added MobX specific propTypes (@mattruby)
+- Merged #44, fixes #73: don't re-render if component was somehow unmounted
+
+## 3.4.0
+
+- Introduced `Provider` / context support (#53 / MobX #300)
+- Fixed issues when using devtools with IE. #66 (By @pvasek)
+
+## 3.3.1
+
+- Added typescript typings form `mobx-react/native` and `mobx-react/custom`
+- Fixed #63: error when using stateless function components when using babel and typescript
+
+## 3.3.0
+
+- Upgraded to MobX 2.2.0
+
+## 3.2.0
+
+- Added support for react-native 0.25 and higher. By @danieldunderfelt.
+
+## 3.1.0
+
+- Added support for custom renderers (without DOM), use: `mobx-react/custom` as import fixes #42
+- Fixed some issues with rollup #43
+- Minor optimization
+
+## 3.0.5
+
+Introduced `componentWillReact`
+
+## 3.0.4
+
+The debug name stateless function components of babel transpiled jsx are now properly picked up if the wrapper is applied after defining the component:
+
+```javascript
+const MyComponent = () => hi
+
+export default observer(MyComponent)
+```
+
+## 3.0.3
+
+Removed peer dependencies, React 15 (and 0.13) are supported as well. By @bkniffler
+
+## 3.0.2
+
+Removed the warning introduced in 3.0.1. It triggered always when using shallow rendering (when using shallow rendering `componentDidMount` won't fire. See https://github.com/facebook/react/issues/4919).
+
+## 3.0.1
+
+Added warning when changing state in `getInitialState` / `constructor`.
+
+## 3.0.0
+
+Upgraded to MobX 2.0.0
+
+## 2.1.5
+
+Improved typescript typings overloads of `observer`
+
+## 2.1.4
+
+Added empty 'dependencies' section to package.json, fixes #26
+
+## 2.1.3
+
+Added support for context to stateless components. (by Kosta-Github).
+
+## 2.1.1
+
+Fixed #12: fixed React warning when a component was unmounted after scheduling a re-render but before executing it.
+
+## 2.1.0
+
+Upped dependency of mobx to 1.1.1.
+
+## 2.0.1
+
+It is now possible to define `propTypes` and `getDefaultProps` on a stateless component:
+
+```javascript
+const myComponent = props => {
+ // render
+}
+
+myComponent.propTypes = {
+ name: React.PropTypes.string
+}
+
+myComponent.defaultProps = {
+ name: "World"
+}
+
+export default observer(myComponent)
+```
+
+All credits to Jiri Spac for this contribution!
+
+## 2.0.0
+
+Use React 0.14 instead of React 0.13. For React 0.13, use version `mobx-react@1.0.2` or higher.
+
+## 1.0.2
+
+Minor fixes and improvements
+
+## 1.0.1
+
+Fixed issue with typescript typings. An example project with MobX, React, Typescript, TSX can be found here: https://github.com/mobxjs/mobx-react-typescript
+
+## 1.0.0
+
+`reactiveComponent` has been renamed to `observer`
+
+### 0.2.3
+
+Added separte import for react-native: use `var reactiveComponent = require('mobx-react/native').reactiveComponent` for native support; webpack clients will refuse to build otherwise.
+
+### 0.2.2
+
+Added react-native as dependency, so that the package works with either `react` or `react-native`.
+
+### 0.2.0
+
+Upgraded to MobX 0.7.0
+
+### 0.1.7
+
+Fixed issue where Babel generated component classes where not properly picked up.
+
+### 0.1.6
+
+`observer` now accepts a pure render function as argument, besides constructor function. For example:
+
+```javascript
+var TodoItem = observer(function TodoItem(props) {
+ var todo = props.todo
+ return
{todo.task}
+})
+```
+
+### 0.1.5
+
+observer is now defined in terms of side effects.
+
+### 0.1.4
+
+Added support for React 0.14(RC) by dropping peer dependency
diff --git a/packages/mobx-react/LICENSE b/packages/mobx-react/LICENSE
new file mode 100644
index 0000000000..b58becae8c
--- /dev/null
+++ b/packages/mobx-react/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2015 Michel Weststrate
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/packages/mobx-react/README.md b/packages/mobx-react/README.md
new file mode 100644
index 0000000000..4bdf704af9
--- /dev/null
+++ b/packages/mobx-react/README.md
@@ -0,0 +1,583 @@
+# mobx-react
+
+[](https://circleci.com/gh/mobxjs/mobx-react)
+[](https://cdnjs.com/libraries/mobx-react)
+[](https://bundlephobia.com/result?p=mobx-react)
+[](https://github.com/mobxjs/mobx/discussions)
+[](https://changelogs.xyz/mobx-react)
+
+Package with React component wrapper for combining React with MobX.
+Exports the `observer` decorator and other utilities.
+For documentation, see the [MobX](https://mobx.js.org) project.
+This package supports both React and React Native.
+
+## Compatibility matrix
+
+Only the latest version is actively maintained. If you're missing a fix or a feature in older version, consider upgrading or using [patch-package](https://www.npmjs.com/package/patch-package)
+
+| NPM Version | Support MobX version | Supported React versions | Added support for: |
+| ----------- | -------------------- | ------------------------ | -------------------------------------------------------------------------------- |
+| v9 | 6.\* | >16.8 | Hooks, React 18.2 in strict mode |
+| v7 | 6.\* | >16.8 < 18.2 | Hooks |
+| v6 | 4.\* / 5.\* | >16.8 <17 | Hooks |
+| v5 | 4.\* / 5.\* | >0.13 <17 | No, but it is possible to use `` sections inside hook based components |
+
+mobx-react 6 / 7 is a repackage of the smaller [mobx-react-lite](https://github.com/mobxjs/mobx/tree/main/packages/mobx-react-lite) package + following features from the `mobx-react@5` package added:
+
+- Support for class based components for `observer` and `@observer`
+- `Provider / inject` to pass stores around (but consider to use `React.createContext` instead)
+- `PropTypes` to describe observable based property checkers (but consider to use TypeScript instead)
+- The `disposeOnUnmount` utility / decorator to easily clean up resources such as reactions created in your class based components.
+
+## Installation
+
+`npm install mobx-react --save`
+
+Or CDN: https://unpkg.com/mobx-react (UMD namespace: `mobxReact`)
+
+```javascript
+import { observer } from "mobx-react"
+```
+
+This package provides the bindings for MobX and React.
+See the [official documentation](https://mobx.js.org/react-integration.html) for how to get started.
+
+For greenfield projects you might want to consider to use [mobx-react-lite](https://github.com/mobxjs/mobx/tree/main/packages/mobx-react-lite), if you intend to only use function based components. `React.createContext` can be used to pass stores around.
+
+## API documentation
+
+Please check [mobx.js.org](https://mobx.js.org/) for the general documentation. The documentation below highlights some specifics.
+
+### `observer(component)`
+
+Function (and decorator) that converts a React component definition, React component class, or stand-alone render function, into a reactive component. A converted component will track which observables are used by its effective `render` and automatically re-render the component when one of these values changes.
+
+#### Functional Components
+
+`React.memo` is automatically applied to functional components provided to `observer`. `observer` does not accept a functional component already wrapped in `React.memo`, or an `observer`, in order to avoid consequences that might arise as a result of wrapping it twice.
+
+#### Class Components
+
+`shouldComponentUpdate` is not supported. As such, it is recommended that class components extend `React.PureComponent`. The `observer` will automatically patch non-pure class components with an internal implementation of `React.PureComponent` if necessary.
+
+Extending `observer` class components is not supported. Always apply `observer` only on the last class in the inheritance chain.
+
+See the [MobX](https://mobx.js.org/react-integration.html#react-integration) documentation for more details.
+
+```javascript
+import { observer } from "mobx-react"
+
+// ---- ES6 syntax ----
+const TodoView = observer(
+ class TodoView extends React.Component {
+ render() {
+ return
+ }
+}
+
+// ---- or just use function components: ----
+const TodoView = observer(({ todo }) =>
{todo.title}
)
+```
+
+##### Note on using props and state in derivations
+
+`mobx-react` version 6 and lower would automatically turn `this.state` and `this.props` into observables.
+This has the benefit that computed properties and reactions were able to observe those.
+However, since this pattern is fundamentally incompatible with `StrictMode` in React 18.2 and higher, this behavior has been removed in React 18.
+
+As a result, we recommend to no longer mark properties as `@computed` in observer components if they depend on `this.state` or `this.props`.
+
+```javascript
+@observer
+class Doubler extends React.Component<{ counter: number }> {
+ @computed // BROKEN! <-- @computed should be removed in mobx-react > 7
+ get doubleValue() {
+ // Changes to this.props will no longer be detected properly, to fix it,
+ // remove the @computed annotation.
+ return this.props * 2
+ }
+
+ render() {
+ return
{this.doubleValue}
+ }
+}
+```
+
+Similarly, reactions will no longer respond to `this.state` / `this.props`. This can be overcome by creating an observable copy:
+
+```javascript
+@observer
+class Alerter extends React.Component<{ counter: number }> {
+ @observable observableCounter: number
+ reactionDisposer
+
+ constructor(props) {
+ this.observableCounter = counter
+ }
+
+ componentDidMount() {
+ // set up a reaction, by observing the observable,
+ // rather than the prop which is non-reactive:
+ this.reactionDisposer = autorun(() => {
+ if (this.observableCounter > 10) {
+ alert("Reached 10!")
+ }
+ })
+ }
+
+ componentDidUpdate() {
+ // sync the observable from props
+ this.observableCounter = this.props.counter
+ }
+
+ componentWillUnmount() {
+ this.reactionDisposer()
+ }
+
+ render() {
+ return
{this.props.counter}
+ }
+}
+```
+
+MobX-react will try to detect cases where `this.props`, `this.state` or `this.context` are used by any other derivation than the `render` method of the owning component and throw.
+This is to make sure that neither computed properties, nor reactions, nor other components accidentally rely on those fields to be reactive.
+
+This includes cases where a render callback is passed to a child, that will read from the props or state of a parent component.
+As a result, passing a function that might later read a property of a parent in a reactive context will throw as well.
+Instead, when using a callback function that is being passed to an `observer` based child, the capture should be captured locally first:
+
+```javascript
+@observer
+class ChildWrapper extends React.Component<{ counter: number }> {
+ render() {
+ // Collapsible is an observer component that should respond to this.counter,
+ // if it is expanded
+
+ // BAD:
+ return
{this.props.counter}
} />
+
+ // GOOD: (causes to pass down a fresh callback whenever counter changes,
+ // that doesn't depend on its parents props)
+ const counter = this.props.counter
+ return
{counter}
} />
+ }
+}
+```
+
+### `Observer`
+
+`Observer` is a React component, which applies `observer` to an anonymous region in your component.
+It takes as children a single, argumentless function which should return exactly one React component.
+The rendering in the function will be tracked and automatically re-rendered when needed.
+This can come in handy when needing to pass render function to external components (for example the React Native listview), or if you
+dislike the `observer` decorator / function.
+
+```javascript
+class App extends React.Component {
+ render() {
+ return (
+
+ {this.props.person.name}
+ {() =>
{this.props.person.name}
}
+
+ )
+ }
+}
+
+const person = observable({ name: "John" })
+
+ReactDOM.render(, document.body)
+person.name = "Mike" // will cause the Observer region to re-render
+```
+
+In case you are a fan of render props, you can use that instead of children. Be advised, that you cannot use both approaches at once, children have a precedence.
+Example
+
+```javascript
+class App extends React.Component {
+ render() {
+ return (
+
+ {this.props.person.name}
+
{this.props.person.name}
} />
+
+ )
+ }
+}
+
+const person = observable({ name: "John" })
+
+ReactDOM.render(, document.body)
+person.name = "Mike" // will cause the Observer region to re-render
+```
+
+### `useLocalObservable` hook
+
+Local observable state can be introduced by using the `useLocalObservable` hook, that runs once to create an observable store. A quick example would be:
+
+```javascript
+import { useLocalObservable, Observer } from "mobx-react-lite"
+
+const Todo = () => {
+ const todo = useLocalObservable(() => ({
+ title: "Test",
+ done: true,
+ toggle() {
+ this.done = !this.done
+ }
+ }))
+
+ return (
+
+ {() => (
+
+ )}
+
+ )
+}
+```
+
+When using `useLocalObservable`, all properties of the returned object will be made observable automatically, getters will be turned into computed properties, and methods will be bound to the store and apply mobx transactions automatically. If new class instances are returned from the initializer, they will be kept as is.
+
+It is important to realize that the store is created only once! It is not possible to specify dependencies to force re-creation, _nor should you directly be referring to props for the initializer function_, as changes in those won't propagate.
+
+Instead, if your store needs to refer to props (or `useState` based local state), the `useLocalObservable` should be combined with the `useAsObservableSource` hook, see below.
+
+Note that in many cases it is possible to extract the initializer function to a function outside the component definition. Which makes it possible to test the store itself in a more straight-forward manner, and avoids creating the initializer closure on each re-render.
+
+_Note: using `useLocalObservable` is mostly beneficial for really complex local state, or to obtain more uniform code base. Note that using a local store might conflict with future React features like concurrent rendering._
+
+### Server Side Rendering with `enableStaticRendering`
+
+When using server side rendering, normal lifecycle hooks of React components are not fired, as the components are rendered only once.
+Since components are never unmounted, `observer` components would in this case leak memory when being rendered server side.
+To avoid leaking memory, call `enableStaticRendering(true)` when using server side rendering.
+
+```javascript
+import { enableStaticRendering } from "mobx-react"
+
+enableStaticRendering(true)
+```
+
+This makes sure the component won't try to react to any future data changes.
+
+### Which components should be marked with `observer`?
+
+The simple rule of thumb is: _all components that render observable data_.
+If you don't want to mark a component as observer, for example to reduce the dependencies of a generic component package, make sure you only pass it plain data.
+
+### Enabling decorators (optional)
+
+Decorators are currently a stage-2 ESNext feature. How to enable them is documented [here](https://mobx.js.org/enabling-decorators.html#enabling-decorators-).
+
+### Should I still use smart and dumb components?
+
+See this [thread](https://www.reddit.com/r/reactjs/comments/4vnxg5/free_eggheadio_course_learn_mobx_react_in_30/d61oh0l).
+TL;DR: the conceptual distinction makes a lot of sense when using MobX as well, but use `observer` on all components.
+
+### `PropTypes`
+
+MobX-react provides the following additional `PropTypes` which can be used to validate against MobX structures:
+
+- `observableArray`
+- `observableArrayOf(React.PropTypes.number)`
+- `observableMap`
+- `observableObject`
+- `arrayOrObservableArray`
+- `arrayOrObservableArrayOf(React.PropTypes.number)`
+- `objectOrObservableObject`
+
+Use `import { PropTypes } from "mobx-react"` to import them, then use for example `PropTypes.observableArray`
+
+### `Provider` and `inject`
+
+_Note: usually there is no need anymore to use `Provider` / `inject` in new code bases; most of its features are now covered by `React.createContext`._
+
+`Provider` is a component that can pass stores (or other stuff) using React's context mechanism to child components.
+This is useful if you have things that you don't want to pass through multiple layers of components explicitly.
+
+`inject` can be used to pick up those stores. It is a higher order component that takes a list of strings and makes those stores available to the wrapped component.
+
+Example (based on the official [context docs](https://facebook.github.io/react/docs/context.html#passing-info-automatically-through-a-tree)):
+
+```javascript
+@inject("color")
+@observer
+class Button extends React.Component {
+ render() {
+ return
+ }
+}
+
+class Message extends React.Component {
+ render() {
+ return (
+
+
+ )
+ }
+}
+```
+
+Notes:
+
+- It is possible to read the stores provided by `Provider` using `React.useContext`, by using the `MobXProviderContext` context that can be imported from `mobx-react`.
+- If a component asks for a store and receives a store via a property with the same name, the property takes precedence. Use this to your advantage when testing!
+- When using both `@inject` and `@observer`, make sure to apply them in the correct order: `observer` should be the inner decorator, `inject` the outer. There might be additional decorators in between.
+- The original component wrapped by `inject` is available as the `wrappedComponent` property of the created higher order component.
+
+#### "The set of provided stores has changed" error
+
+Values provided through `Provider` should be final. Make sure that if you put things in `context` that might change over time, that they are `@observable` or provide some other means to listen to changes, like callbacks. However, if your stores will change over time, like an observable value of another store, MobX will throw an error.
+This restriction exists mainly for legacy reasons. If you have a scenario where you need to modify the set of stores, please leave a comment about it in this issue https://github.com/mobxjs/mobx-react/issues/745. Or a preferred way is to [use React Context](https://reactjs.org/docs/context.html) directly which does not have this restriction.
+
+#### Inject as function
+
+The above example in ES5 would start like:
+
+```javascript
+var Button = inject("color")(
+ observer(
+ class Button extends Component {
+ /* ... etc ... */
+ }
+ )
+)
+```
+
+A functional stateless component would look like:
+
+```javascript
+var Button = inject("color")(
+ observer(({ color }) => {
+ /* ... etc ... */
+ })
+)
+```
+
+#### Customizing inject
+
+Instead of passing a list of store names, it is also possible to create a custom mapper function and pass it to inject.
+The mapper function receives all stores as argument, the properties with which the components are invoked and the context, and should produce a new set of properties,
+that are mapped into the original:
+
+`mapperFunction: (allStores, props, context) => additionalProps`
+
+Since version 4.0 the `mapperFunction` itself is tracked as well, so it is possible to do things like:
+
+```javascript
+const NameDisplayer = ({ name }) =>
{name}
+
+const UserNameDisplayer = inject(stores => ({
+ name: stores.userStore.name
+}))(NameDisplayer)
+
+const user = mobx.observable({
+ name: "Noa"
+})
+
+const App = () => (
+
+
+
+)
+
+ReactDOM.render(, document.body)
+```
+
+_N.B. note that in this *specific* case neither `NameDisplayer` nor `UserNameDisplayer` needs to be decorated with `observer`, since the observable dereferencing is done in the mapper function_
+
+#### Using `PropTypes` and `defaultProps` and other static properties in combination with `inject`
+
+Inject wraps a new component around the component you pass into it.
+This means that assigning a static property to the resulting component, will be applied to the HoC, and not to the original component.
+So if you take the following example:
+
+```javascript
+const UserName = inject("userStore")(({ userStore, bold }) => someRendering())
+
+UserName.propTypes = {
+ bold: PropTypes.boolean.isRequired,
+ userStore: PropTypes.object.isRequired // will always fail
+}
+```
+
+The above propTypes are incorrect, `bold` needs to be provided by the caller of the `UserName` component and is checked by React.
+However, `userStore` does not need to be required! Although it is required for the original stateless function component, it is not
+required for the resulting inject component. After all, the whole point of that component is to provide that `userStore` itself.
+
+So if you want to make assertions on the data that is being injected (either stores or data resulting from a mapper function), the propTypes
+should be defined on the _wrapped_ component. Which is available through the static property `wrappedComponent` on the inject component:
+
+```javascript
+const UserName = inject("userStore")(({ userStore, bold }) => someRendering())
+
+UserName.propTypes = {
+ bold: PropTypes.boolean.isRequired // could be defined either here ...
+}
+
+UserName.wrappedComponent.propTypes = {
+ // ... or here
+ userStore: PropTypes.object.isRequired // correct
+}
+```
+
+The same principle applies to `defaultProps` and other static React properties.
+Note that it is not allowed to redefine `contextTypes` on `inject` components (but is possible to define it on `wrappedComponent`)
+
+Finally, mobx-react will automatically move non React related static properties from wrappedComponent to the inject component so that all static fields are
+actually available to the outside world without needing `.wrappedComponent`.
+
+#### Strongly typing inject
+
+##### With TypeScript
+
+`inject` also accepts a function (`(allStores, nextProps, nextContext) => additionalProps`) that can be used to pick all the desired stores from the available stores like this.
+The `additionalProps` will be merged into the original `nextProps` before being provided to the next component.
+
+```typescript
+import { IUserStore } from "myStore"
+
+@inject(allStores => ({
+ userStore: allStores.userStore as IUserStore
+}))
+class MyComponent extends React.Component<{ userStore?: IUserStore; otherProp: number }, {}> {
+ /* etc */
+}
+```
+
+Make sure to mark `userStore` as an optional property. It should not (necessarily) be passed in by parent components at all!
+
+Note: If you have strict null checking enabled, you could muffle the nullable type by using the `!` operator:
+
+```
+public render() {
+ const {a, b} = this.store!
+ // ...
+}
+```
+
+#### Testing store injection
+
+It is allowed to pass any declared store in directly as a property as well. This makes it easy to set up individual component tests without a provider.
+
+So if you have in your app something like:
+
+```javascript
+
+
+
+```
+
+In your test you can easily test the `Person` component by passing the necessary store as prop directly:
+
+```
+const profile = new Profile()
+const mountedComponent = mount(
+
+)
+```
+
+Bear in mind that using shallow rendering won't provide any useful results when testing injected components; only the injector will be rendered.
+To test with shallow rendering, instantiate the `wrappedComponent` instead: `shallow()`
+
+### disposeOnUnmount(componentInstance, propertyKey | function | function[])
+
+Function (and decorator) that makes sure a function (usually a disposer such as the ones returned by `reaction`, `autorun`, etc.) is automatically executed as part of the componentWillUnmount lifecycle event.
+
+```javascript
+import { disposeOnUnmount } from "mobx-react"
+
+class SomeComponent extends React.Component {
+ // decorator version
+ @disposeOnUnmount
+ someReactionDisposer = reaction(...)
+
+ // decorator version with arrays
+ @disposeOnUnmount
+ someReactionDisposers = [
+ reaction(...),
+ reaction(...)
+ ]
+
+
+ // function version over properties
+ someReactionDisposer = disposeOnUnmount(this, reaction(...))
+
+ // function version inside methods
+ componentDidMount() {
+ // single function
+ disposeOnUnmount(this, reaction(...))
+
+ // or function array
+ disposeOnUnmount(this, [
+ reaction(...),
+ reaction(...)
+ ])
+ }
+}
+```
+
+## DevTools
+
+`mobx-react@6` and higher are no longer compatible with the mobx-react-devtools.
+That is, the MobX react devtools will no longer show render timings or dependency trees of the component.
+The reason is that the standard React devtools are also capable of highlighting re-rendering components.
+And the dependency tree of a component can now be inspected by the standard devtools as well, as shown in the image below:
+
+
+
+## FAQ
+
+**Should I use `observer` for each component?**
+
+You should use `observer` on every component that displays observable data.
+Even the small ones. `observer` allows components to render independently from their parent and in general this means that
+the more you use `observer`, the better the performance become.
+The overhead of `observer` itself is negligible.
+See also [Do child components need `@observer`?](https://github.com/mobxjs/mobx/issues/101)
+
+**I see React warnings about `forceUpdate` / `setState` from React**
+
+The following warning will appear if you trigger a re-rendering between instantiating and rendering a component:
+
+```
+
+Warning: forceUpdate(...): Cannot update during an existing state transition (such as within `render`). Render methods should be a pure function of props and state.`
+
+```
+
+-- or --
+
+```
+
+Warning: setState(...): Cannot update during an existing state transition (such as within `render` or another component's constructor). Render methods should be a pure function of props and state; constructor side-effects are an anti-pattern, but can be moved to `componentWillMount`.
+
+```
+
+Usually this means that (another) component is trying to modify observables used by this components in their `constructor` or `getInitialState` methods.
+This violates the React Lifecycle, `componentWillMount` should be used instead if state needs to be modified before mounting.
diff --git a/packages/mobx-react/__mocks__/react-native.js b/packages/mobx-react/__mocks__/react-native.js
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/packages/mobx-react/__tests__/.eslintrc.yaml b/packages/mobx-react/__tests__/.eslintrc.yaml
new file mode 100644
index 0000000000..111b92a95a
--- /dev/null
+++ b/packages/mobx-react/__tests__/.eslintrc.yaml
@@ -0,0 +1,5 @@
+env:
+ jest: true
+rules:
+ "react/display-name": "off"
+ "react/prop-types": "off"
diff --git a/packages/mobx-react/__tests__/Provider.test.tsx b/packages/mobx-react/__tests__/Provider.test.tsx
new file mode 100644
index 0000000000..effea7a52f
--- /dev/null
+++ b/packages/mobx-react/__tests__/Provider.test.tsx
@@ -0,0 +1,85 @@
+import React from "react"
+import { Provider } from "../src"
+import { render } from "@testing-library/react"
+import { MobXProviderContext } from "../src/Provider"
+import { withConsole } from "./utils/withConsole"
+
+describe("Provider", () => {
+ it("should work in a simple case", () => {
+ function A() {
+ return (
+
+ {({ foo }) => foo}
+
+ )
+ }
+
+ const { container } = render()
+ expect(container).toHaveTextContent("bar")
+ })
+
+ it("should not provide the children prop", () => {
+ function A() {
+ return (
+
+
+ {stores =>
+ Reflect.has(stores, "children")
+ ? "children was provided"
+ : "children was not provided"
+ }
+
+
+ )
+ }
+
+ const { container } = render()
+ expect(container).toHaveTextContent("children was not provided")
+ })
+
+ it("supports overriding stores", () => {
+ function B() {
+ return (
+
+ {({ overridable, nonOverridable }) => `${overridable} ${nonOverridable}`}
+
+ )
+ }
+
+ function A() {
+ return (
+
+
+
+
+
+
+ )
+ }
+ const { container } = render()
+ expect(container).toMatchInlineSnapshot(`
+
+ original original
+ overridden original
+
+`)
+ })
+
+ it("should throw an error when changing stores", () => {
+ function A({ foo }) {
+ return (
+
+ {({ foo }) => foo}
+
+ )
+ }
+
+ const { rerender } = render()
+
+ withConsole(() => {
+ expect(() => {
+ rerender()
+ }).toThrow("The set of provided stores has changed.")
+ })
+ })
+})
diff --git a/packages/mobx-react/__tests__/__snapshots__/hooks.test.tsx.snap b/packages/mobx-react/__tests__/__snapshots__/hooks.test.tsx.snap
new file mode 100644
index 0000000000..9b95cca6aa
--- /dev/null
+++ b/packages/mobx-react/__tests__/__snapshots__/hooks.test.tsx.snap
@@ -0,0 +1,24 @@
+// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
+
+exports[`computed properties react to props when using hooks 1`] = `
+[MockFunction] {
+ "calls": [
+ [
+ "[mobx-react-lite] 'useAsObservableSource' is deprecated, please store the values directly in an observable, for example by using 'useLocalObservable', and sync future updates using 'useEffect' when needed. See the README for examples.",
+ ],
+ [
+ "[mobx-react-lite] 'useLocalStore' is deprecated, use 'useLocalObservable' instead.",
+ ],
+ ],
+ "results": [
+ {
+ "type": "return",
+ "value": undefined,
+ },
+ {
+ "type": "return",
+ "value": undefined,
+ },
+ ],
+}
+`;
diff --git a/packages/mobx-react/__tests__/__snapshots__/observer.test.tsx.snap b/packages/mobx-react/__tests__/__snapshots__/observer.test.tsx.snap
new file mode 100644
index 0000000000..9e895c4c9b
--- /dev/null
+++ b/packages/mobx-react/__tests__/__snapshots__/observer.test.tsx.snap
@@ -0,0 +1,31 @@
+// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
+
+exports[`#3492 should not cause warning by calling forceUpdate on uncommited components 1`] = `[MockFunction]`;
+
+exports[`Redeclaring an existing observer component as an observer should throw 1`] = `"The provided component class (AlreadyObserver) has already been declared as an observer component."`;
+
+exports[`SSR works #3448 1`] = `[MockFunction]`;
+
+exports[`issue 12 1`] = `
+
+
+
+ coffee
+ !
+
+
+ tea
+
+
+
+`;
+
+exports[`issue 12 2`] = `
+
+
+
+ soup
+
+
+
+`;
diff --git a/packages/mobx-react/__tests__/__snapshots__/stateless.test.tsx.snap b/packages/mobx-react/__tests__/__snapshots__/stateless.test.tsx.snap
new file mode 100644
index 0000000000..6292c095e5
--- /dev/null
+++ b/packages/mobx-react/__tests__/__snapshots__/stateless.test.tsx.snap
@@ -0,0 +1,27 @@
+// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
+
+exports[`stateless component with forwardRef is reactive 1`] = `
+
)
+ expect(container).toMatchSnapshot()
+
+ act(() => {
+ transaction(() => {
+ data.items[1].name = "boe"
+ data.items.splice(0, 2, { name: "soup" })
+ data.selected = "tea"
+ })
+ })
+ expect(container).toMatchSnapshot()
+ expect(events).toEqual(["table", "row: coffee", "row: tea", "table", "row: soup"])
+})
+
+test("changing state in render should fail", () => {
+ const data = observable.box(2)
+ const Comp = observer(() => {
+ if (data.get() === 3) {
+ try {
+ data.set(4) // wouldn't throw first time for lack of observers.. (could we tighten this?)
+ } catch (err) {
+ expect(err).toBeInstanceOf(Error)
+ expect(err).toMatch(
+ /Side effects like changing state are not allowed at this point/
+ )
+ }
+ }
+ return
{data.get()}
+ })
+ render()
+
+ act(() => data.set(3))
+ _resetGlobalState()
+})
+
+test("observer component can be injected", () => {
+ const msg: Array = []
+ const baseWarn = console.warn
+ console.warn = m => msg.push(m)
+
+ inject("foo")(
+ observer(
+ class T extends React.Component {
+ render() {
+ return null
+ }
+ }
+ )
+ )
+
+ // N.B, the injected component will be observer since mobx-react 4.0!
+ inject(() => ({}))(
+ observer(
+ class T extends React.Component {
+ render() {
+ return null
+ }
+ }
+ )
+ )
+
+ expect(msg.length).toBe(0)
+ console.warn = baseWarn
+})
+
+test("correctly wraps display name of child component", () => {
+ const A = observer(
+ class ObserverClass extends React.Component {
+ render() {
+ return null
+ }
+ }
+ )
+ const B: React.FunctionComponent = observer(function StatelessObserver() {
+ return null
+ })
+
+ expect(A.name).toEqual("ObserverClass")
+ expect((B as any).type.name).toEqual("StatelessObserver")
+ expect((B as any).type.displayName).toEqual(undefined)
+})
+
+describe("124 - react to changes in this.props via computed", () => {
+ class T extends React.Component {
+ @computed
+ get computedProp() {
+ return this.props.x
+ }
+ render() {
+ return (
+
+ x:
+ {this.computedProp}
+
+ )
+ }
+ }
+
+ const Comp = observer(T)
+
+ class Parent extends React.Component {
+ state = { v: 1 }
+ render() {
+ return (
+
this.setState({ v: 2 })}>
+
+
+ )
+ }
+ }
+
+ test("init state is correct", () => {
+ const { container } = render()
+
+ expect(container).toHaveTextContent("x:1")
+ })
+
+ test("change after click", () => {
+ const { container } = render()
+
+ act(() => container.querySelector("div")!.click())
+ expect(container).toHaveTextContent("x:2")
+ })
+})
+
+// Test on skip: since all reactions are now run in batched updates, the original issues can no longer be reproduced
+//this test case should be deprecated?
+test("should stop updating if error was thrown in render (#134)", () => {
+ const data = observable.box(0)
+ let renderingsCount = 0
+ let lastOwnRenderCount = 0
+ const errors: Array = []
+
+ class Outer extends React.Component {
+ state = { hasError: false }
+
+ render() {
+ return this.state.hasError ?
+ }
+}
+
+inject(({ x }) => ({ x }))(InjectSomeStores)
+
+{
+ class T extends React.Component<{ x: number }> {
+ render() {
+ return
+ }
+ }
+
+ const Injected = inject("test")(T)
+ ;
+}
+
+{
+ // just to make sure it compiles
+ class DisposeOnUnmountComponent extends React.Component<{}> {
+ @disposeOnUnmount
+ methodA = () => {}
+
+ methodB = disposeOnUnmount(this, () => {})
+ manyMethods = disposeOnUnmount(this, [() => {}, () => {}])
+ }
+
+ // manual tests: this should not compile when the decorator is not applied over a react component class
+ /*
+ class DisposeOnUnmountNotAComponent {
+ @disposeOnUnmount
+ methodA = () => {}
+
+ methodB = disposeOnUnmount(this, () => {})
+ }
+ */
+}
+
+{
+ const TestComponent = () => {
+ const observable = useLocalStore(() => ({
+ test: 3
+ }))
+
+ return
{observable.test * 2}
+ }
+ ;
+}
+
+test("ok", () => {
+ // just to satisfy jest
+})
diff --git a/packages/mobx-react/__tests__/utils/killFinalizationRegistry.ts b/packages/mobx-react/__tests__/utils/killFinalizationRegistry.ts
new file mode 100644
index 0000000000..cc8a15bd94
--- /dev/null
+++ b/packages/mobx-react/__tests__/utils/killFinalizationRegistry.ts
@@ -0,0 +1,4 @@
+// We want to be able to test reaction cleanup code that based on FinalizationRegistry & timers on the same run
+// For that we import this file on the beginning on the timer based test to the feature detection will pick the timers impl
+// @ts-ignore
+global.FinalizationRegistry = undefined
diff --git a/packages/mobx-react/__tests__/utils/withConsole.ts b/packages/mobx-react/__tests__/utils/withConsole.ts
new file mode 100644
index 0000000000..56009c2552
--- /dev/null
+++ b/packages/mobx-react/__tests__/utils/withConsole.ts
@@ -0,0 +1,22 @@
+import mockConsole, { MockObj } from "jest-mock-console"
+
+export function withConsole(fn: Function): void
+export function withConsole(settings: MockObj, fn: Function): void
+export function withConsole(props: Array, fn: Function): void
+
+export function withConsole(...args: Array): void {
+ let settings
+ let fn
+ if (typeof args[0] === "function") {
+ fn = args[0]
+ } else if (Array.isArray(args[0]) || typeof args[0] === "object") {
+ settings = args[0]
+
+ if (typeof args[1] === "function") {
+ fn = args[1]
+ }
+ }
+ const restoreConsole = mockConsole(settings)
+ fn && fn()
+ restoreConsole()
+}
diff --git a/packages/mobx-react/batchingForReactDom.js b/packages/mobx-react/batchingForReactDom.js
new file mode 100644
index 0000000000..3c73786c62
--- /dev/null
+++ b/packages/mobx-react/batchingForReactDom.js
@@ -0,0 +1 @@
+require("mobx-react-lite/batchingForReactDom")
diff --git a/packages/mobx-react/batchingForReactNative.js b/packages/mobx-react/batchingForReactNative.js
new file mode 100644
index 0000000000..f6f328548a
--- /dev/null
+++ b/packages/mobx-react/batchingForReactNative.js
@@ -0,0 +1 @@
+require("mobx-react-lite/batchingForReactNative")
diff --git a/packages/mobx-react/batchingOptOut.js b/packages/mobx-react/batchingOptOut.js
new file mode 100644
index 0000000000..f9533d9921
--- /dev/null
+++ b/packages/mobx-react/batchingOptOut.js
@@ -0,0 +1 @@
+require("mobx-react-lite/batchingOptOut")
diff --git a/packages/mobx-react/hooks.png b/packages/mobx-react/hooks.png
new file mode 100644
index 0000000000..0e427bd468
Binary files /dev/null and b/packages/mobx-react/hooks.png differ
diff --git a/packages/mobx-react/jest.config.js b/packages/mobx-react/jest.config.js
new file mode 100644
index 0000000000..9a49a001f2
--- /dev/null
+++ b/packages/mobx-react/jest.config.js
@@ -0,0 +1,7 @@
+const buildConfig = require("../../jest.base.config")
+
+module.exports = buildConfig(__dirname, {
+ testRegex: "__tests__/.*\\.tsx$",
+ setupFilesAfterEnv: [`/jest.setup.ts`],
+ testPathIgnorePatterns: ["node_modules", "/__tests__/utils"]
+})
diff --git a/packages/mobx-react/jest.setup.ts b/packages/mobx-react/jest.setup.ts
new file mode 100644
index 0000000000..c02f52ea96
--- /dev/null
+++ b/packages/mobx-react/jest.setup.ts
@@ -0,0 +1,9 @@
+import "@testing-library/jest-dom/extend-expect"
+import { configure } from "mobx"
+
+global.setImmediate = global.setImmediate || ((fn, ...args) => global.setTimeout(fn, 0, ...args))
+
+configure({ enforceActions: "never" })
+
+// @ts-ignore
+global.__DEV__ = true
diff --git a/packages/mobx-react/package.json b/packages/mobx-react/package.json
new file mode 100644
index 0000000000..030fbdf440
--- /dev/null
+++ b/packages/mobx-react/package.json
@@ -0,0 +1,76 @@
+{
+ "name": "mobx-react",
+ "version": "9.2.1",
+ "description": "React bindings for MobX. Create fully reactive components.",
+ "source": "src/index.ts",
+ "main": "dist/index.js",
+ "umd:main": "dist/mobxreact.umd.production.min.js",
+ "unpkg": "dist/mobxreact.umd.production.min.js",
+ "jsdelivr": "dist/mobxreact.umd.production.min.js",
+ "jsnext:main": "dist/mobxreact.esm.js",
+ "module": "dist/mobxreact.esm.js",
+ "react-native": "dist/mobxreact.esm.js",
+ "types": "dist/index.d.ts",
+ "typings": "dist/index.d.ts",
+ "files": [
+ "src",
+ "dist",
+ "LICENSE",
+ "CHANGELOG.md",
+ "README.md",
+ "batching*"
+ ],
+ "sideEffects": false,
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/mobxjs/mobx.git"
+ },
+ "author": "Michel Weststrate",
+ "license": "MIT",
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/mobx"
+ },
+ "bugs": {
+ "url": "https://github.com/mobxjs/mobx/issues"
+ },
+ "homepage": "https://mobx.js.org",
+ "dependencies": {
+ "mobx-react-lite": "^4.1.1"
+ },
+ "peerDependencies": {
+ "mobx": "^6.9.0",
+ "react": "^16.8.0 || ^17 || ^18 || ^19"
+ },
+ "peerDependenciesMeta": {
+ "react-dom": {
+ "optional": true
+ },
+ "react-native": {
+ "optional": true
+ }
+ },
+ "devDependencies": {
+ "mobx": "^6.15.0",
+ "mobx-react-lite": "^4.1.1",
+ "expose-gc": "^1.0.0"
+ },
+ "keywords": [
+ "mobx",
+ "mobservable",
+ "react-component",
+ "react",
+ "reactjs",
+ "reactive"
+ ],
+ "scripts": {
+ "lint": "eslint src/**/* --ext .js,.ts,.tsx",
+ "build": "node ../../scripts/build.js mobxReact",
+ "build:test": "yarn build --target test",
+ "test": "jest",
+ "test:size": "yarn import-size --report . observer",
+ "test:types": "tsc --noEmit",
+ "test:check": "yarn test:types",
+ "prepublishOnly": "yarn build --target publish"
+ }
+}
diff --git a/packages/mobx-react/src/Provider.tsx b/packages/mobx-react/src/Provider.tsx
new file mode 100644
index 0000000000..b40f1da728
--- /dev/null
+++ b/packages/mobx-react/src/Provider.tsx
@@ -0,0 +1,29 @@
+import React from "react"
+import { shallowEqual } from "./utils/utils"
+import { IValueMap } from "./types/IValueMap"
+
+export const MobXProviderContext = React.createContext({})
+
+export interface ProviderProps extends IValueMap {
+ children: React.ReactNode
+}
+
+export function Provider(props: ProviderProps) {
+ const { children, ...stores } = props
+ const parentValue = React.useContext(MobXProviderContext)
+ const mutableProviderRef = React.useRef({ ...parentValue, ...stores })
+ const value = mutableProviderRef.current
+
+ if (__DEV__) {
+ const newValue = { ...value, ...stores } // spread in previous state for the context based stores
+ if (!shallowEqual(value, newValue)) {
+ throw new Error(
+ "MobX Provider: The set of provided stores has changed. See: https://github.com/mobxjs/mobx-react#the-set-of-provided-stores-has-changed-error."
+ )
+ }
+ }
+
+ return {children}
+}
+
+Provider.displayName = "MobXProvider"
diff --git a/packages/mobx-react/src/disposeOnUnmount.ts b/packages/mobx-react/src/disposeOnUnmount.ts
new file mode 100644
index 0000000000..fb5d7e251a
--- /dev/null
+++ b/packages/mobx-react/src/disposeOnUnmount.ts
@@ -0,0 +1,111 @@
+import React from "react"
+import { patch } from "./utils/utils"
+
+const reactMajorVersion = Number.parseInt(React.version.split(".")[0])
+let warnedAboutDisposeOnUnmountDeprecated = false
+
+type Disposer = () => void
+
+const protoStoreKey = Symbol("disposeOnUnmountProto")
+const instStoreKey = Symbol("disposeOnUnmountInst")
+
+function runDisposersOnWillUnmount() {
+ ;[...(this[protoStoreKey] || []), ...(this[instStoreKey] || [])].forEach(propKeyOrFunction => {
+ const prop =
+ typeof propKeyOrFunction === "string" ? this[propKeyOrFunction] : propKeyOrFunction
+ if (prop !== undefined && prop !== null) {
+ if (Array.isArray(prop)) prop.map(f => f())
+ else prop()
+ }
+ })
+}
+
+/**
+ * @deprecated `disposeOnUnmount` is not compatible with React 18 and higher.
+ */
+export function disposeOnUnmount(target: React.Component, propertyKey: PropertyKey): void
+
+/**
+ * @deprecated `disposeOnUnmount` is not compatible with React 18 and higher.
+ */
+export function disposeOnUnmount>(
+ target: React.Component,
+ fn: TF
+): TF
+
+/**
+ * @deprecated `disposeOnUnmount` is not compatible with React 18 and higher.
+ */
+export function disposeOnUnmount(
+ target: React.Component,
+ propertyKeyOrFunction: PropertyKey | Disposer | Array
+): PropertyKey | Disposer | Array | void {
+ if (Array.isArray(propertyKeyOrFunction)) {
+ return propertyKeyOrFunction.map(fn => disposeOnUnmount(target, fn))
+ }
+
+ if (!warnedAboutDisposeOnUnmountDeprecated) {
+ if (reactMajorVersion >= 18) {
+ console.error(
+ "[mobx-react] disposeOnUnmount is not compatible with React 18 and higher. Don't use it."
+ )
+ } else {
+ console.warn(
+ "[mobx-react] disposeOnUnmount is deprecated. It won't work correctly with React 18 and higher."
+ )
+ }
+ warnedAboutDisposeOnUnmountDeprecated = true
+ }
+
+ const c = Object.getPrototypeOf(target).constructor
+ const c2 = Object.getPrototypeOf(target.constructor)
+ // Special case for react-hot-loader
+ const c3 = Object.getPrototypeOf(Object.getPrototypeOf(target))
+ if (
+ !(
+ c === React.Component ||
+ c === React.PureComponent ||
+ c2 === React.Component ||
+ c2 === React.PureComponent ||
+ c3 === React.Component ||
+ c3 === React.PureComponent
+ )
+ ) {
+ throw new Error(
+ "[mobx-react] disposeOnUnmount only supports direct subclasses of React.Component or React.PureComponent."
+ )
+ }
+
+ if (
+ typeof propertyKeyOrFunction !== "string" &&
+ typeof propertyKeyOrFunction !== "function" &&
+ !Array.isArray(propertyKeyOrFunction)
+ ) {
+ throw new Error(
+ "[mobx-react] disposeOnUnmount only works if the parameter is either a property key or a function."
+ )
+ }
+
+ // decorator's target is the prototype, so it doesn't have any instance properties like props
+ const isDecorator = typeof propertyKeyOrFunction === "string"
+
+ // add property key / function we want run (disposed) to the store
+ const componentWasAlreadyModified = !!target[protoStoreKey] || !!target[instStoreKey]
+ const store = isDecorator
+ ? // decorators are added to the prototype store
+ target[protoStoreKey] || (target[protoStoreKey] = [])
+ : // functions are added to the instance store
+ target[instStoreKey] || (target[instStoreKey] = [])
+
+ store.push(propertyKeyOrFunction)
+
+ // tweak the component class componentWillUnmount if not done already
+ if (!componentWasAlreadyModified) {
+ patch(target, "componentWillUnmount", runDisposersOnWillUnmount)
+ }
+
+ // return the disposer as is if invoked as a non decorator
+ if (typeof propertyKeyOrFunction !== "string") {
+ return propertyKeyOrFunction
+ }
+}
diff --git a/packages/mobx-react/src/globals.d.ts b/packages/mobx-react/src/globals.d.ts
new file mode 100644
index 0000000000..404c55f1ee
--- /dev/null
+++ b/packages/mobx-react/src/globals.d.ts
@@ -0,0 +1 @@
+declare const __DEV__: boolean
diff --git a/packages/mobx-react/src/index.ts b/packages/mobx-react/src/index.ts
new file mode 100644
index 0000000000..65abc7ed72
--- /dev/null
+++ b/packages/mobx-react/src/index.ts
@@ -0,0 +1,30 @@
+import { observable } from "mobx"
+import { Component } from "react"
+
+if (!Component) {
+ throw new Error("mobx-react requires React to be available")
+}
+
+if (!observable) {
+ throw new Error("mobx-react requires mobx to be available")
+}
+
+export {
+ Observer,
+ useObserver,
+ useAsObservableSource,
+ useLocalStore,
+ isUsingStaticRendering,
+ useStaticRendering,
+ enableStaticRendering,
+ observerBatching,
+ useLocalObservable
+} from "mobx-react-lite"
+
+export { observer } from "./observer"
+
+export { MobXProviderContext, Provider, ProviderProps } from "./Provider"
+export { inject } from "./inject"
+export { disposeOnUnmount } from "./disposeOnUnmount"
+export { PropTypes } from "./propTypes"
+export { IWrappedComponent } from "./types/IWrappedComponent"
diff --git a/packages/mobx-react/src/inject.ts b/packages/mobx-react/src/inject.ts
new file mode 100644
index 0000000000..88e0f3341b
--- /dev/null
+++ b/packages/mobx-react/src/inject.ts
@@ -0,0 +1,107 @@
+import React from "react"
+import { observer } from "./observer"
+import { copyStaticProperties } from "./utils/utils"
+import { MobXProviderContext } from "./Provider"
+import { IReactComponent } from "./types/IReactComponent"
+import { IValueMap } from "./types/IValueMap"
+import { IWrappedComponent } from "./types/IWrappedComponent"
+import { IStoresToProps } from "./types/IStoresToProps"
+
+/**
+ * Store Injection
+ */
+function createStoreInjector(
+ grabStoresFn: IStoresToProps,
+ component: IReactComponent,
+ injectNames: string,
+ makeReactive: boolean
+): IReactComponent {
+ // Support forward refs
+ let Injector: IReactComponent = React.forwardRef((props, ref) => {
+ const newProps = { ...props }
+ const context = React.useContext(MobXProviderContext)
+ Object.assign(newProps, grabStoresFn(context || {}, newProps) || {})
+
+ if (ref) {
+ newProps.ref = ref
+ }
+
+ return React.createElement(component, newProps)
+ })
+
+ if (makeReactive) Injector = observer(Injector)
+ Injector["isMobxInjector"] = true // assigned late to suppress observer warning
+
+ // Static fields from component should be visible on the generated Injector
+ copyStaticProperties(component, Injector)
+ Injector["wrappedComponent"] = component
+ Injector.displayName = getInjectName(component, injectNames)
+ return Injector
+}
+
+function getInjectName(component: IReactComponent, injectNames: string): string {
+ let displayName
+ const componentName =
+ component.displayName ||
+ component.name ||
+ (component.constructor && component.constructor.name) ||
+ "Component"
+ if (injectNames) displayName = "inject-with-" + injectNames + "(" + componentName + ")"
+ else displayName = "inject(" + componentName + ")"
+ return displayName
+}
+
+function grabStoresByName(
+ storeNames: Array
+): (
+ baseStores: IValueMap,
+ nextProps: React.ClassAttributes
+) => React.PropsWithRef | undefined {
+ return function (baseStores, nextProps) {
+ storeNames.forEach(function (storeName) {
+ if (
+ storeName in nextProps // prefer props over stores
+ )
+ return
+ if (!(storeName in baseStores))
+ throw new Error(
+ "MobX injector: Store '" +
+ storeName +
+ "' is not available! Make sure it is provided by some Provider"
+ )
+ nextProps[storeName] = baseStores[storeName]
+ })
+ return nextProps
+ }
+}
+
+export function inject(
+ ...stores: Array
+): >(
+ target: T
+) => T & (T extends IReactComponent ? IWrappedComponent
: never)
+export function inject(
+ fn: IStoresToProps
+): (target: T) => T & IWrappedComponent
+
+/**
+ * higher order component that injects stores to a child.
+ * takes either a varargs list of strings, which are stores read from the context,
+ * or a function that manually maps the available stores from the context to props:
+ * storesToProps(mobxStores, props, context) => newProps
+ */
+export function inject(/* fn(stores, nextProps) or ...storeNames */ ...storeNames: Array) {
+ if (typeof arguments[0] === "function") {
+ let grabStoresFn = arguments[0]
+ return (componentClass: React.ComponentClass) =>
+ createStoreInjector(grabStoresFn, componentClass, grabStoresFn.name, true)
+ } else {
+ return (componentClass: React.ComponentClass) =>
+ createStoreInjector(
+ grabStoresByName(storeNames),
+ componentClass,
+ storeNames.join("-"),
+ false
+ )
+ }
+}
diff --git a/packages/mobx-react/src/observer.tsx b/packages/mobx-react/src/observer.tsx
new file mode 100644
index 0000000000..a16bb13887
--- /dev/null
+++ b/packages/mobx-react/src/observer.tsx
@@ -0,0 +1,32 @@
+import * as React from "react"
+import { observer as observerLite } from "mobx-react-lite"
+
+import { makeClassComponentObserver } from "./observerClass"
+import { IReactComponent } from "./types/IReactComponent"
+
+/**
+ * Observer function / decorator
+ */
+export function observer(component: T, context: ClassDecoratorContext): void
+export function observer(component: T): T
+export function observer(component: T, context?: ClassDecoratorContext): T {
+ if (context && context.kind !== "class") {
+ throw new Error("The @observer decorator can be used on classes only")
+ }
+ if (component["isMobxInjector"] === true) {
+ console.warn(
+ "Mobx observer: You are trying to use `observer` on a component that already has `inject`. Please apply `observer` before applying `inject`"
+ )
+ }
+
+ if (
+ Object.prototype.isPrototypeOf.call(React.Component, component) ||
+ Object.prototype.isPrototypeOf.call(React.PureComponent, component)
+ ) {
+ // Class component
+ return makeClassComponentObserver(component as React.ComponentClass) as T
+ } else {
+ // Function component
+ return observerLite(component as React.FunctionComponent) as T
+ }
+}
diff --git a/packages/mobx-react/src/observerClass.ts b/packages/mobx-react/src/observerClass.ts
new file mode 100644
index 0000000000..c428e8003f
--- /dev/null
+++ b/packages/mobx-react/src/observerClass.ts
@@ -0,0 +1,274 @@
+import { PureComponent, Component, ComponentClass, ClassAttributes } from "react"
+import {
+ _allowStateChanges,
+ Reaction,
+ _allowStateReadsStart,
+ _allowStateReadsEnd,
+ _getGlobalState
+} from "mobx"
+import {
+ isUsingStaticRendering,
+ _observerFinalizationRegistry as observerFinalizationRegistry
+} from "mobx-react-lite"
+import { shallowEqual, patch } from "./utils/utils"
+
+const administrationSymbol = Symbol("ObserverAdministration")
+const isMobXReactObserverSymbol = Symbol("isMobXReactObserver")
+
+let observablePropDescriptors: PropertyDescriptorMap
+if (__DEV__) {
+ observablePropDescriptors = {
+ props: createObservablePropDescriptor("props"),
+ state: createObservablePropDescriptor("state"),
+ context: createObservablePropDescriptor("context")
+ }
+}
+
+type ObserverAdministration = {
+ reaction: Reaction | null // also serves as disposed flag
+ forceUpdate: Function | null
+ mounted: boolean // we could use forceUpdate as mounted flag
+ reactionInvalidatedBeforeMount: boolean
+ name: string
+ // Used only on __DEV__
+ props: any
+ state: any
+ context: any
+}
+
+function getAdministration(component: Component): ObserverAdministration {
+ // We create administration lazily, because we can't patch constructor
+ // and the exact moment of initialization partially depends on React internals.
+ // At the time of writing this, the first thing invoked is one of the observable getter/setter (state/props/context).
+ return (component[administrationSymbol] ??= {
+ reaction: null,
+ mounted: false,
+ reactionInvalidatedBeforeMount: false,
+ forceUpdate: null,
+ name: getDisplayName(component.constructor as ComponentClass),
+ state: undefined,
+ props: undefined,
+ context: undefined
+ })
+}
+
+export function makeClassComponentObserver(
+ componentClass: ComponentClass
+): ComponentClass {
+ const { prototype } = componentClass
+
+ if (componentClass[isMobXReactObserverSymbol]) {
+ const displayName = getDisplayName(componentClass)
+ throw new Error(
+ `The provided component class (${displayName}) has already been declared as an observer component.`
+ )
+ } else {
+ componentClass[isMobXReactObserverSymbol] = true
+ }
+
+ if (prototype.componentWillReact) {
+ throw new Error("The componentWillReact life-cycle event is no longer supported")
+ }
+ if (componentClass["__proto__"] !== PureComponent) {
+ if (!prototype.shouldComponentUpdate) {
+ prototype.shouldComponentUpdate = observerSCU
+ } else if (prototype.shouldComponentUpdate !== observerSCU) {
+ // n.b. unequal check, instead of existence check, as @observer might be on superclass as well
+ throw new Error(
+ "It is not allowed to use shouldComponentUpdate in observer based components."
+ )
+ }
+ }
+
+ if (__DEV__) {
+ Object.defineProperties(prototype, observablePropDescriptors)
+ }
+
+ const originalRender = prototype.render
+ if (typeof originalRender !== "function") {
+ const displayName = getDisplayName(componentClass)
+ throw new Error(
+ `[mobx-react] class component (${displayName}) is missing \`render\` method.` +
+ `\n\`observer\` requires \`render\` being a function defined on prototype.` +
+ `\n\`render = () => {}\` or \`render = function() {}\` is not supported.`
+ )
+ }
+
+ prototype.render = function () {
+ Object.defineProperty(this, "render", {
+ // There is no safe way to replace render, therefore it's forbidden.
+ configurable: false,
+ writable: false,
+ value: isUsingStaticRendering()
+ ? originalRender
+ : createReactiveRender.call(this, originalRender)
+ })
+ return this.render()
+ }
+
+ const originalComponentDidMount = prototype.componentDidMount
+ prototype.componentDidMount = function () {
+ if (__DEV__ && this.componentDidMount !== Object.getPrototypeOf(this).componentDidMount) {
+ const displayName = getDisplayName(componentClass)
+ throw new Error(
+ `[mobx-react] \`observer(${displayName}).componentDidMount\` must be defined on prototype.` +
+ `\n\`componentDidMount = () => {}\` or \`componentDidMount = function() {}\` is not supported.`
+ )
+ }
+
+ // `componentDidMount` may not be called at all. React can abandon the instance after `render`.
+ // That's why we use finalization registry to dispose reaction created during render.
+ // Happens with `` see #3492
+ //
+ // `componentDidMount` can be called immediately after `componentWillUnmount` without calling `render` in between.
+ // Happens with ``see #3395.
+ //
+ // If `componentDidMount` is called, it's guaranteed to run synchronously with render (similary to `useLayoutEffect`).
+ // Therefore we don't have to worry about external (observable) state being updated before mount (no state version checking).
+ //
+ // Things may change: "In the future, React will provide a feature that lets components preserve state between unmounts"
+
+ const admin = getAdministration(this)
+
+ admin.mounted = true
+
+ // Component instance committed, prevent reaction disposal.
+ observerFinalizationRegistry.unregister(this)
+
+ // We don't set forceUpdate before mount because it requires a reference to `this`,
+ // therefore `this` could NOT be garbage collected before mount,
+ // preventing reaction disposal by FinalizationRegistry and leading to memory leak.
+ // As an alternative we could have `admin.instanceRef = new WeakRef(this)`, but lets avoid it if possible.
+ admin.forceUpdate = () => this.forceUpdate()
+
+ if (!admin.reaction || admin.reactionInvalidatedBeforeMount) {
+ // Missing reaction:
+ // 1. Instance was unmounted (reaction disposed) and immediately remounted without running render #3395.
+ // 2. Reaction was disposed by finalization registry before mount. Shouldn't ever happen for class components:
+ // `componentDidMount` runs synchronously after render, but our registry are deferred (can't run in between).
+ // In any case we lost subscriptions to observables, so we have to create new reaction and re-render to resubscribe.
+ // The reaction will be created lazily by following render.
+
+ // Reaction invalidated before mount:
+ // 1. A descendant's `componenDidMount` invalidated it's parent #3730
+
+ admin.forceUpdate()
+ }
+ return originalComponentDidMount?.apply(this, arguments)
+ }
+
+ // TODO@major Overly complicated "patch" is only needed to support the deprecated @disposeOnUnmount
+ patch(prototype, "componentWillUnmount", function () {
+ if (isUsingStaticRendering()) {
+ return
+ }
+ const admin = getAdministration(this)
+ admin.reaction?.dispose()
+ admin.reaction = null
+ admin.forceUpdate = null
+ admin.mounted = false
+ admin.reactionInvalidatedBeforeMount = false
+ })
+
+ return componentClass
+}
+
+// Generates a friendly name for debugging
+function getDisplayName(componentClass: ComponentClass) {
+ return componentClass.displayName || componentClass.name || ""
+}
+
+function createReactiveRender(originalRender: any) {
+ const boundOriginalRender = originalRender.bind(this)
+
+ const admin = getAdministration(this)
+
+ function reactiveRender() {
+ if (!admin.reaction) {
+ // Create reaction lazily to support re-mounting #3395
+ admin.reaction = createReaction(admin)
+ if (!admin.mounted) {
+ // React can abandon this instance and never call `componentDidMount`/`componentWillUnmount`,
+ // we have to make sure reaction will be disposed.
+ observerFinalizationRegistry.register(this, admin, this)
+ }
+ }
+
+ let error: unknown = undefined
+ let renderResult = undefined
+ admin.reaction.track(() => {
+ try {
+ // TODO@major
+ // Optimization: replace with _allowStateChangesStart/End (not available in mobx@6.0.0)
+ renderResult = _allowStateChanges(false, boundOriginalRender)
+ } catch (e) {
+ error = e
+ }
+ })
+ if (error) {
+ throw error
+ }
+ return renderResult
+ }
+
+ return reactiveRender
+}
+
+function createReaction(admin: ObserverAdministration) {
+ return new Reaction(`${admin.name}.render()`, () => {
+ if (!admin.mounted) {
+ // This is neccessary to avoid react warning about calling forceUpdate on component that isn't mounted yet.
+ // This happens when component is abandoned after render - our reaction is already created and reacts to changes.
+ // `componenDidMount` runs synchronously after `render`, so unlike functional component, there is no delay during which the reaction could be invalidated.
+ // However `componentDidMount` runs AFTER it's descendants' `componentDidMount`, which CAN invalidate the reaction, see #3730. Therefore remember and forceUpdate on mount.
+ admin.reactionInvalidatedBeforeMount = true
+ return
+ }
+
+ try {
+ admin.forceUpdate?.()
+ } catch (error) {
+ admin.reaction?.dispose()
+ admin.reaction = null
+ }
+ })
+}
+
+function observerSCU(nextProps: ClassAttributes