[
  {
    "path": ".editorconfig",
    "content": "root = true\n\n[*]\ncharset = utf-8\n\n[*.{js,json,yml,html,md}]\nindent_style = tab\ntab_width = 4\ntrim_trailing_whitespace = true\ninsert_final_newline = true\nend_of_line = lf\n\n[{package.json,.travis.yml,.github/**/*.yml}]\nindent_style = space\nindent_size = 2\n"
  },
  {
    "path": ".eslintignore",
    "content": "/coverage\n/node_modules\n/jsconfig.json\n/npm-debug.log\n/.vscode\n/.DS_Store\n/.eslintcache\n\n# These are artifacts from various scripts\n/mithril.js\n/mithril.min.js\n/stream/stream.min.js\n"
  },
  {
    "path": ".eslintrc.js",
    "content": "\"use strict\"\n\nmodule.exports = {\n\t\"env\": {\n\t\t\"browser\": true,\n\t\t\"commonjs\": true,\n\t\t\"es6\": true,\n\t\t\"node\": true\n\t},\n\t\"extends\": \"eslint:recommended\",\n\t\"rules\": {\n\t\t\"accessor-pairs\": \"error\",\n\t\t\"array-bracket-spacing\": [\n\t\t\t\"error\",\n\t\t\t\"never\"\n\t\t],\n\t\t\"array-callback-return\": \"error\",\n\t\t\"arrow-body-style\": \"error\",\n\t\t\"arrow-parens\": \"error\",\n\t\t\"arrow-spacing\": \"error\",\n\t\t\"block-scoped-var\": \"off\",\n\t\t\"block-spacing\": \"off\",\n\t\t\"brace-style\": \"off\",\n\t\t\"callback-return\": \"off\",\n\t\t\"camelcase\": [\n\t\t\t\"error\",\n\t\t\t{\n\t\t\t\t\"properties\": \"never\"\n\t\t\t}\n\t\t],\n\t\t\"comma-dangle\": [\n\t\t\t\"error\",\n\t\t\t\"only-multiline\"\n\t\t],\n\t\t\"comma-spacing\": \"off\",\n\t\t\"comma-style\": [\n\t\t\t\"error\",\n\t\t\t\"last\"\n\t\t],\n\t\t\"complexity\": \"off\",\n\t\t\"computed-property-spacing\": [\n\t\t\t\"error\",\n\t\t\t\"never\"\n\t\t],\n\t\t\"consistent-return\": \"off\",\n\t\t\"consistent-this\": \"off\",\n\t\t\"curly\": \"off\",\n\t\t\"default-case\": \"off\",\n\t\t\"dot-location\": [\n\t\t\t\"error\",\n\t\t\t\"property\"\n\t\t],\n\t\t\"dot-notation\": \"off\",\n\t\t\"eol-last\": \"off\",\n\t\t\"eqeqeq\": \"off\",\n\t\t\"func-names\": \"off\",\n\t\t\"func-style\": \"off\",\n\t\t\"generator-star-spacing\": \"error\",\n\t\t\"global-require\": \"error\",\n\t\t\"guard-for-in\": \"off\",\n\t\t\"handle-callback-err\": \"error\",\n\t\t\"id-blacklist\": \"error\",\n\t\t\"id-length\": \"off\",\n\t\t\"id-match\": \"error\",\n\t\t\"indent\": [\n\t\t\t\"warn\",\n\t\t\t\"tab\",\n\t\t\t{\n\t\t\t\t\"outerIIFEBody\": 0,\n\t\t\t\t\"SwitchCase\": 1\n\t\t\t}\n\t\t],\n\t\t\"init-declarations\": \"off\",\n\t\t\"jsx-quotes\": \"error\",\n\t\t\"key-spacing\": \"off\",\n\t\t\"keyword-spacing\": \"off\",\n\t\t\"linebreak-style\": \"off\",\n\t\t\"lines-around-comment\": \"off\",\n\t\t\"max-depth\": \"off\",\n\t\t\"max-len\": \"off\",\n\t\t\"max-nested-callbacks\": \"error\",\n\t\t\"max-params\": \"off\",\n\t\t\"max-statements\": \"off\",\n\t\t\"max-statements-per-line\": \"off\",\n\t\t\"new-parens\": \"off\",\n\t\t\"newline-after-var\": \"off\",\n\t\t\"newline-before-return\": \"off\",\n\t\t\"newline-per-chained-call\": \"off\",\n\t\t\"no-alert\": \"error\",\n\t\t\"no-array-constructor\": \"error\",\n\t\t\"no-bitwise\": \"error\",\n\t\t\"no-caller\": \"error\",\n\t\t\"no-catch-shadow\": \"off\",\n\t\t\"no-cond-assign\": \"off\",\n\t\t\"no-confusing-arrow\": \"error\",\n\t\t\"no-console\": \"off\",\n\t\t\"no-continue\": \"off\",\n\t\t\"no-div-regex\": \"error\",\n\t\t\"no-duplicate-imports\": \"error\",\n\t\t\"no-else-return\": \"off\",\n\t\t\"no-empty-function\": \"off\",\n\t\t\"no-eq-null\": \"off\",\n\t\t\"no-eval\": \"error\",\n\t\t\"no-extend-native\": \"off\",\n\t\t\"no-extra-bind\": \"error\",\n\t\t\"no-extra-label\": \"error\",\n\t\t\"no-extra-parens\": \"off\",\n\t\t\"no-floating-decimal\": \"error\",\n\t\t\"no-implicit-coercion\": \"error\",\n\t\t\"no-implicit-globals\": \"error\",\n\t\t\"no-implied-eval\": \"error\",\n\t\t\"no-inline-comments\": \"off\",\n\t\t\"no-invalid-this\": \"off\",\n\t\t\"no-iterator\": \"error\",\n\t\t\"no-label-var\": \"off\",\n\t\t\"no-labels\": \"off\",\n\t\t\"no-lone-blocks\": \"error\",\n\t\t\"no-lonely-if\": \"off\",\n\t\t\"no-loop-func\": \"off\",\n\t\t\"no-magic-numbers\": \"off\",\n\t\t\"no-mixed-requires\": \"error\",\n\t\t\"no-multi-spaces\": \"error\",\n\t\t\"no-multi-str\": \"error\",\n\t\t\"no-multiple-empty-lines\": \"error\",\n\t\t\"no-native-reassign\": \"error\",\n\t\t\"no-negated-condition\": \"off\",\n\t\t\"no-nested-ternary\": \"off\",\n\t\t\"no-new\": \"off\",\n\t\t\"no-new-func\": \"off\",\n\t\t\"no-new-object\": \"error\",\n\t\t\"no-new-require\": \"error\",\n\t\t\"no-new-wrappers\": \"error\",\n\t\t\"no-octal-escape\": \"error\",\n\t\t\"no-param-reassign\": \"off\",\n\t\t\"no-path-concat\": \"off\",\n\t\t\"no-plusplus\": \"off\",\n\t\t\"no-process-env\": \"error\",\n\t\t\"no-process-exit\": \"error\",\n\t\t\"no-proto\": \"error\",\n\t\t\"no-redeclare\": \"off\",\n\t\t\"no-restricted-globals\": \"error\",\n\t\t\"no-restricted-imports\": \"error\",\n\t\t\"no-restricted-modules\": \"error\",\n\t\t\"no-restricted-syntax\": \"error\",\n\t\t\"no-return-assign\": \"off\",\n\t\t\"no-script-url\": \"error\",\n\t\t\"no-self-compare\": \"error\",\n\t\t\"no-sequences\": \"off\",\n\t\t\"no-shadow\": \"off\",\n\t\t\"no-shadow-restricted-names\": \"error\",\n\t\t\"no-spaced-func\": \"error\",\n\t\t\"no-sync\": \"off\",\n\t\t\"no-ternary\": \"off\",\n\t\t\"no-throw-literal\": \"off\",\n\t\t\"no-trailing-spaces\": [\n\t\t\t\"error\",\n\t\t\t{\n\t\t\t\t\"skipBlankLines\": true\n\t\t\t}\n\t\t],\n\t\t\"no-undef-init\": \"error\",\n\t\t\"no-undefined\": \"off\",\n\t\t\"no-underscore-dangle\": \"off\",\n\t\t\"no-unmodified-loop-condition\": \"error\",\n\t\t\"no-unneeded-ternary\": \"error\",\n\t\t\"no-unused-expressions\": \"off\",\n\t\t\"no-use-before-define\": \"off\",\n\t\t\"no-useless-call\": \"error\",\n\t\t\"no-useless-concat\": \"error\",\n\t\t\"no-useless-constructor\": \"error\",\n\t\t\"no-useless-escape\": \"off\",\n\t\t\"no-var\": \"off\",\n\t\t\"no-void\": \"off\",\n\t\t\"no-warning-comments\": \"off\",\n\t\t\"no-whitespace-before-property\": \"error\",\n\t\t\"no-with\": \"error\",\n\t\t\"object-curly-spacing\": [\n\t\t\t\"error\",\n\t\t\t\"never\"\n\t\t],\n\t\t\"object-shorthand\": \"off\",\n\t\t\"one-var\": \"off\",\n\t\t\"one-var-declaration-per-line\": \"off\",\n\t\t\"operator-assignment\": [\n\t\t\t\"error\",\n\t\t\t\"always\"\n\t\t],\n\t\t\"operator-linebreak\": \"off\",\n\t\t\"padded-blocks\": \"off\",\n\t\t\"prefer-arrow-callback\": \"off\",\n\t\t\"prefer-const\": \"error\",\n\t\t\"prefer-reflect\": \"off\",\n\t\t\"prefer-rest-params\": \"off\",\n\t\t\"prefer-spread\": \"off\",\n\t\t\"prefer-template\": \"off\",\n\t\t\"quote-props\": \"off\",\n\t\t\"quotes\": [\n\t\t\t\"error\",\n\t\t\t\"double\",\n\t\t\t{\"avoidEscape\": true}\n\t\t],\n\t\t\"radix\": [\n\t\t\t\"error\",\n\t\t\t\"always\"\n\t\t],\n\t\t\"require-jsdoc\": \"off\",\n\t\t\"require-yield\": \"error\",\n\t\t\"semi\": \"off\",\n\t\t\"semi-spacing\": \"off\",\n\t\t\"sort-imports\": \"error\",\n\t\t\"sort-vars\": \"off\",\n\t\t\"space-before-blocks\": \"off\",\n\t\t\"space-before-function-paren\": \"off\",\n\t\t\"space-in-parens\": [\n\t\t\t\"error\",\n\t\t\t\"never\"\n\t\t],\n\t\t\"space-infix-ops\": \"off\",\n\t\t\"space-unary-ops\": \"error\",\n\t\t\"spaced-comment\": \"off\",\n\t\t\"strict\": [\"error\", \"global\"],\n\t\t\"template-curly-spacing\": \"error\",\n\t\t\"valid-jsdoc\": \"off\",\n\t\t\"vars-on-top\": \"off\",\n\t\t\"wrap-iife\": \"off\",\n\t\t\"wrap-regex\": \"error\",\n\t\t\"yield-star-spacing\": \"error\",\n\t\t\"yoda\": \"off\"\n\t},\n\t\"root\": true\n};\n"
  },
  {
    "path": ".gitattributes",
    "content": "* text=auto\n/mithril.js binary\n/mithril.min.js binary\n\n# Assets\n*.png binary\n*.svg binary\n*.ico binary\n"
  },
  {
    "path": ".github/CODEOWNERS",
    "content": "* @MithrilJS/Committers\n/.github/ @MithrilJS/Admins\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/1-core.yml",
    "content": "name: '🐛 Framework Bug'\ndescription: Report a bug in Mithril.js core\nassignees: dead-claudia\nlabels:\n- 'Area: Core'\nbody:\n- type: checkboxes\n  attributes:\n    label: Is there an existing issue for this?\n    description: Please search to see if an issue already exists for the bug you encountered.\n    options:\n    - label: I have searched the existing issues\n      required: true\n- type: input\n  attributes:\n    label: Mithril.js Version\n    description: |\n      Provide the exact version of Mithril.js you're experiencing these issues with. This\n      matters, even if it's really old like version 0.1.0. Do note that bugs in older\n      versions are commonly fixed in newer versions, so you should try to test it\n      against the latest version if you can.\n  validations:\n    required: true\n- type: textarea\n  attributes:\n    label: Browser and OS\n    description: |\n      Provide the name and version of both the browser and operating system you're\n      experiencing these issues with. If it's multiple, feel free to list multiple.\n      This matters, even if it's super ancient.\n  validations:\n    required: true\n- type: textarea\n  attributes:\n    label: Project\n    description: |\n      (Optional) Provide a link to your project, if it happens to be open source or if\n      you created a repo somewhere that we can look into further. If it's spread across\n      multiple repos or projects, feel free to list them all.\n- type: textarea\n  attributes:\n    label: Code\n    description: |\n      What did you try? What code is causing the unexpected behavior? Make sure to\n      try to reduce your code as best as you can while still reproducing the bug, so\n      we can more accurately determine the cause. Ideally, it should just be a bunch\n      of Mithril.js calls with virtually no logic at all, but it's sufficient to just\n      remove unrelated network calls, attributes, and the like.\n\n      In addition, make sure the bug still persists with the latest version of\n      Mithril. If it's an older version, the bug may have already been fixed.\n\n      If you'd prefer, replace this code block with a link to a code playground like\n      any of these:\n\n      - Flems <https://flems.io/mithril> (stores everything in URL hash)\n      - JSFiddle <https://jsfiddle.net>\n      - CodePen <https://codepen.io>\n      - JSBin <https://jsbin.com>\n      - Plunker <https://plnkr.co>\n      - Glitch <https://glitch.com> (supports backend)\n      - CodeSandbox <https://codesandbox.io> (supports backend)\n\n      Or if it's a remote development project on your own server, feel free to provide\n      that if it's serving unminified code we can look at.\n\n      If it's a closed-source repo, it's okay to censor names and pull out irrelevant\n      logic - we'd rather not sign NDAs just to see the code you're having trouble\n      with. We do still need code of some kind that triggers the bug you're running\n      into.\n    render: javascript\n  validations:\n    required: true\n- type: textarea\n  attributes:\n    label: Steps to Reproduce\n    description: |\n      What steps need to be taken to reproduce this behavior? Please include things\n      like specific data that need typed in, specific buttons that need clicked, and\n      so on.\n    placeholder: |\n      1.\n      2.\n      3.\n      4.\n  validations:\n    required: true\n- type: textarea\n  attributes:\n    label: Expected Behavior\n    description: |\n      What did you expect to happen?\n\n      - An alert to pop up?\n      - A specific thing to be logged?\n\n      Please be very specific here.\n  validations:\n    required: true\n- type: textarea\n  attributes:\n    label: Observed Behavior\n    description: |\n      What actually happened?\n\n      - The alert never showed?\n      - The wrong thing was logged?\n\n      Please be very specific here.\n  validations:\n    required: true\n- type: textarea\n  attributes:\n    label: Context\n    description: |\n      (Optional) How is this issue affecting you? What are you trying to do? Providing\n      us context helps us reach a solution that best fits your particular needs.\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/2-stream.yml",
    "content": "name: '🌊 Mithril.js Streams bug'\ndescription: Report an issue with Mithril.js's Streams module\nassignees: dead-claudia\nlabels:\n- 'Area: Stream'\nbody:\n- type: checkboxes\n  attributes:\n    label: Is there an existing issue for this?\n    description: Please search to see if an issue already exists for the bug you encountered.\n    options:\n    - label: I have searched the existing issues\n      required: true\n- type: input\n  attributes:\n    label: Mithril.js Version\n    description: |\n      Provide the exact version of Mithril.js you're experiencing these issues with. This\n      matters, even if it's really old like version 0.1.0. Do note that bugs in older\n      versions are commonly fixed in newer versions, so you should try to test it\n      against the latest version if you can.\n  validations:\n    required: true\n- type: textarea\n  attributes:\n    label: Browser and OS\n    description: |\n      Provide the name and version of both the browser and operating system you're\n      experiencing these issues with. If it's multiple, feel free to list multiple.\n      This matters, even if it's super ancient.\n  validations:\n    required: true\n- type: textarea\n  attributes:\n    label: Project\n    description: |\n      (Optional) Provide a link to your project, if it happens to be open source or if\n      you created a repo somewhere that we can look into further. If it's spread across\n      multiple repos or projects, feel free to list them all.\n- type: textarea\n  attributes:\n    label: Code\n    description: |\n      What did you try? What code is causing the unexpected behavior? Make sure to\n      try to reduce your code as best as you can while still reproducing the bug, so\n      we can more accurately determine the cause. Ideally, it should just be a bunch\n      of Mithril.js calls with virtually no logic at all, but it's sufficient to just\n      remove unrelated network calls, attributes, and the like.\n\n      In addition, make sure the bug still persists with the latest version of\n      Mithril. If it's an older version, the bug may have already been fixed.\n\n      If you'd prefer, replace this code block with a link to a code playground like\n      any of these:\n\n      - Flems <https://flems.io/mithril> (stores everything in URL hash)\n      - JSFiddle <https://jsfiddle.net>\n      - CodePen <https://codepen.io>\n      - JSBin <https://jsbin.com>\n      - Plunker <https://plnkr.co>\n      - Glitch <https://glitch.com> (supports backend)\n      - CodeSandbox <https://codesandbox.io> (supports backend)\n\n      Or if it's a remote development project on your own server, feel free to provide\n      that if it's serving unminified code we can look at.\n\n      If it's a closed-source repo, it's okay to censor names and pull out irrelevant\n      logic - we'd rather not sign NDAs just to see the code you're having trouble\n      with. We do still need code of some kind that triggers the bug you're running\n      into.\n    render: javascript\n  validations:\n    required: true\n- type: textarea\n  attributes:\n    label: Steps to Reproduce\n    description: |\n      What steps need to be taken to reproduce this behavior? Please include things\n      like specific data that need typed in, specific buttons that need clicked, and\n      so on.\n    placeholder: |\n      1.\n      2.\n      3.\n      4.\n  validations:\n    required: true\n- type: textarea\n  attributes:\n    label: Expected Behavior\n    description: |\n      What did you expect to happen?\n\n      - An alert to pop up?\n      - A specific thing to be logged?\n\n      Please be very specific here.\n  validations:\n    required: true\n- type: textarea\n  attributes:\n    label: Observed Behavior\n    description: |\n      What actually happened?\n\n      - The alert never showed?\n      - The wrong thing was logged?\n\n      Please be very specific here.\n  validations:\n    required: true\n- type: textarea\n  attributes:\n    label: Context\n    description: |\n      (Optional) How is this issue affecting you? What are you trying to do? Providing\n      us context helps us reach a solution that best fits your particular needs.\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "content": "blank_issues_enabled: true\ncontact_links:\n  - name: ℹ Questions, Ideas, and Discussions\n    url: https://github.com/MithrilJS/mithril.js/discussions\n    about: |\n      Got a question on how to use Mithril.js? Have a fancy idea of how we could do better? Check\n      out our discussions forum!\n  - name: ℹ Show and Tell\n    url: https://github.com/MithrilJS/mithril.js/discussions/new?category=show-and-tell\n    about: |\n      Got something to show off? Made something so cool, you just have to tell the world? Let us\n      know here in our discussions forum!\n  - name: 🚀 Feature Request or Enhancement\n    url: https://github.com/MithrilJS/mithril.js/discussions/new?category=ideas\n    about: Got a feature request? Let us know here! We do those in our discussion forum.\n  - name: 📄 Documentation issue\n    url: https://github.com/MithrilJS/docs\n    about: Found an issue with our documentation? File that in our docs repo instead.\n  - name: 💻 Zulip Chat\n    url: https://mithril.zulipchat.com/\n    about: Not sure about something? Just want to hang out? Come over to our Zulip chat!\n"
  },
  {
    "path": ".github/PULL_REQUEST_TEMPLATE.md",
    "content": "<!--- Provide a general summary of your changes in the Title above -->\n\n## Description\n<!--- Describe your changes in detail -->\n\n## Motivation and Context\n<!--- Why is this change required? What problem does it solve? -->\n<!--- If it fixes an open issue, please link to the issue here. -->\n\n## How Has This Been Tested?\n<!--- Please describe in detail how you tested your changes. -->\n<!--- Include details of your testing environment, and the tests you ran to -->\n<!--- see how your change affects other areas of the code, etc. -->\n\n## Types of changes\n<!--- What types of changes does your code introduce? Put an `x` in all the boxes that apply: -->\n- [ ] Bug fix (non-breaking change which fixes an issue)\n- [ ] New feature (non-breaking change which adds functionality)\n- [ ] Breaking change (fix or feature that would cause existing functionality to change)\n\n## Checklist\n<!--- Go over all the following points, and put an `x` in all the boxes that apply. -->\n<!--- If you're unsure about any of these, don't hesitate to ask. We're here to help! -->\n- [ ] My code follows the code style of this project.\n- [ ] I have added tests to cover my changes.\n- [ ] All new and existing tests passed.\n- [ ] My change requires a documentation update, and I've opened a pull request to update it already:\n- [ ] I have read https://mithril.js.org/contributing.html.\n"
  },
  {
    "path": ".github/dependabot.yml",
    "content": "# To get started with Dependabot version updates, you'll need to specify which\n# package ecosystems to update and where the package manifests are located.\n# Please see the documentation for all configuration options:\n# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file\n\nversion: 2\nupdates:\n  - package-ecosystem: \"npm\" # See documentation for possible values\n    directory: \"/\" # Location of package manifests\n    schedule:\n      interval: daily\n    groups:\n      security:\n        applies-to: security-updates\n        patterns: ['*']\n      normal:\n        applies-to: version-updates\n        patterns: ['*']\n  - package-ecosystem: github-actions\n    directory: /\n    schedule:\n      interval: daily\n    groups:\n      security:\n        applies-to: security-updates\n        patterns: ['*']\n      normal:\n        applies-to: version-updates\n        patterns: ['*']\n"
  },
  {
    "path": ".github/workflows/issue-create.yml",
    "content": "name: Ping triage on issue create\non:\n  issues:\n    types: [opened]\n  pull_request_target:\n    types: [opened]\njobs:\n  notify_triage:\n    uses: MithrilJS/infra/.github/workflows/notify-triage.yml@main\n    secrets: inherit\n"
  },
  {
    "path": ".github/workflows/notify-release.yml",
    "content": "name: Notify release\n\non:\n  push:\n    tags: ['v*']\n\njobs:\n  update:\n    runs-on: ubuntu-latest\n    steps:\n    - name: Send workflow dispatch to docs\n      run: gh --repo MithrilJS/docs workflow run package-update.yml -f package=mithril\n      env:\n        GH_TOKEN: ${{ secrets.DOCS_UPDATE_TOKEN }}\n"
  },
  {
    "path": ".github/workflows/pr-create-release.yml",
    "content": "name: Warn on opening a PR to `release`\non:\n  pull_request_target:\n    types: [opened]\n    branches: [release]\npermissions:\n  issues: write\njobs:\n  comment:\n    # Don't auto-close actual release PRs\n    if: ${{github.actor != 'JAForbes' || !contains(github.event.issue.title, 'Release - v')}}\n    uses: MithrilJS/infra/.github/workflows/reject-pr.yml@main\n    secrets: inherit\n"
  },
  {
    "path": ".github/workflows/publish-prerelease.yml",
    "content": "name: Publish prerelease and update PR\n\non:\n  workflow_call:\n  workflow_dispatch:\n\njobs:\n  update-pr:\n    concurrency: prr:pre-release\n    runs-on: ubuntu-latest\n    steps:\n    - uses: actions/checkout@v5\n    - uses: actions/setup-node@v6\n      with:\n        node-version: 20\n    - run: npm ci\n    - run: npm run build\n    - run: npx pr-release pr --verbose --target release --source main --compact --verbose --minimize-semver-change\n      env:\n        GITHUB_TOKEN: ${{ secrets.GH_TOKEN }}\n    # The following will publish a prerelease to npm\n    - run: echo \"//registry.npmjs.org/:_authToken=$NPM_TOKEN\" > ~/.npmrc\n      name: Setup NPM Auth\n      env:\n        NPM_TOKEN: ${{ secrets.NPM_TOKEN }}\n    - run: npx pr-release infer-prerelease --preid=next --target release --source main --verbose --publish --minimize-semver-change\n      name: Publish"
  },
  {
    "path": ".github/workflows/push-release.yml",
    "content": "name: Create release when pushing to `release`\n\non:\n  push:\n    branches: [release]\n  workflow_dispatch:\n\nconcurrency: merge-release\n\njobs:\n  merge:\n    runs-on: ubuntu-latest\n\n    steps:\n    - uses: actions/checkout@v5\n      with:\n        ref: main\n    - uses: actions/setup-node@v6\n      with:\n        node-version: 20\n    - run: npm ci\n    - run: npm run build\n    - run: echo \"//registry.npmjs.org/:_authToken=$NPM_TOKEN\" > ~/.npmrc\n      name: Setup NPM Auth\n      env:\n        NPM_TOKEN: ${{ secrets.NPM_TOKEN }}\n    - run: npx pr-release merge --target release --source main --commit --force --clean --changelog ./docs/recent-changes.md --compact --minimize-semver-change --prerelease=\"npm publish\"\n      env:\n        GITHUB_TOKEN: ${{ secrets.GH_TOKEN }}\n    - run: bash scripts/set-versioned-branch.sh release\n"
  },
  {
    "path": ".github/workflows/rollback.yml",
    "content": "name: rollback\n\non:\n  workflow_dispatch:\n\nconcurrency: prr:deploy\n\njobs:\n  pr:\n    runs-on: ubuntu-latest\n\n    steps:\n    - uses: actions/checkout@v5\n    - uses: actions/setup-node@v6\n      with:\n        node-version: 20\n    - run: npm ci\n    - run: npm run build\n    - run: npx pr-release rollback --verbose --target release --source main --verbose --ignore 'package*' --ignore docs/changelog.md --ignore docs/recent-changes.md\n      env:\n        GITHUB_TOKEN: ${{ secrets.GH_TOKEN }}\n    - run: bash scripts/set-versioned-branch.sh release\n"
  },
  {
    "path": ".github/workflows/test.yml",
    "content": "name: Test and maybe release\n\non:\n  pull_request_target:\n    branches: [ main ]\n  push:\n    branches: [ main ]\n  workflow_dispatch:\n\njobs:\n  run-tests:\n    uses: MithrilJS/infra/.github/workflows/run-tests.yml@main\n    with:\n      test-node: true\n      all-versions: true\n    permissions:\n      actions: write\n      contents: read\n\n  publish-prerelease:\n    needs: run-tests\n    if: ${{ github.event_name == 'push' }}\n    concurrency: prr:pre-release\n    runs-on: ubuntu-latest\n    steps:\n    - uses: actions/checkout@v5\n    - uses: actions/setup-node@v6\n      with:\n        node-version: 20\n    - run: npm ci\n    - run: npm run build\n    - run: npx pr-release pr --verbose --target release --source main --compact --verbose --minimize-semver-change\n      env:\n        GITHUB_TOKEN: ${{ secrets.GH_TOKEN }}\n    # The following will publish a prerelease to npm\n    - run: echo \"//registry.npmjs.org/:_authToken=$NPM_TOKEN\" > ~/.npmrc\n      name: Setup NPM Auth\n      env:\n        NPM_TOKEN: ${{ secrets.NPM_TOKEN }}\n    - run: npx pr-release infer-prerelease --preid=next --target release --source main --verbose --publish --minimize-semver-change\n      name: Publish\n"
  },
  {
    "path": ".gitignore",
    "content": "/coverage\n/node_modules\n/jsconfig.json\n/npm-debug.log\n/.vscode\n/.DS_Store\n/.eslintcache\n"
  },
  {
    "path": ".npmignore",
    "content": "# Development-specific files\n/.deploy.env\n/.editorconfig\n/.eslintrc.js\n/.eslintcache\n/.eslintignore\n/.gitattributes\n/.gitignore\n/.travis.yml\n/yarn.lock\n/scripts/\n\n# Exclude all directories named \"tests\" as it's used only for tests. This is\n# intentionally not prefixed with a `/` because it applies to both the root and\n# subdirectories.\ntests/\n\n# Mithril.js' mocks are for internal use only, and it's wholly undocumented for a\n# reason. I've already gotten way too many complaints over users' tests breaking\n# from changes to it in patch releases. Let's force people to finally stop using\n# them.\n/test-utils/\n"
  },
  {
    "path": "LICENSE",
    "content": "The MIT License (MIT)\n\nCopyright (c) 2017 Leo Horie\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# Mithril.js\n\n[![npm Version](https://img.shields.io/npm/v/mithril.svg)](https://www.npmjs.com/package/mithril) &nbsp;\n[![License](https://img.shields.io/npm/l/mithril.svg)](https://github.com/MithrilJS/mithril.js/blob/main/LICENSE) &nbsp;\n[![npm Downloads](https://img.shields.io/npm/dm/mithril.svg)](https://www.npmjs.com/package/mithril) &nbsp;\n[![Build Status](https://img.shields.io/github/actions/workflow/status/MithrilJS/mithril.js/.github%2Fworkflows%2Ftest.yml?branch=main&event=push)](https://github.com/MithrilJS/mithril.js/actions) &nbsp;\n[![Donate at OpenCollective](https://img.shields.io/opencollective/all/mithriljs.svg?colorB=brightgreen)](https://opencollective.com/mithriljs) &nbsp;\n[![Zulip, join chat](https://img.shields.io/badge/zulip-join_chat-brightgreen.svg)](https://mithril.zulipchat.com/)\n\n- [What is Mithril.js?](#what-is-mithriljs)\n- [Installation](#installation)\n- [Documentation](#documentation)\n- [Getting Help](#getting-help)\n- [Contributing](#contributing)\n\n## What is Mithril.js?\n\nA modern client-side JavaScript framework for building Single Page Applications. It's small (<!-- size -->8.93 KB<!-- /size --> gzipped), fast and provides routing and XHR utilities out of the box.\n\nMithril.js is used by companies like Vimeo and Nike, and open source platforms like Lichess 👍.\n\nMithril.js supports Firefox ESR, and the last two versions of Firefox, Edge, Safari, and Chrome. No polyfills required. 👌\n\n## Installation\n\n### CDN\n\n```html\n<!-- Development: whichever you prefer -->\n<script src=\"https://unpkg.com/mithril/mithril.js\"></script>\n<script src=\"https://cdn.jsdelivr.net/npm/mithril/mithril.js\"></script>\n\n<!-- Production: whichever you prefer -->\n<script src=\"https://unpkg.com/mithril/mithril.min.js\"></script>\n<script src=\"https://cdn.jsdelivr.net/npm/mithril/mithril.min.js\"></script>\n```\n\n### npm\n\n```bash\nnpm install mithril --save\n```\n\nThe [\"Getting started\" guide](https://mithril.js.org/#getting-started) is a good place to start learning how to use Mithril.js.\n\nTypeScript type definitions are available from DefinitelyTyped. They can be installed with:\n\n```bash\n$ npm install @types/mithril --save-dev\n```\n\n## Documentation\n\nDocumentation lives on [mithril.js.org](https://mithril.js.org).\n\nYou may be interested in the [API Docs](https://mithril.js.org/api.html), a [Simple Application](https://mithril.js.org/simple-application.html), or perhaps some [Examples](https://mithril.js.org/examples.html).\n\n## Getting Help\n\nMithril.js has an active & welcoming community on [Zulip](https://mithril.zulipchat.com/), or feel free to ask questions on [Stack Overflow](https://stackoverflow.com/questions/tagged/mithril.js) using the `mithril.js` tag.\n\n## Contributing\n\nThere's a [Contributing FAQ](https://mithril.js.org/contributing.html) on the Mithril.js site that hopefully helps, but if not definitely hop into the [Zulip stream](https://mithril.zulipchat.com/) and ask away!\n\n---\n\nThanks for reading!\n\n🎁\n"
  },
  {
    "path": "api/mount-redraw.js",
    "content": "\"use strict\"\n\nvar Vnode = require(\"../render/vnode\")\n\nmodule.exports = function(render, schedule, console) {\n\tvar subscriptions = []\n\tvar pending = false\n\tvar offset = -1\n\n\tfunction sync() {\n\t\tfor (offset = 0; offset < subscriptions.length; offset += 2) {\n\t\t\ttry { render(subscriptions[offset], Vnode(subscriptions[offset + 1]), redraw) }\n\t\t\tcatch (e) { console.error(e) }\n\t\t}\n\t\toffset = -1\n\t}\n\n\tfunction redraw() {\n\t\tif (!pending) {\n\t\t\tpending = true\n\t\t\tschedule(function() {\n\t\t\t\tpending = false\n\t\t\t\tsync()\n\t\t\t})\n\t\t}\n\t}\n\n\tredraw.sync = sync\n\n\tfunction mount(root, component) {\n\t\tif (component != null && component.view == null && typeof component !== \"function\") {\n\t\t\tthrow new TypeError(\"m.mount expects a component, not a vnode.\")\n\t\t}\n\n\t\tvar index = subscriptions.indexOf(root)\n\t\tif (index >= 0) {\n\t\t\tsubscriptions.splice(index, 2)\n\t\t\tif (index <= offset) offset -= 2\n\t\t\trender(root, [])\n\t\t}\n\n\t\tif (component != null) {\n\t\t\tsubscriptions.push(root, component)\n\t\t\trender(root, Vnode(component), redraw)\n\t\t}\n\t}\n\n\treturn {mount: mount, redraw: redraw}\n}\n"
  },
  {
    "path": "api/router.js",
    "content": "\"use strict\"\n\nvar Vnode = require(\"../render/vnode\")\nvar hyperscript = require(\"../render/hyperscript\")\n\nvar decodeURIComponentSafe = require(\"../util/decodeURIComponentSafe\")\nvar buildPathname = require(\"../pathname/build\")\nvar parsePathname = require(\"../pathname/parse\")\nvar compileTemplate = require(\"../pathname/compileTemplate\")\nvar censor = require(\"../util/censor\")\n\nmodule.exports = function($window, mountRedraw) {\n\tvar p = Promise.resolve()\n\n\tvar scheduled = false\n\n\tvar ready = false\n\tvar hasBeenResolved = false\n\n\tvar dom, compiled, fallbackRoute\n\n\tvar currentResolver, component, attrs, currentPath, lastUpdate\n\n\tvar RouterRoot = {\n\t\tonremove: function() {\n\t\t\tready = hasBeenResolved = false\n\t\t\t$window.removeEventListener(\"popstate\", fireAsync, false)\n\t\t},\n\t\tview: function() {\n\t\t\t// The route has already been resolved.\n\t\t\t// Therefore, the following early return is not needed.\n\t\t\t// if (!hasBeenResolved) return\n\n\t\t\tvar vnode = Vnode(component, attrs.key, attrs)\n\t\t\tif (currentResolver) return currentResolver.render(vnode)\n\t\t\t// Wrap in a fragment to preserve existing key semantics\n\t\t\treturn [vnode]\n\t\t},\n\t}\n\n\tvar SKIP = route.SKIP = {}\n\n\tfunction resolveRoute() {\n\t\tscheduled = false\n\t\t// Consider the pathname holistically. The prefix might even be invalid,\n\t\t// but that's not our problem.\n\t\tvar prefix = $window.location.hash\n\t\tif (route.prefix[0] !== \"#\") {\n\t\t\tprefix = $window.location.search + prefix\n\t\t\tif (route.prefix[0] !== \"?\") {\n\t\t\t\tprefix = $window.location.pathname + prefix\n\t\t\t\tif (prefix[0] !== \"/\") prefix = \"/\" + prefix\n\t\t\t}\n\t\t}\n\t\t// This seemingly useless `.concat()` speeds up the tests quite a bit,\n\t\t// since the representation is consistently a relatively poorly\n\t\t// optimized cons string.\n\t\tvar path = prefix.concat()\n\t\t\t.replace(/(?:%[a-f89][a-f0-9])+/gim, decodeURIComponentSafe)\n\t\t\t.slice(route.prefix.length)\n\t\tvar data = parsePathname(path)\n\n\t\tObject.assign(data.params, $window.history.state)\n\n\t\tfunction reject(e) {\n\t\t\tconsole.error(e)\n\t\t\troute.set(fallbackRoute, null, {replace: true})\n\t\t}\n\n\t\tloop(0)\n\t\tfunction loop(i) {\n\t\t\tfor (; i < compiled.length; i++) {\n\t\t\t\tif (compiled[i].check(data)) {\n\t\t\t\t\tvar payload = compiled[i].component\n\t\t\t\t\tvar matchedRoute = compiled[i].route\n\t\t\t\t\tvar localComp = payload\n\t\t\t\t\tvar update = lastUpdate = function(comp) {\n\t\t\t\t\t\tif (update !== lastUpdate) return\n\t\t\t\t\t\tif (comp === SKIP) return loop(i + 1)\n\t\t\t\t\t\tcomponent = comp != null && (typeof comp.view === \"function\" || typeof comp === \"function\")? comp : \"div\"\n\t\t\t\t\t\tattrs = data.params, currentPath = path, lastUpdate = null\n\t\t\t\t\t\tcurrentResolver = payload.render ? payload : null\n\t\t\t\t\t\tif (hasBeenResolved) mountRedraw.redraw()\n\t\t\t\t\t\telse {\n\t\t\t\t\t\t\thasBeenResolved = true\n\t\t\t\t\t\t\tmountRedraw.mount(dom, RouterRoot)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\t// There's no understating how much I *wish* I could\n\t\t\t\t\t// use `async`/`await` here...\n\t\t\t\t\tif (payload.view || typeof payload === \"function\") {\n\t\t\t\t\t\tpayload = {}\n\t\t\t\t\t\tupdate(localComp)\n\t\t\t\t\t}\n\t\t\t\t\telse if (payload.onmatch) {\n\t\t\t\t\t\tp.then(function () {\n\t\t\t\t\t\t\treturn payload.onmatch(data.params, path, matchedRoute)\n\t\t\t\t\t\t}).then(update, path === fallbackRoute ? null : reject)\n\t\t\t\t\t}\n\t\t\t\t\telse update(/* \"div\" */)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (path === fallbackRoute) {\n\t\t\t\tthrow new Error(\"Could not resolve default route \" + fallbackRoute + \".\")\n\t\t\t}\n\t\t\troute.set(fallbackRoute, null, {replace: true})\n\t\t}\n\t}\n\n\tfunction fireAsync() {\n\t\tif (!scheduled) {\n\t\t\tscheduled = true\n\t\t\t// TODO: just do `mountRedraw.redraw()` here and elide the timer\n\t\t\t// dependency. Note that this will muck with tests a *lot*, so it's\n\t\t\t// not as easy of a change as it sounds.\n\t\t\tsetTimeout(resolveRoute)\n\t\t}\n\t}\n\n\tfunction route(root, defaultRoute, routes) {\n\t\tif (!root) throw new TypeError(\"DOM element being rendered to does not exist.\")\n\n\t\tcompiled = Object.keys(routes).map(function(route) {\n\t\t\tif (route[0] !== \"/\") throw new SyntaxError(\"Routes must start with a '/'.\")\n\t\t\tif ((/:([^\\/\\.-]+)(\\.{3})?:/).test(route)) {\n\t\t\t\tthrow new SyntaxError(\"Route parameter names must be separated with either '/', '.', or '-'.\")\n\t\t\t}\n\t\t\treturn {\n\t\t\t\troute: route,\n\t\t\t\tcomponent: routes[route],\n\t\t\t\tcheck: compileTemplate(route),\n\t\t\t}\n\t\t})\n\t\tfallbackRoute = defaultRoute\n\t\tif (defaultRoute != null) {\n\t\t\tvar defaultData = parsePathname(defaultRoute)\n\n\t\t\tif (!compiled.some(function (i) { return i.check(defaultData) })) {\n\t\t\t\tthrow new ReferenceError(\"Default route doesn't match any known routes.\")\n\t\t\t}\n\t\t}\n\t\tdom = root\n\n\t\t$window.addEventListener(\"popstate\", fireAsync, false)\n\n\t\tready = true\n\n\t\t// The RouterRoot component is mounted when the route is first resolved.\n\t\tresolveRoute()\n\t}\n\troute.set = function(path, data, options) {\n\t\tif (lastUpdate != null) {\n\t\t\toptions = options || {}\n\t\t\toptions.replace = true\n\t\t}\n\t\tlastUpdate = null\n\n\t\tpath = buildPathname(path, data)\n\t\tif (ready) {\n\t\t\tfireAsync()\n\t\t\tvar state = options ? options.state : null\n\t\t\tvar title = options ? options.title : null\n\t\t\tif (options && options.replace) $window.history.replaceState(state, title, route.prefix + path)\n\t\t\telse $window.history.pushState(state, title, route.prefix + path)\n\t\t}\n\t\telse {\n\t\t\t$window.location.href = route.prefix + path\n\t\t}\n\t}\n\troute.get = function() {return currentPath}\n\troute.prefix = \"#!\"\n\troute.Link = {\n\t\tview: function(vnode) {\n\t\t\t// Omit the used parameters from the rendered element - they are\n\t\t\t// internal. Also, censor the various lifecycle methods.\n\t\t\t//\n\t\t\t// We don't strip the other parameters because for convenience we\n\t\t\t// let them be specified in the selector as well.\n\t\t\tvar child = hyperscript(\n\t\t\t\tvnode.attrs.selector || \"a\",\n\t\t\t\tcensor(vnode.attrs, [\"options\", \"params\", \"selector\", \"onclick\"]),\n\t\t\t\tvnode.children\n\t\t\t)\n\t\t\tvar options, onclick, href\n\n\t\t\t// Let's provide a *right* way to disable a route link, rather than\n\t\t\t// letting people screw up accessibility on accident.\n\t\t\t//\n\t\t\t// The attribute is coerced so users don't get surprised over\n\t\t\t// `disabled: 0` resulting in a button that's somehow routable\n\t\t\t// despite being visibly disabled.\n\t\t\tif (child.attrs.disabled = Boolean(child.attrs.disabled)) {\n\t\t\t\tchild.attrs.href = null\n\t\t\t\tchild.attrs[\"aria-disabled\"] = \"true\"\n\t\t\t\t// If you *really* do want add `onclick` on a disabled link, use\n\t\t\t\t// an `oncreate` hook to add it.\n\t\t\t} else {\n\t\t\t\toptions = vnode.attrs.options\n\t\t\t\tonclick = vnode.attrs.onclick\n\t\t\t\t// Easier to build it now to keep it isomorphic.\n\t\t\t\thref = buildPathname(child.attrs.href, vnode.attrs.params)\n\t\t\t\tchild.attrs.href = route.prefix + href\n\t\t\t\tchild.attrs.onclick = function(e) {\n\t\t\t\t\tvar result\n\t\t\t\t\tif (typeof onclick === \"function\") {\n\t\t\t\t\t\tresult = onclick.call(e.currentTarget, e)\n\t\t\t\t\t} else if (onclick == null || typeof onclick !== \"object\") {\n\t\t\t\t\t\t// do nothing\n\t\t\t\t\t} else if (typeof onclick.handleEvent === \"function\") {\n\t\t\t\t\t\tonclick.handleEvent(e)\n\t\t\t\t\t}\n\n\t\t\t\t\t// Adapted from React Router's implementation:\n\t\t\t\t\t// https://github.com/ReactTraining/react-router/blob/520a0acd48ae1b066eb0b07d6d4d1790a1d02482/packages/react-router-dom/modules/Link.js\n\t\t\t\t\t//\n\t\t\t\t\t// Try to be flexible and intuitive in how we handle links.\n\t\t\t\t\t// Fun fact: links aren't as obvious to get right as you\n\t\t\t\t\t// would expect. There's a lot more valid ways to click a\n\t\t\t\t\t// link than this, and one might want to not simply click a\n\t\t\t\t\t// link, but right click or command-click it to copy the\n\t\t\t\t\t// link target, etc. Nope, this isn't just for blind people.\n\t\t\t\t\tif (\n\t\t\t\t\t\t// Skip if `onclick` prevented default\n\t\t\t\t\t\tresult !== false && !e.defaultPrevented &&\n\t\t\t\t\t\t// Ignore everything but left clicks\n\t\t\t\t\t\t(e.button === 0 || e.which === 0 || e.which === 1) &&\n\t\t\t\t\t\t// Let the browser handle `target=_blank`, etc.\n\t\t\t\t\t\t(!e.currentTarget.target || e.currentTarget.target === \"_self\") &&\n\t\t\t\t\t\t// No modifier keys\n\t\t\t\t\t\t!e.ctrlKey && !e.metaKey && !e.shiftKey && !e.altKey\n\t\t\t\t\t) {\n\t\t\t\t\t\te.preventDefault()\n\t\t\t\t\t\te.redraw = false\n\t\t\t\t\t\troute.set(href, null, options)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn child\n\t\t},\n\t}\n\troute.param = function(key) {\n\t\treturn attrs && key != null ? attrs[key] : attrs\n\t}\n\n\treturn route\n}\n"
  },
  {
    "path": "api/tests/test-mountRedraw.js",
    "content": "\"use strict\"\n\n// Low-priority TODO: remove the dependency on the renderer here.\nvar o = require(\"ospec\")\nvar components = require(\"../../test-utils/components\")\nvar domMock = require(\"../../test-utils/domMock\")\nvar throttleMocker = require(\"../../test-utils/throttleMock\")\nvar mountRedraw = require(\"../../api/mount-redraw\")\nvar coreRenderer = require(\"../../render/render\")\nvar h = require(\"../../render/hyperscript\")\n\no.spec(\"mount/redraw\", function() {\n\tvar root, m, throttleMock, consoleMock, $document, errors\n\to.beforeEach(function() {\n\t\tvar $window = domMock()\n\t\tconsoleMock = {error: o.spy()}\n\t\tthrottleMock = throttleMocker()\n\t\troot = $window.document.body\n\t\tm = mountRedraw(coreRenderer($window), throttleMock.schedule, consoleMock)\n\t\t$document = $window.document\n\t\terrors = []\n\t})\n\n\to.afterEach(function() {\n\t\to(consoleMock.error.calls.map(function(c) {\n\t\t\treturn c.args[0]\n\t\t})).deepEquals(errors)\n\t\to(throttleMock.queueLength()).equals(0)\n\t})\n\n\to(\"shouldn't error if there are no renderers\", function() {\n\t\tm.redraw()\n\t\tthrottleMock.fire()\n\t})\n\n\to(\"schedules correctly\", function() {\n\t\tvar spy = o.spy()\n\n\t\tm.mount(root, {view: spy})\n\t\to(spy.callCount).equals(1)\n\t\tm.redraw()\n\t\to(spy.callCount).equals(1)\n\t\tthrottleMock.fire()\n\t\to(spy.callCount).equals(2)\n\t})\n\n\to(\"should run a single renderer entry\", function() {\n\t\tvar spy = o.spy()\n\n\t\tm.mount(root, {view: spy})\n\n\t\to(spy.callCount).equals(1)\n\n\t\tm.redraw()\n\t\tm.redraw()\n\t\tm.redraw()\n\n\t\to(spy.callCount).equals(1)\n\t\tthrottleMock.fire()\n\t\to(spy.callCount).equals(2)\n\t})\n\n\to(\"should run all renderer entries\", function() {\n\t\tvar el1 = $document.createElement(\"div\")\n\t\tvar el2 = $document.createElement(\"div\")\n\t\tvar el3 = $document.createElement(\"div\")\n\t\tvar spy1 = o.spy()\n\t\tvar spy2 = o.spy()\n\t\tvar spy3 = o.spy()\n\n\t\tm.mount(el1, {view: spy1})\n\t\tm.mount(el2, {view: spy2})\n\t\tm.mount(el3, {view: spy3})\n\n\t\tm.redraw()\n\n\t\to(spy1.callCount).equals(1)\n\t\to(spy2.callCount).equals(1)\n\t\to(spy3.callCount).equals(1)\n\n\t\tm.redraw()\n\n\t\to(spy1.callCount).equals(1)\n\t\to(spy2.callCount).equals(1)\n\t\to(spy3.callCount).equals(1)\n\n\t\tthrottleMock.fire()\n\n\t\to(spy1.callCount).equals(2)\n\t\to(spy2.callCount).equals(2)\n\t\to(spy3.callCount).equals(2)\n\t})\n\n\to(\"should not redraw when mounting another root\", function() {\n\t\tvar el1 = $document.createElement(\"div\")\n\t\tvar el2 = $document.createElement(\"div\")\n\t\tvar el3 = $document.createElement(\"div\")\n\t\tvar spy1 = o.spy()\n\t\tvar spy2 = o.spy()\n\t\tvar spy3 = o.spy()\n\n\t\tm.mount(el1, {view: spy1})\n\t\to(spy1.callCount).equals(1)\n\t\to(spy2.callCount).equals(0)\n\t\to(spy3.callCount).equals(0)\n\n\t\tm.mount(el2, {view: spy2})\n\t\to(spy1.callCount).equals(1)\n\t\to(spy2.callCount).equals(1)\n\t\to(spy3.callCount).equals(0)\n\n\t\tm.mount(el3, {view: spy3})\n\t\to(spy1.callCount).equals(1)\n\t\to(spy2.callCount).equals(1)\n\t\to(spy3.callCount).equals(1)\n\t})\n\n\to(\"should stop running after mount null\", function() {\n\t\tvar spy = o.spy()\n\n\t\tm.mount(root, {view: spy})\n\t\to(spy.callCount).equals(1)\n\t\tm.mount(root, null)\n\n\t\tm.redraw()\n\n\t\to(spy.callCount).equals(1)\n\t\tthrottleMock.fire()\n\t\to(spy.callCount).equals(1)\n\t})\n\n\to(\"should stop running after mount undefined\", function() {\n\t\tvar spy = o.spy()\n\n\t\tm.mount(root, {view: spy})\n\t\to(spy.callCount).equals(1)\n\t\tm.mount(root, undefined)\n\n\t\tm.redraw()\n\n\t\to(spy.callCount).equals(1)\n\t\tthrottleMock.fire()\n\t\to(spy.callCount).equals(1)\n\t})\n\n\to(\"should stop running after mount no arg\", function() {\n\t\tvar spy = o.spy()\n\n\t\tm.mount(root, {view: spy})\n\t\to(spy.callCount).equals(1)\n\t\tm.mount(root)\n\n\t\tm.redraw()\n\n\t\to(spy.callCount).equals(1)\n\t\tthrottleMock.fire()\n\t\to(spy.callCount).equals(1)\n\t})\n\n\to(\"should invoke remove callback on unmount\", function() {\n\t\tvar spy = o.spy()\n\t\tvar onremove = o.spy()\n\n\t\tm.mount(root, {view: spy, onremove: onremove})\n\t\to(spy.callCount).equals(1)\n\t\tm.mount(root)\n\n\t\to(spy.callCount).equals(1)\n\t\to(onremove.callCount).equals(1)\n\t})\n\n\to(\"should stop running after unsubscribe, even if it occurs after redraw is requested\", function() {\n\t\tvar spy = o.spy()\n\n\t\tm.mount(root, {view: spy})\n\t\to(spy.callCount).equals(1)\n\t\tm.redraw()\n\t\tm.mount(root)\n\n\t\to(spy.callCount).equals(1)\n\t\tthrottleMock.fire()\n\t\to(spy.callCount).equals(1)\n\t})\n\n\to(\"does nothing on invalid unmount\", function() {\n\t\tvar spy = o.spy()\n\n\t\tm.mount(root, {view: spy})\n\t\to(spy.callCount).equals(1)\n\n\t\tm.mount(null)\n\t\tm.redraw()\n\t\tthrottleMock.fire()\n\t\to(spy.callCount).equals(2)\n\t})\n\n\to(\"redraw.sync() redraws all roots synchronously\", function() {\n\t\tvar el1 = $document.createElement(\"div\")\n\t\tvar el2 = $document.createElement(\"div\")\n\t\tvar el3 = $document.createElement(\"div\")\n\t\tvar spy1 = o.spy()\n\t\tvar spy2 = o.spy()\n\t\tvar spy3 = o.spy()\n\n\t\tm.mount(el1, {view: spy1})\n\t\tm.mount(el2, {view: spy2})\n\t\tm.mount(el3, {view: spy3})\n\n\t\to(spy1.callCount).equals(1)\n\t\to(spy2.callCount).equals(1)\n\t\to(spy3.callCount).equals(1)\n\n\t\tm.redraw.sync()\n\n\t\to(spy1.callCount).equals(2)\n\t\to(spy2.callCount).equals(2)\n\t\to(spy3.callCount).equals(2)\n\n\t\tm.redraw.sync()\n\n\t\to(spy1.callCount).equals(3)\n\t\to(spy2.callCount).equals(3)\n\t\to(spy3.callCount).equals(3)\n\t})\n\n\n\to(\"throws on invalid component\", function() {\n\t\to(function() { m.mount(root, {}) }).throws(TypeError)\n\t})\n\n\to(\"skips roots that were synchronously unsubscribed before they were visited\", function() {\n\t\tvar calls = []\n\t\tvar root1 = $document.createElement(\"div\")\n\t\tvar root2 = $document.createElement(\"div\")\n\t\tvar root3 = $document.createElement(\"div\")\n\n\t\tm.mount(root1, {\n\t\t\tonbeforeupdate: function() {\n\t\t\t\tm.mount(root2, null)\n\t\t\t},\n\t\t\tview: function() { calls.push(\"root1\") },\n\t\t})\n\t\tm.mount(root2, {view: function() { calls.push(\"root2\") }})\n\t\tm.mount(root3, {view: function() { calls.push(\"root3\") }})\n\t\to(calls).deepEquals([\n\t\t\t\"root1\", \"root2\", \"root3\",\n\t\t])\n\n\t\tm.redraw.sync()\n\t\to(calls).deepEquals([\n\t\t\t\"root1\", \"root2\", \"root3\",\n\t\t\t\"root1\", \"root3\",\n\t\t])\n\t})\n\n\to(\"keeps its place when synchronously unsubscribing previously visited roots\", function() {\n\t\tvar calls = []\n\t\tvar root1 = $document.createElement(\"div\")\n\t\tvar root2 = $document.createElement(\"div\")\n\t\tvar root3 = $document.createElement(\"div\")\n\n\t\tm.mount(root1, {view: function() { calls.push(\"root1\") }})\n\t\tm.mount(root2, {\n\t\t\tonbeforeupdate: function() {\n\t\t\t\tm.mount(root1, null)\n\t\t\t},\n\t\t\tview: function() { calls.push(\"root2\") },\n\t\t})\n\t\tm.mount(root3, {view: function() { calls.push(\"root3\") }})\n\t\to(calls).deepEquals([\n\t\t\t\"root1\", \"root2\", \"root3\",\n\t\t])\n\n\t\tm.redraw.sync()\n\t\to(calls).deepEquals([\n\t\t\t\"root1\", \"root2\", \"root3\",\n\t\t\t\"root1\", \"root2\", \"root3\",\n\t\t])\n\t})\n\n\to(\"keeps its place when synchronously unsubscribing previously visited roots in the face of errors\", function() {\n\t\terrors = [\"fail\"]\n\t\tvar calls = []\n\t\tvar root1 = $document.createElement(\"div\")\n\t\tvar root2 = $document.createElement(\"div\")\n\t\tvar root3 = $document.createElement(\"div\")\n\n\t\tm.mount(root1, {view: function() { calls.push(\"root1\") }})\n\t\tm.mount(root2, {\n\t\t\tonbeforeupdate: function() {\n\t\t\t\tm.mount(root1, null)\n\t\t\t\tthrow \"fail\"\n\t\t\t},\n\t\t\tview: function() { calls.push(\"root2\") },\n\t\t})\n\t\tm.mount(root3, {view: function() { calls.push(\"root3\") }})\n\t\to(calls).deepEquals([\n\t\t\t\"root1\", \"root2\", \"root3\",\n\t\t])\n\n\t\tm.redraw.sync()\n\t\to(calls).deepEquals([\n\t\t\t\"root1\", \"root2\", \"root3\",\n\t\t\t\"root1\", \"root3\",\n\t\t])\n\t})\n\n\to(\"keeps its place when synchronously unsubscribing the current root\", function() {\n\t\tvar calls = []\n\t\tvar root1 = $document.createElement(\"div\")\n\t\tvar root2 = $document.createElement(\"div\")\n\t\tvar root3 = $document.createElement(\"div\")\n\n\t\tm.mount(root1, {view: function() { calls.push(\"root1\") }})\n\t\tm.mount(root2, {\n\t\t\tonbeforeupdate: function() {\n\t\t\t\ttry { m.mount(root2, null) } catch (e) { calls.push([e.constructor, e.message]) }\n\t\t\t},\n\t\t\tview: function() { calls.push(\"root2\") },\n\t\t})\n\t\tm.mount(root3, {view: function() { calls.push(\"root3\") }})\n\t\to(calls).deepEquals([\n\t\t\t\"root1\", \"root2\", \"root3\",\n\t\t])\n\n\t\tm.redraw.sync()\n\t\to(calls).deepEquals([\n\t\t\t\"root1\", \"root2\", \"root3\",\n\t\t\t\"root1\", [TypeError, \"Node is currently being rendered to and thus is locked.\"], \"root2\", \"root3\",\n\t\t])\n\t})\n\n\to(\"keeps its place when synchronously unsubscribing the current root in the face of an error\", function() {\n\t\terrors = [\n\t\t\t[TypeError, \"Node is currently being rendered to and thus is locked.\"],\n\t\t]\n\t\tvar calls = []\n\t\tvar root1 = $document.createElement(\"div\")\n\t\tvar root2 = $document.createElement(\"div\")\n\t\tvar root3 = $document.createElement(\"div\")\n\n\t\tm.mount(root1, {view: function() { calls.push(\"root1\") }})\n\t\tm.mount(root2, {\n\t\t\tonbeforeupdate: function() {\n\t\t\t\ttry { m.mount(root2, null) } catch (e) { throw [e.constructor, e.message] }\n\t\t\t},\n\t\t\tview: function() { calls.push(\"root2\") },\n\t\t})\n\t\tm.mount(root3, {view: function() { calls.push(\"root3\") }})\n\t\to(calls).deepEquals([\n\t\t\t\"root1\", \"root2\", \"root3\",\n\t\t])\n\n\t\tm.redraw.sync()\n\t\to(calls).deepEquals([\n\t\t\t\"root1\", \"root2\", \"root3\",\n\t\t\t\"root1\", \"root3\",\n\t\t])\n\t})\n\n\tcomponents.forEach(function(cmp){\n\t\to.spec(cmp.kind, function(){\n\t\t\tvar createComponent = cmp.create\n\n\t\t\to(\"throws on invalid `root` DOM node\", function() {\n\t\t\t\to(function() {\n\t\t\t\t\tm.mount(null, createComponent({view: function() {}}))\n\t\t\t\t}).throws(TypeError)\n\t\t\t})\n\n\t\t\to(\"renders into `root` synchronously\", function() {\n\t\t\t\tm.mount(root, createComponent({\n\t\t\t\t\tview: function() {\n\t\t\t\t\t\treturn h(\"div\")\n\t\t\t\t\t}\n\t\t\t\t}))\n\n\t\t\t\to(root.firstChild.nodeName).equals(\"DIV\")\n\t\t\t})\n\n\t\t\to(\"mounting null unmounts\", function() {\n\t\t\t\tm.mount(root, createComponent({\n\t\t\t\t\tview: function() {\n\t\t\t\t\t\treturn h(\"div\")\n\t\t\t\t\t}\n\t\t\t\t}))\n\n\t\t\t\tm.mount(root, null)\n\n\t\t\t\to(root.childNodes.length).equals(0)\n\t\t\t})\n\n\t\t\to(\"Mounting a second root doesn't cause the first one to redraw\", function() {\n\t\t\t\tvar root1 = $document.createElement(\"div\")\n\t\t\t\tvar root2 = $document.createElement(\"div\")\n\t\t\t\tvar view = o.spy()\n\n\t\t\t\tm.mount(root1, createComponent({view: view}))\n\t\t\t\to(view.callCount).equals(1)\n\n\t\t\t\tm.mount(root2, createComponent({view: function() {}}))\n\n\t\t\t\to(view.callCount).equals(1)\n\n\t\t\t\tthrottleMock.fire()\n\t\t\t\to(view.callCount).equals(1)\n\t\t\t})\n\n\t\t\to(\"redraws on events\", function() {\n\t\t\t\tvar onupdate = o.spy()\n\t\t\t\tvar oninit = o.spy()\n\t\t\t\tvar onclick = o.spy()\n\t\t\t\tvar e = $document.createEvent(\"MouseEvents\")\n\n\t\t\t\te.initEvent(\"click\", true, true)\n\n\t\t\t\tm.mount(root, createComponent({\n\t\t\t\t\tview: function() {\n\t\t\t\t\t\treturn h(\"div\", {\n\t\t\t\t\t\t\toninit: oninit,\n\t\t\t\t\t\t\tonupdate: onupdate,\n\t\t\t\t\t\t\tonclick: onclick,\n\t\t\t\t\t\t})\n\t\t\t\t\t}\n\t\t\t\t}))\n\n\t\t\t\troot.firstChild.dispatchEvent(e)\n\n\t\t\t\to(oninit.callCount).equals(1)\n\t\t\t\to(onupdate.callCount).equals(0)\n\n\t\t\t\to(onclick.callCount).equals(1)\n\t\t\t\to(onclick.this).equals(root.firstChild)\n\t\t\t\to(onclick.args[0].type).equals(\"click\")\n\t\t\t\to(onclick.args[0].target).equals(root.firstChild)\n\n\t\t\t\tthrottleMock.fire()\n\n\t\t\t\to(onupdate.callCount).equals(1)\n\t\t\t})\n\n\t\t\to(\"redraws several mount points on events\", function() {\n\t\t\t\tvar onupdate0 = o.spy()\n\t\t\t\tvar oninit0 = o.spy()\n\t\t\t\tvar onclick0 = o.spy()\n\t\t\t\tvar onupdate1 = o.spy()\n\t\t\t\tvar oninit1 = o.spy()\n\t\t\t\tvar onclick1 = o.spy()\n\n\t\t\t\tvar root1 = $document.createElement(\"div\")\n\t\t\t\tvar root2 = $document.createElement(\"div\")\n\t\t\t\tvar e = $document.createEvent(\"MouseEvents\")\n\n\t\t\t\te.initEvent(\"click\", true, true)\n\n\t\t\t\tm.mount(root1, createComponent({\n\t\t\t\t\tview: function() {\n\t\t\t\t\t\treturn h(\"div\", {\n\t\t\t\t\t\t\toninit: oninit0,\n\t\t\t\t\t\t\tonupdate: onupdate0,\n\t\t\t\t\t\t\tonclick: onclick0,\n\t\t\t\t\t\t})\n\t\t\t\t\t}\n\t\t\t\t}))\n\n\t\t\t\to(oninit0.callCount).equals(1)\n\t\t\t\to(onupdate0.callCount).equals(0)\n\n\t\t\t\tm.mount(root2, createComponent({\n\t\t\t\t\tview: function() {\n\t\t\t\t\t\treturn h(\"div\", {\n\t\t\t\t\t\t\toninit: oninit1,\n\t\t\t\t\t\t\tonupdate: onupdate1,\n\t\t\t\t\t\t\tonclick: onclick1,\n\t\t\t\t\t\t})\n\t\t\t\t\t}\n\t\t\t\t}))\n\n\t\t\t\to(oninit1.callCount).equals(1)\n\t\t\t\to(onupdate1.callCount).equals(0)\n\n\t\t\t\troot1.firstChild.dispatchEvent(e)\n\t\t\t\to(onclick0.callCount).equals(1)\n\t\t\t\to(onclick0.this).equals(root1.firstChild)\n\n\t\t\t\tthrottleMock.fire()\n\n\t\t\t\to(onupdate0.callCount).equals(1)\n\t\t\t\to(onupdate1.callCount).equals(1)\n\n\t\t\t\troot2.firstChild.dispatchEvent(e)\n\n\t\t\t\to(onclick1.callCount).equals(1)\n\t\t\t\to(onclick1.this).equals(root2.firstChild)\n\n\t\t\t\tthrottleMock.fire()\n\n\t\t\t\to(onupdate0.callCount).equals(2)\n\t\t\t\to(onupdate1.callCount).equals(2)\n\t\t\t})\n\n\t\t\to(\"event handlers can skip redraw\", function() {\n\t\t\t\tvar onupdate = o.spy(function(){\n\t\t\t\t\tthrow new Error(\"This shouldn't have been called\")\n\t\t\t\t})\n\t\t\t\tvar oninit = o.spy()\n\t\t\t\tvar e = $document.createEvent(\"MouseEvents\")\n\n\t\t\t\te.initEvent(\"click\", true, true)\n\n\t\t\t\tm.mount(root, createComponent({\n\t\t\t\t\tview: function() {\n\t\t\t\t\t\treturn h(\"div\", {\n\t\t\t\t\t\t\toninit: oninit,\n\t\t\t\t\t\t\tonupdate: onupdate,\n\t\t\t\t\t\t\tonclick: function(e) {\n\t\t\t\t\t\t\t\te.redraw = false\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t})\n\t\t\t\t\t}\n\t\t\t\t}))\n\n\t\t\t\troot.firstChild.dispatchEvent(e)\n\n\t\t\t\to(oninit.callCount).equals(1)\n\t\t\t\to(e.redraw).equals(false)\n\n\t\t\t\tthrottleMock.fire()\n\n\t\t\t\to(onupdate.callCount).equals(0)\n\t\t\t\to(e.redraw).equals(false)\n\t\t\t})\n\n\t\t\to(\"redraws when the render function is run\", function() {\n\t\t\t\tvar onupdate = o.spy()\n\t\t\t\tvar oninit = o.spy()\n\n\t\t\t\tm.mount(root, createComponent({\n\t\t\t\t\tview: function() {\n\t\t\t\t\t\treturn h(\"div\", {\n\t\t\t\t\t\t\toninit: oninit,\n\t\t\t\t\t\t\tonupdate: onupdate\n\t\t\t\t\t\t})\n\t\t\t\t\t}\n\t\t\t\t}))\n\n\t\t\t\to(oninit.callCount).equals(1)\n\t\t\t\to(onupdate.callCount).equals(0)\n\n\t\t\t\tm.redraw()\n\n\t\t\t\tthrottleMock.fire()\n\n\t\t\t\to(onupdate.callCount).equals(1)\n\t\t\t})\n\n\t\t\to(\"emits errors correctly\", function() {\n\t\t\t\terrors = [\"foo\", \"bar\", \"baz\"]\n\t\t\t\tvar counter = -1\n\n\t\t\t\tm.mount(root, createComponent({\n\t\t\t\t\tview: function() {\n\t\t\t\t\t\tvar value = errors[counter++]\n\t\t\t\t\t\tif (value != null) throw value\n\t\t\t\t\t\treturn null\n\t\t\t\t\t}\n\t\t\t\t}))\n\n\t\t\t\tm.redraw()\n\t\t\t\tthrottleMock.fire()\n\t\t\t\tm.redraw()\n\t\t\t\tthrottleMock.fire()\n\t\t\t\tm.redraw()\n\t\t\t\tthrottleMock.fire()\n\t\t\t})\n\t\t})\n\t})\n})\n"
  },
  {
    "path": "api/tests/test-router.js",
    "content": "\"use strict\"\n\n// Low-priority TODO: remove the dependency on the renderer here.\nvar o = require(\"ospec\")\nvar browserMock = require(\"../../test-utils/browserMock\")\nvar throttleMocker = require(\"../../test-utils/throttleMock\")\n\nvar m = require(\"../../render/hyperscript\")\nvar coreRenderer = require(\"../../render/render\")\nvar apiMountRedraw = require(\"../../api/mount-redraw\")\nvar apiRouter = require(\"../../api/router\")\n\no.spec(\"route\", function() {\n\t// Note: the `n` parameter used in calls to this are generally found by\n\t// either trial-and-error or by studying the source. If tests are failing,\n\t// find the failing assertions, set `n` to about 10 on the preceding call to\n\t// `waitCycles`, then drop them down incrementally until it fails. The last\n\t// one to succeed is the one you want to keep. And just do that for each\n\t// failing assertion, and it'll eventually work.\n\t//\n\t// This is effectively what I did when designing this and hooking everything\n\t// up. (It would be so much easier to just be able to run the calls with a\n\t// different event loop and just turn it until I get what I want, but JS\n\t// lacks that functionality.)\n\n\t// Use precisely what `m.route` uses, for consistency and to ensure timings\n\t// are aligned.\n\tfunction waitCycles(n) {\n\t\tn = Math.max(n, 1)\n\t\treturn new Promise(function(resolve) {\n\t\t\treturn loop()\n\t\t\tfunction loop() {\n\t\t\t\tif (n === 0) resolve()\n\t\t\t\telse { n--; setTimeout(loop, 4) }\n\t\t\t}\n\t\t})\n\t}\n\n\tvoid [{protocol: \"http:\", hostname: \"localhost\"}, {protocol: \"file:\", hostname: \"/\"}, {protocol: \"http:\", hostname: \"ööö\"}].forEach(function(env) {\n\t\tvoid [\"#\", \"?\", \"\", \"#!\", \"?!\", \"/foo\", \"/föö\"].forEach(function(prefix) {\n\t\t\to.spec(\"using prefix `\" + prefix + \"` starting on \" + env.protocol + \"//\" + env.hostname, function() {\n\t\t\t\tvar $window, root, mountRedraw, route, throttleMock\n\t\t\t\tvar nextID = 0\n\t\t\t\tvar currentTest = 0\n\n\t\t\t\t// Once done, a root should no longer be alive. This verifies\n\t\t\t\t// that, and it's a *very* subtle test bug that can lead to\n\t\t\t\t// some rather unusual consequences. If this fails, use\n\t\t\t\t// `waitCycles(n)` to avoid this.\n\t\t\t\tfunction lock(func) {\n\t\t\t\t\tvar id = currentTest\n\t\t\t\t\tvar start = Date.now()\n\t\t\t\t\ttry {\n\t\t\t\t\t\tthrow new Error()\n\t\t\t\t\t} catch (trace) {\n\t\t\t\t\t\treturn function() {\n\t\t\t\t\t\t\t// This *will* cause a test failure.\n\t\t\t\t\t\t\tif (id != null && id !== currentTest) {\n\t\t\t\t\t\t\t\tid = undefined\n\t\t\t\t\t\t\t\ttrace.message = \"called \" +\n\t\t\t\t\t\t\t\t\t(Date.now() - start) + \"ms after test end\"\n\t\t\t\t\t\t\t\tconsole.error(trace.stack)\n\t\t\t\t\t\t\t\to(\"in test\").equals(\"not in test\")\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\treturn func.apply(this, arguments)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// In case it doesn't get reset\n\t\t\t\tvar realError = console.error\n\n\t\t\t\to.beforeEach(function() {\n\t\t\t\t\tcurrentTest = nextID++\n\t\t\t\t\t$window = browserMock(env)\n\t\t\t\t\t$window.setTimeout = setTimeout\n\t\t\t\t\t// $window.setImmediate = setImmediate\n\t\t\t\t\tthrottleMock = throttleMocker()\n\n\t\t\t\t\troot = $window.document.body\n\n\t\t\t\t\tmountRedraw = apiMountRedraw(coreRenderer($window), throttleMock.schedule, console)\n\t\t\t\t\troute = apiRouter($window, mountRedraw)\n\t\t\t\t\troute.prefix = prefix\n\t\t\t\t\tconsole.error = function() {\n\t\t\t\t\t\trealError.call(this, new Error(\"Unexpected `console.error` call\"))\n\t\t\t\t\t\trealError.apply(this, arguments)\n\t\t\t\t\t}\n\t\t\t\t})\n\n\t\t\t\to.afterEach(function() {\n\t\t\t\t\to(throttleMock.queueLength()).equals(0)\n\t\t\t\t\tcurrentTest = -1 // doesn't match any test\n\t\t\t\t\tconsole.error = realError\n\t\t\t\t})\n\n\t\t\t\to(\"throws on invalid `root` DOM node\", function() {\n\t\t\t\t\tvar threw = false\n\t\t\t\t\ttry {\n\t\t\t\t\t\troute(null, \"/\", {\"/\":{view: lock(function() {})}})\n\t\t\t\t\t} catch (e) {\n\t\t\t\t\t\tthrew = true\n\t\t\t\t\t}\n\t\t\t\t\to(threw).equals(true)\n\t\t\t\t})\n\n\t\t\t\to(\"renders into `root`\", function() {\n\t\t\t\t\t$window.location.href = prefix + \"/\"\n\t\t\t\t\troute(root, \"/\", {\n\t\t\t\t\t\t\"/\" : {\n\t\t\t\t\t\t\tview: lock(function() {\n\t\t\t\t\t\t\t\treturn m(\"div\")\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\n\t\t\t\t\to(root.firstChild.nodeName).equals(\"DIV\")\n\t\t\t\t})\n\n\t\t\t\to(\"resolves to route with escaped unicode\", function() {\n\t\t\t\t\t$window.location.href = prefix + \"/%C3%B6?%C3%B6=%C3%B6\"\n\t\t\t\t\troute(root, \"/ö\", {\n\t\t\t\t\t\t\"/ö\" : {\n\t\t\t\t\t\t\tview: lock(function() {\n\t\t\t\t\t\t\t\treturn m(\"div\")\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\n\t\t\t\t\to(root.firstChild.nodeName).equals(\"DIV\")\n\t\t\t\t})\n\n\t\t\t\to(\"resolves to route with unicode\", function() {\n\t\t\t\t\t$window.location.href = prefix + \"/ö?ö=ö\"\n\t\t\t\t\troute(root, \"/ö\", {\n\t\t\t\t\t\t\"/ö\" : {\n\t\t\t\t\t\t\tview: lock(function() {\n\t\t\t\t\t\t\t\treturn JSON.stringify(route.param()) + \" \" +\n\t\t\t\t\t\t\t\t\troute.get()\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\n\t\t\t\t\to(root.firstChild.nodeValue).equals('{\"ö\":\"ö\"} /ö?ö=ö')\n\t\t\t\t})\n\n\t\t\t\to(\"resolves to route with matching invalid escape\", function() {\n\t\t\t\t\t$window.location.href = prefix + \"/%C3%B6abc%def\"\n\t\t\t\t\troute(root, \"/öabc%def\", {\n\t\t\t\t\t\t\"/öabc%def\" : {\n\t\t\t\t\t\t\tview: lock(function() {\n\t\t\t\t\t\t\t\treturn route.get()\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\n\t\t\t\t\to(root.firstChild.nodeValue).equals(\"/öabc%def\")\n\t\t\t\t})\n\n\t\t\t\to(\"handles parameterized route\", function() {\n\t\t\t\t\t$window.location.href = prefix + \"/test/x\"\n\t\t\t\t\troute(root, \"/test/:a\", {\n\t\t\t\t\t\t\"/test/:a\" : {\n\t\t\t\t\t\t\tview: lock(function(vnode) {\n\t\t\t\t\t\t\t\treturn JSON.stringify(route.param()) + \" \" +\n\t\t\t\t\t\t\t\t\tJSON.stringify(vnode.attrs) + \" \" +\n\t\t\t\t\t\t\t\t\troute.get()\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\n\t\t\t\t\to(root.firstChild.nodeValue).equals(\n\t\t\t\t\t\t'{\"a\":\"x\"} {\"a\":\"x\"} /test/x'\n\t\t\t\t\t)\n\t\t\t\t})\n\n\t\t\t\to(\"handles multi-parameterized route\", function() {\n\t\t\t\t\t$window.location.href = prefix + \"/test/x/y\"\n\t\t\t\t\troute(root, \"/test/:a/:b\", {\n\t\t\t\t\t\t\"/test/:a/:b\" : {\n\t\t\t\t\t\t\tview: lock(function(vnode) {\n\t\t\t\t\t\t\t\treturn JSON.stringify(route.param()) + \" \" +\n\t\t\t\t\t\t\t\t\tJSON.stringify(vnode.attrs) + \" \" +\n\t\t\t\t\t\t\t\t\troute.get()\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\n\t\t\t\t\to(root.firstChild.nodeValue).equals(\n\t\t\t\t\t\t'{\"a\":\"x\",\"b\":\"y\"} {\"a\":\"x\",\"b\":\"y\"} /test/x/y'\n\t\t\t\t\t)\n\t\t\t\t})\n\n\t\t\t\to(\"handles rest parameterized route\", function() {\n\t\t\t\t\t$window.location.href = prefix + \"/test/x/y\"\n\t\t\t\t\troute(root, \"/test/:a...\", {\n\t\t\t\t\t\t\"/test/:a...\" : {\n\t\t\t\t\t\t\tview: lock(function(vnode) {\n\t\t\t\t\t\t\t\treturn JSON.stringify(route.param()) + \" \" +\n\t\t\t\t\t\t\t\t\tJSON.stringify(vnode.attrs) + \" \" +\n\t\t\t\t\t\t\t\t\troute.get()\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\n\t\t\t\t\to(root.firstChild.nodeValue).equals(\n\t\t\t\t\t\t'{\"a\":\"x/y\"} {\"a\":\"x/y\"} /test/x/y'\n\t\t\t\t\t)\n\t\t\t\t})\n\n\t\t\t\to(\"keeps trailing / in rest parameterized route\", function() {\n\t\t\t\t\t$window.location.href = prefix + \"/test/d/\"\n\t\t\t\t\troute(root, \"/test/:a...\", {\n\t\t\t\t\t\t\"/test/:a...\" : {\n\t\t\t\t\t\t\tview: lock(function(vnode) {\n\t\t\t\t\t\t\t\treturn JSON.stringify(route.param()) + \" \" +\n\t\t\t\t\t\t\t\t\tJSON.stringify(vnode.attrs) + \" \" +\n\t\t\t\t\t\t\t\t\troute.get()\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\n\t\t\t\t\to(root.firstChild.nodeValue).equals(\n\t\t\t\t\t\t'{\"a\":\"d/\"} {\"a\":\"d/\"} /test/d/'\n\t\t\t\t\t)\n\t\t\t\t})\n\n\t\t\t\to(\"remove trailing slash to match route if it is before rest operator match (...) \", function() {\n\t\t\t\t\t$window.location.href = prefix + \"/test/d/\"\n\t\t\t\t\troute(root, \"/test/some/path\", {\n\t\t\t\t\t\t\"/test/:a\" : {\n\t\t\t\t\t\t\tview: lock(function(vnode) {\n\t\t\t\t\t\t\t\treturn JSON.stringify(route.param()) + \" \" +\n\t\t\t\t\t\t\t\t\tJSON.stringify(vnode.attrs) + \" \" +\n\t\t\t\t\t\t\t\t\troute.get()\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"/test/:a...\" : {\n\t\t\t\t\t\t\tview: lock(function(vnode) {\n\t\t\t\t\t\t\t\treturn JSON.stringify(route.param()) + \" \" +\n\t\t\t\t\t\t\t\t\tJSON.stringify(vnode.attrs) + \" \" +\n\t\t\t\t\t\t\t\t\troute.get()\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t},\n\t\t\t\t\t})\n\n\t\t\t\t\to(root.firstChild.nodeValue).equals(\n\t\t\t\t\t\t'{\"a\":\"d\"} {\"a\":\"d\"} /test/d/'\n\t\t\t\t\t)\n\t\t\t\t})\n\n\t\t\t\to(\"handles route with search\", function() {\n\t\t\t\t\t$window.location.href = prefix + \"/test?a=b&c=d\"\n\t\t\t\t\troute(root, \"/test\", {\n\t\t\t\t\t\t\"/test\" : {\n\t\t\t\t\t\t\tview: lock(function(vnode) {\n\t\t\t\t\t\t\t\treturn JSON.stringify(route.param()) + \" \" +\n\t\t\t\t\t\t\t\t\tJSON.stringify(vnode.attrs) + \" \" +\n\t\t\t\t\t\t\t\t\troute.get()\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\n\t\t\t\t\to(root.firstChild.nodeValue).equals(\n\t\t\t\t\t\t'{\"a\":\"b\",\"c\":\"d\"} {\"a\":\"b\",\"c\":\"d\"} /test?a=b&c=d'\n\t\t\t\t\t)\n\t\t\t\t})\n\n\t\t\t\to(\"redirects to default route if no match\", function() {\n\t\t\t\t\t$window.location.href = prefix + \"/test\"\n\t\t\t\t\troute(root, \"/other\", {\n\t\t\t\t\t\t\"/other\": {\n\t\t\t\t\t\t\tview: lock(function(vnode) {\n\t\t\t\t\t\t\t\treturn JSON.stringify(route.param()) + \" \" +\n\t\t\t\t\t\t\t\t\tJSON.stringify(vnode.attrs) + \" \" +\n\t\t\t\t\t\t\t\t\troute.get()\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\n\t\t\t\t\treturn waitCycles(1).then(function() {\n\t\t\t\t\t\to(root.firstChild.nodeValue).equals(\"{} {} /other\")\n\t\t\t\t\t})\n\t\t\t\t})\n\n\t\t\t\to(\"handles out of order routes\", function() {\n\t\t\t\t\t$window.location.href = prefix + \"/z/y/x\"\n\n\t\t\t\t\troute(root, \"/z/y/x\", {\n\t\t\t\t\t\t\"/z/y/x\": {\n\t\t\t\t\t\t\tview: lock(function() { return \"1\" }),\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"/:a...\": {\n\t\t\t\t\t\t\tview: lock(function() { return \"2\" }),\n\t\t\t\t\t\t},\n\t\t\t\t\t})\n\n\t\t\t\t\to(root.firstChild.nodeValue).equals(\"1\")\n\t\t\t\t})\n\n\t\t\t\to(\"handles reverse out of order routes\", function() {\n\t\t\t\t\t$window.location.href = prefix + \"/z/y/x\"\n\n\t\t\t\t\troute(root, \"/z/y/x\", {\n\t\t\t\t\t\t\"/:a...\": {\n\t\t\t\t\t\t\tview: lock(function() { return \"2\" }),\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"/z/y/x\": {\n\t\t\t\t\t\t\tview: lock(function() { return \"1\" }),\n\t\t\t\t\t\t},\n\t\t\t\t\t})\n\n\t\t\t\t\to(root.firstChild.nodeValue).equals(\"2\")\n\t\t\t\t})\n\n\t\t\t\to(\"resolves to route on fallback mode\", function() {\n\t\t\t\t\t$window.location.href = \"file://\" + prefix + \"/test\"\n\n\t\t\t\t\troute(root, \"/test\", {\n\t\t\t\t\t\t\"/test\" : {\n\t\t\t\t\t\t\tview: lock(function(vnode) {\n\t\t\t\t\t\t\t\treturn JSON.stringify(route.param()) + \" \" +\n\t\t\t\t\t\t\t\t\tJSON.stringify(vnode.attrs) + \" \" +\n\t\t\t\t\t\t\t\t\troute.get()\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\n\t\t\t\t\to(root.firstChild.nodeValue).equals(\"{} {} /test\")\n\t\t\t\t})\n\n\t\t\t\to(\"routed mount points only redraw asynchronously (POJO component)\", function() {\n\t\t\t\t\tvar view = o.spy()\n\n\t\t\t\t\t$window.location.href = prefix + \"/\"\n\t\t\t\t\troute(root, \"/\", {\"/\":{view:view}})\n\n\t\t\t\t\to(view.callCount).equals(1)\n\n\t\t\t\t\tmountRedraw.redraw()\n\n\t\t\t\t\to(view.callCount).equals(1)\n\n\t\t\t\t\tthrottleMock.fire()\n\n\t\t\t\t\to(view.callCount).equals(2)\n\t\t\t\t})\n\n\t\t\t\to(\"routed mount points only redraw asynchronously (constructible component)\", function() {\n\t\t\t\t\tvar view = o.spy()\n\n\t\t\t\t\tvar Cmp = lock(function(){})\n\t\t\t\t\tCmp.prototype.view = lock(view)\n\n\t\t\t\t\t$window.location.href = prefix + \"/\"\n\t\t\t\t\troute(root, \"/\", {\"/\":Cmp})\n\n\t\t\t\t\to(view.callCount).equals(1)\n\n\t\t\t\t\tmountRedraw.redraw()\n\n\t\t\t\t\to(view.callCount).equals(1)\n\n\t\t\t\t\tthrottleMock.fire()\n\n\t\t\t\t\to(view.callCount).equals(2)\n\t\t\t\t})\n\n\t\t\t\to(\"routed mount points only redraw asynchronously (closure component)\", function() {\n\t\t\t\t\tvar view = o.spy()\n\n\t\t\t\t\tfunction Cmp() {return {view: lock(view)}}\n\n\t\t\t\t\t$window.location.href = prefix + \"/\"\n\t\t\t\t\troute(root, \"/\", {\"/\":lock(Cmp)})\n\n\t\t\t\t\to(view.callCount).equals(1)\n\n\t\t\t\t\tmountRedraw.redraw()\n\n\t\t\t\t\to(view.callCount).equals(1)\n\n\t\t\t\t\tthrottleMock.fire()\n\n\t\t\t\t\to(view.callCount).equals(2)\n\t\t\t\t})\n\n\t\t\t\to(\"subscribes correctly and removes when unmounted\", function() {\n\t\t\t\t\t$window.location.href = prefix + \"/\"\n\n\t\t\t\t\troute(root, \"/\", {\n\t\t\t\t\t\t\"/\" : {\n\t\t\t\t\t\t\tview: lock(function() {\n\t\t\t\t\t\t\t\treturn m(\"div\")\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\n\t\t\t\t\to(root.firstChild.nodeName).equals(\"DIV\")\n\n\t\t\t\t\tmountRedraw.mount(root)\n\n\t\t\t\t\to(root.childNodes.length).equals(0)\n\t\t\t\t})\n\n\t\t\t\to(\"default route doesn't break back button\", function() {\n\t\t\t\t\t$window.location.href = \"http://old.com\"\n\t\t\t\t\t$window.location.href = \"http://new.com\"\n\n\t\t\t\t\troute(root, \"/a\", {\n\t\t\t\t\t\t\"/a\" : {\n\t\t\t\t\t\t\tview: lock(function() {\n\t\t\t\t\t\t\t\treturn m(\"div\")\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\n\t\t\t\t\treturn waitCycles(1).then(function() {\n\t\t\t\t\t\to(root.firstChild.nodeName).equals(\"DIV\")\n\n\t\t\t\t\t\to(route.get()).equals(\"/a\")\n\n\t\t\t\t\t\t$window.history.back()\n\n\t\t\t\t\t\to($window.location.pathname).equals(\"/\")\n\t\t\t\t\t\to($window.location.hostname).equals(\"old.com\")\n\t\t\t\t\t})\n\t\t\t\t})\n\n\t\t\t\to(\"default route does not inherit params\", function() {\n\t\t\t\t\t$window.location.href = \"/invalid?foo=bar\"\n\t\t\t\t\troute(root, \"/a\", {\n\t\t\t\t\t\t\"/a\" : {\n\t\t\t\t\t\t\toninit: lock(function(vnode) {\n\t\t\t\t\t\t\t\to(vnode.attrs.foo).equals(undefined)\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\tview: lock(function() {\n\t\t\t\t\t\t\t\treturn m(\"div\")\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\n\t\t\t\t\treturn waitCycles(1)\n\t\t\t\t})\n\n\t\t\t\to(\"redraws when render function is executed\", function() {\n\t\t\t\t\tvar onupdate = o.spy()\n\t\t\t\t\tvar oninit = o.spy()\n\n\t\t\t\t\t$window.location.href = prefix + \"/\"\n\t\t\t\t\troute(root, \"/\", {\n\t\t\t\t\t\t\"/\" : {\n\t\t\t\t\t\t\tview: lock(function() {\n\t\t\t\t\t\t\t\treturn m(\"div\", {\n\t\t\t\t\t\t\t\t\toninit: oninit,\n\t\t\t\t\t\t\t\t\tonupdate: onupdate\n\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\n\t\t\t\t\to(oninit.callCount).equals(1)\n\n\t\t\t\t\tmountRedraw.redraw()\n\t\t\t\t\tthrottleMock.fire()\n\n\t\t\t\t\to(onupdate.callCount).equals(1)\n\t\t\t\t})\n\n\t\t\t\to(\"redraws on events\", function() {\n\t\t\t\t\tvar onupdate = o.spy()\n\t\t\t\t\tvar oninit = o.spy()\n\t\t\t\t\tvar onclick = o.spy()\n\t\t\t\t\tvar e = $window.document.createEvent(\"MouseEvents\")\n\n\t\t\t\t\te.initEvent(\"click\", true, true)\n\n\t\t\t\t\t$window.location.href = prefix + \"/\"\n\t\t\t\t\troute(root, \"/\", {\n\t\t\t\t\t\t\"/\" : {\n\t\t\t\t\t\t\tview: lock(function() {\n\t\t\t\t\t\t\t\treturn m(\"div\", {\n\t\t\t\t\t\t\t\t\toninit: oninit,\n\t\t\t\t\t\t\t\t\tonupdate: onupdate,\n\t\t\t\t\t\t\t\t\tonclick: onclick,\n\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\n\t\t\t\t\troot.firstChild.dispatchEvent(e)\n\n\t\t\t\t\to(oninit.callCount).equals(1)\n\n\t\t\t\t\to(onclick.callCount).equals(1)\n\t\t\t\t\to(onclick.this).equals(root.firstChild)\n\t\t\t\t\to(onclick.args[0].type).equals(\"click\")\n\t\t\t\t\to(onclick.args[0].target).equals(root.firstChild)\n\n\n\t\t\t\t\tthrottleMock.fire()\n\t\t\t\t\to(onupdate.callCount).equals(1)\n\t\t\t\t})\n\n\t\t\t\to(\"event handlers can skip redraw\", function() {\n\t\t\t\t\tvar onupdate = o.spy()\n\t\t\t\t\tvar oninit = o.spy()\n\t\t\t\t\tvar e = $window.document.createEvent(\"MouseEvents\")\n\n\t\t\t\t\te.initEvent(\"click\", true, true)\n\n\t\t\t\t\t$window.location.href = prefix + \"/\"\n\t\t\t\t\troute(root, \"/\", {\n\t\t\t\t\t\t\"/\" : {\n\t\t\t\t\t\t\tview: lock(function() {\n\t\t\t\t\t\t\t\treturn m(\"div\", {\n\t\t\t\t\t\t\t\t\toninit: oninit,\n\t\t\t\t\t\t\t\t\tonupdate: onupdate,\n\t\t\t\t\t\t\t\t\tonclick: lock(function(e) {\n\t\t\t\t\t\t\t\t\t\te.redraw = false\n\t\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\n\t\t\t\t\to(oninit.callCount).equals(1)\n\n\t\t\t\t\troot.firstChild.dispatchEvent(e)\n\t\t\t\t\tthrottleMock.fire()\n\n\t\t\t\t\t// Wrapped to ensure no redraw fired\n\t\t\t\t\treturn waitCycles(1).then(function() {\n\t\t\t\t\t\to(onupdate.callCount).equals(0)\n\t\t\t\t\t})\n\t\t\t\t})\n\n\t\t\t\to(\"changes location on route.Link\", function() {\n\t\t\t\t\tvar e = $window.document.createEvent(\"MouseEvents\")\n\n\t\t\t\t\te.initEvent(\"click\", true, true)\n\t\t\t\t\te.button = 0\n\n\t\t\t\t\t$window.location.href = prefix + \"/\"\n\t\t\t\t\troute(root, \"/\", {\n\t\t\t\t\t\t\"/\" : {\n\t\t\t\t\t\t\tview: lock(function() {\n\t\t\t\t\t\t\t\treturn m(route.Link, {href: \"/test\"})\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"/test\" : {\n\t\t\t\t\t\t\tview : lock(function() {\n\t\t\t\t\t\t\t\treturn m(\"div\")\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\n\t\t\t\t\tvar slash = prefix[0] === \"/\" ? \"\" : \"/\"\n\n\t\t\t\t\to($window.location.href).equals(env.protocol + \"//\" + (env.hostname === \"/\" ? \"\" : env.hostname) + slash + (prefix ? prefix + \"/\" : \"\"))\n\n\t\t\t\t\troot.firstChild.dispatchEvent(e)\n\t\t\t\t\tthrottleMock.fire()\n\t\t\t\t\to($window.location.href).equals(env.protocol + \"//\" + (env.hostname === \"/\" ? \"\" : env.hostname) + slash + (prefix ? prefix + \"/\" : \"\") + \"test\")\n\t\t\t\t})\n\n\t\t\t\to(\"passes options on route.Link\", function() {\n\t\t\t\t\tvar opts = {}\n\t\t\t\t\tvar e = $window.document.createEvent(\"MouseEvents\")\n\n\t\t\t\t\te.initEvent(\"click\", true, true)\n\t\t\t\t\te.button = 0\n\t\t\t\t\t$window.location.href = prefix + \"/\"\n\n\t\t\t\t\troute(root, \"/\", {\n\t\t\t\t\t\t\"/\" : {\n\t\t\t\t\t\t\tview: lock(function() {\n\t\t\t\t\t\t\t\treturn m(route.Link, {\n\t\t\t\t\t\t\t\t\thref: \"/test\",\n\t\t\t\t\t\t\t\t\toptions: opts,\n\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"/test\" : {\n\t\t\t\t\t\t\tview : lock(function() {\n\t\t\t\t\t\t\t\treturn m(\"div\")\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\t\t\t\t\troute.set = o.spy(route.set)\n\n\t\t\t\t\troot.firstChild.dispatchEvent(e)\n\n\t\t\t\t\to(route.set.callCount).equals(1)\n\t\t\t\t\to(route.set.args[2]).equals(opts)\n\t\t\t\t})\n\n\t\t\t\to(\"passes params on route.Link\", function() {\n\t\t\t\t\tvar e = $window.document.createEvent(\"MouseEvents\")\n\n\t\t\t\t\te.initEvent(\"click\", true, true)\n\t\t\t\t\te.button = 0\n\t\t\t\t\t$window.location.href = prefix + \"/\"\n\n\t\t\t\t\troute(root, \"/\", {\n\t\t\t\t\t\t\"/\" : {\n\t\t\t\t\t\t\tview: lock(function() {\n\t\t\t\t\t\t\t\treturn m(route.Link, {\n\t\t\t\t\t\t\t\t\thref: \"/test\",\n\t\t\t\t\t\t\t\t\tparams: {key: \"value\"},\n\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"/test\" : {\n\t\t\t\t\t\t\tview : lock(function() {\n\t\t\t\t\t\t\t\treturn m(\"div\")\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\t\t\t\t\troute.set = o.spy(route.set)\n\n\t\t\t\t\troot.firstChild.dispatchEvent(e)\n\n\t\t\t\t\to(route.set.callCount).equals(1)\n\t\t\t\t\to(route.set.args[0]).equals(\"/test?key=value\")\n\t\t\t\t})\n\n\t\t\t\to(\"route.Link can render without routes or dom access\", function() {\n\t\t\t\t\t$window = browserMock(env)\n\t\t\t\t\tvar render = coreRenderer($window)\n\t\t\t\t\troute = apiRouter(null, null)\n\t\t\t\t\troute.prefix = prefix\n\t\t\t\t\troot = $window.document.body\n\n\t\t\t\t\trender(root, m(route.Link, {href: \"/test\", foo: \"bar\"}, \"text\"))\n\n\t\t\t\t\to(root.childNodes.length).equals(1)\n\t\t\t\t\to(root.firstChild.nodeName).equals(\"A\")\n\t\t\t\t\to(root.firstChild.href).equals(prefix + \"/test\")\n\t\t\t\t\to(root.firstChild.hasAttribute(\"aria-disabled\")).equals(false)\n\t\t\t\t\to(root.firstChild.hasAttribute(\"disabled\")).equals(false)\n\t\t\t\t\to(root.firstChild.attributes[\"foo\"].value).equals(\"bar\")\n\t\t\t\t\to(root.firstChild.childNodes.length).equals(1)\n\t\t\t\t\to(root.firstChild.firstChild.nodeName).equals(\"#text\")\n\t\t\t\t\to(root.firstChild.firstChild.nodeValue).equals(\"text\")\n\t\t\t\t})\n\n\t\t\t\to(\"route.Link keeps magic attributes from being double-called\", function() {\n\t\t\t\t\t$window = browserMock(env)\n\t\t\t\t\tvar render = coreRenderer($window)\n\t\t\t\t\troute = apiRouter(null, null)\n\t\t\t\t\troute.prefix = prefix\n\t\t\t\t\troot = $window.document.body\n\n\t\t\t\t\tvar oninit = o.spy()\n\t\t\t\t\tvar oncreate = o.spy()\n\t\t\t\t\tvar onbeforeupdate = o.spy()\n\t\t\t\t\tvar onupdate = o.spy()\n\t\t\t\t\tvar onbeforeremove = o.spy()\n\t\t\t\t\tvar onremove = o.spy()\n\n\t\t\t\t\trender(root, m(route.Link, {\n\t\t\t\t\t\thref: \"/test\",\n\t\t\t\t\t\toninit: oninit,\n\t\t\t\t\t\toncreate: oncreate,\n\t\t\t\t\t\tonbeforeupdate: onbeforeupdate,\n\t\t\t\t\t\tonupdate: onupdate,\n\t\t\t\t\t\tonbeforeremove: onbeforeremove,\n\t\t\t\t\t\tonremove: onremove,\n\t\t\t\t\t}, \"text\"))\n\n\t\t\t\t\to(oninit.callCount).equals(1)\n\t\t\t\t\to(oncreate.callCount).equals(1)\n\t\t\t\t\to(onbeforeupdate.callCount).equals(0)\n\t\t\t\t\to(onupdate.callCount).equals(0)\n\t\t\t\t\to(onbeforeremove.callCount).equals(0)\n\t\t\t\t\to(onremove.callCount).equals(0)\n\n\t\t\t\t\trender(root, m(route.Link, {\n\t\t\t\t\t\thref: \"/test\",\n\t\t\t\t\t\toninit: oninit,\n\t\t\t\t\t\toncreate: oncreate,\n\t\t\t\t\t\tonbeforeupdate: onbeforeupdate,\n\t\t\t\t\t\tonupdate: onupdate,\n\t\t\t\t\t\tonbeforeremove: onbeforeremove,\n\t\t\t\t\t\tonremove: onremove,\n\t\t\t\t\t}, \"text\"))\n\n\t\t\t\t\to(oninit.callCount).equals(1)\n\t\t\t\t\to(oncreate.callCount).equals(1)\n\t\t\t\t\to(onbeforeupdate.callCount).equals(1)\n\t\t\t\t\to(onupdate.callCount).equals(1)\n\t\t\t\t\to(onbeforeremove.callCount).equals(0)\n\t\t\t\t\to(onremove.callCount).equals(0)\n\n\t\t\t\t\trender(root, [])\n\n\t\t\t\t\to(oninit.callCount).equals(1)\n\t\t\t\t\to(oncreate.callCount).equals(1)\n\t\t\t\t\to(onbeforeupdate.callCount).equals(1)\n\t\t\t\t\to(onupdate.callCount).equals(1)\n\t\t\t\t\to(onbeforeremove.callCount).equals(1)\n\t\t\t\t\to(onremove.callCount).equals(1)\n\t\t\t\t})\n\n\t\t\t\to(\"route.Link can render other tag without routes or dom access\", function() {\n\t\t\t\t\t$window = browserMock(env)\n\t\t\t\t\tvar render = coreRenderer($window)\n\t\t\t\t\troute = apiRouter(null, null)\n\t\t\t\t\troute.prefix = prefix\n\t\t\t\t\troot = $window.document.body\n\n\t\t\t\t\trender(root, m(route.Link, {selector: \"button\", href: \"/test\", foo: \"bar\"}, \"text\"))\n\n\t\t\t\t\to(root.childNodes.length).equals(1)\n\t\t\t\t\to(root.firstChild.nodeName).equals(\"BUTTON\")\n\t\t\t\t\to(root.firstChild.attributes[\"href\"].value).equals(prefix + \"/test\")\n\t\t\t\t\to(root.firstChild.hasAttribute(\"aria-disabled\")).equals(false)\n\t\t\t\t\to(root.firstChild.hasAttribute(\"disabled\")).equals(false)\n\t\t\t\t\to(root.firstChild.attributes[\"foo\"].value).equals(\"bar\")\n\t\t\t\t\to(root.firstChild.childNodes.length).equals(1)\n\t\t\t\t\to(root.firstChild.firstChild.nodeName).equals(\"#text\")\n\t\t\t\t\to(root.firstChild.firstChild.nodeValue).equals(\"text\")\n\t\t\t\t})\n\n\t\t\t\to(\"route.Link can render other selector without routes or dom access\", function() {\n\t\t\t\t\t$window = browserMock(env)\n\t\t\t\t\tvar render = coreRenderer($window)\n\t\t\t\t\troute = apiRouter(null, null)\n\t\t\t\t\troute.prefix = prefix\n\t\t\t\t\troot = $window.document.body\n\n\t\t\t\t\trender(root, m(route.Link, {selector: \"button[href=/test]\", foo: \"bar\"}, \"text\"))\n\n\t\t\t\t\to(root.childNodes.length).equals(1)\n\t\t\t\t\to(root.firstChild.nodeName).equals(\"BUTTON\")\n\t\t\t\t\to(root.firstChild.attributes[\"href\"].value).equals(prefix + \"/test\")\n\t\t\t\t\to(root.firstChild.hasAttribute(\"aria-disabled\")).equals(false)\n\t\t\t\t\to(root.firstChild.hasAttribute(\"disabled\")).equals(false)\n\t\t\t\t\to(root.firstChild.attributes[\"foo\"].value).equals(\"bar\")\n\t\t\t\t\to(root.firstChild.childNodes.length).equals(1)\n\t\t\t\t\to(root.firstChild.firstChild.nodeName).equals(\"#text\")\n\t\t\t\t\to(root.firstChild.firstChild.nodeValue).equals(\"text\")\n\t\t\t\t})\n\n\t\t\t\to(\"route.Link can render not disabled\", function() {\n\t\t\t\t\t$window = browserMock(env)\n\t\t\t\t\tvar render = coreRenderer($window)\n\t\t\t\t\troute = apiRouter(null, null)\n\t\t\t\t\troute.prefix = prefix\n\t\t\t\t\troot = $window.document.body\n\n\t\t\t\t\trender(root, m(route.Link, {href: \"/test\", disabled: false, foo: \"bar\"}, \"text\"))\n\n\t\t\t\t\to(root.childNodes.length).equals(1)\n\t\t\t\t\to(root.firstChild.nodeName).equals(\"A\")\n\t\t\t\t\to(root.firstChild.href).equals(prefix + \"/test\")\n\t\t\t\t\to(root.firstChild.hasAttribute(\"aria-disabled\")).equals(false)\n\t\t\t\t\to(root.firstChild.hasAttribute(\"disabled\")).equals(false)\n\t\t\t\t\to(root.firstChild.attributes[\"foo\"].value).equals(\"bar\")\n\t\t\t\t\to(root.firstChild.childNodes.length).equals(1)\n\t\t\t\t\to(root.firstChild.firstChild.nodeName).equals(\"#text\")\n\t\t\t\t\to(root.firstChild.firstChild.nodeValue).equals(\"text\")\n\t\t\t\t})\n\n\t\t\t\to(\"route.Link can render falsy disabled\", function() {\n\t\t\t\t\t$window = browserMock(env)\n\t\t\t\t\tvar render = coreRenderer($window)\n\t\t\t\t\troute = apiRouter(null, null)\n\t\t\t\t\troute.prefix = prefix\n\t\t\t\t\troot = $window.document.body\n\n\t\t\t\t\trender(root, m(route.Link, {href: \"/test\", disabled: 0, foo: \"bar\"}, \"text\"))\n\n\t\t\t\t\to(root.childNodes.length).equals(1)\n\t\t\t\t\to(root.firstChild.nodeName).equals(\"A\")\n\t\t\t\t\to(root.firstChild.href).equals(prefix + \"/test\")\n\t\t\t\t\to(root.firstChild.hasAttribute(\"aria-disabled\")).equals(false)\n\t\t\t\t\to(root.firstChild.hasAttribute(\"disabled\")).equals(false)\n\t\t\t\t\to(root.firstChild.attributes[\"foo\"].value).equals(\"bar\")\n\t\t\t\t\to(root.firstChild.childNodes.length).equals(1)\n\t\t\t\t\to(root.firstChild.firstChild.nodeName).equals(\"#text\")\n\t\t\t\t\to(root.firstChild.firstChild.nodeValue).equals(\"text\")\n\t\t\t\t})\n\n\t\t\t\to(\"route.Link can render disabled\", function() {\n\t\t\t\t\t$window = browserMock(env)\n\t\t\t\t\tvar render = coreRenderer($window)\n\t\t\t\t\troute = apiRouter(null, null)\n\t\t\t\t\troute.prefix = prefix\n\t\t\t\t\troot = $window.document.body\n\n\t\t\t\t\trender(root, m(route.Link, {href: \"/test\", disabled: true, foo: \"bar\"}, \"text\"))\n\n\t\t\t\t\to(root.childNodes.length).equals(1)\n\t\t\t\t\to(root.firstChild.nodeName).equals(\"A\")\n\t\t\t\t\to(root.firstChild.href).equals(\"\")\n\t\t\t\t\to(root.firstChild.attributes[\"aria-disabled\"].value).equals(\"true\")\n\t\t\t\t\to(root.firstChild.attributes[\"foo\"].value).equals(\"bar\")\n\t\t\t\t\to(root.firstChild.attributes[\"disabled\"].value).equals(\"\")\n\t\t\t\t\to(root.firstChild.childNodes.length).equals(1)\n\t\t\t\t\to(root.firstChild.firstChild.nodeName).equals(\"#text\")\n\t\t\t\t\to(root.firstChild.firstChild.nodeValue).equals(\"text\")\n\t\t\t\t})\n\n\t\t\t\to(\"route.Link can render truthy disabled\", function() {\n\t\t\t\t\t$window = browserMock(env)\n\t\t\t\t\tvar render = coreRenderer($window)\n\t\t\t\t\troute = apiRouter(null, null)\n\t\t\t\t\troute.prefix = prefix\n\t\t\t\t\troot = $window.document.body\n\n\t\t\t\t\trender(root, m(route.Link, {href: \"/test\", disabled: 1, foo: \"bar\"}, \"text\"))\n\n\t\t\t\t\to(root.childNodes.length).equals(1)\n\t\t\t\t\to(root.firstChild.nodeName).equals(\"A\")\n\t\t\t\t\to(root.firstChild.href).equals(\"\")\n\t\t\t\t\to(root.firstChild.attributes[\"aria-disabled\"].value).equals(\"true\")\n\t\t\t\t\to(root.firstChild.attributes[\"foo\"].value).equals(\"bar\")\n\t\t\t\t\to(root.firstChild.attributes[\"disabled\"].value).equals(\"\")\n\t\t\t\t\to(root.firstChild.childNodes.length).equals(1)\n\t\t\t\t\to(root.firstChild.firstChild.nodeName).equals(\"#text\")\n\t\t\t\t\to(root.firstChild.firstChild.nodeValue).equals(\"text\")\n\t\t\t\t})\n\n\t\t\t\to(\"route.Link doesn't redraw on wrong button\", function() {\n\t\t\t\t\tvar e = $window.document.createEvent(\"MouseEvents\")\n\n\t\t\t\t\te.initEvent(\"click\", true, true)\n\t\t\t\t\te.button = 10\n\n\t\t\t\t\t$window.location.href = prefix + \"/\"\n\t\t\t\t\troute(root, \"/\", {\n\t\t\t\t\t\t\"/\" : {\n\t\t\t\t\t\t\tview: lock(function() {\n\t\t\t\t\t\t\t\treturn m(route.Link, {href: \"/test\"})\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"/test\" : {\n\t\t\t\t\t\t\tview : lock(function() {\n\t\t\t\t\t\t\t\treturn m(\"div\")\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\n\t\t\t\t\tvar slash = prefix[0] === \"/\" ? \"\" : \"/\"\n\n\t\t\t\t\to($window.location.href).equals(env.protocol + \"//\" + (env.hostname === \"/\" ? \"\" : env.hostname) + slash + (prefix ? prefix + \"/\" : \"\"))\n\n\t\t\t\t\troot.firstChild.dispatchEvent(e)\n\t\t\t\t\tthrottleMock.fire()\n\t\t\t\t\to($window.location.href).equals(env.protocol + \"//\" + (env.hostname === \"/\" ? \"\" : env.hostname) + slash + (prefix ? prefix + \"/\" : \"\"))\n\t\t\t\t})\n\n\t\t\t\to(\"route.Link doesn't redraw on preventDefault\", function() {\n\t\t\t\t\tvar e = $window.document.createEvent(\"MouseEvents\")\n\n\t\t\t\t\te.initEvent(\"click\", true, true)\n\t\t\t\t\te.button = 0\n\n\t\t\t\t\t$window.location.href = prefix + \"/\"\n\t\t\t\t\troute(root, \"/\", {\n\t\t\t\t\t\t\"/\" : {\n\t\t\t\t\t\t\tview: lock(function() {\n\t\t\t\t\t\t\t\treturn m(route.Link, {\n\t\t\t\t\t\t\t\t\thref: \"/test\",\n\t\t\t\t\t\t\t\t\tonclick: function(e) {\n\t\t\t\t\t\t\t\t\t\te.preventDefault()\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"/test\" : {\n\t\t\t\t\t\t\tview : lock(function() {\n\t\t\t\t\t\t\t\treturn m(\"div\")\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\n\t\t\t\t\tvar slash = prefix[0] === \"/\" ? \"\" : \"/\"\n\n\t\t\t\t\to($window.location.href).equals(env.protocol + \"//\" + (env.hostname === \"/\" ? \"\" : env.hostname) + slash + (prefix ? prefix + \"/\" : \"\"))\n\n\t\t\t\t\troot.firstChild.dispatchEvent(e)\n\t\t\t\t\tthrottleMock.fire()\n\t\t\t\t\to($window.location.href).equals(env.protocol + \"//\" + (env.hostname === \"/\" ? \"\" : env.hostname) + slash + (prefix ? prefix + \"/\" : \"\"))\n\t\t\t\t})\n\n\t\t\t\to(\"route.Link doesn't redraw on preventDefault in handleEvent\", function() {\n\t\t\t\t\tvar e = $window.document.createEvent(\"MouseEvents\")\n\n\t\t\t\t\te.initEvent(\"click\", true, true)\n\t\t\t\t\te.button = 0\n\n\t\t\t\t\t$window.location.href = prefix + \"/\"\n\t\t\t\t\troute(root, \"/\", {\n\t\t\t\t\t\t\"/\" : {\n\t\t\t\t\t\t\tview: lock(function() {\n\t\t\t\t\t\t\t\treturn m(route.Link, {\n\t\t\t\t\t\t\t\t\thref: \"/test\",\n\t\t\t\t\t\t\t\t\tonclick: {\n\t\t\t\t\t\t\t\t\t\thandleEvent: function(e) {\n\t\t\t\t\t\t\t\t\t\t\te.preventDefault()\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"/test\" : {\n\t\t\t\t\t\t\tview : lock(function() {\n\t\t\t\t\t\t\t\treturn m(\"div\")\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\n\t\t\t\t\tvar slash = prefix[0] === \"/\" ? \"\" : \"/\"\n\n\t\t\t\t\to($window.location.href).equals(env.protocol + \"//\" + (env.hostname === \"/\" ? \"\" : env.hostname) + slash + (prefix ? prefix + \"/\" : \"\"))\n\n\t\t\t\t\troot.firstChild.dispatchEvent(e)\n\t\t\t\t\tthrottleMock.fire()\n\t\t\t\t\to($window.location.href).equals(env.protocol + \"//\" + (env.hostname === \"/\" ? \"\" : env.hostname) + slash + (prefix ? prefix + \"/\" : \"\"))\n\t\t\t\t})\n\n\t\t\t\to(\"route.Link doesn't redraw on return false\", function() {\n\t\t\t\t\tvar e = $window.document.createEvent(\"MouseEvents\")\n\n\t\t\t\t\te.initEvent(\"click\", true, true)\n\t\t\t\t\te.button = 0\n\n\t\t\t\t\t$window.location.href = prefix + \"/\"\n\t\t\t\t\troute(root, \"/\", {\n\t\t\t\t\t\t\"/\" : {\n\t\t\t\t\t\t\tview: lock(function() {\n\t\t\t\t\t\t\t\treturn m(route.Link, {\n\t\t\t\t\t\t\t\t\thref: \"/test\",\n\t\t\t\t\t\t\t\t\tonclick: function() {\n\t\t\t\t\t\t\t\t\t\treturn false\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"/test\" : {\n\t\t\t\t\t\t\tview : lock(function() {\n\t\t\t\t\t\t\t\treturn m(\"div\")\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\n\t\t\t\t\tvar slash = prefix[0] === \"/\" ? \"\" : \"/\"\n\n\t\t\t\t\to($window.location.href).equals(env.protocol + \"//\" + (env.hostname === \"/\" ? \"\" : env.hostname) + slash + (prefix ? prefix + \"/\" : \"\"))\n\n\t\t\t\t\troot.firstChild.dispatchEvent(e)\n\t\t\t\t\tthrottleMock.fire()\n\t\t\t\t\to($window.location.href).equals(env.protocol + \"//\" + (env.hostname === \"/\" ? \"\" : env.hostname) + slash + (prefix ? prefix + \"/\" : \"\"))\n\t\t\t\t})\n\n\t\t\t\to(\"accepts RouteResolver with onmatch that returns Component\", function() {\n\t\t\t\t\tvar matchCount = 0\n\t\t\t\t\tvar renderCount = 0\n\t\t\t\t\tvar Component = {\n\t\t\t\t\t\tview: lock(function() {\n\t\t\t\t\t\t\treturn m(\"span\")\n\t\t\t\t\t\t})\n\t\t\t\t\t}\n\n\t\t\t\t\tvar resolver = {\n\t\t\t\t\t\tonmatch: lock(function(args, requestedPath, route) {\n\t\t\t\t\t\t\tmatchCount++\n\n\t\t\t\t\t\t\to(args.id).equals(\"abc\")\n\t\t\t\t\t\t\to(requestedPath).equals(\"/abc\")\n\t\t\t\t\t\t\to(route).equals(\"/:id\")\n\t\t\t\t\t\t\to(this).equals(resolver)\n\t\t\t\t\t\t\treturn Component\n\t\t\t\t\t\t}),\n\t\t\t\t\t\trender: lock(function(vnode) {\n\t\t\t\t\t\t\trenderCount++\n\n\t\t\t\t\t\t\to(vnode.attrs.id).equals(\"abc\")\n\t\t\t\t\t\t\to(this).equals(resolver)\n\n\t\t\t\t\t\t\treturn vnode\n\t\t\t\t\t\t}),\n\t\t\t\t\t}\n\n\t\t\t\t\t$window.location.href = prefix + \"/abc\"\n\t\t\t\t\troute(root, \"/abc\", {\n\t\t\t\t\t\t\"/:id\" : resolver\n\t\t\t\t\t})\n\n\t\t\t\t\treturn waitCycles(1).then(function() {\n\t\t\t\t\t\to(matchCount).equals(1)\n\t\t\t\t\t\to(renderCount).equals(1)\n\t\t\t\t\t\to(root.firstChild.nodeName).equals(\"SPAN\")\n\t\t\t\t\t})\n\t\t\t\t})\n\n\t\t\t\to(\"accepts RouteResolver with onmatch that returns route.SKIP\", function() {\n\t\t\t\t\tvar match1Count = 0\n\t\t\t\t\tvar match2Count = 0\n\t\t\t\t\tvar render1 = o.spy()\n\t\t\t\t\tvar render2Count = 0\n\t\t\t\t\tvar Component = {\n\t\t\t\t\t\tview: lock(function() {\n\t\t\t\t\t\t\treturn m(\"span\")\n\t\t\t\t\t\t})\n\t\t\t\t\t}\n\n\t\t\t\t\tvar resolver1 = {\n\t\t\t\t\t\tonmatch: lock(function(args, requestedPath, key) {\n\t\t\t\t\t\t\tmatch1Count++\n\n\t\t\t\t\t\t\to(args.id1).equals(\"abc\")\n\t\t\t\t\t\t\to(requestedPath).equals(\"/abc\")\n\t\t\t\t\t\t\to(key).equals(\"/:id1\")\n\t\t\t\t\t\t\to(this).equals(resolver1)\n\t\t\t\t\t\t\treturn route.SKIP\n\t\t\t\t\t\t}),\n\t\t\t\t\t\trender: lock(render1),\n\t\t\t\t\t}\n\n\t\t\t\t\tvar resolver2 = {\n\t\t\t\t\t\tonmatch: function(args, requestedPath, key) {\n\t\t\t\t\t\t\tmatch2Count++\n\n\t\t\t\t\t\t\to(args.id2).equals(\"abc\")\n\t\t\t\t\t\t\to(requestedPath).equals(\"/abc\")\n\t\t\t\t\t\t\to(key).equals(\"/:id2\")\n\t\t\t\t\t\t\to(this).equals(resolver2)\n\t\t\t\t\t\t\treturn Component\n\t\t\t\t\t\t},\n\t\t\t\t\t\trender: function(vnode) {\n\t\t\t\t\t\t\trender2Count++\n\n\t\t\t\t\t\t\to(vnode.attrs.id2).equals(\"abc\")\n\t\t\t\t\t\t\to(this).equals(resolver2)\n\t\t\t\t\t\t\to(render1.callCount).equals(0)\n\n\t\t\t\t\t\t\treturn vnode\n\t\t\t\t\t\t},\n\t\t\t\t\t}\n\n\t\t\t\t\t$window.location.href = prefix + \"/abc\"\n\t\t\t\t\troute(root, \"/abc\", {\n\t\t\t\t\t\t\"/:id1\" : resolver1,\n\t\t\t\t\t\t\"/:id2\" : resolver2\n\t\t\t\t\t})\n\n\t\t\t\t\treturn waitCycles(4).then(function() {\n\t\t\t\t\t\to(match1Count).equals(1)\n\t\t\t\t\t\to(match2Count).equals(1)\n\t\t\t\t\t\to(render2Count).equals(1)\n\t\t\t\t\t\to(render1.callCount).equals(0)\n\t\t\t\t\t\to(root.firstChild.nodeName).equals(\"SPAN\")\n\t\t\t\t\t})\n\t\t\t\t})\n\n\t\t\t\to(\"accepts RouteResolver with onmatch that returns Promise<Component>\", function() {\n\t\t\t\t\tvar matchCount = 0\n\t\t\t\t\tvar renderCount = 0\n\t\t\t\t\tvar Component = {\n\t\t\t\t\t\tview: lock(function() {\n\t\t\t\t\t\t\treturn m(\"span\")\n\t\t\t\t\t\t})\n\t\t\t\t\t}\n\n\t\t\t\t\tvar resolver = {\n\t\t\t\t\t\tonmatch: lock(function(args, requestedPath, route) {\n\t\t\t\t\t\t\tmatchCount++\n\n\t\t\t\t\t\t\to(args.id).equals(\"abc\")\n\t\t\t\t\t\t\to(requestedPath).equals(\"/abc\")\n\t\t\t\t\t\t\to(route).equals(\"/:id\")\n\t\t\t\t\t\t\to(this).equals(resolver)\n\t\t\t\t\t\t\treturn Promise.resolve(Component)\n\t\t\t\t\t\t}),\n\t\t\t\t\t\trender: lock(function(vnode) {\n\t\t\t\t\t\t\trenderCount++\n\n\t\t\t\t\t\t\to(vnode.attrs.id).equals(\"abc\")\n\t\t\t\t\t\t\to(this).equals(resolver)\n\n\t\t\t\t\t\t\treturn vnode\n\t\t\t\t\t\t}),\n\t\t\t\t\t}\n\n\t\t\t\t\t$window.location.href = prefix + \"/abc\"\n\t\t\t\t\troute(root, \"/abc\", {\n\t\t\t\t\t\t\"/:id\" : resolver\n\t\t\t\t\t})\n\n\t\t\t\t\treturn waitCycles(10).then(function() {\n\t\t\t\t\t\to(matchCount).equals(1)\n\t\t\t\t\t\to(renderCount).equals(1)\n\t\t\t\t\t\to(root.firstChild.nodeName).equals(\"SPAN\")\n\t\t\t\t\t})\n\t\t\t\t})\n\n\t\t\t\to(\"accepts RouteResolver with onmatch that returns Promise<undefined>\", function() {\n\t\t\t\t\tvar matchCount = 0\n\t\t\t\t\tvar renderCount = 0\n\n\t\t\t\t\tvar resolver = {\n\t\t\t\t\t\tonmatch: lock(function(args, requestedPath, route) {\n\t\t\t\t\t\t\tmatchCount++\n\n\t\t\t\t\t\t\to(args.id).equals(\"abc\")\n\t\t\t\t\t\t\to(requestedPath).equals(\"/abc\")\n\t\t\t\t\t\t\to(route).equals(\"/:id\")\n\t\t\t\t\t\t\to(this).equals(resolver)\n\t\t\t\t\t\t\treturn Promise.resolve()\n\t\t\t\t\t\t}),\n\t\t\t\t\t\trender: lock(function(vnode) {\n\t\t\t\t\t\t\trenderCount++\n\n\t\t\t\t\t\t\to(vnode.attrs.id).equals(\"abc\")\n\t\t\t\t\t\t\to(this).equals(resolver)\n\n\t\t\t\t\t\t\treturn vnode\n\t\t\t\t\t\t}),\n\t\t\t\t\t}\n\n\t\t\t\t\t$window.location.href = prefix + \"/abc\"\n\t\t\t\t\troute(root, \"/abc\", {\n\t\t\t\t\t\t\"/:id\" : resolver\n\t\t\t\t\t})\n\n\t\t\t\t\treturn waitCycles(2).then(function() {\n\t\t\t\t\t\to(matchCount).equals(1)\n\t\t\t\t\t\to(renderCount).equals(1)\n\t\t\t\t\t\to(root.firstChild.nodeName).equals(\"DIV\")\n\t\t\t\t\t})\n\t\t\t\t})\n\n\t\t\t\to(\"accepts RouteResolver with onmatch that returns Promise<any>\", function() {\n\t\t\t\t\tvar matchCount = 0\n\t\t\t\t\tvar renderCount = 0\n\n\t\t\t\t\tvar resolver = {\n\t\t\t\t\t\tonmatch: lock(function(args, requestedPath, route) {\n\t\t\t\t\t\t\tmatchCount++\n\n\t\t\t\t\t\t\to(args.id).equals(\"abc\")\n\t\t\t\t\t\t\to(requestedPath).equals(\"/abc\")\n\t\t\t\t\t\t\to(route).equals(\"/:id\")\n\t\t\t\t\t\t\to(this).equals(resolver)\n\t\t\t\t\t\t\treturn Promise.resolve([])\n\t\t\t\t\t\t}),\n\t\t\t\t\t\trender: lock(function(vnode) {\n\t\t\t\t\t\t\trenderCount++\n\n\t\t\t\t\t\t\to(vnode.attrs.id).equals(\"abc\")\n\t\t\t\t\t\t\to(this).equals(resolver)\n\n\t\t\t\t\t\t\treturn vnode\n\t\t\t\t\t\t}),\n\t\t\t\t\t}\n\n\t\t\t\t\t$window.location.href = prefix + \"/abc\"\n\t\t\t\t\troute(root, \"/abc\", {\n\t\t\t\t\t\t\"/:id\" : resolver\n\t\t\t\t\t})\n\n\t\t\t\t\treturn waitCycles(2).then(function() {\n\t\t\t\t\t\to(matchCount).equals(1)\n\t\t\t\t\t\to(renderCount).equals(1)\n\t\t\t\t\t\to(root.firstChild.nodeName).equals(\"DIV\")\n\t\t\t\t\t})\n\t\t\t\t})\n\n\t\t\t\to(\"accepts RouteResolver with onmatch that returns rejected Promise\", function() {\n\t\t\t\t\tvar matchCount = 0\n\t\t\t\t\tvar renderCount = 0\n\t\t\t\t\tvar spy = o.spy()\n\t\t\t\t\tvar error = new Error(\"error\")\n\t\t\t\t\tvar errorSpy = console.error = o.spy()\n\n\t\t\t\t\tvar resolver = {\n\t\t\t\t\t\tonmatch: lock(function() {\n\t\t\t\t\t\t\tmatchCount++\n\t\t\t\t\t\t\treturn Promise.reject(error)\n\t\t\t\t\t\t}),\n\t\t\t\t\t\trender: lock(function(vnode) {\n\t\t\t\t\t\t\trenderCount++\n\t\t\t\t\t\t\treturn vnode\n\t\t\t\t\t\t}),\n\t\t\t\t\t}\n\n\t\t\t\t\t$window.location.href = prefix + \"/test/1\"\n\t\t\t\t\troute(root, \"/default\", {\n\t\t\t\t\t\t\"/default\" : {view: spy},\n\t\t\t\t\t\t\"/test/:id\" : resolver\n\t\t\t\t\t})\n\n\t\t\t\t\treturn waitCycles(3).then(function() {\n\t\t\t\t\t\to(matchCount).equals(1)\n\t\t\t\t\t\to(renderCount).equals(0)\n\t\t\t\t\t\to(spy.callCount).equals(1)\n\t\t\t\t\t\to(errorSpy.callCount).equals(1)\n\t\t\t\t\t\to(errorSpy.args[0]).equals(error)\n\t\t\t\t\t})\n\t\t\t\t})\n\n\t\t\t\to(\"accepts RouteResolver without `render` method as payload\", function() {\n\t\t\t\t\tvar matchCount = 0\n\t\t\t\t\tvar Component = {\n\t\t\t\t\t\tview: lock(function() {\n\t\t\t\t\t\t\treturn m(\"div\")\n\t\t\t\t\t\t})\n\t\t\t\t\t}\n\n\t\t\t\t\t$window.location.href = prefix + \"/abc\"\n\t\t\t\t\troute(root, \"/abc\", {\n\t\t\t\t\t\t\"/:id\" : {\n\t\t\t\t\t\t\tonmatch: lock(function(args, requestedPath, route) {\n\t\t\t\t\t\t\t\tmatchCount++\n\n\t\t\t\t\t\t\t\to(args.id).equals(\"abc\")\n\t\t\t\t\t\t\t\to(requestedPath).equals(\"/abc\")\n\t\t\t\t\t\t\t\to(route).equals(\"/:id\")\n\n\t\t\t\t\t\t\t\treturn Component\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t},\n\t\t\t\t\t})\n\n\t\t\t\t\treturn waitCycles(2).then(function() {\n\t\t\t\t\t\to(matchCount).equals(1)\n\t\t\t\t\t\to(root.firstChild.nodeName).equals(\"DIV\")\n\t\t\t\t\t})\n\t\t\t\t})\n\n\t\t\t\to(\"changing `key` param resets the component\", function(){\n\t\t\t\t\tvar oninit = o.spy()\n\t\t\t\t\tvar Component = {\n\t\t\t\t\t\toninit: oninit,\n\t\t\t\t\t\tview: lock(function() {\n\t\t\t\t\t\t\treturn m(\"div\")\n\t\t\t\t\t\t})\n\t\t\t\t\t}\n\t\t\t\t\t$window.location.href = prefix + \"/abc\"\n\t\t\t\t\troute(root, \"/abc\", {\n\t\t\t\t\t\t\"/:key\": Component,\n\t\t\t\t\t})\n\t\t\t\t\treturn waitCycles(1).then(function() {\n\t\t\t\t\t\to(oninit.callCount).equals(1)\n\t\t\t\t\t\troute.set(\"/def\")\n\t\t\t\t\t\treturn waitCycles(1).then(function() {\n\t\t\t\t\t\t\tthrottleMock.fire()\n\t\t\t\t\t\t\to(oninit.callCount).equals(2)\n\t\t\t\t\t\t})\n\t\t\t\t\t})\n\t\t\t\t})\n\n\t\t\t\to(\"accepts RouteResolver without `onmatch` method as payload\", function() {\n\t\t\t\t\tvar renderCount = 0\n\t\t\t\t\tvar Component = {\n\t\t\t\t\t\tview: lock(function() {\n\t\t\t\t\t\t\treturn m(\"div\")\n\t\t\t\t\t\t})\n\t\t\t\t\t}\n\n\t\t\t\t\t$window.location.href = prefix + \"/abc\"\n\t\t\t\t\troute(root, \"/abc\", {\n\t\t\t\t\t\t\"/:id\" : {\n\t\t\t\t\t\t\trender: lock(function(vnode) {\n\t\t\t\t\t\t\t\trenderCount++\n\n\t\t\t\t\t\t\t\to(vnode.attrs.id).equals(\"abc\")\n\n\t\t\t\t\t\t\t\treturn m(Component)\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t},\n\t\t\t\t\t})\n\n\t\t\t\t\to(root.firstChild.nodeName).equals(\"DIV\")\n\t\t\t\t\to(renderCount).equals(1)\n\t\t\t\t})\n\n\t\t\t\to(\"RouteResolver `render` does not have component semantics\", function() {\n\t\t\t\t\t$window.location.href = prefix + \"/a\"\n\t\t\t\t\troute(root, \"/a\", {\n\t\t\t\t\t\t\"/a\" : {\n\t\t\t\t\t\t\trender: lock(function() {\n\t\t\t\t\t\t\t\treturn m(\"div\", m(\"p\"))\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"/b\" : {\n\t\t\t\t\t\t\trender: lock(function() {\n\t\t\t\t\t\t\t\treturn m(\"div\", m(\"a\"))\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t},\n\t\t\t\t\t})\n\n\t\t\t\t\tvar dom = root.firstChild\n\t\t\t\t\tvar child = dom.firstChild\n\n\t\t\t\t\to(root.firstChild.nodeName).equals(\"DIV\")\n\n\t\t\t\t\troute.set(\"/b\")\n\n\t\t\t\t\treturn waitCycles(1).then(function() {\n\t\t\t\t\t\tthrottleMock.fire()\n\n\t\t\t\t\t\to(root.firstChild).equals(dom)\n\t\t\t\t\t\to(root.firstChild.firstChild).notEquals(child)\n\t\t\t\t\t})\n\t\t\t\t})\n\n\t\t\t\to(\"calls onmatch and view correct number of times\", function() {\n\t\t\t\t\tvar matchCount = 0\n\t\t\t\t\tvar renderCount = 0\n\t\t\t\t\tvar Component = {\n\t\t\t\t\t\tview: lock(function() {\n\t\t\t\t\t\t\treturn m(\"div\")\n\t\t\t\t\t\t})\n\t\t\t\t\t}\n\n\t\t\t\t\t$window.location.href = prefix + \"/\"\n\t\t\t\t\troute(root, \"/\", {\n\t\t\t\t\t\t\"/\" : {\n\t\t\t\t\t\t\tonmatch: lock(function() {\n\t\t\t\t\t\t\t\tmatchCount++\n\t\t\t\t\t\t\t\treturn Component\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\trender: lock(function(vnode) {\n\t\t\t\t\t\t\t\trenderCount++\n\t\t\t\t\t\t\t\treturn vnode\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t},\n\t\t\t\t\t})\n\n\t\t\t\t\treturn waitCycles(1).then(function() {\n\t\t\t\t\t\to(matchCount).equals(1)\n\t\t\t\t\t\to(renderCount).equals(1)\n\n\t\t\t\t\t\tmountRedraw.redraw()\n\t\t\t\t\t\tthrottleMock.fire()\n\n\t\t\t\t\t\to(matchCount).equals(1)\n\t\t\t\t\t\to(renderCount).equals(2)\n\t\t\t\t\t})\n\t\t\t\t})\n\n\t\t\t\to(\"calls onmatch and view correct number of times when not onmatch returns undefined\", function() {\n\t\t\t\t\tvar matchCount = 0\n\t\t\t\t\tvar renderCount = 0\n\t\t\t\t\tvar Component = {\n\t\t\t\t\t\tview: lock(function() {\n\t\t\t\t\t\t\treturn m(\"div\")\n\t\t\t\t\t\t})\n\t\t\t\t\t}\n\n\t\t\t\t\t$window.location.href = prefix + \"/\"\n\t\t\t\t\troute(root, \"/\", {\n\t\t\t\t\t\t\"/\" : {\n\t\t\t\t\t\t\tonmatch: lock(function() {\n\t\t\t\t\t\t\t\tmatchCount++\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\trender: lock(function() {\n\t\t\t\t\t\t\t\trenderCount++\n\t\t\t\t\t\t\t\treturn m(Component)\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t},\n\t\t\t\t\t})\n\n\t\t\t\t\treturn waitCycles(2).then(function() {\n\t\t\t\t\t\to(matchCount).equals(1)\n\t\t\t\t\t\to(renderCount).equals(1)\n\n\t\t\t\t\t\tmountRedraw.redraw()\n\t\t\t\t\t\tthrottleMock.fire()\n\n\t\t\t\t\t\to(matchCount).equals(1)\n\t\t\t\t\t\to(renderCount).equals(2)\n\t\t\t\t\t})\n\t\t\t\t})\n\n\t\t\t\to(\"onmatch can redirect to another route\", function() {\n\t\t\t\t\tvar redirected = false\n\t\t\t\t\tvar render = o.spy()\n\n\t\t\t\t\t$window.location.href = prefix + \"/a\"\n\t\t\t\t\troute(root, \"/a\", {\n\t\t\t\t\t\t\"/a\" : {\n\t\t\t\t\t\t\tonmatch: lock(function() {\n\t\t\t\t\t\t\t\troute.set(\"/b\")\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\trender: lock(render)\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"/b\" : {\n\t\t\t\t\t\t\tview: lock(function() {\n\t\t\t\t\t\t\t\tredirected = true\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\n\t\t\t\t\treturn waitCycles(2).then(function() {\n\t\t\t\t\t\to(render.callCount).equals(0)\n\t\t\t\t\t\to(redirected).equals(true)\n\t\t\t\t\t})\n\t\t\t\t})\n\n\t\t\t\to(\"onmatch can redirect to another route that has RouteResolver with only onmatch\", function() {\n\t\t\t\t\tvar redirected = false\n\t\t\t\t\tvar render = o.spy()\n\t\t\t\t\tvar view = o.spy(function() {return m(\"div\")})\n\n\t\t\t\t\t$window.location.href = prefix + \"/a\"\n\t\t\t\t\troute(root, \"/a\", {\n\t\t\t\t\t\t\"/a\" : {\n\t\t\t\t\t\t\tonmatch: lock(function() {\n\t\t\t\t\t\t\t\troute.set(\"/b\", {}, {state: {a: 5}})\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\trender: lock(render)\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"/b\" : {\n\t\t\t\t\t\t\tonmatch: lock(function() {\n\t\t\t\t\t\t\t\tredirected = true\n\t\t\t\t\t\t\t\treturn {view: lock(view)}\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\n\t\t\t\t\treturn waitCycles(3).then(function() {\n\t\t\t\t\t\to(render.callCount).equals(0)\n\t\t\t\t\t\to(redirected).equals(true)\n\t\t\t\t\t\to(view.callCount).equals(1)\n\t\t\t\t\t\to(root.childNodes.length).equals(1)\n\t\t\t\t\t\to(root.firstChild.nodeName).equals(\"DIV\")\n\t\t\t\t\t\to($window.history.state).deepEquals({a: 5})\n\t\t\t\t\t})\n\t\t\t\t})\n\n\t\t\t\to(\"onmatch can redirect to another route that has RouteResolver with only render\", function() {\n\t\t\t\t\tvar redirected = false\n\t\t\t\t\tvar render = o.spy()\n\n\t\t\t\t\t$window.location.href = prefix + \"/a\"\n\t\t\t\t\troute(root, \"/a\", {\n\t\t\t\t\t\t\"/a\" : {\n\t\t\t\t\t\t\tonmatch: lock(function() {\n\t\t\t\t\t\t\t\troute.set(\"/b\")\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\trender: lock(render)\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"/b\" : {\n\t\t\t\t\t\t\trender: lock(function(){\n\t\t\t\t\t\t\t\tredirected = true\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\n\t\t\t\t\treturn waitCycles(2).then(function() {\n\t\t\t\t\t\to(render.callCount).equals(0)\n\t\t\t\t\t\to(redirected).equals(true)\n\t\t\t\t\t})\n\t\t\t\t})\n\n\t\t\t\to(\"onmatch can redirect to another route that has RouteResolver whose onmatch resolves asynchronously\", function() {\n\t\t\t\t\tvar redirected = false\n\t\t\t\t\tvar render = o.spy()\n\t\t\t\t\tvar view = o.spy()\n\n\t\t\t\t\t$window.location.href = prefix + \"/a\"\n\t\t\t\t\troute(root, \"/a\", {\n\t\t\t\t\t\t\"/a\" : {\n\t\t\t\t\t\t\tonmatch: lock(function() {\n\t\t\t\t\t\t\t\troute.set(\"/b\")\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\trender: lock(render)\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"/b\" : {\n\t\t\t\t\t\t\tonmatch: lock(function() {\n\t\t\t\t\t\t\t\tredirected = true\n\t\t\t\t\t\t\t\treturn waitCycles(1).then(function(){\n\t\t\t\t\t\t\t\t\treturn {view: view}\n\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\n\t\t\t\t\treturn waitCycles(6).then(function() {\n\t\t\t\t\t\to(render.callCount).equals(0)\n\t\t\t\t\t\to(redirected).equals(true)\n\t\t\t\t\t\to(view.callCount).equals(1)\n\t\t\t\t\t})\n\t\t\t\t})\n\n\t\t\t\to(\"onmatch can redirect to another route asynchronously\", function() {\n\t\t\t\t\tvar redirected = false\n\t\t\t\t\tvar render = o.spy()\n\t\t\t\t\tvar view = o.spy()\n\n\t\t\t\t\t$window.location.href = prefix + \"/a\"\n\t\t\t\t\troute(root, \"/a\", {\n\t\t\t\t\t\t\"/a\" : {\n\t\t\t\t\t\t\tonmatch: lock(function() {\n\t\t\t\t\t\t\t\twaitCycles(1).then(function() {route.set(\"/b\")})\n\t\t\t\t\t\t\t\treturn new Promise(function() {})\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\trender: lock(render)\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"/b\" : {\n\t\t\t\t\t\t\tonmatch: lock(function() {\n\t\t\t\t\t\t\t\tredirected = true\n\t\t\t\t\t\t\t\treturn {view: lock(view)}\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\n\t\t\t\t\treturn waitCycles(5).then(function() {\n\t\t\t\t\t\to(render.callCount).equals(0)\n\t\t\t\t\t\to(redirected).equals(true)\n\t\t\t\t\t\to(view.callCount).equals(1)\n\t\t\t\t\t})\n\t\t\t\t})\n\n\t\t\t\to(\"onmatch can redirect with window.history.back()\", function() {\n\n\t\t\t\t\tvar render = o.spy()\n\t\t\t\t\tvar component = {view: o.spy()}\n\n\t\t\t\t\t$window.location.href = prefix + \"/a\"\n\t\t\t\t\troute(root, \"/a\", {\n\t\t\t\t\t\t\"/a\" : {\n\t\t\t\t\t\t\tonmatch: lock(function() {\n\t\t\t\t\t\t\t\treturn component\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\trender: lock(function(vnode) {\n\t\t\t\t\t\t\t\treturn vnode\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"/b\" : {\n\t\t\t\t\t\t\tonmatch: lock(function() {\n\t\t\t\t\t\t\t\t$window.history.back()\n\t\t\t\t\t\t\t\treturn new Promise(function() {})\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\trender: lock(render)\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\n\t\t\t\t\treturn waitCycles(2).then(function() {\n\t\t\t\t\t\tthrottleMock.fire()\n\n\t\t\t\t\t\troute.set(\"/b\")\n\t\t\t\t\t\to(render.callCount).equals(0)\n\t\t\t\t\t\to(component.view.callCount).equals(1)\n\n\t\t\t\t\t\treturn waitCycles(4).then(function() {\n\t\t\t\t\t\t\tthrottleMock.fire()\n\n\t\t\t\t\t\t\to(render.callCount).equals(0)\n\t\t\t\t\t\t\to(component.view.callCount).equals(2)\n\t\t\t\t\t\t})\n\t\t\t\t\t})\n\t\t\t\t})\n\n\t\t\t\to(\"onmatch can redirect to a non-existent route that defaults to a RouteResolver with onmatch\", function() {\n\t\t\t\t\tvar redirected = false\n\t\t\t\t\tvar render = o.spy()\n\n\t\t\t\t\t$window.location.href = prefix + \"/a\"\n\t\t\t\t\troute(root, \"/b\", {\n\t\t\t\t\t\t\"/a\" : {\n\t\t\t\t\t\t\tonmatch: lock(function() {\n\t\t\t\t\t\t\t\troute.set(\"/c\")\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\trender: lock(render)\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"/b\" : {\n\t\t\t\t\t\t\tonmatch: lock(function(){\n\t\t\t\t\t\t\t\tredirected = true\n\t\t\t\t\t\t\t\treturn {view: lock(function() {})}\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\n\t\t\t\t\treturn waitCycles(3).then(function() {\n\t\t\t\t\t\to(render.callCount).equals(0)\n\t\t\t\t\t\to(redirected).equals(true)\n\t\t\t\t\t})\n\t\t\t\t})\n\n\t\t\t\to(\"onmatch can redirect to a non-existent route that defaults to a RouteResolver with render\", function() {\n\t\t\t\t\tvar redirected = false\n\t\t\t\t\tvar render = o.spy()\n\n\t\t\t\t\t$window.location.href = prefix + \"/a\"\n\t\t\t\t\troute(root, \"/b\", {\n\t\t\t\t\t\t\"/a\" : {\n\t\t\t\t\t\t\tonmatch: lock(function() {\n\t\t\t\t\t\t\t\troute.set(\"/c\")\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\trender: lock(render)\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"/b\" : {\n\t\t\t\t\t\t\trender: lock(function(){\n\t\t\t\t\t\t\t\tredirected = true\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\n\t\t\t\t\treturn waitCycles(3).then(function() {\n\t\t\t\t\t\to(render.callCount).equals(0)\n\t\t\t\t\t\to(redirected).equals(true)\n\t\t\t\t\t})\n\t\t\t\t})\n\n\t\t\t\to(\"onmatch can redirect to a non-existent route that defaults to a component\", function() {\n\t\t\t\t\tvar redirected = false\n\t\t\t\t\tvar render = o.spy()\n\n\t\t\t\t\t$window.location.href = prefix + \"/a\"\n\t\t\t\t\troute(root, \"/b\", {\n\t\t\t\t\t\t\"/a\" : {\n\t\t\t\t\t\t\tonmatch: lock(function() {\n\t\t\t\t\t\t\t\troute.set(\"/c\")\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\trender: lock(render)\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"/b\" : {\n\t\t\t\t\t\t\tview: lock(function(){\n\t\t\t\t\t\t\t\tredirected = true\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\n\t\t\t\t\treturn waitCycles(3).then(function() {\n\t\t\t\t\t\to(render.callCount).equals(0)\n\t\t\t\t\t\to(redirected).equals(true)\n\t\t\t\t\t})\n\t\t\t\t})\n\n\t\t\t\to(\"the previous view redraws while onmatch resolution is pending (#1268)\", function() {\n\t\t\t\t\tvar view = o.spy()\n\t\t\t\t\tvar onmatch = o.spy(function() {\n\t\t\t\t\t\treturn new Promise(function() {})\n\t\t\t\t\t})\n\n\t\t\t\t\t$window.location.href = prefix + \"/a\"\n\t\t\t\t\troute(root, \"/\", {\n\t\t\t\t\t\t\"/a\": {view: lock(view)},\n\t\t\t\t\t\t\"/b\": {onmatch: lock(onmatch)},\n\t\t\t\t\t\t\"/\": {view: lock(function() {})}\n\t\t\t\t\t})\n\n\t\t\t\t\to(view.callCount).equals(1)\n\t\t\t\t\to(onmatch.callCount).equals(0)\n\n\t\t\t\t\troute.set(\"/b\")\n\n\t\t\t\t\treturn waitCycles(1).then(function() {\n\t\t\t\t\t\to(view.callCount).equals(1)\n\t\t\t\t\t\to(onmatch.callCount).equals(1)\n\n\t\t\t\t\t\tmountRedraw.redraw()\n\t\t\t\t\t\tthrottleMock.fire()\n\n\t\t\t\t\t\to(view.callCount).equals(2)\n\t\t\t\t\t\to(onmatch.callCount).equals(1)\n\t\t\t\t\t})\n\t\t\t\t})\n\n\t\t\t\to(\"when two async routes are racing, the last one set cancels the finalization of the first\", function(done) {\n\t\t\t\t\tvar renderA = o.spy()\n\t\t\t\t\tvar renderB = o.spy()\n\t\t\t\t\tvar onmatchA = o.spy(function(){\n\t\t\t\t\t\treturn waitCycles(3)\n\t\t\t\t\t})\n\n\t\t\t\t\t$window.location.href = prefix + \"/a\"\n\t\t\t\t\troute(root, \"/a\", {\n\t\t\t\t\t\t\"/a\": {\n\t\t\t\t\t\t\tonmatch: lock(onmatchA),\n\t\t\t\t\t\t\trender: lock(renderA)\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"/b\": {\n\t\t\t\t\t\t\tonmatch: lock(function(){\n\t\t\t\t\t\t\t\tvar p = new Promise(function(fulfill) {\n\t\t\t\t\t\t\t\t\to(onmatchA.callCount).equals(1)\n\t\t\t\t\t\t\t\t\to(renderA.callCount).equals(0)\n\t\t\t\t\t\t\t\t\to(renderB.callCount).equals(0)\n\n\t\t\t\t\t\t\t\t\twaitCycles(3).then(function(){\n\t\t\t\t\t\t\t\t\t\to(onmatchA.callCount).equals(1)\n\t\t\t\t\t\t\t\t\t\to(renderA.callCount).equals(0)\n\t\t\t\t\t\t\t\t\t\to(renderB.callCount).equals(0)\n\n\t\t\t\t\t\t\t\t\t\tfulfill()\n\t\t\t\t\t\t\t\t\t\treturn p\n\t\t\t\t\t\t\t\t\t}).then(function(){\n\t\t\t\t\t\t\t\t\t\treturn waitCycles(1)\n\t\t\t\t\t\t\t\t\t}).then(function(){\n\t\t\t\t\t\t\t\t\t\to(onmatchA.callCount).equals(1)\n\t\t\t\t\t\t\t\t\t\to(renderA.callCount).equals(0)\n\t\t\t\t\t\t\t\t\t\to(renderB.callCount).equals(1)\n\t\t\t\t\t\t\t\t\t}).then(done, done)\n\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t\treturn p\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\trender: lock(renderB)\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\n\t\t\t\t\twaitCycles(1).then(lock(function() {\n\t\t\t\t\t\to(onmatchA.callCount).equals(1)\n\t\t\t\t\t\to(renderA.callCount).equals(0)\n\t\t\t\t\t\to(renderB.callCount).equals(0)\n\t\t\t\t\t\troute.set(\"/b\")\n\t\t\t\t\t\to(onmatchA.callCount).equals(1)\n\t\t\t\t\t\to(renderA.callCount).equals(0)\n\t\t\t\t\t\to(renderB.callCount).equals(0)\n\t\t\t\t\t}))\n\t\t\t\t})\n\n\t\t\t\to(\"m.route.set(m.route.get()) re-runs the resolution logic (#1180)\", function(){\n\t\t\t\t\tvar onmatch = o.spy()\n\t\t\t\t\tvar render = o.spy(function() {return m(\"div\")})\n\n\t\t\t\t\t$window.location.href = prefix + \"/\"\n\t\t\t\t\troute(root, \"/\", {\n\t\t\t\t\t\t\"/\": {\n\t\t\t\t\t\t\tonmatch: lock(onmatch),\n\t\t\t\t\t\t\trender: lock(render)\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\n\t\t\t\t\treturn waitCycles(1).then(function() {\n\t\t\t\t\t\tthrottleMock.fire()\n\n\t\t\t\t\t\to(onmatch.callCount).equals(1)\n\t\t\t\t\t\to(render.callCount).equals(1)\n\n\t\t\t\t\t\troute.set(route.get())\n\n\t\t\t\t\t\treturn waitCycles(2).then(function() {\n\t\t\t\t\t\t\tthrottleMock.fire()\n\n\t\t\t\t\t\t\to(onmatch.callCount).equals(2)\n\t\t\t\t\t\t\to(render.callCount).equals(2)\n\t\t\t\t\t\t})\n\t\t\t\t\t})\n\t\t\t\t})\n\n\t\t\t\to(\"m.route.get() returns the last fully resolved route (#1276)\", function(){\n\t\t\t\t\t$window.location.href = prefix + \"/\"\n\n\t\t\t\t\troute(root, \"/\", {\n\t\t\t\t\t\t\"/\": {view: lock(function() {})},\n\t\t\t\t\t\t\"/2\": {\n\t\t\t\t\t\t\tonmatch: lock(function() {\n\t\t\t\t\t\t\t\treturn new Promise(function() {})\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\n\n\t\t\t\t\to(route.get()).equals(\"/\")\n\n\t\t\t\t\troute.set(\"/2\")\n\n\t\t\t\t\treturn waitCycles(1).then(function() {\n\t\t\t\t\t\to(route.get()).equals(\"/\")\n\t\t\t\t\t})\n\t\t\t\t})\n\n\t\t\t\to(\"routing with RouteResolver works more than once\", function() {\n\t\t\t\t\t$window.location.href = prefix + \"/a\"\n\t\t\t\t\troute(root, \"/a\", {\n\t\t\t\t\t\t\"/a\": {\n\t\t\t\t\t\t\trender: lock(function() {\n\t\t\t\t\t\t\t\treturn m(\"a\", \"a\")\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"/b\": {\n\t\t\t\t\t\t\trender: lock(function() {\n\t\t\t\t\t\t\t\treturn m(\"b\", \"b\")\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\n\t\t\t\t\troute.set(\"/b\")\n\n\t\t\t\t\treturn waitCycles(1).then(function() {\n\t\t\t\t\t\tthrottleMock.fire()\n\n\t\t\t\t\t\to(root.firstChild.nodeName).equals(\"B\")\n\n\t\t\t\t\t\troute.set(\"/a\")\n\n\t\t\t\t\t\treturn waitCycles(1).then(function() {\n\t\t\t\t\t\t\tthrottleMock.fire()\n\n\t\t\t\t\t\t\to(root.firstChild.nodeName).equals(\"A\")\n\t\t\t\t\t\t})\n\t\t\t\t\t})\n\t\t\t\t})\n\n\t\t\t\to(\"calling route.set invalidates pending onmatch resolution\", function() {\n\t\t\t\t\tvar rendered = false\n\t\t\t\t\tvar resolved\n\t\t\t\t\t$window.location.href = prefix + \"/a\"\n\t\t\t\t\troute(root, \"/a\", {\n\t\t\t\t\t\t\"/a\": {\n\t\t\t\t\t\t\tonmatch: lock(function() {\n\t\t\t\t\t\t\t\treturn waitCycles(2).then(function() {\n\t\t\t\t\t\t\t\t\treturn {view: lock(function() {rendered = true})}\n\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\trender: lock(function() {\n\t\t\t\t\t\t\t\trendered = true\n\t\t\t\t\t\t\t\tresolved = \"a\"\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"/b\": {\n\t\t\t\t\t\t\tview: lock(function() {\n\t\t\t\t\t\t\t\tresolved = \"b\"\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\n\t\t\t\t\troute.set(\"/b\")\n\n\t\t\t\t\treturn waitCycles(1).then(function() {\n\t\t\t\t\t\to(rendered).equals(false)\n\t\t\t\t\t\to(resolved).equals(\"b\")\n\n\t\t\t\t\t\treturn waitCycles(1).then(function() {\n\t\t\t\t\t\t\to(rendered).equals(false)\n\t\t\t\t\t\t\to(resolved).equals(\"b\")\n\t\t\t\t\t\t})\n\t\t\t\t\t})\n\t\t\t\t})\n\n\t\t\t\to(\"route changes activate onbeforeremove\", function() {\n\t\t\t\t\tvar spy = o.spy()\n\n\t\t\t\t\t$window.location.href = prefix + \"/a\"\n\t\t\t\t\troute(root, \"/a\", {\n\t\t\t\t\t\t\"/a\": {\n\t\t\t\t\t\t\tonbeforeremove: lock(spy),\n\t\t\t\t\t\t\tview: lock(function() {})\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"/b\": {\n\t\t\t\t\t\t\tview: lock(function() {})\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\n\t\t\t\t\troute.set(\"/b\")\n\n\t\t\t\t\t// setting the route is asynchronous\n\t\t\t\t\treturn waitCycles(1).then(function() {\n\t\t\t\t\t\tthrottleMock.fire()\n\t\t\t\t\t\to(spy.callCount).equals(1)\n\t\t\t\t\t})\n\t\t\t\t})\n\n\t\t\t\to(\"asynchronous route.set in onmatch works\", function() {\n\t\t\t\t\tvar rendered = false, resolved\n\t\t\t\t\troute(root, \"/a\", {\n\t\t\t\t\t\t\"/a\": {\n\t\t\t\t\t\t\tonmatch: lock(function() {\n\t\t\t\t\t\t\t\treturn Promise.resolve().then(lock(function() {\n\t\t\t\t\t\t\t\t\troute.set(\"/b\")\n\t\t\t\t\t\t\t\t}))\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\trender: lock(function() {\n\t\t\t\t\t\t\t\trendered = true\n\t\t\t\t\t\t\t\tresolved = \"a\"\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"/b\": {\n\t\t\t\t\t\t\tview: lock(function() {\n\t\t\t\t\t\t\t\tresolved = \"b\"\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t},\n\t\t\t\t\t})\n\n\t\t\t\t\t// tick for popstate for /a\n\t\t\t\t\t// tick for onmatch\n\t\t\t\t\t// tick for promise in onmatch\n\t\t\t\t\t// tick for onpopstate for /b\n\t\t\t\t\treturn waitCycles(4).then(function() {\n\t\t\t\t\t\to(rendered).equals(false)\n\t\t\t\t\t\to(resolved).equals(\"b\")\n\t\t\t\t\t})\n\t\t\t\t})\n\n\t\t\t\to(\"throttles\", function() {\n\t\t\t\t\tvar i = 0\n\t\t\t\t\t$window.location.href = prefix + \"/\"\n\t\t\t\t\troute(root, \"/\", {\n\t\t\t\t\t\t\"/\": {view: lock(function() {i++})}\n\t\t\t\t\t})\n\t\t\t\t\tvar before = i\n\n\t\t\t\t\tmountRedraw.redraw()\n\t\t\t\t\tmountRedraw.redraw()\n\t\t\t\t\tmountRedraw.redraw()\n\t\t\t\t\tmountRedraw.redraw()\n\t\t\t\t\tvar after = i\n\n\t\t\t\t\tthrottleMock.fire()\n\n\t\t\t\t\to(before).equals(1) // routes synchronously\n\t\t\t\t\to(after).equals(1) // redraws asynchronously\n\t\t\t\t\to(i).equals(2)\n\t\t\t\t})\n\n\t\t\t\to(\"m.route.param is available outside of route handlers\", function() {\n\t\t\t\t\t$window.location.href = prefix + \"/\"\n\n\t\t\t\t\troute(root, \"/1\", {\n\t\t\t\t\t\t\"/:id\" : {\n\t\t\t\t\t\t\tview : lock(function() {\n\t\t\t\t\t\t\t\to(route.param(\"id\")).equals(\"1\")\n\n\t\t\t\t\t\t\t\treturn m(\"div\")\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\n\t\t\t\t\to(route.param(\"id\")).equals(undefined);\n\t\t\t\t\to(route.param()).deepEquals(undefined);\n\n\t\t\t\t\treturn waitCycles(1).then(function() {\n\t\t\t\t\t\to(route.param(\"id\")).equals(\"1\")\n\t\t\t\t\t\to(route.param()).deepEquals({id:\"1\"})\n\t\t\t\t\t})\n\t\t\t\t})\n\n\t\t\t\to(\"route component is mounted after the route is initially resolved (synchronous)\", function() {\n\t\t\t\t\tvar Component = {\n\t\t\t\t\t\tview: lock(function() {\n\t\t\t\t\t\t\t// the first rendered vnode is cleared\n\t\t\t\t\t\t\to(root.childNodes.length).equals(0)\n\t\t\t\t\t\t\treturn m(\"span\")\n\t\t\t\t\t\t})\n\t\t\t\t\t}\n\n\t\t\t\t\t// initial root node\n\t\t\t\t\troot.textContent = \"foo\"\n\t\t\t\t\to(root.childNodes.length).equals(1)\n\t\t\t\t\to(root.childNodes[0].nodeName).equals(\"#text\")\n\t\t\t\t\to(root.childNodes[0].nodeValue).equals(\"foo\")\n\n\t\t\t\t\t// render another vnode first\n\t\t\t\t\tvar render = coreRenderer($window)\n\t\t\t\t\tvar vnode = m(\"a\", \"loading...\")\n\t\t\t\t\trender(root, vnode)\n\t\t\t\t\to(root.childNodes.length).equals(1)\n\t\t\t\t\to(root.childNodes[0].nodeName).equals(\"A\")\n\t\t\t\t\to(root.childNodes[0].firstChild.nodeName).equals(\"#text\")\n\t\t\t\t\to(root.childNodes[0].firstChild.nodeValue).equals(\"loading...\")\n\n\t\t\t\t\t// call route() (mount synchronously)\n\t\t\t\t\t$window.location.href = prefix + \"/\"\n\t\t\t\t\troute(root, \"/\", {\n\t\t\t\t\t\t\"/\" : Component\n\t\t\t\t\t})\n\n\t\t\t\t\t// route component is mounted and the first rendered vnode is cleared\n\t\t\t\t\to(root.childNodes.length).equals(1)\n\t\t\t\t\to(root.childNodes[0]).notEquals(vnode.dom)\n\t\t\t\t\to(root.childNodes[0].nodeName).equals(\"SPAN\")\n\t\t\t\t\to(root.childNodes[0].childNodes.length).equals(0)\n\t\t\t\t})\n\n\t\t\t\to(\"route component is mounted after the route is initially resolved (render, synchronous)\", function() {\n\t\t\t\t\tvar Component = {\n\t\t\t\t\t\trender: lock(function() {\n\t\t\t\t\t\t\t// the first rendered vnode is cleared\n\t\t\t\t\t\t\to(root.childNodes.length).equals(0)\n\t\t\t\t\t\t\treturn m(\"span\")\n\t\t\t\t\t\t})\n\t\t\t\t\t}\n\n\t\t\t\t\t// initial root node\n\t\t\t\t\troot.textContent = \"foo\"\n\t\t\t\t\to(root.childNodes.length).equals(1)\n\t\t\t\t\to(root.childNodes[0].nodeName).equals(\"#text\")\n\t\t\t\t\to(root.childNodes[0].nodeValue).equals(\"foo\")\n\n\t\t\t\t\t// render another vnode first\n\t\t\t\t\tvar render = coreRenderer($window)\n\t\t\t\t\tvar vnode = m(\"a\", \"loading...\")\n\t\t\t\t\trender(root, vnode)\n\t\t\t\t\to(root.childNodes.length).equals(1)\n\t\t\t\t\to(root.childNodes[0].nodeName).equals(\"A\")\n\t\t\t\t\to(root.childNodes[0].firstChild.nodeName).equals(\"#text\")\n\t\t\t\t\to(root.childNodes[0].firstChild.nodeValue).equals(\"loading...\")\n\n\t\t\t\t\t// call route() (mount synchronously)\n\t\t\t\t\t$window.location.href = prefix + \"/\"\n\t\t\t\t\troute(root, \"/\", {\n\t\t\t\t\t\t\"/\" : Component\n\t\t\t\t\t})\n\n\t\t\t\t\t// route component is mounted and the first rendered vnode is cleared\n\t\t\t\t\to(root.childNodes.length).equals(1)\n\t\t\t\t\to(root.childNodes[0]).notEquals(vnode.dom)\n\t\t\t\t\to(root.childNodes[0].nodeName).equals(\"SPAN\")\n\t\t\t\t\to(root.childNodes[0].childNodes.length).equals(0)\n\t\t\t\t})\n\n\t\t\t\to(\"route component is mounted after the route is initially resolved (onmatch, asynchronous)\", function() {\n\t\t\t\t\tvar Component = {\n\t\t\t\t\t\tview: lock(function() {\n\t\t\t\t\t\t\treturn m(\"span\")\n\t\t\t\t\t\t})\n\t\t\t\t\t}\n\n\t\t\t\t\t// check for the order of calling onmatch and render\n\t\t\t\t\tvar count = 0\n\n\t\t\t\t\tvar resolver = {\n\t\t\t\t\t\tonmatch: lock(function() {\n\t\t\t\t\t\t\tcount += 1\n\t\t\t\t\t\t\to(count).equals(1)\n\n\t\t\t\t\t\t\t// the first rendered vnode is not yet cleared\n\t\t\t\t\t\t\to(root.childNodes.length).equals(1)\n\t\t\t\t\t\t\to(root.childNodes[0]).equals(vnode.dom)\n\t\t\t\t\t\t\to(root.childNodes[0].nodeName).equals(\"A\")\n\t\t\t\t\t\t\to(root.childNodes[0].firstChild.nodeName).equals(\"#text\")\n\t\t\t\t\t\t\to(root.childNodes[0].firstChild.nodeValue).equals(\"loading...\")\n\t\t\t\t\t\t\treturn Component\n\t\t\t\t\t\t}),\n\t\t\t\t\t\trender: lock(function(vnode) {\n\t\t\t\t\t\t\tcount += 1\n\t\t\t\t\t\t\to(count).equals(2)\n\n\t\t\t\t\t\t\t// the first rendered vnode is cleared\n\t\t\t\t\t\t\to(root.childNodes.length).equals(0)\n\t\t\t\t\t\t\treturn vnode\n\t\t\t\t\t\t})\n\t\t\t\t\t}\n\n\t\t\t\t\t// initial root node\n\t\t\t\t\troot.textContent = \"foo\"\n\t\t\t\t\to(root.childNodes.length).equals(1)\n\t\t\t\t\to(root.childNodes[0].nodeName).equals(\"#text\")\n\t\t\t\t\to(root.childNodes[0].nodeValue).equals(\"foo\")\n\n\t\t\t\t\t// render another vnode first\n\t\t\t\t\tvar render = coreRenderer($window)\n\t\t\t\t\tvar vnode = m(\"a\", \"loading...\")\n\t\t\t\t\trender(root, vnode)\n\t\t\t\t\to(root.childNodes.length).equals(1)\n\t\t\t\t\to(root.childNodes[0].nodeName).equals(\"A\")\n\t\t\t\t\to(root.childNodes[0].firstChild.nodeName).equals(\"#text\")\n\t\t\t\t\to(root.childNodes[0].firstChild.nodeValue).equals(\"loading...\")\n\n\t\t\t\t\t// call route() (mount asynchronously)\n\t\t\t\t\t$window.location.href = prefix + \"/\"\n\t\t\t\t\troute(root, \"/\", {\n\t\t\t\t\t\t\"/\" : resolver\n\t\t\t\t\t})\n\n\t\t\t\t\t// the first rendered vnode is not yet cleared\n\t\t\t\t\to(root.childNodes.length).equals(1)\n\t\t\t\t\to(root.childNodes[0]).equals(vnode.dom)\n\t\t\t\t\to(root.childNodes[0].nodeName).equals(\"A\")\n\t\t\t\t\to(root.childNodes[0].firstChild.nodeName).equals(\"#text\")\n\t\t\t\t\to(root.childNodes[0].firstChild.nodeValue).equals(\"loading...\")\n\n\t\t\t\t\t// The count of route resolver method calls is still 0\n\t\t\t\t\to(count).equals(0)\n\n\t\t\t\t\treturn waitCycles(1).then(function() {\n\t\t\t\t\t\t// route component is mounted and the first rendered vnode is cleared\n\t\t\t\t\t\to(root.childNodes.length).equals(1)\n\t\t\t\t\t\to(root.childNodes[0]).notEquals(vnode.dom)\n\t\t\t\t\t\to(root.childNodes[0].nodeName).equals(\"SPAN\")\n\t\t\t\t\t\to(root.childNodes[0].childNodes.length).equals(0)\n\n\t\t\t\t\t\to(count).equals(2)\n\t\t\t\t\t})\n\t\t\t\t})\n\n\t\t\t\to(\"error in the route component is thrown and not caught in the initial rendering (#2621)\", function() {\n\t\t\t\t\tvar Component = {\n\t\t\t\t\t\tview: lock(function() {\n\t\t\t\t\t\t\tthrow Error(\"foo\")\n\t\t\t\t\t\t})\n\t\t\t\t\t}\n\n\t\t\t\t\t// Errors thrown during redrawing of mounted components are caught in m.mount()\n\t\t\t\t\t// and console.error is called.\n\t\t\t\t\t// Therefore, spy is used to confirm that console.error is not called\n\t\t\t\t\t// when it is first mounted.\n\t\t\t\t\tvar spy = o.spy(console.error)\n\t\t\t\t\tconsole.error = spy\n\n\t\t\t\t\t$window.location.href = prefix + \"/\"\n\t\t\t\t\to(function(){\n\t\t\t\t\t\troute(root, \"/\", {\n\t\t\t\t\t\t\t\"/\" : Component\n\t\t\t\t\t\t})\n\t\t\t\t\t}).throws(\"foo\")\n\n\t\t\t\t\to(spy.callCount).equals(0)\n\t\t\t\t})\n\t\t\t})\n\t\t})\n\t})\n})\n"
  },
  {
    "path": "api/tests/test-routerGetSet.js",
    "content": "\"use strict\"\n\n// Low-priority TODO: remove the dependency on the renderer here.\nvar o = require(\"ospec\")\nvar browserMock = require(\"../../test-utils/browserMock\")\nvar throttleMocker = require(\"../../test-utils/throttleMock\")\n\nvar apiMountRedraw = require(\"../../api/mount-redraw\")\nvar coreRenderer = require(\"../../render/render\")\nvar apiRouter = require(\"../../api/router\")\n\no.spec(\"route.get/route.set\", function() {\n\tvoid [{protocol: \"http:\", hostname: \"localhost\"}, {protocol: \"file:\", hostname: \"/\"}].forEach(function(env) {\n\t\tvoid [\"#\", \"?\", \"\", \"#!\", \"?!\", \"/foo\"].forEach(function(prefix) {\n\t\t\to.spec(\"using prefix `\" + prefix + \"` starting on \" + env.protocol + \"//\" + env.hostname, function() {\n\t\t\t\tvar $window, root, mountRedraw, route, throttleMock\n\n\t\t\t\to.beforeEach(function() {\n\t\t\t\t\t$window = browserMock(env)\n\t\t\t\t\tthrottleMock = throttleMocker()\n\t\t\t\t\t$window.setTimeout = setTimeout\n\n\t\t\t\t\troot = $window.document.body\n\n\t\t\t\t\tmountRedraw = apiMountRedraw(coreRenderer($window), throttleMock.schedule, console)\n\t\t\t\t\troute = apiRouter($window, mountRedraw)\n\t\t\t\t\troute.prefix = prefix\n\t\t\t\t})\n\n\t\t\t\to.afterEach(function() {\n\t\t\t\t\to(throttleMock.queueLength()).equals(0)\n\t\t\t\t})\n\n\t\t\t\to(\"gets route\", function() {\n\t\t\t\t\t$window.location.href = prefix + \"/test\"\n\t\t\t\t\troute(root, \"/test\", {\"/test\": {view: function() {}}})\n\n\t\t\t\t\to(route.get()).equals(\"/test\")\n\t\t\t\t})\n\n\t\t\t\to(\"gets route w/ params\", function() {\n\t\t\t\t\t$window.location.href = prefix + \"/other/x/y/z?c=d#e=f\"\n\n\t\t\t\t\troute(root, \"/other/x/y/z?c=d#e=f\", {\n\t\t\t\t\t\t\"/test\": {view: function() {}},\n\t\t\t\t\t\t\"/other/:a/:b...\": {view: function() {}},\n\t\t\t\t\t})\n\n\t\t\t\t\to(route.get()).equals(\"/other/x/y/z?c=d#e=f\")\n\t\t\t\t})\n\n\t\t\t\to(\"gets route w/ escaped unicode\", function() {\n\t\t\t\t\t$window.location.href = prefix + encodeURI(\"/ö/é/å?ö=ö#ö=ö\")\n\n\t\t\t\t\troute(root, \"/ö/é/å?ö=ö#ö=ö\", {\n\t\t\t\t\t\t\"/test\": {view: function() {}},\n\t\t\t\t\t\t\"/ö/:a/:b...\": {view: function() {}},\n\t\t\t\t\t})\n\n\t\t\t\t\to(route.get()).equals(\"/ö/é/å?ö=ö#ö=ö\")\n\t\t\t\t})\n\n\t\t\t\to(\"gets route w/ unicode\", function() {\n\t\t\t\t\t$window.location.href = prefix + \"/ö/é/å?ö=ö#ö=ö\"\n\n\t\t\t\t\troute(root, \"/ö/é/å?ö=ö#ö=ö\", {\n\t\t\t\t\t\t\"/test\": {view: function() {}},\n\t\t\t\t\t\t\"/ö/:a/:b...\": {view: function() {}},\n\t\t\t\t\t})\n\n\t\t\t\t\to(route.get()).equals(\"/ö/é/å?ö=ö#ö=ö\")\n\t\t\t\t})\n\n\t\t\t\to(\"sets path asynchronously\", function(done) {\n\t\t\t\t\t$window.location.href = prefix + \"/a\"\n\t\t\t\t\tvar spy1 = o.spy()\n\t\t\t\t\tvar spy2 = o.spy()\n\n\t\t\t\t\troute(root, \"/a\", {\n\t\t\t\t\t\t\"/a\": {view: spy1},\n\t\t\t\t\t\t\"/b\": {view: spy2},\n\t\t\t\t\t})\n\n\t\t\t\t\to(spy1.callCount).equals(1)\n\t\t\t\t\to(spy2.callCount).equals(0)\n\t\t\t\t\troute.set(\"/b\")\n\t\t\t\t\to(spy1.callCount).equals(1)\n\t\t\t\t\to(spy2.callCount).equals(0)\n\t\t\t\t\tsetTimeout(function() {\n\t\t\t\t\t\tthrottleMock.fire()\n\n\t\t\t\t\t\to(spy1.callCount).equals(1)\n\t\t\t\t\t\to(spy2.callCount).equals(1)\n\t\t\t\t\t\tdone()\n\t\t\t\t\t})\n\t\t\t\t})\n\n\t\t\t\to(\"sets fallback asynchronously\", function(done) {\n\t\t\t\t\t$window.location.href = prefix + \"/b\"\n\t\t\t\t\tvar spy1 = o.spy()\n\t\t\t\t\tvar spy2 = o.spy()\n\n\t\t\t\t\troute(root, \"/a\", {\n\t\t\t\t\t\t\"/a\": {view: spy1},\n\t\t\t\t\t\t\"/b\": {view: spy2},\n\t\t\t\t\t})\n\n\t\t\t\t\to(spy1.callCount).equals(0)\n\t\t\t\t\to(spy2.callCount).equals(1)\n\t\t\t\t\troute.set(\"/c\")\n\t\t\t\t\to(spy1.callCount).equals(0)\n\t\t\t\t\to(spy2.callCount).equals(1)\n\t\t\t\t\tsetTimeout(function() {\n\t\t\t\t\t\t// Yep, before even the throttle mechanism takes hold.\n\t\t\t\t\t\to(route.get()).equals(\"/b\")\n\t\t\t\t\t\tsetTimeout(function() {\n\t\t\t\t\t\t\t// Yep, before even the throttle mechanism takes hold.\n\t\t\t\t\t\t\to(route.get()).equals(\"/a\")\n\t\t\t\t\t\t\tthrottleMock.fire()\n\n\t\t\t\t\t\t\to(spy1.callCount).equals(1)\n\t\t\t\t\t\t\to(spy2.callCount).equals(1)\n\t\t\t\t\t\t\tdone()\n\t\t\t\t\t\t})\n\t\t\t\t\t})\n\t\t\t\t})\n\n\t\t\t\to(\"exposes new route asynchronously\", function(done) {\n\t\t\t\t\t$window.location.href = prefix + \"/test\"\n\t\t\t\t\troute(root, \"/test\", {\n\t\t\t\t\t\t\"/test\": {view: function() {}},\n\t\t\t\t\t\t\"/other/:a/:b...\": {view: function() {}},\n\t\t\t\t\t})\n\n\t\t\t\t\troute.set(\"/other/x/y/z?c=d#e=f\")\n\t\t\t\t\tsetTimeout(function() {\n\t\t\t\t\t\t// Yep, before even the throttle mechanism takes hold.\n\t\t\t\t\t\to(route.get()).equals(\"/other/x/y/z?c=d#e=f\")\n\t\t\t\t\t\tthrottleMock.fire()\n\t\t\t\t\t\tdone()\n\t\t\t\t\t})\n\t\t\t\t})\n\n\t\t\t\to(\"exposes new escaped unicode route asynchronously\", function(done) {\n\t\t\t\t\t$window.location.href = prefix + \"/test\"\n\t\t\t\t\troute(root, \"/test\", {\n\t\t\t\t\t\t\"/test\": {view: function() {}},\n\t\t\t\t\t\t\"/ö\": {view: function() {}},\n\t\t\t\t\t})\n\n\t\t\t\t\troute.set(encodeURI(\"/ö?ö=ö#ö=ö\"))\n\t\t\t\t\tsetTimeout(function() {\n\t\t\t\t\t\t// Yep, before even the throttle mechanism takes hold.\n\t\t\t\t\t\to(route.get()).equals(\"/ö?ö=ö#ö=ö\")\n\t\t\t\t\t\tthrottleMock.fire()\n\t\t\t\t\t\tdone()\n\t\t\t\t\t})\n\t\t\t\t})\n\n\t\t\t\to(\"exposes new unescaped unicode route asynchronously\", function(done) {\n\t\t\t\t\t$window.location.href = \"file://\" + prefix + \"/test\"\n\t\t\t\t\troute(root, \"/test\", {\n\t\t\t\t\t\t\"/test\": {view: function() {}},\n\t\t\t\t\t\t\"/ö\": {view: function() {}},\n\t\t\t\t\t})\n\n\t\t\t\t\troute.set(\"/ö?ö=ö#ö=ö\")\n\t\t\t\t\tsetTimeout(function() {\n\t\t\t\t\t\t// Yep, before even the throttle mechanism takes hold.\n\t\t\t\t\t\to(route.get()).equals(\"/ö?ö=ö#ö=ö\")\n\t\t\t\t\t\tthrottleMock.fire()\n\t\t\t\t\t\tdone()\n\t\t\t\t\t})\n\t\t\t\t})\n\n\t\t\t\to(\"exposes new route asynchronously on fallback mode\", function(done) {\n\t\t\t\t\t$window.location.href = prefix + \"/test\"\n\t\t\t\t\troute(root, \"/test\", {\n\t\t\t\t\t\t\"/test\": {view: function() {}},\n\t\t\t\t\t\t\"/other/:a/:b...\": {view: function() {}},\n\t\t\t\t\t})\n\n\t\t\t\t\troute.set(\"/other/x/y/z?c=d#e=f\")\n\t\t\t\t\tsetTimeout(function() {\n\t\t\t\t\t\t// Yep, before even the throttle mechanism takes hold.\n\t\t\t\t\t\to(route.get()).equals(\"/other/x/y/z?c=d#e=f\")\n\t\t\t\t\t\tthrottleMock.fire()\n\t\t\t\t\t\tdone()\n\t\t\t\t\t})\n\t\t\t\t})\n\n\t\t\t\to(\"sets route via pushState/onpopstate\", function(done) {\n\t\t\t\t\t$window.location.href = prefix + \"/test\"\n\t\t\t\t\troute(root, \"/test\", {\n\t\t\t\t\t\t\"/test\": {view: function() {}},\n\t\t\t\t\t\t\"/other/:a/:b...\": {view: function() {}},\n\t\t\t\t\t})\n\n\t\t\t\t\tsetTimeout(function() {\n\t\t\t\t\t\t$window.history.pushState(null, null, prefix + \"/other/x/y/z?c=d#e=f\")\n\t\t\t\t\t\t$window.onpopstate()\n\n\t\t\t\t\t\tsetTimeout(function() {\n\t\t\t\t\t\t\t// Yep, before even the throttle mechanism takes hold.\n\t\t\t\t\t\t\to(route.get()).equals(\"/other/x/y/z?c=d#e=f\")\n\t\t\t\t\t\t\tthrottleMock.fire()\n\n\t\t\t\t\t\t\tdone()\n\t\t\t\t\t\t})\n\t\t\t\t\t})\n\t\t\t\t})\n\n\t\t\t\to(\"sets parameterized route\", function(done) {\n\t\t\t\t\t$window.location.href = prefix + \"/test\"\n\t\t\t\t\troute(root, \"/test\", {\n\t\t\t\t\t\t\"/test\": {view: function() {}},\n\t\t\t\t\t\t\"/other/:a/:b...\": {view: function() {}},\n\t\t\t\t\t})\n\n\t\t\t\t\troute.set(\"/other/:a/:b\", {a: \"x\", b: \"y/z\", c: \"d\", e: \"f\"})\n\t\t\t\t\tsetTimeout(function() {\n\t\t\t\t\t\t// Yep, before even the throttle mechanism takes hold.\n\t\t\t\t\t\to(route.get()).equals(\"/other/x/y%2Fz?c=d&e=f\")\n\t\t\t\t\t\tthrottleMock.fire()\n\t\t\t\t\t\tdone()\n\t\t\t\t\t})\n\t\t\t\t})\n\n\t\t\t\to(\"replace:true works\", function(done) {\n\t\t\t\t\t$window.location.href = prefix + \"/test\"\n\t\t\t\t\troute(root, \"/test\", {\n\t\t\t\t\t\t\"/test\": {view: function() {}},\n\t\t\t\t\t\t\"/other\": {view: function() {}},\n\t\t\t\t\t})\n\n\t\t\t\t\troute.set(\"/other\", null, {replace: true})\n\n\t\t\t\t\tsetTimeout(function() {\n\t\t\t\t\t\tthrottleMock.fire()\n\t\t\t\t\t\t$window.history.back()\n\t\t\t\t\t\to($window.location.href).equals(env.protocol + \"//\" + (env.hostname === \"/\" ? \"\" : env.hostname) + \"/\")\n\t\t\t\t\t\tdone()\n\t\t\t\t\t})\n\t\t\t\t})\n\n\t\t\t\to(\"replace:false works\", function(done) {\n\t\t\t\t\t$window.location.href = prefix + \"/test\"\n\t\t\t\t\troute(root, \"/test\", {\n\t\t\t\t\t\t\"/test\": {view: function() {}},\n\t\t\t\t\t\t\"/other\": {view: function() {}},\n\t\t\t\t\t})\n\n\t\t\t\t\troute.set(\"/other\", null, {replace: false})\n\n\t\t\t\t\tsetTimeout(function() {\n\t\t\t\t\t\tthrottleMock.fire()\n\t\t\t\t\t\t$window.history.back()\n\t\t\t\t\t\tvar slash = prefix[0] === \"/\" ? \"\" : \"/\"\n\t\t\t\t\t\to($window.location.href).equals(env.protocol + \"//\" + (env.hostname === \"/\" ? \"\" : env.hostname) + slash + (prefix ? prefix + \"/\" : \"\") + \"test\")\n\t\t\t\t\t\tdone()\n\t\t\t\t\t})\n\t\t\t\t})\n\n\t\t\t\to(\"state works\", function(done) {\n\t\t\t\t\t$window.location.href = prefix + \"/test\"\n\t\t\t\t\troute(root, \"/test\", {\n\t\t\t\t\t\t\"/test\": {view: function() {}},\n\t\t\t\t\t\t\"/other\": {view: function() {}},\n\t\t\t\t\t})\n\n\t\t\t\t\troute.set(\"/other\", null, {state: {a: 1}})\n\t\t\t\t\tsetTimeout(function() {\n\t\t\t\t\t\tthrottleMock.fire()\n\t\t\t\t\t\to($window.history.state).deepEquals({a: 1})\n\t\t\t\t\t\tdone()\n\t\t\t\t\t})\n\t\t\t\t})\n\t\t\t})\n\t\t})\n\t})\n})\n"
  },
  {
    "path": "browser.js",
    "content": "\"use strict\"\n\nvar m = require(\"./index\")\nif (typeof module !== \"undefined\") module[\"exports\"] = m\nelse window.m = m\n"
  },
  {
    "path": "docs/code-of-conduct.md",
    "content": "<!--meta-description\nCode of Conduct Covenant for contributors to the Mithril.js project\n-->\n\n# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nIn the interest of fostering an open and welcoming environment, we as\ncontributors and maintainers pledge to making participation in our project and\nour community a harassment-free experience for everyone, regardless of age, body\nsize, disability, ethnicity, gender identity and expression, level of experience,\nnationality, personal appearance, race, religion, or sexual identity and\norientation.\n\n## Our Standards\n\nExamples of behavior that contributes to creating a positive environment\ninclude:\n\n* Using welcoming and inclusive language\n* Being respectful of differing viewpoints and experiences\n* Gracefully accepting constructive criticism\n* Focusing on what is best for the community\n* Showing empathy towards other community members\n\nExamples of unacceptable behavior by participants include:\n\n* The use of sexualized language or imagery and unwelcome sexual attention or\nadvances\n* Trolling, insulting/derogatory comments, and personal or political attacks\n* Public or private harassment\n* Publishing others' private information, such as a physical or electronic\n  address, without explicit permission\n* Other conduct which could reasonably be considered inappropriate in a\n  professional setting\n\n## Our Responsibilities\n\nProject maintainers are responsible for clarifying the standards of acceptable\nbehavior and are expected to take appropriate and fair corrective action in\nresponse to any instances of unacceptable behavior.\n\nProject maintainers have the right and responsibility to remove, edit, or\nreject comments, commits, code, wiki edits, issues, and other contributions\nthat are not aligned to this Code of Conduct, or to ban temporarily or\npermanently any contributor for other behaviors that they deem inappropriate,\nthreatening, offensive, or harmful.\n\n## Scope\n\nThis Code of Conduct applies both within project spaces and in public spaces\nwhen an individual is representing the project or its community. Examples of\nrepresenting a project or community include using an official project e-mail\naddress, posting via an official social media account, or acting as an appointed\nrepresentative at an online or offline event. Representation of a project may be\nfurther defined and clarified by project maintainers.\n\n## Enforcement\n\nInstances of abusive, harassing, or otherwise unacceptable behavior may be\nreported by contacting the project team at [contact@claudiameadows.dev](mailto:contact@claudiameadows.dev?subject=Mithril%20Code%20of%20Conduct). All\ncomplaints will be reviewed and investigated and will result in a response that\nis deemed necessary and appropriate to the circumstances. The project team is\nobligated to maintain confidentiality with regard to the reporter of an incident.\nFurther details of specific enforcement policies may be posted separately.\n\nProject maintainers who do not follow or enforce the Code of Conduct in good\nfaith may face temporary or permanent repercussions as determined by other\nmembers of the project's leadership.\n\nAs a general policy, we generally do not disclose any particular action taken in accordance with this Code of Conduct beyond those who need to know.\n\n## Attribution\n\nThis Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,\navailable at [https://contributor-covenant.org/version/1/4][version]\n\n[homepage]: https://contributor-covenant.org\n[version]: https://contributor-covenant.org/version/1/4/\n"
  },
  {
    "path": "docs/contributing.md",
    "content": "<!--meta-description\nContribution guide for Mithril.js\n-->\n# Contributing FAQs\n\n- [How do I go about contributing ideas or new features?](#how-do-i-go-about-contributing-ideas-or-new-features?)\n- [How should I report bugs?](#how-should-i-report-bugs?)\n- [How do I send a pull request?](#how-do-i-send-a-pull-request?)\n- [I'm submitting a PR. How do I run tests?](#i'm-submitting-a-pr-how-do-i-run-tests?)\n- [How do I build Mithril.js?](#how-do-i-build-mithril?)\n- [Is there a style guide?](#is-there-a-style-guide?)\n- [How do I embed live previews in docs?](#how-do-I-embed-live-previews-in-docs?)\n- [Why do tests mock the browser APIs?](#why-do-tests-mock-the-browser-apis?)\n- [Why does Mithril.js use its own testing framework and not Mocha/Jasmine/Tape?](#why-does-mithril-use-its-own-testing-framework-and-not-mochajasminetape?)\n- [Why doesn't the Mithril.js codebase use ES6 via Babel or Bublé? Would a PR to upgrade be welcome?](#why-doesn't-the-mithril-codebase-use-es6-via-babel-or-bublé?-would-a-pr-to-upgrade-be-welcome?)\n- [Why doesn't the Mithril.js codebase use trailing semi-colons? Would a PR to add them be welcome?](#why-doesn't-the-mithril-codebase-use-trailing-semi-colons?-would-a-pr-to-add-them-be-welcome?)\n- [Why does the Mithril.js codebase use a mix of `instanceof` and `typeof` checks instead of `Object.prototype.toString.call`, `Array.isArray`, etc? Would a PR to refactor those checks be welcome?](#why-does-the-mithril-codebase-use-a-mix-of-instanceof-and-typeof-checks-instead-of-objectprototypetostringcall,-arrayisarray,-etc?-would-a-pr-to-refactor-those-checks-be-welcome?)\n- [What should I know in advance when attempting a performance related contribution?](#What-should-I-know-in-advance-when-attempting-a-performance-related-contribution?)\n- [Do you all accept donations?](#do-you-all-accept-donations?)\n\n## How do I go about contributing ideas or new features?\n\nCreate an [issue thread on GitHub](https://github.com/MithrilJS/mithril.js/issues/new) to suggest your idea so the community can discuss it.\n\nIf the consensus is that it's a good idea, the fastest way to get it into a release is to send a pull request. Without a PR, the time to implement the feature will depend on the bandwidth of the development team and its list of priorities.\n\n\n\n## How should I report bugs?\n\nIdeally, the best way to report bugs is to provide a small snippet of code where the issue can be reproduced (via jsfiddle, jsbin, a gist, etc). Even better would be to submit a pull request with a fix and tests. If you don't know how to test your fix, or lint or whatever, submit anyways, and we can help you.\n\n\n\n## How do I send a pull request?\n\nTo send a pull request:\n\n- fork the repo (button at the top right in GitHub)\n- clone the forked repo to your computer (green button in GitHub)\n- Switch to the `main` branch (run `git checkout main`)\n- create a feature branch (run `git checkout -b the-feature-branch-name`)\n- make your changes\n- run the tests (run `npm test`)\n- push your changes to your fork\n- submit a pull request (go to the pull requests tab in GitHub, click the green button and select your feature branch)\n\n\n\n## I'm submitting a PR. How do I run tests?\n\nAfter having run `npm install` (a one-time operation), run `npm run test` from the command line to run all tests.\n\nWhile testing, you can modify a test to use `o.only(description, test)` instead of `o(description, test)` if you wish to run only a specific test to speed up your debugging experience. Don't forget to remove the `.only` after you're done!\n\n\n\n## How do I build Mithril.js?\n\nIf all you're trying to do is run examples in the codebase, you don't need to build Mithril.js, you can just open the various html files and things should just work.\n\nTo generate the bundled file for testing, run `npm run dev` from the command line. To generate the minified file, run `npm run build`.\n\n\n\n## Is there a style guide?\n\nYes, there's an `eslint` configuration, but it's not strict about formatting at all. If your contribution passes `npm run lint`, it's good enough for a PR (and it can still be accepted even if it doesn't pass).\n\nSpacing and formatting inconsistencies may be fixed after the fact, and we don't want that kind of stuff getting in the way of contributing.\n\n\n\n## How do I embed live previews in docs?\n\nAny code tag marked as `js` and not `javascript` will automatically be wrapped in a live Flems preview.\n\n\n\n## Why do tests mock the browser APIs?\n\nMost notoriously, because it's impossible to test the router and some side effects properly otherwise. Also, mocks allow the tests to run under Node.js without requiring heavy dependencies like PhantomJS/ChromeDriver/JSDOM.\n\nAnother important reason is that it allows us to document browser API quirks via code, through the tests for the mocks.\n\n\n\n## Why does Mithril.js use its own testing framework and not Mocha/Jasmine/Tape?\n\nMainly to avoid requiring dependencies. `ospec` is customized to provide only essential information for common testing workflows (namely, no spamming ok's on pass, and accurate noiseless errors on failure)\n\n\n\n## Why doesn't the Mithril.js codebase use ES6 via Babel or Bublé? Would a PR to upgrade be welcome?\n\nBeing able to run Mithril.js' raw source code in all supported browsers is a requirement for all browser-related modules in this repo. In addition, transpiled code is generally much bulkier.\n\n\n\n## Why doesn't the Mithril.js codebase use trailing semi-colons? Would a PR to add them be welcome?\n\nI don't use them. Adding them means the semi-colon usage in the codebase will eventually become inconsistent. Besides, [we aren't the only one who've decided to drop the semicolon](https://standardjs.com/#who-uses-javascript-standard-style). (We don't use Standard, though.)\n\n\n\n## Why does the Mithril.js codebase use a mix of `instanceof` and `typeof` checks instead of `Object.prototype.toString.call`, `Array.isArray`, etc? Would a PR to refactor those checks be welcome?\n\nMithril.js avoids peeking at objects' [[class]] string for performance considerations. Many type checks are seemingly inconsistent, weird or convoluted because those specific constructs demonstrated the best performance profile in benchmarks compared to alternatives.\n\nType checks are generally already irreducible expressions and having micro-modules for type checking subroutines would add maintenance overhead.\n\n\n\n## What should I know in advance when attempting a performance related contribution?\n\nYou should be trying to reduce the number of DOM operations or reduce algorithmic complexity in a hot spot. Anything else is likely a waste of time. Specifically, micro-optimizations like caching array lengths, caching object property values and inlining functions won't have any positive impact in modern JavaScript engines.\n\nKeep object properties consistent (i.e. ensure the data objects always have the same properties and that properties are always in the same order) to allow the engine to keep using JIT'ed structs instead of hashmaps. Always place null checks first in compound type checking expressions to allow the JavaScript engine to optimize to type-specific code paths. Prefer for loops over Array methods and try to pull conditionals out of loops if possible.\n\n\n\n## Do you all accept donations?\n\nYes, we do, over at [our OpenCollective page](https://opencollective.com/mithriljs). We don't actively seek donations, but they are very much appreciated and are used to support development and related expenses. Both one-time and recurring donations are accepted.\n"
  },
  {
    "path": "docs/credits.md",
    "content": "<!--meta-description\nList of especially notable contributors to Mithril.js\n-->\n\n# Credits\n\nMithril.js was originally written by Leo Horie, but it is where it is today thanks to the hard work and great ideas of many people.\n\nSpecial thanks to:\n\n- Pat Cavit, who exposed most of the public API for Mithril.js 1.0, brought in test coverage and automated the publishing process\n- Claudia Meadows, who brought in linting, modernized the test suite and has been a strong voice in design discussions\n- Zoli Kahan, who replaced the original Promise implementation with one that actually worked properly\n- Alec Embke, who single-handedly wrote the JSON-P implementation\n- Barney Carroll, who suggested many great ideas and relentlessly pushed Mithril.js to the limit to uncover design issues prior to Mithril.js 1.0\n- Dominic Gannaway, who offered insanely meticulous technical insight into rendering performance\n- Boris Letocha, whose search space reduction algorithm is the basis for Mithril.js' virtual DOM engine\n- Joel Richard, whose monomorphic virtual DOM structure is the basis for Mithril.js' vnode implementation\n- Simon Friis Vindum, whose open source work was an inspiration to many design decisions for Mithril.js 1.0\n- Boris Kaul, for his awesome work on the benchmarking tools used to develop Mithril.js\n- Leon Sorokin, for writing a DOM instrumentation tool that helped improve performance in Mithril.js 1.0\n- Jordan Walke, whose work on React was prior art to the implementation of keys in Mithril.js\n- Pierre-Yves Gérardy, who consistently makes high quality contributions\n- Gyandeep Singh, who contributed significant IE performance improvements\n\nOther people who also deserve recognition:\n\n- Arthur Clemens - creator of [Polythene](https://github.com/ArthurClemens/Polythene) and the [HTML-to-Mithril converter](https://arthurclemens.github.io/mithril-template-converter/index.html)\n- Stephan Hoyer - creator of [mithril-node-render](https://github.com/StephanHoyer/mithril-node-render), [mithril-query](https://github.com/StephanHoyer/mithril-query) and [mithril-source-hint](https://github.com/StephanHoyer/mithril-source-hint)\n- the countless people who have reported and fixed bugs, participated in discussions, and helped promote Mithril.js\n"
  },
  {
    "path": "docs/recent-changes.md",
    "content": "\n# Release v2.3.8\n\n### Patch Changes\n\n#### [refactor execSelector (@kfule)](https://github.com/MithrilJS/mithril.js/pull/3056)\n\n\n#### [Make `vnode.domSize` assignment consistent between create and update. (@kfule)](https://github.com/MithrilJS/mithril.js/pull/3055)\n\nThis PR makes the code and behavior of create and update processes more consistent.\n#### [Bump rimraf from 6.0.1 to 6.1.0 in the normal group (@dependabot[bot])](https://github.com/MithrilJS/mithril.js/pull/3054)\n\nBumps the normal group with 1 update: [rimraf](https://github.com/isaacs/rimraf).  Updates `rimraf` from 6.0.1 to 6.1.0.  Changelog.  Sourced from rimraf's changelog.\n#### [Fix URI decoder bug and reduce bundle size through module tailoring and cleanup (@kfule)](https://github.com/MithrilJS/mithril.js/pull/3050)\n\nThis fixes the URI decoder used in the Router to decode more strictly.\n#### [refactor `Vnode.normalizeChildren` (@kfule)](https://github.com/MithrilJS/mithril.js/pull/3052)\n\n`Vnode.normalizeChildren` now preallocates the array length and performs key-consistency checks after normalization.\n#### [Bump actions/setup-node from 5 to 6 in the normal group (@dependabot[bot])](https://github.com/MithrilJS/mithril.js/pull/3053)\n\nBumps the normal group with 1 update: [actions/setup-node](https://github.com/actions/setup-node).  Updates `actions/setup-node` from 5 to 6.  Release notes.\n#### [Bump actions/setup-node from 4 to 5 in the normal group (@dependabot[bot])](https://github.com/MithrilJS/mithril.js/pull/3047)\n\nBumps the normal group with 1 update: [actions/setup-node](https://github.com/actions/setup-node).  Updates `actions/setup-node` from 4 to 5.  Release notes.\n#### [docs: edited the link to the build badge (@Olexandr88)](https://github.com/MithrilJS/mithril.js/pull/3045)\n\n# Release v2.3.7\n\n### Patch Changes\n\n#### [Make the attrs of non-element vnodes always non-null. (@kfule)](https://github.com/MithrilJS/mithril.js/pull/3042)\n\nIn #3041, it seemed that the case of non-element vnodes was not fully considered in terms of not breaking existing behavior.\n\n# Release v2.3.6\n\n### Patch Changes\n\n#### [Make the attrs of non-element vnodes always non-null. (@kfule)](https://github.com/MithrilJS/mithril.js/pull/3042)\n\nIn #3041, it seemed that the case of non-element vnodes was not fully considered in terms of not breaking existing behavior.\n\n# Release v2.3.5\n\n### Patch Changes\n\n#### [Assorted Performance Improvements (@kfule)](https://github.com/MithrilJS/mithril.js/pull/3041)\n\nThis PR improves performance through the following changes: Adoption of the spread syntax, which can be optimized in modern browsers.\n#### [Bump actions/checkout from 4 to 5 in the normal group (@dependabot[bot])](https://github.com/MithrilJS/mithril.js/pull/3039)\n\nBumps the normal group with 1 update: [actions/checkout](https://github.com/actions/checkout).  Updates `actions/checkout` from 4 to 5.  Release notes.\n\n# Release v2.3.4\n\n### Patch Changes\n\n#### [Fix the error message selection condition (@dead-claudia)](https://github.com/MithrilJS/mithril.js/pull/3037)\n\nThe previous condition was basically \"if this is non-nullish or a boolean\".  That \"or a boolean\" is very obviously redundant.\n#### [bundler: fix mangled comments and double suffixes (@kfule)](https://github.com/MithrilJS/mithril.js/pull/3032)\n\nThis PR removes unnecessary suffixes from comments in the bundle file.  It also fixes the strange double suffix (`mountRedraw00`).\n\n# Release v2.3.3\n\n### Patch Changes\n\n#### [router: delay mounting RouterRoot until the first route is resolved (fixes #2621) (@kfule)](https://github.com/MithrilJS/mithril.js/pull/3030)\n\nThis PR delays the initial mounting of the router component until after the route has been resolved.\n#### [Bump glob from 11.0.2 to 11.0.3 in the normal group (@dependabot[bot])](https://github.com/MithrilJS/mithril.js/pull/3029)\n\nBumps the normal group with 1 update: [glob](https://github.com/isaacs/node-glob).  Updates `glob` from 11.0.2 to 11.0.3.  Commits.  af2e7ce 11.0.3.\n\n# Release v2.3.2\n\n### Patch Changes\n\n#### [Refactor router, fixes #2505 and #2778 (@kfule)](https://github.com/MithrilJS/mithril.js/pull/3027)\n\nThis PR refactors the router code to fix two issues (#2505 and #2778).\n\n# Release v2.3.1\n\n### Patch Changes\n\n#### [set trailing slash optional in route matching (@touletan)](https://github.com/MithrilJS/mithril.js/pull/3025)\n\nRegexp has been updated to set trailing slash as optional in route matching.  link to issue 3024.  New test has been added.\n\n# Release v2.3.0\n\n### Minor Changes\n\n#### [feat: Make redraws when Promises returned by event handlers are completed (@kfule)](https://github.com/MithrilJS/mithril.js/pull/3020)\n\nThis PR allows redraw on completion of the async event handler.  This PR makes redraws when Promises returned by event handlers are completed.\n   \n### Patch Changes\n\n#### [Allow additional async redraw even if the first redraw is skipped (@kfule)](https://github.com/MithrilJS/mithril.js/pull/3021)\n\nThis PR allows asynchronous redraw processing even if the first redraw is skipped by setting `event.redraw=false` before await in the async function.\n#### [Bump glob from 11.0.1 to 11.0.2 in the normal group (@dependabot[bot])](https://github.com/MithrilJS/mithril.js/pull/3019)\n\nBumps the normal group with 1 update: [glob](https://github.com/isaacs/node-glob).  Updates `glob` from 11.0.1 to 11.0.2.  Commits.  fd61f24 11.0.2.\n#### [Fix badge for build status (@kfule)](https://github.com/MithrilJS/mithril.js/pull/3015)\n\nThe URL for the Shields.io badge for build status has been corrected.\n\n# Release v2.2.15\n\n### Patch Changes\n\n#### [[refactor] Limit the condition of the option tag to `selected` attribute in isFormAttribute() (@kfule)](https://github.com/MithrilJS/mithril.js/pull/3011)\n\nThis PR limits the evaluation of whether a tag is `option` to only when setting the `selected` attribute.\n#### [test-perf: Load Benckmark.js first in Node.js (@kfule)](https://github.com/MithrilJS/mithril.js/pull/3008)\n\nSince Node21, global.navigator has been implemented, and together with browserMock, Benchmark.js incorrectly identifies the execution environment as a browser.\n\n# Release v2.2.14\n\n### Patch Changes\n\n#### [Improve handling of is-elements and Fix tiny bugs of setAttr()/updateStyle() (@kfule)](https://github.com/MithrilJS/mithril.js/pull/2988)\n\nFixes a few tiny bugs in attributes and style properties updates, and improves handling of is-elements in updateNode().\n#### [domFor: always get generation from delayedRemoval instead of parameter (@kfule)](https://github.com/MithrilJS/mithril.js/pull/3007)\n\nThe `generation` of domFor is no longer passed as a parameter.  This allows domFor to work well in onbeforeremove and onremove and reduces the amount of code.\n#### [render: wrap stateResult and attrsResult in Promise.resolve(), fix #2592 (@kfule)](https://github.com/MithrilJS/mithril.js/pull/3005)\n\nThis PR wraps the return value of onbeforeremove in Promise.resolve().  This ensures that thenable objects are also always processed asynchronously.  fix #2592.\n\n# Release v2.2.13\n\n### Patch Changes\n\n#### [Fix form checkValidity(), remove vnode.dom === .activeElement from setAttr() (Continued from #2257) (@kfule)](https://github.com/MithrilJS/mithril.js/pull/3002)\n\nRemove vnode.dom === activeElement(vnode.dom) from setAttribute() to fix validityCheck(), to fix https://github.com/MithrilJS/mithril.js/issues/2256.\n#### [Bump glob from 11.0.0 to 11.0.1 in the normal group (@dependabot[bot])](https://github.com/MithrilJS/mithril.js/pull/3001)\n\nBumps the normal group with 1 update: [glob](https://github.com/isaacs/node-glob).  Updates `glob` from 11.0.0 to 11.0.1.  Commits.  148ef61 11.0.1.\n\n# Release v2.2.12\n\n### Patch Changes\n\n#### [disable Terser's \"reduce_funcs\" option for performance (@kfule)](https://github.com/MithrilJS/mithril.js/pull/3000)\n\nTerser's  “reduce_funcs” option seems to degrade performance.  So, disable it.\n#### [Bump chokidar from 4.0.1 to 4.0.3 in the normal group across 1 directory (@dependabot[bot])](https://github.com/MithrilJS/mithril.js/pull/2998)\n\nBumps the normal group with 1 update in the / directory: [chokidar](https://github.com/paulmillr/chokidar).  Updates `chokidar` from 4.0.1 to 4.0.3.  Release notes.\n\n# Release v2.2.11\n\n### Patch Changes\n\n#### [Use new pr-release prerelease hook (Fixes #2987) (@JAForbes)](https://github.com/MithrilJS/mithril.js/pull/2996)\n\nPer @dead-claudia's suggestion, pr-release now allows you to invoke a custom command before creating the github release.\n#### [updateStyle(): use setProperty() when css vars and dashed-properties, fixes #2989 (@kfule)](https://github.com/MithrilJS/mithril.js/pull/2991)\n\nThis PR changes updateStyle() to use setProperty() for dashed-properties.  This PR maybe fixes #2989.\n#### [Delete .github/ISSUE_TEMPLATE/0-docs.yml (@dead-claudia)](https://github.com/MithrilJS/mithril.js/pull/2993)\n\nDo a much better job discouraging filing docs bugs here.\n\n# Release v2.2.10\n\n### Patch Changes\n\n#### [[refactor] Performance improvement of updateStyle() (@kfule)](https://github.com/MithrilJS/mithril.js/pull/2985)\n\nThis is a refactoring to improve the performance of `updateStyle()`.\n\n# Release v2.2.9\n\n### Patch Changes\n\n#### [[refactor] Refactoring of hyperscript.js and render.js, including performance improvements (@kfule)](https://github.com/MithrilJS/mithril.js/pull/2983)\n\nRefactor hyperscript.js and render.js.  In particular, the replacement of fix #2622 appears to have significantly improved the performance regression.\n\n# Release v2.2.8\n\n### Patch Changes\n\n#### [m.domFor(): workaround for unintentional mangling. Fix #2842 (@kfule)](https://github.com/MithrilJS/mithril.js/pull/2981)\n\nRefactoring of domFor() for the internal bundler.  https://github.com/MithrilJS/mithril.js/blob/cfa890f68571df1ab8543097f7fa61c34ee93683/mithril.js#L157.\n#### [Drop Istanbul to kill install warnings (@dead-claudia)](https://github.com/MithrilJS/mithril.js/pull/2979)\n\nTitle's pretty self-explanatory.  Also, this isn't really used much in practice.  From a local run: ```.  $ npm ci.\n\n# Release v2.2.7\n\n### Patch Changes\n\n#### [m.domFor(): workaround for unintentional mangling. Fix #2842 (@kfule)](https://github.com/MithrilJS/mithril.js/pull/2981)\n\nRefactoring of domFor() for the internal bundler.  https://github.com/MithrilJS/mithril.js/blob/cfa890f68571df1ab8543097f7fa61c34ee93683/mithril.js#L157.\n#### [Drop Istanbul to kill install warnings (@dead-claudia)](https://github.com/MithrilJS/mithril.js/pull/2979)\n\nTitle's pretty self-explanatory.  Also, this isn't really used much in practice.  From a local run: ```.  $ npm ci.\n\n# Release v2.2.6\n\n### Patch Changes\n\n#### [m.domFor(): workaround for unintentional mangling. Fix #2842 (@kfule)](https://github.com/MithrilJS/mithril.js/pull/2981)\n\nRefactoring of domFor() for the internal bundler.  https://github.com/MithrilJS/mithril.js/blob/cfa890f68571df1ab8543097f7fa61c34ee93683/mithril.js#L157.\n#### [Drop Istanbul to kill install warnings (@dead-claudia)](https://github.com/MithrilJS/mithril.js/pull/2979)\n\nTitle's pretty self-explanatory.  Also, this isn't really used much in practice.  From a local run: ```.  $ npm ci.\n\n# Release v2.2.5\n\n### Patch Changes\n\n#### [Bump the normal group across 1 directory with 2 updates (@dependabot[bot])](https://github.com/MithrilJS/mithril.js/pull/2976)\n\nBumps the normal group with 2 updates in the / directory: [chokidar](https://github.com/paulmillr/chokidar) and [eslint](https://github.com/eslint/eslint).\n#### [Cleaning up code by making vnode.attrs always non-null (@kfule)](https://github.com/MithrilJS/mithril.js/pull/2977)\n\nCommit f9e5163 made vnode.attrs always non-null, so there is no need for code to make vnode.attrs null or assume vnode.attrs is null.\n\n# Release v2.2.4\n\n### Patch Changes\n\n#### [Bump gh-pages from 2.1.1 to 5.0.0 (@dependabot[bot])](https://github.com/MithrilJS/mithril.js/pull/2890)\n\nBumps [gh-pages](https://github.com/tschaub/gh-pages) from 2.1.1 to 5.0.0.  Release notes.  Sourced from gh-pages's releases.  v5.0.0.\n#### [Bump @babel/parser from 7.7.5 to 7.25.6 (@dependabot[bot])](https://github.com/MithrilJS/mithril.js/pull/2959)\n\nBumps [@babel/parser](https://github.com/babel/babel/tree/HEAD/packages/babel-parser) from 7.7.5 to 7.25.6.  Release notes.  Sourced from @​babel/parser's releases.\n#### [Bump minimatch from 3.0.4 to 3.1.2 (@dependabot[bot])](https://github.com/MithrilJS/mithril.js/pull/2816)\n\nBumps [minimatch](https://github.com/isaacs/minimatch) from 3.0.4 to 3.1.2.  Commits.  699c459 3.1.2.  2f2b5ff fix: trim pattern.  25d7c0d 3.1.1.\n#### [Bump yaml and lint-staged (@dependabot[bot])](https://github.com/MithrilJS/mithril.js/pull/2830)\n\nBumps [yaml](https://github.com/eemeli/yaml) to 2.2.2 and updates ancestor dependency [lint-staged](https://github.com/okonet/lint-staged).\n#### [Bump gh-pages from 5.0.0 to 6.1.1 (@dependabot[bot])](https://github.com/MithrilJS/mithril.js/pull/2958)\n\nBumps [gh-pages](https://github.com/tschaub/gh-pages) from 5.0.0 to 6.1.1.  Release notes.  Sourced from gh-pages's releases.  v6.1.1.  Fixes.\n#### [Bump glob from 7.1.4 to 11.0.0 (@dependabot[bot])](https://github.com/MithrilJS/mithril.js/pull/2957)\n\nBumps [glob](https://github.com/isaacs/node-glob) from 7.1.4 to 11.0.0.  Changelog.  Sourced from glob's changelog.  changeglob.  11.0.  Drop support for node before v20.\n#### [Bump rimraf from 3.0.2 to 6.0.1 (@dependabot[bot])](https://github.com/MithrilJS/mithril.js/pull/2960)\n\nBumps [rimraf](https://github.com/isaacs/rimraf) from 3.0.2 to 6.0.1.  Changelog.  Sourced from rimraf's changelog.  6.0.  Drop support for nodes before v20.\n#### [Bump lint-staged from 13.2.1 to 15.2.10 (@dependabot[bot])](https://github.com/MithrilJS/mithril.js/pull/2961)\n\n[//]: # (dependabot-start).  ⚠️  **Dependabot is rebasing this PR** ⚠️.  Rebasing might not happen immediately, so don't worry if this takes some time.\n#### [Revise issue templates (@dead-claudia)](https://github.com/MithrilJS/mithril.js/pull/2955)\n\n\n#### [Update ospec and a few other dependencies (@dead-claudia)](https://github.com/MithrilJS/mithril.js/pull/2907)\n\n1.  Update ospec to the version I just published.  2.\n#### [Fix some outstanding bugs in the docs linter. (@dead-claudia)](https://github.com/MithrilJS/mithril.js/pull/2906)\n\nMissed an edge case in the task queue, and I also wanted to fully dedupe network requests.  Locally it passes.\n#### [Rewrite docs linter, ease JSFiddle request debugging (@dead-claudia)](https://github.com/MithrilJS/mithril.js/pull/2904)\n\nI'll defer to the commit descriptions.  They're self-descriptive.  The first diff is quite large.\n#### [Update vnodes.md (@dead-claudia)](https://github.com/MithrilJS/mithril.js/pull/2903)\n\nFix a broken link.  Did some further digging (it's been a while since I've played with the scripts) and found that the JSFiddle errors are just warnings.\n#### [Migrate to Node 20, clean up workflows (@dead-claudia)](https://github.com/MithrilJS/mithril.js/pull/2902)\n\n**Note: ignore the commits.  It's a mess.  Just read the combined diff - the PR itself is the standalone unit.  I plan to squash this as I merge anyways.**.\n#### [Remove dependance on global window and document (@KoryNunn)](https://github.com/MithrilJS/mithril.js/pull/2897)\n\nUse window and document from render target instead of using globals.  This makes unit and intergration testing much easier.\n#### [Bump braces from 3.0.2 to 3.0.3 (@dependabot[bot])](https://github.com/MithrilJS/mithril.js/pull/2896)\n\nBumps [braces](https://github.com/micromatch/braces) from 3.0.2 to 3.0.3.  Commits.  74b2db2 3.0.3.  88f1429 update eslint.  lint, fix unit tests.\n#### [Tweak docs with warning to fix #2508 (@dead-claudia)](https://github.com/MithrilJS/mithril.js/pull/2895)\n\n[z] Documentation change.  [z] My change requires a change to the documentation.  [z] I have updated the documentation accordingly.\n#### [Bump qs from 6.5.2 to 6.5.3 (@dependabot[bot])](https://github.com/MithrilJS/mithril.js/pull/2807)\n\nBumps [qs](https://github.com/ljharb/qs) from 6.5.2 to 6.5.3.  Changelog.  Sourced from qs's changelog.  6.5.3.  [Fix] parse: ignore __proto__ keys (#428).\n#### [Temporarily host REM on fly to fix the docs (@JAForbes)](https://github.com/MithrilJS/mithril.js/pull/2893)\n\nFixes REM examples in the docs.  The documentation currently has a dead link as REM is no longer hosted on heroku.\n#### [Move from individual code owners to just pinging all collaborators (@dead-claudia)](https://github.com/MithrilJS/mithril.js/pull/2844)\n\nMost collaborators have commit access, and it'd make it a little easier (and more likely) for pull requests to get reviewed.\n#### [docs: absolute url in version selector to avoid 404 errors (2 of 2) (@tbreuss)](https://github.com/MithrilJS/mithril.js/pull/2839)\n\nFixes #2832 (2 of 2 pull requests).  See my comment at https://github.com/MithrilJS/mithril.js/pull/2835#issuecomment-1535657892.\n#### [fix markdown editor example, bump marked.js version up (@tbreuss)](https://github.com/MithrilJS/mithril.js/pull/2848)\n\nFixes the strange behavior of markdown editor example.  Using newest version of marked.js, fixed strange behavior of markdown editor example.  See #2845.\n#### [Bump word-wrap from 1.2.3 to 1.2.4 (@dependabot[bot])](https://github.com/MithrilJS/mithril.js/pull/2856)\n\nBumps [word-wrap](https://github.com/jonschlinkert/word-wrap) from 1.2.3 to 1.2.4.  Release notes.  Sourced from word-wrap's releases.  1.2.4.  What's Changed.\n#### [Add missing `m.censor` to API navigation (@dead-claudia)](https://github.com/MithrilJS/mithril.js/pull/2843)\n\nNot sure how I forgot about this when I added the method.\n#### [docs: fix regex for parsing page title (@tbreuss)](https://github.com/MithrilJS/mithril.js/pull/2837)\n\nFixes https://github.com/MithrilJS/mithril.js/issues/2833.  I tested the generated documentation on my dev machine successfully.\n#### [docs: fix broken anchor link on github/npm (@tbreuss)](https://github.com/MithrilJS/mithril.js/pull/2838)\n\nFixed a not working anchor link on github and npm by removing the question mark.\n#### [hyperscript: handles shared empty attrs, fixes #2821 (@kfule)](https://github.com/MithrilJS/mithril.js/pull/2822)\n\nWhenever there are selector-derived attrs, the attrs object will be regenerated and not shared.\n#### [Fix typos in `stream()` docs (@mtsknn)](https://github.com/MithrilJS/mithril.js/pull/2825)\n\nNoticed these typos while reading through the page.\n#### [Bump async from 2.6.3 to 2.6.4 (@dependabot[bot])](https://github.com/MithrilJS/mithril.js/pull/2815)\n\nBumps [async](https://github.com/caolan/async) from 2.6.3 to 2.6.4.  Changelog.  Sourced from async's changelog.  v2.6.4.\n\n# Release v2.2.3\n\n### Patch Changes\n\n#### [Use markdown for the README badges (@pygy)](https://github.com/MithrilJS/mithril.js/pull/2773)\n\nUse markdown for the README badges.\n\n# Release v2.2.2\n\n### Patch Changes\n\n#### [Use markdown for the README badges (@pygy)](https://github.com/MithrilJS/mithril.js/pull/2773)\n\nUse markdown for the README badges.\n\n# Release v2.2.1\n\n### Patch Changes\n\n#### [Move the chat to Zulip (@pygy)](https://github.com/MithrilJS/mithril.js/pull/2771)\n\nThis updates the documentation to link to the new Zulip chat room.\n\n# Release v2.2.0\n\n### Minor Changes\n\n#### [m.censor: work around a bunder bug (@kfule)](https://github.com/MithrilJS/mithril.js/pull/2752)\n\nThe internal bundler sometimes mangles the words in RegExp literals incorrectly.  Please see below.\n#### [Warn about reusing mutated attrs object - fixes #2719 (@StephanHoyer)](https://github.com/MithrilJS/mithril.js/pull/2722)\n\n\n#### [Send URLSearchParams as request body without extra configuration (@Coteh)](https://github.com/MithrilJS/mithril.js/pull/2695)\n\nThis PR fixes an oddity I noticed in the way `m.request` handles `URLSearchParams` object.  It now handles it in the same sort of way XHR and Fetch do it.\n#### [Add `params:` to `m.route.Link`, fix docs (@dead-claudia)](https://github.com/MithrilJS/mithril.js/pull/2537)\n\nAdd `params:` to `m.route.Link`.  Minor fix to docs to reflect reality with `m.route.Link`'s `disabled:` attribute.\n#### [Allow Mithril to be loaded in non-browser environments without modification (@dead-claudia)](https://github.com/MithrilJS/mithril.js/pull/2633)\n\nRecast the global reads to all be guarded with `typeof`, so that if they aren't defined, they're just `null`.\n#### [Add a `m.Fragment = \"[\"` utility for JSX users. (@dead-claudia)](https://github.com/MithrilJS/mithril.js/pull/2744)\n\nThe title says it all, and the diff's obvious.  Resolves https://github.com/MithrilJS/mithril.js/issues/2640 and probably others.\n   \n### Patch Changes\n\n#### [Enable --minimize-semver-change for pr-release (@JAForbes)](https://github.com/MithrilJS/mithril.js/pull/2769)\n\nMinimizes semver changes on release to the minimum required version bump to satisfy major/minor/patch semver ranges.  Minimizes the semver change so that.\n#### [Clean up m.route.Link (@barneycarroll)](https://github.com/MithrilJS/mithril.js/pull/2768)\n\nAn attempt at better demonstrating `m.route.Link` with less text.  Fixes #2767.\n#### [Runtime-deprecate ospec, change `change-log` to `changelog`, fix a few assorted bugs (@dead-claudia)](https://github.com/MithrilJS/mithril.js/pull/2578)\n\nThis PR is in two parts: 1.  Revise the build system and some of the local dev setup.  Fully split ospec from the repo, and add it as a dependency.\n#### [Add meta description to docs (@StephanHoyer)](https://github.com/MithrilJS/mithril.js/pull/2316)\n\nrework of #2149.  added a meta description parser and meta descriptions to all docs pages.  because google.  built the docs, inspected the output manually.\n#### [Fixed badges, consistent naming of Mithril.js (@tbreuss)](https://github.com/MithrilJS/mithril.js/pull/2750)\n\nuse consistent naming of Mithril.js.  fix badges in README.  Fixes issue #2749.\n#### [Catch malformed URI Components (@jdiderik)](https://github.com/MithrilJS/mithril.js/pull/2711)\n\nFix for error thrown when a value contains non-valid / malformed URI Component.  Example: test=%c5%a1%e8ZM%80%82H.  will throw \"URI malformed\".\n#### [Correctly handle invalid escapes in routes based on 0a5ead31c9fbd7b153c521c7f9d3df7bf826ce6c (@StephanHoyer)](https://github.com/MithrilJS/mithril.js/pull/2743)\n\nfixes #2061.  @dead-claudia I just redid your change but slightly different in order to handle a mix of wrong and right encodings properly.\n#### [Standardise vnode text representation (@barneycarroll)](https://github.com/MithrilJS/mithril.js/pull/2670)\n\nThis addresses the crucial feature of #2669: text is always represented as virtual text nodes, never as a `vnode.text`.\n#### [Issue 2624 no content 204 parse (@Evoke-PHP)](https://github.com/MithrilJS/mithril.js/pull/2641)\n\nAdded guard so that JSON.parse does not fail on IE11 with no content empty string being parsed.  Fixes https://github.com/MithrilJS/mithril.js/issues/2624.\n#### [[m.request] work around a bundler bug, fix #2647 (@pygy)](https://github.com/MithrilJS/mithril.js/pull/2655)\n\nThe bundler mangles identifier-like strings within RegExps, this works around the problem by not using such RegExps.\n#### [Reject request on XHR timeout (@kevinfiol)](https://github.com/MithrilJS/mithril.js/pull/2646)\n\nDerived from PR #2581.  Allows requests to properly reject on event of a timeout.\n#### [Remove extra isLifecycleMethod call from removeAttr (@ZeikJT)](https://github.com/MithrilJS/mithril.js/pull/2594)\n\nRemoving an extra isLifecycleMethod in the removeAttr method, it isn't needed since it's already checked on the previous line.\n#### [Fix #2601 (@dead-claudia)](https://github.com/MithrilJS/mithril.js/pull/2603)\n\nFix issue where ending a stream in the middle of a stream callback would result in erroneous parent stream state for the rest of that emit.  Fixes #2601.\n#### [Add streams to releases again, include minified bundle, drop internal stuff from npm (@dead-claudia)](https://github.com/MithrilJS/mithril.js/pull/2539)\n\nAdd `stream/stream.js` to releases again.  Add `stream/stream.min.js` now that the process is remotely sane now.\n#### [Make errors and their messages more accurate and helpful (@dead-claudia)](https://github.com/MithrilJS/mithril.js/pull/2536)\n\nI updated error messages to be much more helpful.\n#### [Fix assertion descriptions (@soulofmischief)](https://github.com/MithrilJS/mithril.js/pull/2405)\n\nI moved the return statement to the end of define() so that it returns even if the comparison fails.\n#### [Fix branch target (@dead-claudia)](https://github.com/MithrilJS/mithril.js/pull/2765)\n\nhttps://github.com/MithrilJS/mithril.js/runs/6199543939?check_suite_focus=true.\n#### [Automate mithril's release workflow (@JAForbes)](https://github.com/MithrilJS/mithril.js/pull/2760)\n\nAutomated releases, pre-releases, (code) rollbacks and recovery, npm publishing, change log management just by using normal github flow.\n#### [rework jsx docs (@StephanHoyer)](https://github.com/MithrilJS/mithril.js/pull/2758)\n\n\n#### [Add Simple Application Flems Supporting v2.0.4 and up (@tbreuss)](https://github.com/MithrilJS/mithril.js/pull/2751)\n\nAdded Flems for Simple Application supporting v2.0.4 of Mithril.js.  Fixes Issue #2710.\n#### [Make example work with webpack v5.69.1 (@StephanHoyer)](https://github.com/MithrilJS/mithril.js/pull/2757)\n\nfixes #2634.\n#### [2604: correct and move text about statements in view method (@kevinfiol)](https://github.com/MithrilJS/mithril.js/pull/2748)\n\nAddresses #2604.\n#### [Fix lint errors (@StephanHoyer)](https://github.com/MithrilJS/mithril.js/pull/2745)\n\n\n#### [WIP: Update modularisation details in Installation docs (@orbitbot)](https://github.com/MithrilJS/mithril.js/pull/2620)\n\nadded link to flems.io as an easier way to just try out the framework.  -.  Documentation has grown a bit stale.\n#### [Added power support for the travis.yml file with ppc64le (@sreekanth370)](https://github.com/MithrilJS/mithril.js/pull/2644)\n\nAdded power support for the travis.yml file with ppc64le.  This is part of the Ubuntu distribution for ppc64le.\n#### [Updated babel/webpack docs to work with latest versions (@pereriksson)](https://github.com/MithrilJS/mithril.js/pull/2649)\n\nAs a developer I tried setting up Mithril with Babel and Webpack but failed because of a variety of errors.\n#### [[docs] route redirection using the history API (@pygy)](https://github.com/MithrilJS/mithril.js/pull/1767)\n\nThis is an attempt at fixing #1759, but there may be more to be added.  Feedback welcome.  ping @dontwork.\n#### [Bump path-parse from 1.0.6 to 1.0.7 (@dependabot[bot])](https://github.com/MithrilJS/mithril.js/pull/2718)\n\nBumps [path-parse](https://github.com/jbgutierrez/path-parse) from 1.0.6 to 1.0.7.  Commits.  See full diff in compare view.\n#### [Bump glob-parent from 5.1.0 to 5.1.2 (@dependabot[bot])](https://github.com/MithrilJS/mithril.js/pull/2693)\n\nBumps [glob-parent](https://github.com/gulpjs/glob-parent) from 5.1.0 to 5.1.2.  Release notes.  Sourced from glob-parent's releases.  v5.1.2.  Bug Fixes.\n#### [Bump ajv from 6.10.2 to 6.12.6 (@dependabot[bot])](https://github.com/MithrilJS/mithril.js/pull/2741)\n\nBumps [ajv](https://github.com/ajv-validator/ajv) from 6.10.2 to 6.12.6.  Release notes.  Sourced from ajv's releases.  v6.12.6.  Fix performance issue of \"url\" format.\n#### [Update standalone usage (@ghost)](https://github.com/MithrilJS/mithril.js/pull/2651)\n\n\n#### [Avoid double encoding of function signatures - fixes #2720 (@StephanHoyer)](https://github.com/MithrilJS/mithril.js/pull/2721)\n\n\n#### [Show previous versions (@mike-ward)](https://github.com/MithrilJS/mithril.js/pull/2353)\n\nAdd Dropdown that shows links to archived versions of the documentation.\n#### [docs: improve m.request return value description (@GAumala)](https://github.com/MithrilJS/mithril.js/pull/2206)\n\nIn the m.request return value description, add a line informing that error status codes cause the promise to reject.\n#### [A note on JSX events (@pereriksson)](https://github.com/MithrilJS/mithril.js/pull/2648)\n\nNaming JSX events according to their documentation produces unexpected results with incorrectly named events when using JSX with Mithril.\n#### [Document route resolution cancellation, fixes #1759 (@barneycarroll)](https://github.com/MithrilJS/mithril.js/pull/2672)\n\nAlso fixes a broken internal link.\n#### [Bump marked from 0.7.0 to 4.0.10 (@dependabot[bot])](https://github.com/MithrilJS/mithril.js/pull/2717)\n\nBumps [marked](https://github.com/markedjs/marked) from 0.7.0 to 4.0.10.  Release notes.  Sourced from marked's releases.  v4.0.10.  4.0.10 (2022-01-13).  Bug Fixes.\n#### [Flems in docs (#2348) [skip ci] (@porsager)](https://github.com/MithrilJS/mithril.js/pull/2348)\n\nAdded flems instead of the current codepen samples.\n#### [Remove old TOC link (@ArthurClemens)](https://github.com/MithrilJS/mithril.js/pull/2698)\n\nContent was moved some time ago and linked section no longer exists.\n#### [Cavemansspa patch 1 (@cavemansspa)](https://github.com/MithrilJS/mithril.js/pull/2696)\n\nDocumentation update.\n#### [Bump hosted-git-info from 2.8.4 to 2.8.9 (@dependabot[bot])](https://github.com/MithrilJS/mithril.js/pull/2684)\n\nBumps [hosted-git-info](https://github.com/npm/hosted-git-info) from 2.8.4 to 2.8.9.  Changelog.  Sourced from hosted-git-info's changelog.  2.8.9 (2021-04-07).\n#### [Bump lodash from 4.17.20 to 4.17.21 (@dependabot[bot])](https://github.com/MithrilJS/mithril.js/pull/2680)\n\nBumps [lodash](https://github.com/lodash/lodash) from 4.17.20 to 4.17.21.  Commits.  f299b52 Bump to v4.17.21.\n#### [Bump handlebars from 4.7.6 to 4.7.7 (@dependabot[bot])](https://github.com/MithrilJS/mithril.js/pull/2679)\n\nBumps [handlebars](https://github.com/wycats/handlebars.js) from 4.7.6 to 4.7.7.  Changelog.  Sourced from handlebars's changelog.  v4.7.7 - February 15th, 2021.\n#### [Remove unreachable keyed node logic, fixes #2597 (@barneycarroll)](https://github.com/MithrilJS/mithril.js/pull/2673)\n\n\n#### [Delete test-utils/README.md (@dead-claudia)](https://github.com/MithrilJS/mithril.js/pull/2674)\n\nWe don't expose this publicly anymore, so there's literally no justification for this file's existence.\n#### [simple-application.md: consistent use of type=submit (@danbst)](https://github.com/MithrilJS/mithril.js/pull/2657)\n\nWhen following tutorial and typing everything in, I was confused that Save button didn't work.\n#### [Fix inconsistent capitalizations of \"JavaScript\" (@mtsknn)](https://github.com/MithrilJS/mithril.js/pull/2639)\n\n\"Javascript\"/\"javascript\" → \"JavaScript\".  Fixes #2398, or at least I can't find any more incorrect capitalizations.\n#### [fix some typos (@osban)](https://github.com/MithrilJS/mithril.js/pull/2487)\n\nFound some typos.  Mainly unescaped `|` in tables, but also a few other irregularities.  Not all problems are visible in the website docs.\n#### [Replace mocha by ospec in testing page (@gamtiq)](https://github.com/MithrilJS/mithril.js/pull/2585)\n\nFixed a typo in testing doc page.  Currently there is reference to `mocha` in the page whereas `opsec` is used.\n#### [Bump acorn from 7.1.0 to 7.4.0 (@dependabot[bot])](https://github.com/MithrilJS/mithril.js/pull/2630)\n\nBumps [acorn](https://github.com/acornjs/acorn) from 7.1.0 to 7.4.0.  Commits.  54efb62 Mark version 7.4.0.\n#### [Bump handlebars from 4.4.2 to 4.7.6 (@dependabot[bot])](https://github.com/MithrilJS/mithril.js/pull/2629)\n\nBumps [handlebars](https://github.com/wycats/handlebars.js) from 4.4.2 to 4.7.6.  Changelog.  Sourced from handlebars's changelog.  v4.7.6 - April 3rd, 2020.\n#### [Bump lodash from 4.17.15 to 4.17.20 (@dependabot[bot])](https://github.com/MithrilJS/mithril.js/pull/2628)\n\nBumps [lodash](https://github.com/lodash/lodash) from 4.17.15 to 4.17.20.  Commits.  ded9bc6 Bump to v4.17.20.  63150ef Documentation fixes.\n#### [Bump minimist from 1.2.0 to 1.2.3 (@dependabot[bot])](https://github.com/MithrilJS/mithril.js/pull/2627)\n\nBumps [minimist](https://github.com/substack/minimist) from 1.2.0 to 1.2.3.  Commits.  6457d74 1.2.3.  38a4d1c even more aggressive checks for protocol pollution.\n#### [Update installation.md (@purefan)](https://github.com/MithrilJS/mithril.js/pull/2608)\n\nOffer to install mithril as a webpack plugin.  Just makes my life easier by not having to include mithril in every one of my js files.\n#### [replace slave label with replica (@stephanos)](https://github.com/MithrilJS/mithril.js/pull/2605)\n\nOne of the example is using the antiquated word \"slave\" for a database replica.  I updated the language and tested the change.\n#### [ES6 and m.trust docs patch (@kczx3)](https://github.com/MithrilJS/mithril.js/pull/2593)\n\nWhile reading through some of the documentation I saw some issues with both the ES6 and `m.trust` pages.\n#### [docs: Fix simple typo, subsequece -> subsequence (@timgates42)](https://github.com/MithrilJS/mithril.js/pull/2582)\n\nThere is a small typo in mithril.js, render/render.js.  Should read `subsequence` rather than `subsequece`.\n#### [change link to go to ospec instead of mocha (@akessner)](https://github.com/MithrilJS/mithril.js/pull/2576)\n\nChange the link to point to ospec docs in github.  ospec link went to mochajs.  [issue 2575](https://github.com/MithrilJS/mithril.js/issues/2575).  N/A.  N/A.  N/A.\n#### [updated to the Vimeo showcase (@CreaturesInUnitards)](https://github.com/MithrilJS/mithril.js/pull/2573)\n\nThe scrimba version of Mithril 0-60 was built on their beta platform, and doesn't really even work anymore.\n#### [adding more community examples (@boazblake)](https://github.com/MithrilJS/mithril.js/pull/2567)\n\n\n#### [Exclude archive of previous docs (@cztomsik)](https://github.com/MithrilJS/mithril.js/pull/2561)\n\nupdate .npmignore so that archives are not included in the resulting package.  space/bandwidth savings.  fix #2552.\n#### [Pimp the docs linter (and assorted changes) (@pygy)](https://github.com/MithrilJS/mithril.js/pull/2553)\n\nAdd an optional cache for faster runs.  Add a final report.  Don't return anything from `exec()`.  Cover more files.  Look for a \"--cache\" option.\n#### [Recast key docs to be much clearer and more accurate (@dead-claudia)](https://github.com/MithrilJS/mithril.js/pull/2540)\n\nRecast key docs to be much clearer and more accurate, including a few Flems examples to help intuitively explain things.\n#### [Add `m.censor`, adjust `m.route.Link` to use it (@dead-claudia)](https://github.com/MithrilJS/mithril.js/pull/2538)\n\nAdd `m.censor`.  Adjust `m.route.Link` to use it.  Restructure a few things for better code reuse.  Fixes #2472.\n#### [Update fetch() browser support in docs (@qgustavor)](https://github.com/MithrilJS/mithril.js/pull/2522)\n\nAs [Can I use](https://caniuse.com/#feat=fetch) shows `fetch()` supported since Safari 10.1 and iOS Safari 10.3.\n#### [docs: Add release dates to all change-log files (@maranomynet)](https://github.com/MithrilJS/mithril.js/pull/2513)\n\nI'd like to introduce release dates to the change log files.  Release dates are human-friendly and add a bit of historical perspective to change-log files.\n\n# Release v2.1.0\n\n### Minor Changes\n\n#### [m.censor: work around a bunder bug (@kfule)](https://github.com/MithrilJS/mithril.js/pull/2752)\n\nThe internal bundler sometimes mangles the words in RegExp literals incorrectly.  Please see below.\n#### [Warn about reusing mutated attrs object - fixes #2719 (@StephanHoyer)](https://github.com/MithrilJS/mithril.js/pull/2722)\n\n\n#### [Send URLSearchParams as request body without extra configuration (@Coteh)](https://github.com/MithrilJS/mithril.js/pull/2695)\n\nThis PR fixes an oddity I noticed in the way `m.request` handles `URLSearchParams` object.  It now handles it in the same sort of way XHR and Fetch do it.\n#### [Add `params:` to `m.route.Link`, fix docs (@dead-claudia)](https://github.com/MithrilJS/mithril.js/pull/2537)\n\nAdd `params:` to `m.route.Link`.  Minor fix to docs to reflect reality with `m.route.Link`'s `disabled:` attribute.\n#### [Allow Mithril to be loaded in non-browser environments without modification (@dead-claudia)](https://github.com/MithrilJS/mithril.js/pull/2633)\n\nRecast the global reads to all be guarded with `typeof`, so that if they aren't defined, they're just `null`.\n#### [Add a `m.Fragment = \"[\"` utility for JSX users. (@dead-claudia)](https://github.com/MithrilJS/mithril.js/pull/2744)\n\nThe title says it all, and the diff's obvious.  Resolves https://github.com/MithrilJS/mithril.js/issues/2640 and probably others.\n   \n### Patch Changes\n\n#### [Enable --minimize-semver-change for pr-release (@JAForbes)](https://github.com/MithrilJS/mithril.js/pull/2769)\n\nMinimizes semver changes on release to the minimum required version bump to satisfy major/minor/patch semver ranges.  Minimizes the semver change so that.\n#### [Clean up m.route.Link (@barneycarroll)](https://github.com/MithrilJS/mithril.js/pull/2768)\n\nAn attempt at better demonstrating `m.route.Link` with less text.  Fixes #2767.\n#### [Runtime-deprecate ospec, change `change-log` to `changelog`, fix a few assorted bugs (@dead-claudia)](https://github.com/MithrilJS/mithril.js/pull/2578)\n\nThis PR is in two parts: 1.  Revise the build system and some of the local dev setup.  Fully split ospec from the repo, and add it as a dependency.\n#### [Add meta description to docs (@StephanHoyer)](https://github.com/MithrilJS/mithril.js/pull/2316)\n\nrework of #2149.  added a meta description parser and meta descriptions to all docs pages.  because google.  built the docs, inspected the output manually.\n#### [Fixed badges, consistent naming of Mithril.js (@tbreuss)](https://github.com/MithrilJS/mithril.js/pull/2750)\n\nuse consistent naming of Mithril.js.  fix badges in README.  Fixes issue #2749.\n#### [Catch malformed URI Components (@jdiderik)](https://github.com/MithrilJS/mithril.js/pull/2711)\n\nFix for error thrown when a value contains non-valid / malformed URI Component.  Example: test=%c5%a1%e8ZM%80%82H.  will throw \"URI malformed\".\n#### [Correctly handle invalid escapes in routes based on 0a5ead31c9fbd7b153c521c7f9d3df7bf826ce6c (@StephanHoyer)](https://github.com/MithrilJS/mithril.js/pull/2743)\n\nfixes #2061.  @dead-claudia I just redid your change but slightly different in order to handle a mix of wrong and right encodings properly.\n#### [Standardise vnode text representation (@barneycarroll)](https://github.com/MithrilJS/mithril.js/pull/2670)\n\nThis addresses the crucial feature of #2669: text is always represented as virtual text nodes, never as a `vnode.text`.\n#### [Issue 2624 no content 204 parse (@Evoke-PHP)](https://github.com/MithrilJS/mithril.js/pull/2641)\n\nAdded guard so that JSON.parse does not fail on IE11 with no content empty string being parsed.  Fixes https://github.com/MithrilJS/mithril.js/issues/2624.\n#### [[m.request] work around a bundler bug, fix #2647 (@pygy)](https://github.com/MithrilJS/mithril.js/pull/2655)\n\nThe bundler mangles identifier-like strings within RegExps, this works around the problem by not using such RegExps.\n#### [Reject request on XHR timeout (@kevinfiol)](https://github.com/MithrilJS/mithril.js/pull/2646)\n\nDerived from PR #2581.  Allows requests to properly reject on event of a timeout.\n#### [Remove extra isLifecycleMethod call from removeAttr (@ZeikJT)](https://github.com/MithrilJS/mithril.js/pull/2594)\n\nRemoving an extra isLifecycleMethod in the removeAttr method, it isn't needed since it's already checked on the previous line.\n#### [Fix #2601 (@dead-claudia)](https://github.com/MithrilJS/mithril.js/pull/2603)\n\nFix issue where ending a stream in the middle of a stream callback would result in erroneous parent stream state for the rest of that emit.  Fixes #2601.\n#### [Add streams to releases again, include minified bundle, drop internal stuff from npm (@dead-claudia)](https://github.com/MithrilJS/mithril.js/pull/2539)\n\nAdd `stream/stream.js` to releases again.  Add `stream/stream.min.js` now that the process is remotely sane now.\n#### [Make errors and their messages more accurate and helpful (@dead-claudia)](https://github.com/MithrilJS/mithril.js/pull/2536)\n\nI updated error messages to be much more helpful.\n#### [Fix assertion descriptions (@soulofmischief)](https://github.com/MithrilJS/mithril.js/pull/2405)\n\nI moved the return statement to the end of define() so that it returns even if the comparison fails.\n#### [Fix branch target (@dead-claudia)](https://github.com/MithrilJS/mithril.js/pull/2765)\n\nhttps://github.com/MithrilJS/mithril.js/runs/6199543939?check_suite_focus=true.\n#### [Automate mithril's release workflow (@JAForbes)](https://github.com/MithrilJS/mithril.js/pull/2760)\n\nAutomated releases, pre-releases, (code) rollbacks and recovery, npm publishing, change log management just by using normal github flow.\n#### [rework jsx docs (@StephanHoyer)](https://github.com/MithrilJS/mithril.js/pull/2758)\n\n\n#### [Add Simple Application Flems Supporting v2.0.4 and up (@tbreuss)](https://github.com/MithrilJS/mithril.js/pull/2751)\n\nAdded Flems for Simple Application supporting v2.0.4 of Mithril.js.  Fixes Issue #2710.\n#### [Make example work with webpack v5.69.1 (@StephanHoyer)](https://github.com/MithrilJS/mithril.js/pull/2757)\n\nfixes #2634.\n#### [2604: correct and move text about statements in view method (@kevinfiol)](https://github.com/MithrilJS/mithril.js/pull/2748)\n\nAddresses #2604.\n#### [Fix lint errors (@StephanHoyer)](https://github.com/MithrilJS/mithril.js/pull/2745)\n\n\n#### [WIP: Update modularisation details in Installation docs (@orbitbot)](https://github.com/MithrilJS/mithril.js/pull/2620)\n\nadded link to flems.io as an easier way to just try out the framework.  -.  Documentation has grown a bit stale.\n#### [Added power support for the travis.yml file with ppc64le (@sreekanth370)](https://github.com/MithrilJS/mithril.js/pull/2644)\n\nAdded power support for the travis.yml file with ppc64le.  This is part of the Ubuntu distribution for ppc64le.\n#### [Updated babel/webpack docs to work with latest versions (@pereriksson)](https://github.com/MithrilJS/mithril.js/pull/2649)\n\nAs a developer I tried setting up Mithril with Babel and Webpack but failed because of a variety of errors.\n#### [[docs] route redirection using the history API (@pygy)](https://github.com/MithrilJS/mithril.js/pull/1767)\n\nThis is an attempt at fixing #1759, but there may be more to be added.  Feedback welcome.  ping @dontwork.\n#### [Bump path-parse from 1.0.6 to 1.0.7 (@dependabot[bot])](https://github.com/MithrilJS/mithril.js/pull/2718)\n\nBumps [path-parse](https://github.com/jbgutierrez/path-parse) from 1.0.6 to 1.0.7.  Commits.  See full diff in compare view.\n#### [Bump glob-parent from 5.1.0 to 5.1.2 (@dependabot[bot])](https://github.com/MithrilJS/mithril.js/pull/2693)\n\nBumps [glob-parent](https://github.com/gulpjs/glob-parent) from 5.1.0 to 5.1.2.  Release notes.  Sourced from glob-parent's releases.  v5.1.2.  Bug Fixes.\n#### [Bump ajv from 6.10.2 to 6.12.6 (@dependabot[bot])](https://github.com/MithrilJS/mithril.js/pull/2741)\n\nBumps [ajv](https://github.com/ajv-validator/ajv) from 6.10.2 to 6.12.6.  Release notes.  Sourced from ajv's releases.  v6.12.6.  Fix performance issue of \"url\" format.\n#### [Update standalone usage (@ghost)](https://github.com/MithrilJS/mithril.js/pull/2651)\n\n\n#### [Avoid double encoding of function signatures - fixes #2720 (@StephanHoyer)](https://github.com/MithrilJS/mithril.js/pull/2721)\n\n\n#### [Show previous versions (@mike-ward)](https://github.com/MithrilJS/mithril.js/pull/2353)\n\nAdd Dropdown that shows links to archived versions of the documentation.\n#### [docs: improve m.request return value description (@GAumala)](https://github.com/MithrilJS/mithril.js/pull/2206)\n\nIn the m.request return value description, add a line informing that error status codes cause the promise to reject.\n#### [A note on JSX events (@pereriksson)](https://github.com/MithrilJS/mithril.js/pull/2648)\n\nNaming JSX events according to their documentation produces unexpected results with incorrectly named events when using JSX with Mithril.\n#### [Document route resolution cancellation, fixes #1759 (@barneycarroll)](https://github.com/MithrilJS/mithril.js/pull/2672)\n\nAlso fixes a broken internal link.\n#### [Bump marked from 0.7.0 to 4.0.10 (@dependabot[bot])](https://github.com/MithrilJS/mithril.js/pull/2717)\n\nBumps [marked](https://github.com/markedjs/marked) from 0.7.0 to 4.0.10.  Release notes.  Sourced from marked's releases.  v4.0.10.  4.0.10 (2022-01-13).  Bug Fixes.\n#### [Flems in docs (#2348) [skip ci] (@porsager)](https://github.com/MithrilJS/mithril.js/pull/2348)\n\nAdded flems instead of the current codepen samples.\n#### [Remove old TOC link (@ArthurClemens)](https://github.com/MithrilJS/mithril.js/pull/2698)\n\nContent was moved some time ago and linked section no longer exists.\n#### [Cavemansspa patch 1 (@cavemansspa)](https://github.com/MithrilJS/mithril.js/pull/2696)\n\nDocumentation update.\n#### [Bump hosted-git-info from 2.8.4 to 2.8.9 (@dependabot[bot])](https://github.com/MithrilJS/mithril.js/pull/2684)\n\nBumps [hosted-git-info](https://github.com/npm/hosted-git-info) from 2.8.4 to 2.8.9.  Changelog.  Sourced from hosted-git-info's changelog.  2.8.9 (2021-04-07).\n#### [Bump lodash from 4.17.20 to 4.17.21 (@dependabot[bot])](https://github.com/MithrilJS/mithril.js/pull/2680)\n\nBumps [lodash](https://github.com/lodash/lodash) from 4.17.20 to 4.17.21.  Commits.  f299b52 Bump to v4.17.21.\n#### [Bump handlebars from 4.7.6 to 4.7.7 (@dependabot[bot])](https://github.com/MithrilJS/mithril.js/pull/2679)\n\nBumps [handlebars](https://github.com/wycats/handlebars.js) from 4.7.6 to 4.7.7.  Changelog.  Sourced from handlebars's changelog.  v4.7.7 - February 15th, 2021.\n#### [Remove unreachable keyed node logic, fixes #2597 (@barneycarroll)](https://github.com/MithrilJS/mithril.js/pull/2673)\n\n\n#### [Delete test-utils/README.md (@dead-claudia)](https://github.com/MithrilJS/mithril.js/pull/2674)\n\nWe don't expose this publicly anymore, so there's literally no justification for this file's existence.\n#### [simple-application.md: consistent use of type=submit (@danbst)](https://github.com/MithrilJS/mithril.js/pull/2657)\n\nWhen following tutorial and typing everything in, I was confused that Save button didn't work.\n#### [Fix inconsistent capitalizations of \"JavaScript\" (@mtsknn)](https://github.com/MithrilJS/mithril.js/pull/2639)\n\n\"Javascript\"/\"javascript\" → \"JavaScript\".  Fixes #2398, or at least I can't find any more incorrect capitalizations.\n#### [fix some typos (@osban)](https://github.com/MithrilJS/mithril.js/pull/2487)\n\nFound some typos.  Mainly unescaped `|` in tables, but also a few other irregularities.  Not all problems are visible in the website docs.\n#### [Replace mocha by ospec in testing page (@gamtiq)](https://github.com/MithrilJS/mithril.js/pull/2585)\n\nFixed a typo in testing doc page.  Currently there is reference to `mocha` in the page whereas `opsec` is used.\n#### [Bump acorn from 7.1.0 to 7.4.0 (@dependabot[bot])](https://github.com/MithrilJS/mithril.js/pull/2630)\n\nBumps [acorn](https://github.com/acornjs/acorn) from 7.1.0 to 7.4.0.  Commits.  54efb62 Mark version 7.4.0.\n#### [Bump handlebars from 4.4.2 to 4.7.6 (@dependabot[bot])](https://github.com/MithrilJS/mithril.js/pull/2629)\n\nBumps [handlebars](https://github.com/wycats/handlebars.js) from 4.4.2 to 4.7.6.  Changelog.  Sourced from handlebars's changelog.  v4.7.6 - April 3rd, 2020.\n#### [Bump lodash from 4.17.15 to 4.17.20 (@dependabot[bot])](https://github.com/MithrilJS/mithril.js/pull/2628)\n\nBumps [lodash](https://github.com/lodash/lodash) from 4.17.15 to 4.17.20.  Commits.  ded9bc6 Bump to v4.17.20.  63150ef Documentation fixes.\n#### [Bump minimist from 1.2.0 to 1.2.3 (@dependabot[bot])](https://github.com/MithrilJS/mithril.js/pull/2627)\n\nBumps [minimist](https://github.com/substack/minimist) from 1.2.0 to 1.2.3.  Commits.  6457d74 1.2.3.  38a4d1c even more aggressive checks for protocol pollution.\n#### [Update installation.md (@purefan)](https://github.com/MithrilJS/mithril.js/pull/2608)\n\nOffer to install mithril as a webpack plugin.  Just makes my life easier by not having to include mithril in every one of my js files.\n#### [replace slave label with replica (@stephanos)](https://github.com/MithrilJS/mithril.js/pull/2605)\n\nOne of the example is using the antiquated word \"slave\" for a database replica.  I updated the language and tested the change.\n#### [ES6 and m.trust docs patch (@kczx3)](https://github.com/MithrilJS/mithril.js/pull/2593)\n\nWhile reading through some of the documentation I saw some issues with both the ES6 and `m.trust` pages.\n#### [docs: Fix simple typo, subsequece -> subsequence (@timgates42)](https://github.com/MithrilJS/mithril.js/pull/2582)\n\nThere is a small typo in mithril.js, render/render.js.  Should read `subsequence` rather than `subsequece`.\n#### [change link to go to ospec instead of mocha (@akessner)](https://github.com/MithrilJS/mithril.js/pull/2576)\n\nChange the link to point to ospec docs in github.  ospec link went to mochajs.  [issue 2575](https://github.com/MithrilJS/mithril.js/issues/2575).  N/A.  N/A.  N/A.\n#### [updated to the Vimeo showcase (@CreaturesInUnitards)](https://github.com/MithrilJS/mithril.js/pull/2573)\n\nThe scrimba version of Mithril 0-60 was built on their beta platform, and doesn't really even work anymore.\n#### [adding more community examples (@boazblake)](https://github.com/MithrilJS/mithril.js/pull/2567)\n\n\n#### [Exclude archive of previous docs (@cztomsik)](https://github.com/MithrilJS/mithril.js/pull/2561)\n\nupdate .npmignore so that archives are not included in the resulting package.  space/bandwidth savings.  fix #2552.\n#### [Pimp the docs linter (and assorted changes) (@pygy)](https://github.com/MithrilJS/mithril.js/pull/2553)\n\nAdd an optional cache for faster runs.  Add a final report.  Don't return anything from `exec()`.  Cover more files.  Look for a \"--cache\" option.\n#### [Recast key docs to be much clearer and more accurate (@dead-claudia)](https://github.com/MithrilJS/mithril.js/pull/2540)\n\nRecast key docs to be much clearer and more accurate, including a few Flems examples to help intuitively explain things.\n#### [Add `m.censor`, adjust `m.route.Link` to use it (@dead-claudia)](https://github.com/MithrilJS/mithril.js/pull/2538)\n\nAdd `m.censor`.  Adjust `m.route.Link` to use it.  Restructure a few things for better code reuse.  Fixes #2472.\n#### [Update fetch() browser support in docs (@qgustavor)](https://github.com/MithrilJS/mithril.js/pull/2522)\n\nAs [Can I use](https://caniuse.com/#feat=fetch) shows `fetch()` supported since Safari 10.1 and iOS Safari 10.3.\n#### [docs: Add release dates to all change-log files (@maranomynet)](https://github.com/MithrilJS/mithril.js/pull/2513)\n\nI'd like to introduce release dates to the change log files.  Release dates are human-friendly and add a bit of historical perspective to change-log files.\n"
  },
  {
    "path": "docs/releasing.md",
    "content": "<!--meta-description\nDescribes how we do releases of Mithril.js\n-->\n\n# Mithril.js Release Processes\n\nMithril.js' release process is automated by [pr-release].  pr-release is maintained by a long time Mithril.js community member [@JAForbes](https://github.com/JAForbes).\n\npr-release handles the following:\n\n- Generating changelog entries\n- Automating the semver version\n- Publishing releases and pre-releases to npm\n- Creating github releases\n- Rollbacks\n\n## For contributors\n\nContributors should create their feature branch targetting the default branch `main`.  When this branch is merged `pr-release` will either generate or update a release PR from `main` to `release`.\n\nThe description and title will be managed by [pr-release], including the semver version.\n\nContributors who have permissions should add the correct semver label to their PR (`major` | `minor` | `patch`).  If no label is set, `patch` is assumed.\n\nIf you do not have permissions, the maintainer will set the label on your behalf.\n\n## Changelog\n\nCurrently, `docs/recent-changes.md` holds an automatically prepended log of changes, managed by pr-release. Ideally, I want to get rid of this and just have pr-release somehow push to https://github.com/MithrilJS/docs automatically, but that may take some work.\n\n## For maintainers\n\nWhenever a new feature branch is opened, a reviewing maintainer should add the correct semver label to their PR (`major` | `minor` | `patch`).  If no label is set, `patch` is assumed.\n\nIf a `major` or `minor` feature branch is merged but no labels were set, you can still go back and edit the semver labels.  On label change the release pr will automatically be regenerated and will recalculate the semver version.\n\n[pr-release]: https://pr-release.org/\n"
  },
  {
    "path": "hyperscript.js",
    "content": "\"use strict\"\n\nvar hyperscript = require(\"./render/hyperscript\")\n\nhyperscript.trust = require(\"./render/trust\")\nhyperscript.fragment = require(\"./render/fragment\")\n\nmodule.exports = hyperscript\n"
  },
  {
    "path": "index.js",
    "content": "\"use strict\"\n\nvar hyperscript = require(\"./hyperscript\")\nvar mountRedraw = require(\"./mount-redraw\")\nvar request = require(\"./request\")\nvar router = require(\"./route\")\n\nvar m = function m() { return hyperscript.apply(this, arguments) }\nm.m = hyperscript\nm.trust = hyperscript.trust\nm.fragment = hyperscript.fragment\nm.Fragment = \"[\"\nm.mount = mountRedraw.mount\nm.route = router\nm.render = require(\"./render\")\nm.redraw = mountRedraw.redraw\nm.request = request.request\nm.parseQueryString = require(\"./querystring/parse\")\nm.buildQueryString = require(\"./querystring/build\")\nm.parsePathname = require(\"./pathname/parse\")\nm.buildPathname = require(\"./pathname/build\")\nm.vnode = require(\"./render/vnode\")\nm.censor = require(\"./util/censor\")\nm.domFor = require(\"./render/domFor\")\n\nmodule.exports = m\n"
  },
  {
    "path": "mithril.js",
    "content": ";(function() {\n\"use strict\"\nfunction Vnode(tag, key, attrs0, children, text, dom) {\n\treturn {tag: tag, key: key, attrs: attrs0, children: children, text: text, dom: dom, is: undefined, domSize: undefined, state: undefined, events: undefined, instance: undefined}\n}\nVnode.normalize = function(node) {\n\tif (Array.isArray(node)) return Vnode(\"[\", undefined, undefined, Vnode.normalizeChildren(node), undefined, undefined)\n\tif (node == null || typeof node === \"boolean\") return null\n\tif (typeof node === \"object\") return node\n\treturn Vnode(\"#\", undefined, undefined, String(node), undefined, undefined)\n}\nVnode.normalizeChildren = function(input) {\n\t// Preallocate the array length (initially holey) and fill every index immediately in order.\n\t// Benchmarking shows better performance on V8.\n\tvar children = new Array(input.length)\n\t// Count the number of keyed normalized vnodes for consistency check.\n\t// Note: this is a perf-sensitive check.\n\t// Fun fact: merging the loop like this is somehow faster than splitting\n\t// the check within updateNodes(), noticeably so.\n\tvar numKeyed = 0\n\tfor (var i = 0; i < input.length; i++) {\n\t\tchildren[i] = Vnode.normalize(input[i])\n\t\tif (children[i] !== null && children[i].key != null) numKeyed++\n\t}\n\tif (numKeyed !== 0 && numKeyed !== input.length) {\n\t\tthrow new TypeError(children.includes(null)\n\t\t\t? \"In fragments, vnodes must either all have keys or none have keys. You may wish to consider using an explicit keyed empty fragment, m.fragment({key: ...}), instead of a hole.\"\n\t\t\t: \"In fragments, vnodes must either all have keys or none have keys.\"\n\t\t)\n\t}\n\treturn children\n}\n// Note: the processing of variadic parameters is perf-sensitive.\n//\n// In native ES6, it might be preferable to define hyperscript and fragment\n// factories with a final ...args parameter and call hyperscriptVnode(...args),\n// since modern engines can optimize spread calls.\n//\n// However, benchmarks showed this was not faster. As a result, spread is used\n// only in the parameter lists of hyperscript and fragment, while an array is\n// passed to hyperscriptVnode.\nvar hyperscriptVnode = function(attrs1, children0) {\n\tif (attrs1 == null || typeof attrs1 === \"object\" && attrs1.tag == null && !Array.isArray(attrs1)) {\n\t\tif (children0.length === 1 && Array.isArray(children0[0])) children0 = children0[0]\n\t} else {\n\t\tchildren0 = children0.length === 0 && Array.isArray(attrs1) ? attrs1 : [attrs1, ...children0]\n\t\tattrs1 = undefined\n\t}\n\treturn Vnode(\"\", attrs1 && attrs1.key, attrs1, children0)\n}\n// This exists so I'm only saving it once.\nvar hasOwn = {}.hasOwnProperty\n// This is an attrs object that is used by default when attrs is undefined or null.\nvar emptyAttrs = {}\n// This Map manages the following:\n// - Whether an attrs is cached attrs generated by compileSelector().\n// - Whether the cached attrs is \"static\", i.e., does not contain any form attributes.\n// These information will be useful to skip updating attrs in render().\n//\n// Since the attrs used as keys in this map are not released from the selectorCache object,\n// there is no risk of memory leaks. Therefore, Map is used here instead of WeakMap.\nvar cachedAttrsIsStaticMap = new Map([[emptyAttrs, true]])\nvar selectorParser = /(?:(^|#|\\.)([^#\\.\\[\\]]+))|(\\[(.+?)(?:\\s*=\\s*(\"|'|)((?:\\\\[\"'\\]]|.)*?)\\5)?\\])/g\nvar selectorCache = Object.create(null)\nfunction isEmpty(object) {\n\tfor (var key in object) if (hasOwn.call(object, key)) return false\n\treturn true\n}\nfunction isFormAttributeKey(key) {\n\treturn key === \"value\" || key === \"checked\" || key === \"selectedIndex\" || key === \"selected\"\n}\nfunction compileSelector(selector) {\n\tvar match, tag = \"div\", classes = [], attrs = {}, isStatic = true\n\twhile (match = selectorParser.exec(selector)) {\n\t\tvar type = match[1], value = match[2]\n\t\tif (type === \"\" && value !== \"\") tag = value\n\t\telse if (type === \"#\") attrs.id = value\n\t\telse if (type === \".\") classes.push(value)\n\t\telse if (match[3][0] === \"[\") {\n\t\t\tvar attrValue = match[6]\n\t\t\tif (attrValue) attrValue = attrValue.replace(/\\\\([\"'])/g, \"$1\").replace(/\\\\\\\\/g, \"\\\\\")\n\t\t\tif (match[4] === \"class\") classes.push(attrValue)\n\t\t\telse {\n\t\t\t\tattrs[match[4]] = attrValue === \"\" ? attrValue : attrValue || true\n\t\t\t\tif (isFormAttributeKey(match[4])) isStatic = false\n\t\t\t}\n\t\t}\n\t}\n\tif (classes.length > 0) attrs.className = classes.join(\" \")\n\tif (isEmpty(attrs)) attrs = emptyAttrs\n\telse cachedAttrsIsStaticMap.set(attrs, isStatic)\n\treturn selectorCache[selector] = {tag: tag, attrs: attrs, is: attrs.is}\n}\nfunction execSelector(state, vnode) {\n\tvnode.tag = state.tag\n\tvar attrs = vnode.attrs\n\tif (attrs == null) {\n\t\tvnode.attrs = state.attrs\n\t\tvnode.is = state.is\n\t\treturn vnode\n\t}\n\tif (hasOwn.call(attrs, \"class\")) {\n\t\tif (attrs.class != null) attrs.className = attrs.class\n\t\tattrs.class = null\n\t}\n\tif (state.attrs !== emptyAttrs) {\n\t\tvar className = attrs.className\n\t\tattrs = Object.assign({}, state.attrs, attrs)\n\t\tif (state.attrs.className != null) attrs.className =\n\t\t\tclassName != null\n\t\t\t\t? String(state.attrs.className) + \" \" + String(className)\n\t\t\t\t: state.attrs.className\n\t}\n\t// workaround for #2622 (reorder keys in attrs to set \"type\" first)\n\t// The DOM does things to inputs based on the \"type\", so it needs set first.\n\t// See: https://github.com/MithrilJS/mithril.js/issues/2622\n\tif (state.tag === \"input\" && hasOwn.call(attrs, \"type\")) {\n\t\tattrs = Object.assign({type: attrs.type}, attrs)\n\t}\n\t// This reduces the complexity of the evaluation of \"is\" within the render function.\n\tvnode.is = attrs.is\n\tvnode.attrs = attrs\n\treturn vnode\n}\nfunction hyperscript(selector, attrs, ...children) {\n\tif (selector == null || typeof selector !== \"string\" && typeof selector !== \"function\" && typeof selector.view !== \"function\") {\n\t\tthrow Error(\"The selector must be either a string or a component.\");\n\t}\n\tvar vnode = hyperscriptVnode(attrs, children)\n\tif (typeof selector === \"string\") {\n\t\tvnode.children = Vnode.normalizeChildren(vnode.children)\n\t\tif (selector !== \"[\") return execSelector(selectorCache[selector] || compileSelector(selector), vnode)\n\t}\n\tif (vnode.attrs == null) vnode.attrs = {}\n\tvnode.tag = selector\n\treturn vnode\n}\nhyperscript.trust = function(html) {\n\tif (html == null) html = \"\"\n\treturn Vnode(\"<\", undefined, undefined, html, undefined, undefined)\n}\nhyperscript.fragment = function(attrs4, ...children1) {\n\tvar vnode2 = hyperscriptVnode(attrs4, children1)\n\tif (vnode2.attrs == null) vnode2.attrs = {}\n\tvnode2.tag = \"[\"\n\tvnode2.children = Vnode.normalizeChildren(vnode2.children)\n\treturn vnode2\n}\nvar delayedRemoval = new WeakMap\nfunction *domFor(vnode4) {\n\t// To avoid unintended mangling of the internal bundler,\n\t// parameter destructuring is not used here.\n\tvar dom = vnode4.dom\n\tvar domSize0 = vnode4.domSize\n\tvar generation0 = delayedRemoval.get(dom)\n\tif (dom != null) do {\n\t\tvar nextSibling = dom.nextSibling\n\t\tif (delayedRemoval.get(dom) === generation0) {\n\t\t\tyield dom\n\t\t\tdomSize0--\n\t\t}\n\t\tdom = nextSibling\n\t}\n\twhile (domSize0)\n}\nvar _14 = function() {\n\tvar nameSpace = {\n\t\tsvg: \"http://www.w3.org/2000/svg\",\n\t\tmath: \"http://www.w3.org/1998/Math/MathML\"\n\t}\n\tvar currentRedraw\n\tvar currentRender\n\tfunction getDocument(dom) {\n\t\treturn dom.ownerDocument;\n\t}\n\tfunction getNameSpace(vnode3) {\n\t\treturn vnode3.attrs && vnode3.attrs.xmlns || nameSpace[vnode3.tag]\n\t}\n\t//sanity check to discourage people from doing `vnode.state = ...`\n\tfunction checkState(vnode3, original) {\n\t\tif (vnode3.state !== original) throw new Error(\"'vnode.state' must not be modified.\")\n\t}\n\t//Note: the hook is passed as the `this` argument to allow proxying the\n\t//arguments without requiring a full array allocation to do so. It also\n\t//takes advantage of the fact the current `vnode` is the first argument in\n\t//all lifecycle methods.\n\tfunction callHook(vnode3) {\n\t\tvar original = vnode3.state\n\t\ttry {\n\t\t\treturn this.apply(original, arguments)\n\t\t} finally {\n\t\t\tcheckState(vnode3, original)\n\t\t}\n\t}\n\t// IE11 (at least) throws an UnspecifiedError when accessing document.activeElement when\n\t// inside an iframe. Catch and swallow this error, and heavy-handidly return null.\n\tfunction activeElement(dom) {\n\t\ttry {\n\t\t\treturn getDocument(dom).activeElement\n\t\t} catch (e) {\n\t\t\treturn null\n\t\t}\n\t}\n\t//create\n\tfunction createNodes(parent, vnodes, start, end, hooks, nextSibling, ns) {\n\t\tfor (var i = start; i < end; i++) {\n\t\t\tvar vnode3 = vnodes[i]\n\t\t\tif (vnode3 != null) {\n\t\t\t\tcreateNode(parent, vnode3, hooks, ns, nextSibling)\n\t\t\t}\n\t\t}\n\t}\n\tfunction createNode(parent, vnode3, hooks, ns, nextSibling) {\n\t\tvar tag = vnode3.tag\n\t\tif (typeof tag === \"string\") {\n\t\t\tvnode3.state = {}\n\t\t\tif (vnode3.attrs != null) initLifecycle(vnode3.attrs, vnode3, hooks)\n\t\t\tswitch (tag) {\n\t\t\t\tcase \"#\": createText(parent, vnode3, nextSibling); break\n\t\t\t\tcase \"<\": createHTML(parent, vnode3, ns, nextSibling); break\n\t\t\t\tcase \"[\": createFragment(parent, vnode3, hooks, ns, nextSibling); break\n\t\t\t\tdefault: createElement(parent, vnode3, hooks, ns, nextSibling)\n\t\t\t}\n\t\t}\n\t\telse createComponent(parent, vnode3, hooks, ns, nextSibling)\n\t}\n\tfunction createText(parent, vnode3, nextSibling) {\n\t\tvnode3.dom = getDocument(parent).createTextNode(vnode3.children)\n\t\tinsertDOM(parent, vnode3.dom, nextSibling)\n\t}\n\tvar possibleParents = {caption: \"table\", thead: \"table\", tbody: \"table\", tfoot: \"table\", tr: \"tbody\", th: \"tr\", td: \"tr\", colgroup: \"table\", col: \"colgroup\"}\n\tfunction createHTML(parent, vnode3, ns, nextSibling) {\n\t\tvar match0 = vnode3.children.match(/^\\s*?<(\\w+)/im) || []\n\t\t// not using the proper parent makes the child element(s) vanish.\n\t\t//     var div = document.createElement(\"div\")\n\t\t//     div.innerHTML = \"<td>i</td><td>j</td>\"\n\t\t//     console.log(div.innerHTML)\n\t\t// --> \"ij\", no <td> in sight.\n\t\tvar temp = getDocument(parent).createElement(possibleParents[match0[1]] || \"div\")\n\t\tif (ns === \"http://www.w3.org/2000/svg\") {\n\t\t\ttemp.innerHTML = \"<svg xmlns=\\\"http://www.w3.org/2000/svg\\\">\" + vnode3.children + \"</svg>\"\n\t\t\ttemp = temp.firstChild\n\t\t} else {\n\t\t\ttemp.innerHTML = vnode3.children\n\t\t}\n\t\tvnode3.dom = temp.firstChild\n\t\tvnode3.domSize = temp.childNodes.length\n\t\tvar fragment = getDocument(parent).createDocumentFragment()\n\t\tvar child\n\t\twhile (child = temp.firstChild) {\n\t\t\tfragment.appendChild(child)\n\t\t}\n\t\tinsertDOM(parent, fragment, nextSibling)\n\t}\n\tfunction createFragment(parent, vnode3, hooks, ns, nextSibling) {\n\t\tvar fragment = getDocument(parent).createDocumentFragment()\n\t\tif (vnode3.children != null) {\n\t\t\tvar children2 = vnode3.children\n\t\t\tcreateNodes(fragment, children2, 0, children2.length, hooks, null, ns)\n\t\t}\n\t\tvnode3.dom = fragment.firstChild\n\t\tvnode3.domSize = fragment.childNodes.length\n\t\tinsertDOM(parent, fragment, nextSibling)\n\t}\n\tfunction createElement(parent, vnode3, hooks, ns, nextSibling) {\n\t\tvar tag = vnode3.tag\n\t\tvar attrs5 = vnode3.attrs\n\t\tvar is = vnode3.is\n\t\tns = getNameSpace(vnode3) || ns\n\t\tvar element = ns ?\n\t\t\tis ? getDocument(parent).createElementNS(ns, tag, {is: is}) : getDocument(parent).createElementNS(ns, tag) :\n\t\t\tis ? getDocument(parent).createElement(tag, {is: is}) : getDocument(parent).createElement(tag)\n\t\tvnode3.dom = element\n\t\tif (attrs5 != null) {\n\t\t\tsetAttrs(vnode3, attrs5, ns)\n\t\t}\n\t\tinsertDOM(parent, element, nextSibling)\n\t\tif (!maybeSetContentEditable(vnode3)) {\n\t\t\tif (vnode3.children != null) {\n\t\t\t\tvar children2 = vnode3.children\n\t\t\t\tcreateNodes(element, children2, 0, children2.length, hooks, null, ns)\n\t\t\t\tif (vnode3.tag === \"select\" && attrs5 != null) setLateSelectAttrs(vnode3, attrs5)\n\t\t\t}\n\t\t}\n\t}\n\tfunction initComponent(vnode3, hooks) {\n\t\tvar sentinel\n\t\tif (typeof vnode3.tag.view === \"function\") {\n\t\t\tvnode3.state = Object.create(vnode3.tag)\n\t\t\tsentinel = vnode3.state.view\n\t\t\tif (sentinel.$$reentrantLock$$ != null) return\n\t\t\tsentinel.$$reentrantLock$$ = true\n\t\t} else {\n\t\t\tvnode3.state = void 0\n\t\t\tsentinel = vnode3.tag\n\t\t\tif (sentinel.$$reentrantLock$$ != null) return\n\t\t\tsentinel.$$reentrantLock$$ = true\n\t\t\tvnode3.state = (vnode3.tag.prototype != null && typeof vnode3.tag.prototype.view === \"function\") ? new vnode3.tag(vnode3) : vnode3.tag(vnode3)\n\t\t}\n\t\tinitLifecycle(vnode3.state, vnode3, hooks)\n\t\tif (vnode3.attrs != null) initLifecycle(vnode3.attrs, vnode3, hooks)\n\t\tvnode3.instance = Vnode.normalize(callHook.call(vnode3.state.view, vnode3))\n\t\tif (vnode3.instance === vnode3) throw Error(\"A view cannot return the vnode it received as argument\")\n\t\tsentinel.$$reentrantLock$$ = null\n\t}\n\tfunction createComponent(parent, vnode3, hooks, ns, nextSibling) {\n\t\tinitComponent(vnode3, hooks)\n\t\tif (vnode3.instance != null) {\n\t\t\tcreateNode(parent, vnode3.instance, hooks, ns, nextSibling)\n\t\t\tvnode3.dom = vnode3.instance.dom\n\t\t\tvnode3.domSize = vnode3.instance.domSize\n\t\t}\n\t\telse {\n\t\t\tvnode3.domSize = 0\n\t\t}\n\t}\n\t//update\n\t/**\n\t * @param {Element|Fragment} parent - the parent element\n\t * @param {Vnode[] | null} old - the list of vnodes of the last `render()` call for\n\t *                               this part of the tree\n\t * @param {Vnode[] | null} vnodes - as above, but for the current `render()` call.\n\t * @param {Function[]} hooks - an accumulator of post-render hooks (oncreate/onupdate)\n\t * @param {Element | null} nextSibling - the next DOM node if we're dealing with a\n\t *                                       fragment that is not the last item in its\n\t *                                       parent\n\t * @param {'svg' | 'math' | String | null} ns) - the current XML namespace, if any\n\t * @returns void\n\t */\n\t// This function diffs and patches lists of vnodes, both keyed and unkeyed.\n\t//\n\t// We will:\n\t//\n\t// 1. describe its general structure\n\t// 2. focus on the diff algorithm optimizations\n\t// 3. discuss DOM node operations.\n\t// ## Overview:\n\t//\n\t// The updateNodes() function:\n\t// - deals with trivial cases\n\t// - determines whether the lists are keyed or unkeyed based on the first non-null node\n\t//   of each list.\n\t// - diffs them and patches the DOM if needed (that's the brunt of the code)\n\t// - manages the leftovers: after diffing, are there:\n\t//   - old nodes left to remove?\n\t// \t - new nodes to insert?\n\t// \t deal with them!\n\t//\n\t// The lists are only iterated over once, with an exception for the nodes in `old` that\n\t// are visited in the fourth part of the diff and in the `removeNodes` loop.\n\t// ## Diffing\n\t//\n\t// Reading https://github.com/localvoid/ivi/blob/ddc09d06abaef45248e6133f7040d00d3c6be853/packages/ivi/src/vdom/implementation.ts#L617-L837\n\t// may be good for context on longest increasing subsequence-based logic for moving nodes.\n\t//\n\t// In order to diff keyed lists, one has to\n\t//\n\t// 1) match nodes in both lists, per key, and update them accordingly\n\t// 2) create the nodes present in the new list, but absent in the old one\n\t// 3) remove the nodes present in the old list, but absent in the new one\n\t// 4) figure out what nodes in 1) to move in order to minimize the DOM operations.\n\t//\n\t// To achieve 1) one can create a dictionary of keys => index (for the old list), then iterate\n\t// over the new list and for each new vnode, find the corresponding vnode in the old list using\n\t// the map.\n\t// 2) is achieved in the same step: if a new node has no corresponding entry in the map, it is new\n\t// and must be created.\n\t// For the removals, we actually remove the nodes that have been updated from the old list.\n\t// The nodes that remain in that list after 1) and 2) have been performed can be safely removed.\n\t// The fourth step is a bit more complex and relies on the longest increasing subsequence (LIS)\n\t// algorithm.\n\t//\n\t// the longest increasing subsequence is the list of nodes that can remain in place. Imagine going\n\t// from `1,2,3,4,5` to `4,5,1,2,3` where the numbers are not necessarily the keys, but the indices\n\t// corresponding to the keyed nodes in the old list (keyed nodes `e,d,c,b,a` => `b,a,e,d,c` would\n\t//  match the above lists, for example).\n\t//\n\t// In there are two increasing subsequences: `4,5` and `1,2,3`, the latter being the longest. We\n\t// can update those nodes without moving them, and only call `insertNode` on `4` and `5`.\n\t//\n\t// @localvoid adapted the algo to also support node deletions and insertions (the `lis` is actually\n\t// the longest increasing subsequence *of old nodes still present in the new list*).\n\t//\n\t// It is a general algorithm that is fireproof in all circumstances, but it requires the allocation\n\t// and the construction of a `key => oldIndex` map, and three arrays (one with `newIndex => oldIndex`,\n\t// the `LIS` and a temporary one to create the LIS).\n\t//\n\t// So we cheat where we can: if the tails of the lists are identical, they are guaranteed to be part of\n\t// the LIS and can be updated without moving them.\n\t//\n\t// If two nodes are swapped, they are guaranteed not to be part of the LIS, and must be moved (with\n\t// the exception of the last node if the list is fully reversed).\n\t//\n\t// ## Finding the next sibling.\n\t//\n\t// `updateNode()` and `createNode()` expect a nextSibling parameter to perform DOM operations.\n\t// When the list is being traversed top-down, at any index, the DOM nodes up to the previous\n\t// vnode reflect the content of the new list, whereas the rest of the DOM nodes reflect the old\n\t// list. The next sibling must be looked for in the old list using `getNextSibling(... oldStart + 1 ...)`.\n\t//\n\t// In the other scenarios (swaps, upwards traversal, map-based diff),\n\t// the new vnodes list is traversed upwards. The DOM nodes at the bottom of the list reflect the\n\t// bottom part of the new vnodes list, and we can use the `v.dom`  value of the previous node\n\t// as the next sibling (cached in the `nextSibling` variable).\n\t// ## DOM node moves\n\t//\n\t// In most scenarios `updateNode()` and `createNode()` perform the DOM operations. However,\n\t// this is not the case if the node moved (second and fourth part of the diff algo). We move\n\t// the old DOM nodes before updateNode runs because it enables us to use the cached `nextSibling`\n\t// variable rather than fetching it using `getNextSibling()`.\n\tfunction updateNodes(parent, old, vnodes, hooks, nextSibling, ns) {\n\t\tif (old === vnodes || old == null && vnodes == null) return\n\t\telse if (old == null || old.length === 0) createNodes(parent, vnodes, 0, vnodes.length, hooks, nextSibling, ns)\n\t\telse if (vnodes == null || vnodes.length === 0) removeNodes(parent, old, 0, old.length)\n\t\telse {\n\t\t\tvar isOldKeyed = old[0] != null && old[0].key != null\n\t\t\tvar isKeyed = vnodes[0] != null && vnodes[0].key != null\n\t\t\tvar start = 0, oldStart = 0\n\t\t\tif (!isOldKeyed) while (oldStart < old.length && old[oldStart] == null) oldStart++\n\t\t\tif (!isKeyed) while (start < vnodes.length && vnodes[start] == null) start++\n\t\t\tif (isOldKeyed !== isKeyed) {\n\t\t\t\tremoveNodes(parent, old, oldStart, old.length)\n\t\t\t\tcreateNodes(parent, vnodes, start, vnodes.length, hooks, nextSibling, ns)\n\t\t\t} else if (!isKeyed) {\n\t\t\t\t// Don't index past the end of either list (causes deopts).\n\t\t\t\tvar commonLength = old.length < vnodes.length ? old.length : vnodes.length\n\t\t\t\t// Rewind if necessary to the first non-null index on either side.\n\t\t\t\t// We could alternatively either explicitly create or remove nodes when `start !== oldStart`\n\t\t\t\t// but that would be optimizing for sparse lists which are more rare than dense ones.\n\t\t\t\tstart = start < oldStart ? start : oldStart\n\t\t\t\tfor (; start < commonLength; start++) {\n\t\t\t\t\to = old[start]\n\t\t\t\t\tv = vnodes[start]\n\t\t\t\t\tif (o === v || o == null && v == null) continue\n\t\t\t\t\telse if (o == null) createNode(parent, v, hooks, ns, getNextSibling(old, start + 1, nextSibling))\n\t\t\t\t\telse if (v == null) removeNode(parent, o)\n\t\t\t\t\telse updateNode(parent, o, v, hooks, getNextSibling(old, start + 1, nextSibling), ns)\n\t\t\t\t}\n\t\t\t\tif (old.length > commonLength) removeNodes(parent, old, start, old.length)\n\t\t\t\tif (vnodes.length > commonLength) createNodes(parent, vnodes, start, vnodes.length, hooks, nextSibling, ns)\n\t\t\t} else {\n\t\t\t\t// keyed diff\n\t\t\t\tvar oldEnd = old.length - 1, end = vnodes.length - 1, map, o, v, oe, ve, topSibling\n\t\t\t\t// bottom-up\n\t\t\t\twhile (oldEnd >= oldStart && end >= start) {\n\t\t\t\t\toe = old[oldEnd]\n\t\t\t\t\tve = vnodes[end]\n\t\t\t\t\tif (oe.key !== ve.key) break\n\t\t\t\t\tif (oe !== ve) updateNode(parent, oe, ve, hooks, nextSibling, ns)\n\t\t\t\t\tif (ve.dom != null) nextSibling = ve.dom\n\t\t\t\t\toldEnd--, end--\n\t\t\t\t}\n\t\t\t\t// top-down\n\t\t\t\twhile (oldEnd >= oldStart && end >= start) {\n\t\t\t\t\to = old[oldStart]\n\t\t\t\t\tv = vnodes[start]\n\t\t\t\t\tif (o.key !== v.key) break\n\t\t\t\t\toldStart++, start++\n\t\t\t\t\tif (o !== v) updateNode(parent, o, v, hooks, getNextSibling(old, oldStart, nextSibling), ns)\n\t\t\t\t}\n\t\t\t\t// swaps and list reversals\n\t\t\t\twhile (oldEnd >= oldStart && end >= start) {\n\t\t\t\t\tif (start === end) break\n\t\t\t\t\tif (o.key !== ve.key || oe.key !== v.key) break\n\t\t\t\t\ttopSibling = getNextSibling(old, oldStart, nextSibling)\n\t\t\t\t\tmoveDOM(parent, oe, topSibling)\n\t\t\t\t\tif (oe !== v) updateNode(parent, oe, v, hooks, topSibling, ns)\n\t\t\t\t\tif (++start <= --end) moveDOM(parent, o, nextSibling)\n\t\t\t\t\tif (o !== ve) updateNode(parent, o, ve, hooks, nextSibling, ns)\n\t\t\t\t\tif (ve.dom != null) nextSibling = ve.dom\n\t\t\t\t\toldStart++; oldEnd--\n\t\t\t\t\toe = old[oldEnd]\n\t\t\t\t\tve = vnodes[end]\n\t\t\t\t\to = old[oldStart]\n\t\t\t\t\tv = vnodes[start]\n\t\t\t\t}\n\t\t\t\t// bottom up once again\n\t\t\t\twhile (oldEnd >= oldStart && end >= start) {\n\t\t\t\t\tif (oe.key !== ve.key) break\n\t\t\t\t\tif (oe !== ve) updateNode(parent, oe, ve, hooks, nextSibling, ns)\n\t\t\t\t\tif (ve.dom != null) nextSibling = ve.dom\n\t\t\t\t\toldEnd--, end--\n\t\t\t\t\toe = old[oldEnd]\n\t\t\t\t\tve = vnodes[end]\n\t\t\t\t}\n\t\t\t\tif (start > end) removeNodes(parent, old, oldStart, oldEnd + 1)\n\t\t\t\telse if (oldStart > oldEnd) createNodes(parent, vnodes, start, end + 1, hooks, nextSibling, ns)\n\t\t\t\telse {\n\t\t\t\t\t// inspired by ivi https://github.com/ivijs/ivi/ by Boris Kaul\n\t\t\t\t\tvar originalNextSibling = nextSibling, vnodesLength = end - start + 1, oldIndices = new Array(vnodesLength), li=0, i=0, pos = 2147483647, matched = 0, map, lisIndices\n\t\t\t\t\tfor (i = 0; i < vnodesLength; i++) oldIndices[i] = -1\n\t\t\t\t\tfor (i = end; i >= start; i--) {\n\t\t\t\t\t\tif (map == null) map = getKeyMap(old, oldStart, oldEnd + 1)\n\t\t\t\t\t\tve = vnodes[i]\n\t\t\t\t\t\tvar oldIndex = map[ve.key]\n\t\t\t\t\t\tif (oldIndex != null) {\n\t\t\t\t\t\t\tpos = (oldIndex < pos) ? oldIndex : -1 // becomes -1 if nodes were re-ordered\n\t\t\t\t\t\t\toldIndices[i-start] = oldIndex\n\t\t\t\t\t\t\toe = old[oldIndex]\n\t\t\t\t\t\t\told[oldIndex] = null\n\t\t\t\t\t\t\tif (oe !== ve) updateNode(parent, oe, ve, hooks, nextSibling, ns)\n\t\t\t\t\t\t\tif (ve.dom != null) nextSibling = ve.dom\n\t\t\t\t\t\t\tmatched++\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tnextSibling = originalNextSibling\n\t\t\t\t\tif (matched !== oldEnd - oldStart + 1) removeNodes(parent, old, oldStart, oldEnd + 1)\n\t\t\t\t\tif (matched === 0) createNodes(parent, vnodes, start, end + 1, hooks, nextSibling, ns)\n\t\t\t\t\telse {\n\t\t\t\t\t\tif (pos === -1) {\n\t\t\t\t\t\t\t// the indices of the indices of the items that are part of the\n\t\t\t\t\t\t\t// longest increasing subsequence in the oldIndices list\n\t\t\t\t\t\t\tlisIndices = makeLisIndices(oldIndices)\n\t\t\t\t\t\t\tli = lisIndices.length - 1\n\t\t\t\t\t\t\tfor (i = end; i >= start; i--) {\n\t\t\t\t\t\t\t\tv = vnodes[i]\n\t\t\t\t\t\t\t\tif (oldIndices[i-start] === -1) createNode(parent, v, hooks, ns, nextSibling)\n\t\t\t\t\t\t\t\telse {\n\t\t\t\t\t\t\t\t\tif (lisIndices[li] === i - start) li--\n\t\t\t\t\t\t\t\t\telse moveDOM(parent, v, nextSibling)\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tif (v.dom != null) nextSibling = vnodes[i].dom\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tfor (i = end; i >= start; i--) {\n\t\t\t\t\t\t\t\tv = vnodes[i]\n\t\t\t\t\t\t\t\tif (oldIndices[i-start] === -1) createNode(parent, v, hooks, ns, nextSibling)\n\t\t\t\t\t\t\t\tif (v.dom != null) nextSibling = vnodes[i].dom\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tfunction updateNode(parent, old, vnode3, hooks, nextSibling, ns) {\n\t\tvar oldTag = old.tag, tag = vnode3.tag\n\t\tif (oldTag === tag && old.is === vnode3.is) {\n\t\t\tvnode3.state = old.state\n\t\t\tvnode3.events = old.events\n\t\t\tif (shouldNotUpdate(vnode3, old)) return\n\t\t\tif (typeof oldTag === \"string\") {\n\t\t\t\tif (vnode3.attrs != null) {\n\t\t\t\t\tupdateLifecycle(vnode3.attrs, vnode3, hooks)\n\t\t\t\t}\n\t\t\t\tswitch (oldTag) {\n\t\t\t\t\tcase \"#\": updateText(old, vnode3); break\n\t\t\t\t\tcase \"<\": updateHTML(parent, old, vnode3, ns, nextSibling); break\n\t\t\t\t\tcase \"[\": updateFragment(parent, old, vnode3, hooks, nextSibling, ns); break\n\t\t\t\t\tdefault: updateElement(old, vnode3, hooks, ns)\n\t\t\t\t}\n\t\t\t}\n\t\t\telse updateComponent(parent, old, vnode3, hooks, nextSibling, ns)\n\t\t}\n\t\telse {\n\t\t\tremoveNode(parent, old)\n\t\t\tcreateNode(parent, vnode3, hooks, ns, nextSibling)\n\t\t}\n\t}\n\tfunction updateText(old, vnode3) {\n\t\tif (old.children.toString() !== vnode3.children.toString()) {\n\t\t\told.dom.nodeValue = vnode3.children\n\t\t}\n\t\tvnode3.dom = old.dom\n\t}\n\tfunction updateHTML(parent, old, vnode3, ns, nextSibling) {\n\t\tif (old.children !== vnode3.children) {\n\t\t\tremoveDOM(parent, old)\n\t\t\tcreateHTML(parent, vnode3, ns, nextSibling)\n\t\t}\n\t\telse {\n\t\t\tvnode3.dom = old.dom\n\t\t\tvnode3.domSize = old.domSize\n\t\t}\n\t}\n\tfunction updateFragment(parent, old, vnode3, hooks, nextSibling, ns) {\n\t\tupdateNodes(parent, old.children, vnode3.children, hooks, nextSibling, ns)\n\t\tvar domSize = 0, children2 = vnode3.children\n\t\tvnode3.dom = null\n\t\tif (children2 != null) {\n\t\t\tfor (var i = 0; i < children2.length; i++) {\n\t\t\t\tvar child = children2[i]\n\t\t\t\tif (child != null && child.dom != null) {\n\t\t\t\t\tif (vnode3.dom == null) vnode3.dom = child.dom\n\t\t\t\t\tdomSize += child.domSize || 1\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tvnode3.domSize = domSize\n\t}\n\tfunction updateElement(old, vnode3, hooks, ns) {\n\t\tvar element = vnode3.dom = old.dom\n\t\tns = getNameSpace(vnode3) || ns\n\t\tif (old.attrs != vnode3.attrs || (vnode3.attrs != null && !cachedAttrsIsStaticMap.get(vnode3.attrs))) {\n\t\t\tupdateAttrs(vnode3, old.attrs, vnode3.attrs, ns)\n\t\t}\n\t\tif (!maybeSetContentEditable(vnode3)) {\n\t\t\tupdateNodes(element, old.children, vnode3.children, hooks, null, ns)\n\t\t}\n\t}\n\tfunction updateComponent(parent, old, vnode3, hooks, nextSibling, ns) {\n\t\tvnode3.instance = Vnode.normalize(callHook.call(vnode3.state.view, vnode3))\n\t\tif (vnode3.instance === vnode3) throw Error(\"A view cannot return the vnode it received as argument\")\n\t\tupdateLifecycle(vnode3.state, vnode3, hooks)\n\t\tif (vnode3.attrs != null) updateLifecycle(vnode3.attrs, vnode3, hooks)\n\t\tif (vnode3.instance != null) {\n\t\t\tif (old.instance == null) createNode(parent, vnode3.instance, hooks, ns, nextSibling)\n\t\t\telse updateNode(parent, old.instance, vnode3.instance, hooks, nextSibling, ns)\n\t\t\tvnode3.dom = vnode3.instance.dom\n\t\t\tvnode3.domSize = vnode3.instance.domSize\n\t\t}\n\t\telse {\n\t\t\tif (old.instance != null) removeNode(parent, old.instance)\n\t\t\tvnode3.domSize = 0\n\t\t}\n\t}\n\tfunction getKeyMap(vnodes, start, end) {\n\t\tvar map = Object.create(null)\n\t\tfor (; start < end; start++) {\n\t\t\tvar vnode3 = vnodes[start]\n\t\t\tif (vnode3 != null) {\n\t\t\t\tvar key = vnode3.key\n\t\t\t\tif (key != null) map[key] = start\n\t\t\t}\n\t\t}\n\t\treturn map\n\t}\n\t// Lifted from ivi https://github.com/ivijs/ivi/\n\t// takes a list of unique numbers (-1 is special and can\n\t// occur multiple times) and returns an array with the indices\n\t// of the items that are part of the longest increasing\n\t// subsequence\n\tvar lisTemp = []\n\tfunction makeLisIndices(a) {\n\t\tvar result = [0]\n\t\tvar u = 0, v = 0, i = 0\n\t\tvar il = lisTemp.length = a.length\n\t\tfor (var i = 0; i < il; i++) lisTemp[i] = a[i]\n\t\tfor (var i = 0; i < il; ++i) {\n\t\t\tif (a[i] === -1) continue\n\t\t\tvar j = result[result.length - 1]\n\t\t\tif (a[j] < a[i]) {\n\t\t\t\tlisTemp[i] = j\n\t\t\t\tresult.push(i)\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tu = 0\n\t\t\tv = result.length - 1\n\t\t\twhile (u < v) {\n\t\t\t\t// Fast integer average without overflow.\n\t\t\t\t// eslint-disable-next-line no-bitwise\n\t\t\t\tvar c = (u >>> 1) + (v >>> 1) + (u & v & 1)\n\t\t\t\tif (a[result[c]] < a[i]) {\n\t\t\t\t\tu = c + 1\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tv = c\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (a[i] < a[result[u]]) {\n\t\t\t\tif (u > 0) lisTemp[i] = result[u - 1]\n\t\t\t\tresult[u] = i\n\t\t\t}\n\t\t}\n\t\tu = result.length\n\t\tv = result[u - 1]\n\t\twhile (u-- > 0) {\n\t\t\tresult[u] = v\n\t\t\tv = lisTemp[v]\n\t\t}\n\t\tlisTemp.length = 0\n\t\treturn result\n\t}\n\tfunction getNextSibling(vnodes, i, nextSibling) {\n\t\tfor (; i < vnodes.length; i++) {\n\t\t\tif (vnodes[i] != null && vnodes[i].dom != null) return vnodes[i].dom\n\t\t}\n\t\treturn nextSibling\n\t}\n\t// This handles fragments with zombie children (removed from vdom, but persisted in DOM through onbeforeremove)\n\tfunction moveDOM(parent, vnode3, nextSibling) {\n\t\tif (vnode3.dom != null) {\n\t\t\tvar target\n\t\t\tif (vnode3.domSize == null || vnode3.domSize === 1) {\n\t\t\t\t// don't allocate for the common case\n\t\t\t\ttarget = vnode3.dom\n\t\t\t} else {\n\t\t\t\ttarget = getDocument(parent).createDocumentFragment()\n\t\t\t\tfor (var dom of domFor(vnode3)) target.appendChild(dom)\n\t\t\t}\n\t\t\tinsertDOM(parent, target, nextSibling)\n\t\t}\n\t}\n\tfunction insertDOM(parent, dom, nextSibling) {\n\t\tif (nextSibling != null) parent.insertBefore(dom, nextSibling)\n\t\telse parent.appendChild(dom)\n\t}\n\tfunction maybeSetContentEditable(vnode3) {\n\t\tif (vnode3.attrs == null || (\n\t\t\tvnode3.attrs.contenteditable == null && // attribute\n\t\t\tvnode3.attrs.contentEditable == null // property\n\t\t)) return false\n\t\tvar children2 = vnode3.children\n\t\tif (children2 != null && children2.length === 1 && children2[0].tag === \"<\") {\n\t\t\tvar content = children2[0].children\n\t\t\tif (vnode3.dom.innerHTML !== content) vnode3.dom.innerHTML = content\n\t\t}\n\t\telse if (children2 != null && children2.length !== 0) throw new Error(\"Child node of a contenteditable must be trusted.\")\n\t\treturn true\n\t}\n\t//remove\n\tfunction removeNodes(parent, vnodes, start, end) {\n\t\tfor (var i = start; i < end; i++) {\n\t\t\tvar vnode3 = vnodes[i]\n\t\t\tif (vnode3 != null) removeNode(parent, vnode3)\n\t\t}\n\t}\n\tfunction tryBlockRemove(parent, vnode3, source, counter) {\n\t\tvar original = vnode3.state\n\t\tvar result = callHook.call(source.onbeforeremove, vnode3)\n\t\tif (result == null) return\n\t\tvar generation = currentRender\n\t\tfor (var dom of domFor(vnode3)) delayedRemoval.set(dom, generation)\n\t\tcounter.v++\n\t\tPromise.resolve(result).finally(function () {\n\t\t\tcheckState(vnode3, original)\n\t\t\ttryResumeRemove(parent, vnode3, counter)\n\t\t})\n\t}\n\tfunction tryResumeRemove(parent, vnode3, counter) {\n\t\tif (--counter.v === 0) {\n\t\t\tonremove(vnode3)\n\t\t\tremoveDOM(parent, vnode3)\n\t\t}\n\t}\n\tfunction removeNode(parent, vnode3) {\n\t\tvar counter = {v: 1}\n\t\tif (typeof vnode3.tag !== \"string\" && typeof vnode3.state.onbeforeremove === \"function\") tryBlockRemove(parent, vnode3, vnode3.state, counter)\n\t\tif (vnode3.attrs && typeof vnode3.attrs.onbeforeremove === \"function\") tryBlockRemove(parent, vnode3, vnode3.attrs, counter)\n\t\ttryResumeRemove(parent, vnode3, counter)\n\t}\n\tfunction removeDOM(parent, vnode3) {\n\t\tif (vnode3.dom == null) return\n\t\tif (vnode3.domSize == null || vnode3.domSize === 1) {\n\t\t\tparent.removeChild(vnode3.dom)\n\t\t} else {\n\t\t\tfor (var dom of domFor(vnode3)) parent.removeChild(dom)\n\t\t}\n\t}\n\tfunction onremove(vnode3) {\n\t\tif (typeof vnode3.tag !== \"string\" && typeof vnode3.state.onremove === \"function\") callHook.call(vnode3.state.onremove, vnode3)\n\t\tif (vnode3.attrs && typeof vnode3.attrs.onremove === \"function\") callHook.call(vnode3.attrs.onremove, vnode3)\n\t\tif (typeof vnode3.tag !== \"string\") {\n\t\t\tif (vnode3.instance != null) onremove(vnode3.instance)\n\t\t} else {\n\t\t\tif (vnode3.events != null) vnode3.events._ = null\n\t\t\tvar children2 = vnode3.children\n\t\t\tif (Array.isArray(children2)) {\n\t\t\t\tfor (var i = 0; i < children2.length; i++) {\n\t\t\t\t\tvar child = children2[i]\n\t\t\t\t\tif (child != null) onremove(child)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\t//attrs\n\tfunction setAttrs(vnode3, attrs5, ns) {\n\t\tfor (var key in attrs5) {\n\t\t\tsetAttr(vnode3, key, null, attrs5[key], ns)\n\t\t}\n\t}\n\tfunction setAttr(vnode3, key, old, value, ns) {\n\t\tif (key === \"key\" || value == null || isLifecycleMethod(key) || (old === value && !isFormAttribute(vnode3, key)) && typeof value !== \"object\") return\n\t\tif (key[0] === \"o\" && key[1] === \"n\") return updateEvent(vnode3, key, value)\n\t\tif (key.slice(0, 6) === \"xlink:\") vnode3.dom.setAttributeNS(\"http://www.w3.org/1999/xlink\", key.slice(6), value)\n\t\telse if (key === \"style\") updateStyle(vnode3.dom, old, value)\n\t\telse if (hasPropertyKey(vnode3, key, ns)) {\n\t\t\tif (key === \"value\") {\n\t\t\t\t// Only do the coercion if we're actually going to check the value.\n\t\t\t\t/* eslint-disable no-implicit-coercion */\n\t\t\t\t//setting input[value] to same value by typing on focused element moves cursor to end in Chrome\n\t\t\t\t//setting input[type=file][value] to same value causes an error to be generated if it's non-empty\n\t\t\t\t//minlength/maxlength validation isn't performed on script-set values(#2256)\n\t\t\t\tif ((vnode3.tag === \"input\" || vnode3.tag === \"textarea\") && vnode3.dom.value === \"\" + value) return\n\t\t\t\t//setting select[value] to same value while having select open blinks select dropdown in Chrome\n\t\t\t\tif (vnode3.tag === \"select\" && old !== null && vnode3.dom.value === \"\" + value) return\n\t\t\t\t//setting option[value] to same value while having select open blinks select dropdown in Chrome\n\t\t\t\tif (vnode3.tag === \"option\" && old !== null && vnode3.dom.value === \"\" + value) return\n\t\t\t\t//setting input[type=file][value] to different value is an error if it's non-empty\n\t\t\t\t// Not ideal, but it at least works around the most common source of uncaught exceptions for now.\n\t\t\t\tif (vnode3.tag === \"input\" && vnode3.attrs.type === \"file\" && \"\" + value !== \"\") { console.error(\"`value` is read-only on file inputs!\"); return }\n\t\t\t\t/* eslint-enable no-implicit-coercion */\n\t\t\t}\n\t\t\t// If you assign an input type that is not supported by IE 11 with an assignment expression, an error will occur.\n\t\t\tif (vnode3.tag === \"input\" && key === \"type\") vnode3.dom.setAttribute(key, value)\n\t\t\telse vnode3.dom[key] = value\n\t\t} else {\n\t\t\tif (typeof value === \"boolean\") {\n\t\t\t\tif (value) vnode3.dom.setAttribute(key, \"\")\n\t\t\t\telse vnode3.dom.removeAttribute(key)\n\t\t\t}\n\t\t\telse vnode3.dom.setAttribute(key === \"className\" ? \"class\" : key, value)\n\t\t}\n\t}\n\tfunction removeAttr(vnode3, key, old, ns) {\n\t\tif (key === \"key\" || old == null || isLifecycleMethod(key)) return\n\t\tif (key[0] === \"o\" && key[1] === \"n\") updateEvent(vnode3, key, undefined)\n\t\telse if (key === \"style\") updateStyle(vnode3.dom, old, null)\n\t\telse if (\n\t\t\thasPropertyKey(vnode3, key, ns)\n\t\t\t&& key !== \"className\"\n\t\t\t&& key !== \"title\" // creates \"null\" as title\n\t\t\t&& !(key === \"value\" && (\n\t\t\t\tvnode3.tag === \"option\"\n\t\t\t\t|| vnode3.tag === \"select\" && vnode3.dom.selectedIndex === -1 && vnode3.dom === activeElement(vnode3.dom)\n\t\t\t))\n\t\t\t&& !(vnode3.tag === \"input\" && key === \"type\")\n\t\t) {\n\t\t\tvnode3.dom[key] = null\n\t\t} else {\n\t\t\tvar nsLastIndex = key.indexOf(\":\")\n\t\t\tif (nsLastIndex !== -1) key = key.slice(nsLastIndex + 1)\n\t\t\tif (old !== false) vnode3.dom.removeAttribute(key === \"className\" ? \"class\" : key)\n\t\t}\n\t}\n\tfunction setLateSelectAttrs(vnode3, attrs5) {\n\t\tif (\"value\" in attrs5) {\n\t\t\tif(attrs5.value === null) {\n\t\t\t\tif (vnode3.dom.selectedIndex !== -1) vnode3.dom.value = null\n\t\t\t} else {\n\t\t\t\tvar normalized = \"\" + attrs5.value // eslint-disable-line no-implicit-coercion\n\t\t\t\tif (vnode3.dom.value !== normalized || vnode3.dom.selectedIndex === -1) {\n\t\t\t\t\tvnode3.dom.value = normalized\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (\"selectedIndex\" in attrs5) setAttr(vnode3, \"selectedIndex\", null, attrs5.selectedIndex, undefined)\n\t}\n\tfunction updateAttrs(vnode3, old, attrs5, ns) {\n\t\t// Some attributes may NOT be case-sensitive (e.g. data-***),\n\t\t// so removal should be done first to prevent accidental removal for newly setting values.\n\t\tvar val\n\t\tif (old != null) {\n\t\t\tif (old === attrs5 && !cachedAttrsIsStaticMap.has(attrs5)) {\n\t\t\t\tconsole.warn(\"Don't reuse attrs object, use new object for every redraw, this will throw in next major\")\n\t\t\t}\n\t\t\tfor (var key in old) {\n\t\t\t\tif (((val = old[key]) != null) && (attrs5 == null || attrs5[key] == null)) {\n\t\t\t\t\tremoveAttr(vnode3, key, val, ns)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (attrs5 != null) {\n\t\t\tfor (var key in attrs5) {\n\t\t\t\tsetAttr(vnode3, key, old && old[key], attrs5[key], ns)\n\t\t\t}\n\t\t}\n\t}\n\tfunction isFormAttribute(vnode3, attr) {\n\t\treturn attr === \"value\" || attr === \"checked\" || attr === \"selectedIndex\" || attr === \"selected\" && (vnode3.dom === activeElement(vnode3.dom) || vnode3.tag === \"option\" && vnode3.dom.parentNode === activeElement(vnode3.dom))\n\t}\n\tfunction isLifecycleMethod(attr) {\n\t\treturn attr === \"oninit\" || attr === \"oncreate\" || attr === \"onupdate\" || attr === \"onremove\" || attr === \"onbeforeremove\" || attr === \"onbeforeupdate\"\n\t}\n\tfunction hasPropertyKey(vnode3, key, ns) {\n\t\t// Filter out namespaced keys\n\t\treturn ns === undefined && (\n\t\t\t// If it's a custom element, just keep it.\n\t\t\tvnode3.tag.indexOf(\"-\") > -1 || vnode3.is ||\n\t\t\t// If it's a normal element, let's try to avoid a few browser bugs.\n\t\t\tkey !== \"href\" && key !== \"list\" && key !== \"form\" && key !== \"width\" && key !== \"height\"// && key !== \"type\"\n\t\t\t// Defer the property check until *after* we check everything.\n\t\t) && key in vnode3.dom\n\t}\n\t//style\n\tfunction updateStyle(element, old, style) {\n\t\tif (old === style) {\n\t\t\t// Styles are equivalent, do nothing.\n\t\t} else if (style == null) {\n\t\t\t// New style is missing, just clear it.\n\t\t\telement.style = \"\"\n\t\t} else if (typeof style !== \"object\") {\n\t\t\t// New style is a string, let engine deal with patching.\n\t\t\telement.style = style\n\t\t} else if (old == null || typeof old !== \"object\") {\n\t\t\t// `old` is missing or a string, `style` is an object.\n\t\t\telement.style = \"\"\n\t\t\t// Add new style properties\n\t\t\tfor (var key in style) {\n\t\t\t\tvar value = style[key]\n\t\t\t\tif (value != null) {\n\t\t\t\t\tif (key.includes(\"-\")) element.style.setProperty(key, String(value))\n\t\t\t\t\telse element.style[key] = String(value)\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\t// Both old & new are (different) objects.\n\t\t\t// Remove style properties that no longer exist\n\t\t\t// Style properties may have two cases(dash-case and camelCase),\n\t\t\t// so removal should be done first to prevent accidental removal for newly setting values.\n\t\t\tfor (var key in old) {\n\t\t\t\tif (old[key] != null && style[key] == null) {\n\t\t\t\t\tif (key.includes(\"-\")) element.style.removeProperty(key)\n\t\t\t\t\telse element.style[key] = \"\"\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Update style properties that have changed\n\t\t\tfor (var key in style) {\n\t\t\t\tvar value = style[key]\n\t\t\t\tif (value != null && (value = String(value)) !== String(old[key])) {\n\t\t\t\t\tif (key.includes(\"-\")) element.style.setProperty(key, value)\n\t\t\t\t\telse element.style[key] = value\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\t// Here's an explanation of how this works:\n\t// 1. The event names are always (by design) prefixed by `on`.\n\t// 2. The EventListener interface accepts either a function or an object\n\t//    with a `handleEvent` method.\n\t// 3. The object does not inherit from `Object.prototype`, to avoid\n\t//    any potential interference with that (e.g. setters).\n\t// 4. The event name is remapped to the handler before calling it.\n\t// 5. In function-based event handlers, `ev.target === this`. We replicate\n\t//    that below.\n\t// 6. In function-based event handlers, `return false` prevents the default\n\t//    action and stops event propagation. We replicate that below.\n\tfunction EventDict() {\n\t\t// Save this, so the current redraw is correctly tracked.\n\t\tthis._ = currentRedraw\n\t}\n\tEventDict.prototype = Object.create(null)\n\tEventDict.prototype.handleEvent = function (ev) {\n\t\tvar handler = this[\"on\" + ev.type]\n\t\tvar result\n\t\tif (typeof handler === \"function\") result = handler.call(ev.currentTarget, ev)\n\t\telse if (typeof handler.handleEvent === \"function\") handler.handleEvent(ev)\n\t\tvar self = this\n\t\tif (self._ != null) {\n\t\t\tif (ev.redraw !== false) (0, self._)()\n\t\t\tif (result != null && typeof result.then === \"function\") {\n\t\t\t\tPromise.resolve(result).then(function () {\n\t\t\t\t\tif (self._ != null && ev.redraw !== false) (0, self._)()\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\t\tif (result === false) {\n\t\t\tev.preventDefault()\n\t\t\tev.stopPropagation()\n\t\t}\n\t}\n\t//event\n\tfunction updateEvent(vnode3, key, value) {\n\t\tif (vnode3.events != null) {\n\t\t\tvnode3.events._ = currentRedraw\n\t\t\tif (vnode3.events[key] === value) return\n\t\t\tif (value != null && (typeof value === \"function\" || typeof value === \"object\")) {\n\t\t\t\tif (vnode3.events[key] == null) vnode3.dom.addEventListener(key.slice(2), vnode3.events, false)\n\t\t\t\tvnode3.events[key] = value\n\t\t\t} else {\n\t\t\t\tif (vnode3.events[key] != null) vnode3.dom.removeEventListener(key.slice(2), vnode3.events, false)\n\t\t\t\tvnode3.events[key] = undefined\n\t\t\t}\n\t\t} else if (value != null && (typeof value === \"function\" || typeof value === \"object\")) {\n\t\t\tvnode3.events = new EventDict()\n\t\t\tvnode3.dom.addEventListener(key.slice(2), vnode3.events, false)\n\t\t\tvnode3.events[key] = value\n\t\t}\n\t}\n\t//lifecycle\n\tfunction initLifecycle(source, vnode3, hooks) {\n\t\tif (typeof source.oninit === \"function\") callHook.call(source.oninit, vnode3)\n\t\tif (typeof source.oncreate === \"function\") hooks.push(callHook.bind(source.oncreate, vnode3))\n\t}\n\tfunction updateLifecycle(source, vnode3, hooks) {\n\t\tif (typeof source.onupdate === \"function\") hooks.push(callHook.bind(source.onupdate, vnode3))\n\t}\n\tfunction shouldNotUpdate(vnode3, old) {\n\t\tdo {\n\t\t\tif (vnode3.attrs != null && typeof vnode3.attrs.onbeforeupdate === \"function\") {\n\t\t\t\tvar force = callHook.call(vnode3.attrs.onbeforeupdate, vnode3, old)\n\t\t\t\tif (force !== undefined && !force) break\n\t\t\t}\n\t\t\tif (typeof vnode3.tag !== \"string\" && typeof vnode3.state.onbeforeupdate === \"function\") {\n\t\t\t\tvar force = callHook.call(vnode3.state.onbeforeupdate, vnode3, old)\n\t\t\t\tif (force !== undefined && !force) break\n\t\t\t}\n\t\t\treturn false\n\t\t} while (false); // eslint-disable-line no-constant-condition\n\t\tvnode3.dom = old.dom\n\t\tvnode3.domSize = old.domSize\n\t\tvnode3.instance = old.instance\n\t\t// One would think having the actual latest attributes would be ideal,\n\t\t// but it doesn't let us properly diff based on our current internal\n\t\t// representation. We have to save not only the old DOM info, but also\n\t\t// the attributes used to create it, as we diff *that*, not against the\n\t\t// DOM directly (with a few exceptions in `setAttr`). And, of course, we\n\t\t// need to save the children and text as they are conceptually not\n\t\t// unlike special \"attributes\" internally.\n\t\tvnode3.attrs = old.attrs\n\t\tvnode3.children = old.children\n\t\tvnode3.text = old.text\n\t\treturn true\n\t}\n\tvar currentDOM\n\treturn function(dom, vnodes, redraw) {\n\t\tif (!dom) throw new TypeError(\"DOM element being rendered to does not exist.\")\n\t\tif (currentDOM != null && dom.contains(currentDOM)) {\n\t\t\tthrow new TypeError(\"Node is currently being rendered to and thus is locked.\")\n\t\t}\n\t\tvar prevRedraw = currentRedraw\n\t\tvar prevDOM = currentDOM\n\t\tvar hooks = []\n\t\tvar active = activeElement(dom)\n\t\tvar namespace = dom.namespaceURI\n\t\tcurrentDOM = dom\n\t\tcurrentRedraw = typeof redraw === \"function\" ? redraw : undefined\n\t\tcurrentRender = {}\n\t\ttry {\n\t\t\t// First time rendering into a node clears it out\n\t\t\tif (dom.vnodes == null) dom.textContent = \"\"\n\t\t\tvnodes = Vnode.normalizeChildren(Array.isArray(vnodes) ? vnodes : [vnodes])\n\t\t\tupdateNodes(dom, dom.vnodes, vnodes, hooks, null, namespace === \"http://www.w3.org/1999/xhtml\" ? undefined : namespace)\n\t\t\tdom.vnodes = vnodes\n\t\t\t// `document.activeElement` can return null: https://html.spec.whatwg.org/multipage/interaction.html#dom-document-activeelement\n\t\t\tif (active != null && activeElement(dom) !== active && typeof active.focus === \"function\") active.focus()\n\t\t\tfor (var i = 0; i < hooks.length; i++) hooks[i]()\n\t\t} finally {\n\t\t\tcurrentRedraw = prevRedraw\n\t\t\tcurrentDOM = prevDOM\n\t\t}\n\t}\n}\nvar render = _14()\nvar _21 = function(render2, schedule, console) {\n\tvar subscriptions = []\n\tvar pending = false\n\tvar offset = -1\n\tfunction sync() {\n\t\tfor (offset = 0; offset < subscriptions.length; offset += 2) {\n\t\t\ttry { render2(subscriptions[offset], Vnode(subscriptions[offset + 1]), redraw) }\n\t\t\tcatch (e) { console.error(e) }\n\t\t}\n\t\toffset = -1\n\t}\n\tfunction redraw() {\n\t\tif (!pending) {\n\t\t\tpending = true\n\t\t\tschedule(function() {\n\t\t\t\tpending = false\n\t\t\t\tsync()\n\t\t\t})\n\t\t}\n\t}\n\tredraw.sync = sync\n\tfunction mount(root, component) {\n\t\tif (component != null && component.view == null && typeof component !== \"function\") {\n\t\t\tthrow new TypeError(\"m.mount expects a component, not a vnode.\")\n\t\t}\n\t\tvar index = subscriptions.indexOf(root)\n\t\tif (index >= 0) {\n\t\t\tsubscriptions.splice(index, 2)\n\t\t\tif (index <= offset) offset -= 2\n\t\t\trender2(root, [])\n\t\t}\n\t\tif (component != null) {\n\t\t\tsubscriptions.push(root, component)\n\t\t\trender2(root, Vnode(component), redraw)\n\t\t}\n\t}\n\treturn {mount: mount, redraw: redraw}\n}\nvar mountRedraw = _21(render, typeof requestAnimationFrame !== \"undefined\" ? requestAnimationFrame : null, typeof console !== \"undefined\" ? console : null)\nvar buildQueryString = function(object) {\n\tif (Object.prototype.toString.call(object) !== \"[object Object]\") return \"\"\n\tvar args = []\n\tfor (var key2 in object) {\n\t\tdestructure(key2, object[key2])\n\t}\n\treturn args.join(\"&\")\n\tfunction destructure(key2, value1) {\n\t\tif (Array.isArray(value1)) {\n\t\t\tfor (var i = 0; i < value1.length; i++) {\n\t\t\t\tdestructure(key2 + \"[\" + i + \"]\", value1[i])\n\t\t\t}\n\t\t}\n\t\telse if (Object.prototype.toString.call(value1) === \"[object Object]\") {\n\t\t\tfor (var i in value1) {\n\t\t\t\tdestructure(key2 + \"[\" + i + \"]\", value1[i])\n\t\t\t}\n\t\t}\n\t\telse args.push(encodeURIComponent(key2) + (value1 != null && value1 !== \"\" ? \"=\" + encodeURIComponent(value1) : \"\"))\n\t}\n}\n// Returns `path` from `template` + `params`\nvar buildPathname = function(template, params) {\n\tif ((/:([^\\/\\.-]+)(\\.{3})?:/).test(template)) {\n\t\tthrow new SyntaxError(\"Template parameter names must be separated by either a '/', '-', or '.'.\")\n\t}\n\tif (params == null) return template\n\tvar queryIndex = template.indexOf(\"?\")\n\tvar hashIndex = template.indexOf(\"#\")\n\tvar queryEnd = hashIndex < 0 ? template.length : hashIndex\n\tvar pathEnd = queryIndex < 0 ? queryEnd : queryIndex\n\tvar path = template.slice(0, pathEnd)\n\tvar query = {}\n\tObject.assign(query, params)\n\tvar resolved = path.replace(/:([^\\/\\.-]+)(\\.{3})?/g, function(m3, key1, variadic) {\n\t\tdelete query[key1]\n\t\t// If no such parameter exists, don't interpolate it.\n\t\tif (params[key1] == null) return m3\n\t\t// Escape normal parameters, but not variadic ones.\n\t\treturn variadic ? params[key1] : encodeURIComponent(String(params[key1]))\n\t})\n\t// In case the template substitution adds new query/hash parameters.\n\tvar newQueryIndex = resolved.indexOf(\"?\")\n\tvar newHashIndex = resolved.indexOf(\"#\")\n\tvar newQueryEnd = newHashIndex < 0 ? resolved.length : newHashIndex\n\tvar newPathEnd = newQueryIndex < 0 ? newQueryEnd : newQueryIndex\n\tvar result0 = resolved.slice(0, newPathEnd)\n\tif (queryIndex >= 0) result0 += template.slice(queryIndex, queryEnd)\n\tif (newQueryIndex >= 0) result0 += (queryIndex < 0 ? \"?\" : \"&\") + resolved.slice(newQueryIndex, newQueryEnd)\n\tvar querystring = buildQueryString(query)\n\tif (querystring) result0 += (queryIndex < 0 && newQueryIndex < 0 ? \"?\" : \"&\") + querystring\n\tif (hashIndex >= 0) result0 += template.slice(hashIndex)\n\tif (newHashIndex >= 0) result0 += (hashIndex < 0 ? \"\" : \"&\") + resolved.slice(newHashIndex)\n\treturn result0\n}\nvar _25 = function($window, oncompletion) {\n\tfunction PromiseProxy(executor) {\n\t\treturn new Promise(executor)\n\t}\n\tfunction makeRequest(url, args) {\n\t\treturn new Promise(function(resolve, reject) {\n\t\t\turl = buildPathname(url, args.params)\n\t\t\tvar method = args.method != null ? args.method.toUpperCase() : \"GET\"\n\t\t\tvar body = args.body\n\t\t\tvar assumeJSON = (args.serialize == null || args.serialize === JSON.serialize) && !(body instanceof $window.FormData || body instanceof $window.URLSearchParams)\n\t\t\tvar responseType = args.responseType || (typeof args.extract === \"function\" ? \"\" : \"json\")\n\t\t\tvar xhr = new $window.XMLHttpRequest(), aborted = false, isTimeout = false\n\t\t\tvar original0 = xhr, replacedAbort\n\t\t\tvar abort = xhr.abort\n\t\t\txhr.abort = function() {\n\t\t\t\taborted = true\n\t\t\t\tabort.call(this)\n\t\t\t}\n\t\t\txhr.open(method, url, args.async !== false, typeof args.user === \"string\" ? args.user : undefined, typeof args.password === \"string\" ? args.password : undefined)\n\t\t\tif (assumeJSON && body != null && !hasHeader(args, \"content-type\")) {\n\t\t\t\txhr.setRequestHeader(\"Content-Type\", \"application/json; charset=utf-8\")\n\t\t\t}\n\t\t\tif (typeof args.deserialize !== \"function\" && !hasHeader(args, \"accept\")) {\n\t\t\t\txhr.setRequestHeader(\"Accept\", \"application/json, text/*\")\n\t\t\t}\n\t\t\tif (args.withCredentials) xhr.withCredentials = args.withCredentials\n\t\t\tif (args.timeout) xhr.timeout = args.timeout\n\t\t\txhr.responseType = responseType\n\t\t\tfor (var key0 in args.headers) {\n\t\t\t\tif (hasOwn.call(args.headers, key0)) {\n\t\t\t\t\txhr.setRequestHeader(key0, args.headers[key0])\n\t\t\t\t}\n\t\t\t}\n\t\t\txhr.onreadystatechange = function(ev) {\n\t\t\t\t// Don't throw errors on xhr.abort().\n\t\t\t\tif (aborted) return\n\t\t\t\tif (ev.target.readyState === 4) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tvar success = (ev.target.status >= 200 && ev.target.status < 300) || ev.target.status === 304 || (/^file:\\/\\//i).test(url)\n\t\t\t\t\t\t// When the response type isn't \"\" or \"text\",\n\t\t\t\t\t\t// `xhr.responseText` is the wrong thing to use.\n\t\t\t\t\t\t// Browsers do the right thing and throw here, and we\n\t\t\t\t\t\t// should honor that and do the right thing by\n\t\t\t\t\t\t// preferring `xhr.response` where possible/practical.\n\t\t\t\t\t\tvar response = ev.target.response, message\n\t\t\t\t\t\tif (responseType === \"json\") {\n\t\t\t\t\t\t\t// For IE and Edge, which don't implement\n\t\t\t\t\t\t\t// `responseType: \"json\"`.\n\t\t\t\t\t\t\tif (!ev.target.responseType && typeof args.extract !== \"function\") {\n\t\t\t\t\t\t\t\t// Handle no-content which will not parse.\n\t\t\t\t\t\t\t\ttry { response = JSON.parse(ev.target.responseText) }\n\t\t\t\t\t\t\t\tcatch (e) { response = null }\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else if (!responseType || responseType === \"text\") {\n\t\t\t\t\t\t\t// Only use this default if it's text. If a parsed\n\t\t\t\t\t\t\t// document is needed on old IE and friends (all\n\t\t\t\t\t\t\t// unsupported), the user should use a custom\n\t\t\t\t\t\t\t// `config` instead. They're already using this at\n\t\t\t\t\t\t\t// their own risk.\n\t\t\t\t\t\t\tif (response == null) response = ev.target.responseText\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (typeof args.extract === \"function\") {\n\t\t\t\t\t\t\tresponse = args.extract(ev.target, args)\n\t\t\t\t\t\t\tsuccess = true\n\t\t\t\t\t\t} else if (typeof args.deserialize === \"function\") {\n\t\t\t\t\t\t\tresponse = args.deserialize(response)\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (success) {\n\t\t\t\t\t\t\tif (typeof args.type === \"function\") {\n\t\t\t\t\t\t\t\tif (Array.isArray(response)) {\n\t\t\t\t\t\t\t\t\tfor (var i = 0; i < response.length; i++) {\n\t\t\t\t\t\t\t\t\t\tresponse[i] = new args.type(response[i])\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\telse response = new args.type(response)\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tresolve(response)\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse {\n\t\t\t\t\t\t\tvar completeErrorResponse = function() {\n\t\t\t\t\t\t\t\ttry { message = ev.target.responseText }\n\t\t\t\t\t\t\t\tcatch (e) { message = response }\n\t\t\t\t\t\t\t\tvar error = new Error(message)\n\t\t\t\t\t\t\t\terror.code = ev.target.status\n\t\t\t\t\t\t\t\terror.response = response\n\t\t\t\t\t\t\t\treject(error)\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (xhr.status === 0) {\n\t\t\t\t\t\t\t\t// Use setTimeout to push this code block onto the event queue\n\t\t\t\t\t\t\t\t// This allows `xhr.ontimeout` to run in the case that there is a timeout\n\t\t\t\t\t\t\t\t// Without this setTimeout, `xhr.ontimeout` doesn't have a chance to reject\n\t\t\t\t\t\t\t\t// as `xhr.onreadystatechange` will run before it\n\t\t\t\t\t\t\t\tsetTimeout(function() {\n\t\t\t\t\t\t\t\t\tif (isTimeout) return\n\t\t\t\t\t\t\t\t\tcompleteErrorResponse()\n\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t} else completeErrorResponse()\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tcatch (e) {\n\t\t\t\t\t\treject(e)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\txhr.ontimeout = function (ev) {\n\t\t\t\tisTimeout = true\n\t\t\t\tvar error = new Error(\"Request timed out\")\n\t\t\t\terror.code = ev.target.status\n\t\t\t\treject(error)\n\t\t\t}\n\t\t\tif (typeof args.config === \"function\") {\n\t\t\t\txhr = args.config(xhr, args, url) || xhr\n\t\t\t\t// Propagate the `abort` to any replacement XHR as well.\n\t\t\t\tif (xhr !== original0) {\n\t\t\t\t\treplacedAbort = xhr.abort\n\t\t\t\t\txhr.abort = function() {\n\t\t\t\t\t\taborted = true\n\t\t\t\t\t\treplacedAbort.call(this)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (body == null) xhr.send()\n\t\t\telse if (typeof args.serialize === \"function\") xhr.send(args.serialize(body))\n\t\t\telse if (body instanceof $window.FormData || body instanceof $window.URLSearchParams) xhr.send(body)\n\t\t\telse xhr.send(JSON.stringify(body))\n\t\t})\n\t}\n\t// In case the global Promise is some userland library's where they rely on\n\t// `foo instanceof this.constructor`, `this.constructor.resolve(value)`, or\n\t// similar. Let's *not* break them.\n\tPromiseProxy.prototype = Promise.prototype\n\tPromiseProxy.__proto__ = Promise // eslint-disable-line no-proto\n\tfunction hasHeader(args, name) {\n\t\tfor (var key0 in args.headers) {\n\t\t\tif (hasOwn.call(args.headers, key0) && key0.toLowerCase() === name) return true\n\t\t}\n\t\treturn false\n\t}\n\treturn {\n\t\trequest: function(url, args) {\n\t\t\tif (typeof url !== \"string\") { args = url; url = url.url }\n\t\t\telse if (args == null) args = {}\n\t\t\tvar promise = makeRequest(url, args)\n\t\t\tif (args.background === true) return promise\n\t\t\tvar count = 0\n\t\t\tfunction complete() {\n\t\t\t\tif (--count === 0 && typeof oncompletion === \"function\") oncompletion()\n\t\t\t}\n\t\t\treturn wrap(promise)\n\t\t\tfunction wrap(promise) {\n\t\t\t\tvar then = promise.then\n\t\t\t\t// Set the constructor, so engines know to not await or resolve\n\t\t\t\t// this as a native promise. At the time of writing, this is\n\t\t\t\t// only necessary for V8, but their behavior is the correct\n\t\t\t\t// behavior per spec. See this spec issue for more details:\n\t\t\t\t// https://github.com/tc39/ecma262/issues/1577. Also, see the\n\t\t\t\t// corresponding comment in `request/tests/test-request.js` for\n\t\t\t\t// a bit more background on the issue at hand.\n\t\t\t\tpromise.constructor = PromiseProxy\n\t\t\t\tpromise.then = function() {\n\t\t\t\t\tcount++\n\t\t\t\t\tvar next = then.apply(promise, arguments)\n\t\t\t\t\tnext.then(complete, function(e) {\n\t\t\t\t\t\tcomplete()\n\t\t\t\t\t\tif (count === 0) throw e\n\t\t\t\t\t})\n\t\t\t\t\treturn wrap(next)\n\t\t\t\t}\n\t\t\t\treturn promise\n\t\t\t}\n\t\t}\n\t}\n}\nvar request = _25(typeof window !== \"undefined\" ? window : null, mountRedraw.redraw)\n/*\nPercent encodings encode UTF-8 bytes, so this regexp needs to match that.\nHere's how UTF-8 encodes stuff:\n- `00-7F`: 1-byte, for U+0000-U+007F\n- `C2-DF 80-BF`: 2-byte, for U+0080-U+07FF\n- `E0-EF 80-BF 80-BF`: 3-byte, encodes U+0800-U+FFFF\n- `F0-F4 80-BF 80-BF 80-BF`: 4-byte, encodes U+10000-U+10FFFF\nIn this, there's a number of invalid byte sequences:\n- `80-BF`: Continuation byte, invalid as start\n- `C0-C1 80-BF`: Overlong encoding for U+0000-U+007F\n- `E0 80-9F 80-BF`: Overlong encoding for U+0080-U+07FF\n- `ED A0-BF 80-BF`: Encoding for UTF-16 surrogate U+D800-U+DFFF\n- `F0 80-8F 80-BF 80-BF`: Overlong encoding for U+0800-U+FFFF\n- `F4 90-BF`: RFC 3629 restricted UTF-8 to only code points UTF-16 could encode.\n- `F5-FF`: RFC 3629 restricted UTF-8 to only code points UTF-16 could encode.\nSo in reality, only the following sequences can encode are valid characters:\n- 00-7F\n- C2-DF 80-BF\n- E0    A0-BF 80-BF\n- E1-EC 80-BF 80-BF\n- ED    80-9F 80-BF\n- EE-EF 80-BF 80-BF\n- F0    90-BF 80-BF 80-BF\n- F1-F3 80-BF 80-BF 80-BF\n- F4    80-8F 80-BF 80-BF\nThe regexp just tries to match this as compactly as possible.\n*/\nvar validUtf8Encodings = /%(?:[0-7]|(?!c[01]|e0%[89]|ed%[ab]|f0%8|f4%[9ab])(?:c|d|(?:e|f[0-4]%[89ab])[\\da-f]%[89ab])[\\da-f]%[89ab])[\\da-f]/gi\nvar decodeURIComponentSafe = function(str) {\n\treturn String(str).replace(validUtf8Encodings, decodeURIComponent)\n}\nvar parseQueryString = function(string) {\n\tif (string === \"\" || string == null) return {}\n\tif (string.charAt(0) === \"?\") string = string.slice(1)\n\tvar entries = string.split(\"&\"), counters = {}, data0 = {}\n\tfor (var i = 0; i < entries.length; i++) {\n\t\tvar entry = entries[i].split(\"=\")\n\t\tvar key4 = decodeURIComponentSafe(entry[0])\n\t\tvar value2 = entry.length === 2 ? decodeURIComponentSafe(entry[1]) : \"\"\n\t\tif (value2 === \"true\") value2 = true\n\t\telse if (value2 === \"false\") value2 = false\n\t\tvar levels = key4.split(/\\]\\[?|\\[/)\n\t\tvar cursor = data0\n\t\tif (key4.indexOf(\"[\") > -1) levels.pop()\n\t\tfor (var j0 = 0; j0 < levels.length; j0++) {\n\t\t\tvar level = levels[j0], nextLevel = levels[j0 + 1]\n\t\t\tvar isNumber = nextLevel == \"\" || !isNaN(parseInt(nextLevel, 10))\n\t\t\tif (level === \"\") {\n\t\t\t\tvar key4 = levels.slice(0, j0).join()\n\t\t\t\tif (counters[key4] == null) {\n\t\t\t\t\tcounters[key4] = Array.isArray(cursor) ? cursor.length : 0\n\t\t\t\t}\n\t\t\t\tlevel = counters[key4]++\n\t\t\t}\n\t\t\t// Disallow direct prototype pollution\n\t\t\telse if (level === \"__proto__\") break\n\t\t\tif (j0 === levels.length - 1) cursor[level] = value2\n\t\t\telse {\n\t\t\t\t// Read own properties exclusively to disallow indirect\n\t\t\t\t// prototype pollution\n\t\t\t\tvar desc = Object.getOwnPropertyDescriptor(cursor, level)\n\t\t\t\tif (desc != null) desc = desc.value\n\t\t\t\tif (desc == null) cursor[level] = desc = isNumber ? [] : {}\n\t\t\t\tcursor = desc\n\t\t\t}\n\t\t}\n\t}\n\treturn data0\n}\n// Returns `{path, params}` from `url`\nvar parsePathname = function(url) {\n\tvar queryIndex0 = url.indexOf(\"?\")\n\tvar hashIndex0 = url.indexOf(\"#\")\n\tvar queryEnd0 = hashIndex0 < 0 ? url.length : hashIndex0\n\tvar pathEnd0 = queryIndex0 < 0 ? queryEnd0 : queryIndex0\n\tvar path1 = url.slice(0, pathEnd0).replace(/\\/{2,}/g, \"/\")\n\tif (!path1) path1 = \"/\"\n\telse {\n\t\tif (path1[0] !== \"/\") path1 = \"/\" + path1\n\t}\n\treturn {\n\t\tpath: path1,\n\t\tparams: queryIndex0 < 0\n\t\t\t? {}\n\t\t\t: parseQueryString(url.slice(queryIndex0 + 1, queryEnd0)),\n\t}\n}\n// Compiles a template into a function that takes a resolved path (without query\n// strings) and returns an object containing the template parameters with their\n// parsed values. This expects the input of the compiled template to be the\n// output of `parsePathname`. Note that it does *not* remove query parameters\n// specified in the template.\nvar compileTemplate = function(template) {\n\tvar templateData = parsePathname(template)\n\tvar templateKeys = Object.keys(templateData.params)\n\tvar keys = []\n\tvar regexp = new RegExp(\"^\" + templateData.path.replace(\n\t\t// I escape literal text so people can use things like `:file.:ext` or\n\t\t// `:lang-:locale` in routes. This is all merged into one pass so I\n\t\t// don't also accidentally escape `-` and make it harder to detect it to\n\t\t// ban it from template parameters.\n\t\t/:([^\\/.-]+)(\\.{3}|\\.(?!\\.)|-)?|[\\\\^$*+.()|\\[\\]{}]/g,\n\t\tfunction(m4, key5, extra) {\n\t\t\tif (key5 == null) return \"\\\\\" + m4\n\t\t\tkeys.push({k: key5, r: extra === \"...\"})\n\t\t\tif (extra === \"...\") return \"(.*)\"\n\t\t\tif (extra === \".\") return \"([^/]+)\\\\.\"\n\t\t\treturn \"([^/]+)\" + (extra || \"\")\n\t\t}\n\t) + \"\\\\/?$\")\n\treturn function(data1) {\n\t\t// First, check the params. Usually, there isn't any, and it's just\n\t\t// checking a static set.\n\t\tfor (var i = 0; i < templateKeys.length; i++) {\n\t\t\tif (templateData.params[templateKeys[i]] !== data1.params[templateKeys[i]]) return false\n\t\t}\n\t\t// If no interpolations exist, let's skip all the ceremony\n\t\tif (!keys.length) return regexp.test(data1.path)\n\t\tvar values = regexp.exec(data1.path)\n\t\tif (values == null) return false\n\t\tfor (var i = 0; i < keys.length; i++) {\n\t\t\tdata1.params[keys[i].k] = keys[i].r ? values[i + 1] : decodeURIComponent(values[i + 1])\n\t\t}\n\t\treturn true\n\t}\n}\n// Note: this is mildly perf-sensitive.\n//\n// It does *not* use `delete` - dynamic `delete`s usually cause objects to bail\n// out into dictionary mode and just generally cause a bunch of optimization\n// issues within engines.\n//\n// Ideally, I would've preferred to do this, if it weren't for the optimization\n// issues:\n//\n// ```js\n// const hasOwn = hasOwn\n// const magic = [\n//     \"key\", \"oninit\", \"oncreate\", \"onbeforeupdate\", \"onupdate\",\n//     \"onbeforeremove\", \"onremove\",\n// ]\n// var censor = (attrs, extras) => {\n//     const result = Object.assign(Object.create(null), attrs)\n//     for (const key of magic) delete result[key]\n//     if (extras != null) for (const key of extras) delete result[key]\n//     return result\n// }\n// ```\nvar magic = /^(?:key|oninit|oncreate|onbeforeupdate|onupdate|onbeforeremove|onremove)$/\nvar censor = function(attrs7, extras) {\n\tvar result2 = {}\n\tif (extras != null) {\n\t\tfor (var key6 in attrs7) {\n\t\t\tif (hasOwn.call(attrs7, key6) && !magic.test(key6) && extras.indexOf(key6) < 0) {\n\t\t\t\tresult2[key6] = attrs7[key6]\n\t\t\t}\n\t\t}\n\t} else {\n\t\tfor (var key6 in attrs7) {\n\t\t\tif (hasOwn.call(attrs7, key6) && !magic.test(key6)) {\n\t\t\t\tresult2[key6] = attrs7[key6]\n\t\t\t}\n\t\t}\n\t}\n\treturn result2\n}\nvar _31 = function($window, mountRedraw0) {\n\tvar p = Promise.resolve()\n\tvar scheduled = false\n\tvar ready = false\n\tvar hasBeenResolved = false\n\tvar dom0, compiled, fallbackRoute\n\tvar currentResolver, component, attrs6, currentPath, lastUpdate\n\tvar RouterRoot = {\n\t\tonremove: function() {\n\t\t\tready = hasBeenResolved = false\n\t\t\t$window.removeEventListener(\"popstate\", fireAsync, false)\n\t\t},\n\t\tview: function() {\n\t\t\t// The route has already been resolved.\n\t\t\t// Therefore, the following early return is not needed.\n\t\t\t// if (!hasBeenResolved) return\n\t\t\tvar vnode6 = Vnode(component, attrs6.key, attrs6)\n\t\t\tif (currentResolver) return currentResolver.render(vnode6)\n\t\t\t// Wrap in a fragment to preserve existing key semantics\n\t\t\treturn [vnode6]\n\t\t},\n\t}\n\tvar SKIP = route.SKIP = {}\n\tfunction resolveRoute() {\n\t\tscheduled = false\n\t\t// Consider the pathname holistically. The prefix might even be invalid,\n\t\t// but that's not our problem.\n\t\tvar prefix = $window.location.hash\n\t\tif (route.prefix[0] !== \"#\") {\n\t\t\tprefix = $window.location.search + prefix\n\t\t\tif (route.prefix[0] !== \"?\") {\n\t\t\t\tprefix = $window.location.pathname + prefix\n\t\t\t\tif (prefix[0] !== \"/\") prefix = \"/\" + prefix\n\t\t\t}\n\t\t}\n\t\tvar path0 = decodeURIComponentSafe(prefix).slice(route.prefix.length)\n\t\tvar data = parsePathname(path0)\n\t\tObject.assign(data.params, $window.history.state)\n\t\tfunction reject(e) {\n\t\t\tconsole.error(e)\n\t\t\troute.set(fallbackRoute, null, {replace: true})\n\t\t}\n\t\tloop(0)\n\t\tfunction loop(i) {\n\t\t\tfor (; i < compiled.length; i++) {\n\t\t\t\tif (compiled[i].check(data)) {\n\t\t\t\t\tvar payload = compiled[i].component\n\t\t\t\t\tvar matchedRoute = compiled[i].route\n\t\t\t\t\tvar localComp = payload\n\t\t\t\t\tvar update = lastUpdate = function(comp) {\n\t\t\t\t\t\tif (update !== lastUpdate) return\n\t\t\t\t\t\tif (comp === SKIP) return loop(i + 1)\n\t\t\t\t\t\tcomponent = comp != null && (typeof comp.view === \"function\" || typeof comp === \"function\")? comp : \"div\"\n\t\t\t\t\t\tattrs6 = data.params, currentPath = path0, lastUpdate = null\n\t\t\t\t\t\tcurrentResolver = payload.render ? payload : null\n\t\t\t\t\t\tif (hasBeenResolved) mountRedraw0.redraw()\n\t\t\t\t\t\telse {\n\t\t\t\t\t\t\thasBeenResolved = true\n\t\t\t\t\t\t\tmountRedraw0.mount(dom0, RouterRoot)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\t// There's no understating how much I *wish* I could\n\t\t\t\t\t// use `async`/`await` here...\n\t\t\t\t\tif (payload.view || typeof payload === \"function\") {\n\t\t\t\t\t\tpayload = {}\n\t\t\t\t\t\tupdate(localComp)\n\t\t\t\t\t}\n\t\t\t\t\telse if (payload.onmatch) {\n\t\t\t\t\t\tp.then(function () {\n\t\t\t\t\t\t\treturn payload.onmatch(data.params, path0, matchedRoute)\n\t\t\t\t\t\t}).then(update, path0 === fallbackRoute ? null : reject)\n\t\t\t\t\t}\n\t\t\t\t\telse update(/* \"div\" */)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (path0 === fallbackRoute) {\n\t\t\t\tthrow new Error(\"Could not resolve default route \" + fallbackRoute + \".\")\n\t\t\t}\n\t\t\troute.set(fallbackRoute, null, {replace: true})\n\t\t}\n\t}\n\tfunction fireAsync() {\n\t\tif (!scheduled) {\n\t\t\tscheduled = true\n\t\t\t// TODO: just do `mountRedraw.redraw()` here and elide the timer\n\t\t\t// dependency. Note that this will muck with tests a *lot*, so it's\n\t\t\t// not as easy of a change as it sounds.\n\t\t\tsetTimeout(resolveRoute)\n\t\t}\n\t}\n\tfunction route(root, defaultRoute, routes) {\n\t\tif (!root) throw new TypeError(\"DOM element being rendered to does not exist.\")\n\t\tcompiled = Object.keys(routes).map(function(route) {\n\t\t\tif (route[0] !== \"/\") throw new SyntaxError(\"Routes must start with a '/'.\")\n\t\t\tif ((/:([^\\/\\.-]+)(\\.{3})?:/).test(route)) {\n\t\t\t\tthrow new SyntaxError(\"Route parameter names must be separated with either '/', '.', or '-'.\")\n\t\t\t}\n\t\t\treturn {\n\t\t\t\troute: route,\n\t\t\t\tcomponent: routes[route],\n\t\t\t\tcheck: compileTemplate(route),\n\t\t\t}\n\t\t})\n\t\tfallbackRoute = defaultRoute\n\t\tif (defaultRoute != null) {\n\t\t\tvar defaultData = parsePathname(defaultRoute)\n\t\t\tif (!compiled.some(function (i) { return i.check(defaultData) })) {\n\t\t\t\tthrow new ReferenceError(\"Default route doesn't match any known routes.\")\n\t\t\t}\n\t\t}\n\t\tdom0 = root\n\t\t$window.addEventListener(\"popstate\", fireAsync, false)\n\t\tready = true\n\t\t// The RouterRoot component is mounted when the route is first resolved.\n\t\tresolveRoute()\n\t}\n\troute.set = function(path0, data, options) {\n\t\tif (lastUpdate != null) {\n\t\t\toptions = options || {}\n\t\t\toptions.replace = true\n\t\t}\n\t\tlastUpdate = null\n\t\tpath0 = buildPathname(path0, data)\n\t\tif (ready) {\n\t\t\tfireAsync()\n\t\t\tvar state = options ? options.state : null\n\t\t\tvar title = options ? options.title : null\n\t\t\tif (options && options.replace) $window.history.replaceState(state, title, route.prefix + path0)\n\t\t\telse $window.history.pushState(state, title, route.prefix + path0)\n\t\t}\n\t\telse {\n\t\t\t$window.location.href = route.prefix + path0\n\t\t}\n\t}\n\troute.get = function() {return currentPath}\n\troute.prefix = \"#!\"\n\troute.Link = {\n\t\tview: function(vnode6) {\n\t\t\t// Omit the used parameters from the rendered element - they are\n\t\t\t// internal. Also, censor the various lifecycle methods.\n\t\t\t//\n\t\t\t// We don't strip the other parameters because for convenience we\n\t\t\t// let them be specified in the selector as well.\n\t\t\tvar child0 = hyperscript(\n\t\t\t\tvnode6.attrs.selector || \"a\",\n\t\t\t\tcensor(vnode6.attrs, [\"options\", \"params\", \"selector\", \"onclick\"]),\n\t\t\t\tvnode6.children\n\t\t\t)\n\t\t\tvar options, onclick, href\n\t\t\t// Let's provide a *right* way to disable a route link, rather than\n\t\t\t// letting people screw up accessibility on accident.\n\t\t\t//\n\t\t\t// The attribute is coerced so users don't get surprised over\n\t\t\t// `disabled: 0` resulting in a button that's somehow routable\n\t\t\t// despite being visibly disabled.\n\t\t\tif (child0.attrs.disabled = Boolean(child0.attrs.disabled)) {\n\t\t\t\tchild0.attrs.href = null\n\t\t\t\tchild0.attrs[\"aria-disabled\"] = \"true\"\n\t\t\t\t// If you *really* do want add `onclick` on a disabled link, use\n\t\t\t\t// an `oncreate` hook to add it.\n\t\t\t} else {\n\t\t\t\toptions = vnode6.attrs.options\n\t\t\t\tonclick = vnode6.attrs.onclick\n\t\t\t\t// Easier to build it now to keep it isomorphic.\n\t\t\t\thref = buildPathname(child0.attrs.href, vnode6.attrs.params)\n\t\t\t\tchild0.attrs.href = route.prefix + href\n\t\t\t\tchild0.attrs.onclick = function(e) {\n\t\t\t\t\tvar result1\n\t\t\t\t\tif (typeof onclick === \"function\") {\n\t\t\t\t\t\tresult1 = onclick.call(e.currentTarget, e)\n\t\t\t\t\t} else if (onclick == null || typeof onclick !== \"object\") {\n\t\t\t\t\t\t// do nothing\n\t\t\t\t\t} else if (typeof onclick.handleEvent === \"function\") {\n\t\t\t\t\t\tonclick.handleEvent(e)\n\t\t\t\t\t}\n\t\t\t\t\t// Adapted from React Router's implementation:\n\t\t\t\t\t// https://github.com/ReactTraining/react-router/blob/520a0acd48ae1b066eb0b07d6d4d1790a1d02482/packages/react-router-dom/modules/Link.js\n\t\t\t\t\t//\n\t\t\t\t\t// Try to be flexible and intuitive in how we handle links.\n\t\t\t\t\t// Fun fact: links aren't as obvious to get right as you\n\t\t\t\t\t// would expect. There's a lot more valid ways to click a\n\t\t\t\t\t// link than this, and one might want to not simply click a\n\t\t\t\t\t// link, but right click or command-click it to copy the\n\t\t\t\t\t// link target, etc. Nope, this isn't just for blind people.\n\t\t\t\t\tif (\n\t\t\t\t\t\t// Skip if `onclick` prevented default\n\t\t\t\t\t\tresult1 !== false && !e.defaultPrevented &&\n\t\t\t\t\t\t// Ignore everything but left clicks\n\t\t\t\t\t\t(e.button === 0 || e.which === 0 || e.which === 1) &&\n\t\t\t\t\t\t// Let the browser handle `target=_blank`, etc.\n\t\t\t\t\t\t(!e.currentTarget.target || e.currentTarget.target === \"_self\") &&\n\t\t\t\t\t\t// No modifier keys\n\t\t\t\t\t\t!e.ctrlKey && !e.metaKey && !e.shiftKey && !e.altKey\n\t\t\t\t\t) {\n\t\t\t\t\t\te.preventDefault()\n\t\t\t\t\t\te.redraw = false\n\t\t\t\t\t\troute.set(href, null, options)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn child0\n\t\t},\n\t}\n\troute.param = function(key3) {\n\t\treturn attrs6 && key3 != null ? attrs6[key3] : attrs6\n\t}\n\treturn route\n}\nvar router = _31(typeof window !== \"undefined\" ? window : null, mountRedraw)\nvar m = function m() { return hyperscript.apply(this, arguments) }\nm.m = hyperscript\nm.trust = hyperscript.trust\nm.fragment = hyperscript.fragment\nm.Fragment = \"[\"\nm.mount = mountRedraw.mount\nm.route = router\nm.render = render\nm.redraw = mountRedraw.redraw\nm.request = request.request\nm.parseQueryString = parseQueryString\nm.buildQueryString = buildQueryString\nm.parsePathname = parsePathname\nm.buildPathname = buildPathname\nm.vnode = Vnode\nm.censor = censor\nm.domFor = domFor\nif (typeof module !== \"undefined\") module[\"exports\"] = m\nelse window.m = m\n}());"
  },
  {
    "path": "mount-redraw.js",
    "content": "\"use strict\"\n\nvar render = require(\"./render\")\n\nmodule.exports = require(\"./api/mount-redraw\")(render, typeof requestAnimationFrame !== \"undefined\" ? requestAnimationFrame : null, typeof console !== \"undefined\" ? console : null)\n"
  },
  {
    "path": "mount.js",
    "content": "\"use strict\"\n\nmodule.exports = require(\"./mount-redraw\").mount\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"mithril\",\n  \"version\": \"2.3.8\",\n  \"description\": \"A framework for building brilliant applications\",\n  \"author\": \"Leo Horie\",\n  \"license\": \"MIT\",\n  \"unpkg\": \"mithril.min.js\",\n  \"jsdelivr\": \"mithril.min.js\",\n  \"repository\": \"github:MithrilJS/mithril.js\",\n  \"scripts\": {\n    \"watch\": \"run-p watch:**\",\n    \"watch:js\": \"node scripts/bundler browser.js -output mithril.js -watch\",\n    \"build\": \"run-p build:browser build:min build:stream-min\",\n    \"build:browser\": \"node scripts/bundler browser.js -output mithril.js\",\n    \"build:min\": \"node scripts/bundler browser.js -output mithril.min.js -minify -save\",\n    \"build:stream-min\": \"node scripts/minify-stream\",\n    \"cleanup:lint\": \"rimraf .eslintcache\",\n    \"lint\": \"run-s -cn lint:**\",\n    \"lint:js\": \"eslint . --cache\",\n    \"perf\": \"node performance/test-perf.js\",\n    \"pretest\": \"npm run lint\",\n    \"test\": \"run-s test:js\",\n    \"test:js\": \"ospec\"\n  },\n  \"devDependencies\": {\n    \"benchmark\": \"^2.1.4\",\n    \"chokidar\": \"^4.0.1\",\n    \"eslint\": \"^8.9.0\",\n    \"glob\": \"^13.0.0\",\n    \"npm-run-all\": \"^4.1.5\",\n    \"ospec\": \"4.2.1\",\n    \"rimraf\": \"^6.0.1\",\n    \"terser\": \"^5.7.2\"\n  }\n}\n"
  },
  {
    "path": "pathname/build.js",
    "content": "\"use strict\"\n\nvar buildQueryString = require(\"../querystring/build\")\n\n// Returns `path` from `template` + `params`\nmodule.exports = function(template, params) {\n\tif ((/:([^\\/\\.-]+)(\\.{3})?:/).test(template)) {\n\t\tthrow new SyntaxError(\"Template parameter names must be separated by either a '/', '-', or '.'.\")\n\t}\n\tif (params == null) return template\n\tvar queryIndex = template.indexOf(\"?\")\n\tvar hashIndex = template.indexOf(\"#\")\n\tvar queryEnd = hashIndex < 0 ? template.length : hashIndex\n\tvar pathEnd = queryIndex < 0 ? queryEnd : queryIndex\n\tvar path = template.slice(0, pathEnd)\n\tvar query = {}\n\n\tObject.assign(query, params)\n\n\tvar resolved = path.replace(/:([^\\/\\.-]+)(\\.{3})?/g, function(m, key, variadic) {\n\t\tdelete query[key]\n\t\t// If no such parameter exists, don't interpolate it.\n\t\tif (params[key] == null) return m\n\t\t// Escape normal parameters, but not variadic ones.\n\t\treturn variadic ? params[key] : encodeURIComponent(String(params[key]))\n\t})\n\n\t// In case the template substitution adds new query/hash parameters.\n\tvar newQueryIndex = resolved.indexOf(\"?\")\n\tvar newHashIndex = resolved.indexOf(\"#\")\n\tvar newQueryEnd = newHashIndex < 0 ? resolved.length : newHashIndex\n\tvar newPathEnd = newQueryIndex < 0 ? newQueryEnd : newQueryIndex\n\tvar result = resolved.slice(0, newPathEnd)\n\n\tif (queryIndex >= 0) result += template.slice(queryIndex, queryEnd)\n\tif (newQueryIndex >= 0) result += (queryIndex < 0 ? \"?\" : \"&\") + resolved.slice(newQueryIndex, newQueryEnd)\n\tvar querystring = buildQueryString(query)\n\tif (querystring) result += (queryIndex < 0 && newQueryIndex < 0 ? \"?\" : \"&\") + querystring\n\tif (hashIndex >= 0) result += template.slice(hashIndex)\n\tif (newHashIndex >= 0) result += (hashIndex < 0 ? \"\" : \"&\") + resolved.slice(newHashIndex)\n\treturn result\n}\n"
  },
  {
    "path": "pathname/compileTemplate.js",
    "content": "\"use strict\"\n\nvar parsePathname = require(\"./parse\")\nvar decodeURIComponentSafe = require(\"../util/decodeURIComponentSafe\")\n\n// Compiles a template into a function that takes a resolved path (without query\n// strings) and returns an object containing the template parameters with their\n// parsed values. This expects the input of the compiled template to be the\n// output of `parsePathname`. Note that it does *not* remove query parameters\n// specified in the template.\nmodule.exports = function(template) {\n\tvar templateData = parsePathname(template)\n\tvar templateKeys = Object.keys(templateData.params)\n\tvar keys = []\n\tvar regexp = new RegExp(\"^\" + templateData.path.replace(\n\t\t// I escape literal text so people can use things like `:file.:ext` or\n\t\t// `:lang-:locale` in routes. This is all merged into one pass so I\n\t\t// don't also accidentally escape `-` and make it harder to detect it to\n\t\t// ban it from template parameters.\n\t\t/:([^\\/.-]+)(\\.{3}|\\.(?!\\.)|-)?|[\\\\^$*+.()|\\[\\]{}]/g,\n\t\tfunction(m, key, extra) {\n\t\t\tif (key == null) return \"\\\\\" + m\n\t\t\tkeys.push({k: key, r: extra === \"...\"})\n\t\t\tif (extra === \"...\") return \"(.*)\"\n\t\t\tif (extra === \".\") return \"([^/]+)\\\\.\"\n\t\t\treturn \"([^/]+)\" + (extra || \"\")\n\t\t}\n\t) + \"\\\\/?$\")\n\treturn function(data) {\n\t\t// First, check the params. Usually, there isn't any, and it's just\n\t\t// checking a static set.\n\t\tfor (var i = 0; i < templateKeys.length; i++) {\n\t\t\tif (templateData.params[templateKeys[i]] !== data.params[templateKeys[i]]) return false\n\t\t}\n\t\t// If no interpolations exist, let's skip all the ceremony\n\t\tif (!keys.length) return regexp.test(data.path)\n\t\tvar values = regexp.exec(data.path)\n\t\tif (values == null) return false\n\t\tfor (var i = 0; i < keys.length; i++) {\n\t\t\tdata.params[keys[i].k] = keys[i].r ? values[i + 1] : decodeURIComponentSafe(values[i + 1])\n\t\t}\n\t\treturn true\n\t}\n}\n"
  },
  {
    "path": "pathname/parse.js",
    "content": "\"use strict\"\n\nvar parseQueryString = require(\"../querystring/parse\")\n\n// Returns `{path, params}` from `url`\nmodule.exports = function(url) {\n\tvar queryIndex = url.indexOf(\"?\")\n\tvar hashIndex = url.indexOf(\"#\")\n\tvar queryEnd = hashIndex < 0 ? url.length : hashIndex\n\tvar pathEnd = queryIndex < 0 ? queryEnd : queryIndex\n\tvar path = url.slice(0, pathEnd).replace(/\\/{2,}/g, \"/\")\n\n\tif (!path) path = \"/\"\n\telse {\n\t\tif (path[0] !== \"/\") path = \"/\" + path\n\t}\n\treturn {\n\t\tpath: path,\n\t\tparams: queryIndex < 0\n\t\t\t? {}\n\t\t\t: parseQueryString(url.slice(queryIndex + 1, queryEnd)),\n\t}\n}\n"
  },
  {
    "path": "pathname/tests/test-buildPathname.js",
    "content": "\"use strict\"\n\nvar o = require(\"ospec\")\nvar buildPathname = require(\"../../pathname/build\")\n\no.spec(\"buildPathname\", function() {\n\tfunction test(prefix) {\n\t\to(\"returns path if no params\", function () {\n\t\t\tvar string = buildPathname(prefix + \"/route/foo\", undefined)\n\n\t\t\to(string).equals(prefix + \"/route/foo\")\n\t\t})\n\t\to(\"skips interpolation if no params\", function () {\n\t\t\tvar string = buildPathname(prefix + \"/route/:id\", undefined)\n\n\t\t\to(string).equals(prefix + \"/route/:id\")\n\t\t})\n\t\to(\"appends query strings\", function () {\n\t\t\tvar string = buildPathname(prefix + \"/route/foo\", {a: \"b\", c: 1})\n\n\t\t\to(string).equals(prefix + \"/route/foo?a=b&c=1\")\n\t\t})\n\t\to(\"inserts template parameters at end\", function () {\n\t\t\tvar string = buildPathname(prefix + \"/route/:id\", {id: \"1\"})\n\n\t\t\to(string).equals(prefix + \"/route/1\")\n\t\t})\n\t\to(\"inserts template parameters at beginning\", function () {\n\t\t\tvar string = buildPathname(prefix + \"/:id/foo\", {id: \"1\"})\n\n\t\t\to(string).equals(prefix + \"/1/foo\")\n\t\t})\n\t\to(\"inserts template parameters at middle\", function () {\n\t\t\tvar string = buildPathname(prefix + \"/route/:id/foo\", {id: \"1\"})\n\n\t\t\to(string).equals(prefix + \"/route/1/foo\")\n\t\t})\n\t\to(\"inserts variadic paths\", function () {\n\t\t\tvar string = buildPathname(prefix + \"/route/:foo...\", {foo: \"id/1\"})\n\n\t\t\to(string).equals(prefix + \"/route/id/1\")\n\t\t})\n\t\to(\"inserts variadic paths with initial slashes\", function () {\n\t\t\tvar string = buildPathname(prefix + \"/route/:foo...\", {foo: \"/id/1\"})\n\n\t\t\to(string).equals(prefix + \"/route//id/1\")\n\t\t})\n\t\to(\"skips template parameters at end if param missing\", function () {\n\t\t\tvar string = buildPathname(prefix + \"/route/:id\", {param: 1})\n\n\t\t\to(string).equals(prefix + \"/route/:id?param=1\")\n\t\t})\n\t\to(\"skips template parameters at beginning if param missing\", function () {\n\t\t\tvar string = buildPathname(prefix + \"/:id/foo\", {param: 1})\n\n\t\t\to(string).equals(prefix + \"/:id/foo?param=1\")\n\t\t})\n\t\to(\"skips template parameters at middle if param missing\", function () {\n\t\t\tvar string = buildPathname(prefix + \"/route/:id/foo\", {param: 1})\n\n\t\t\to(string).equals(prefix + \"/route/:id/foo?param=1\")\n\t\t})\n\t\to(\"skips variadic template parameters if param missing\", function () {\n\t\t\tvar string = buildPathname(prefix + \"/route/:foo...\", {param: \"/id/1\"})\n\n\t\t\to(string).equals(prefix + \"/route/:foo...?param=%2Fid%2F1\")\n\t\t})\n\t\to(\"handles escaped values\", function() {\n\t\t\tvar data = buildPathname(prefix + \"/route/:foo\", {\"foo\": \";:@&=+$,/?%#\"})\n\n\t\t\to(data).equals(prefix + \"/route/%3B%3A%40%26%3D%2B%24%2C%2F%3F%25%23\")\n\t\t})\n\t\to(\"handles unicode\", function() {\n\t\t\tvar data = buildPathname(prefix + \"/route/:ö\", {\"ö\": \"ö\"})\n\n\t\t\to(data).equals(prefix + \"/route/%C3%B6\")\n\t\t})\n\t\to(\"handles zero\", function() {\n\t\t\tvar string = buildPathname(prefix + \"/route/:a\", {a: 0})\n\n\t\t\to(string).equals(prefix + \"/route/0\")\n\t\t})\n\t\to(\"handles false\", function() {\n\t\t\tvar string = buildPathname(prefix + \"/route/:a\", {a: false})\n\n\t\t\to(string).equals(prefix + \"/route/false\")\n\t\t})\n\t\to(\"handles dashes\", function() {\n\t\t\tvar string = buildPathname(prefix + \"/:lang-:region/route\", {\n\t\t\t\tlang: \"en\",\n\t\t\t\tregion: \"US\"\n\t\t\t})\n\n\t\t\to(string).equals(prefix + \"/en-US/route\")\n\t\t})\n\t\to(\"handles dots\", function() {\n\t\t\tvar string = buildPathname(prefix + \"/:file.:ext/view\", {\n\t\t\t\tfile: \"image\",\n\t\t\t\text: \"png\"\n\t\t\t})\n\n\t\t\to(string).equals(prefix + \"/image.png/view\")\n\t\t})\n\t\to(\"merges query strings\", function() {\n\t\t\tvar string = buildPathname(prefix + \"/item?a=1&b=2\", {c: 3})\n\n\t\t\to(string).equals(prefix + \"/item?a=1&b=2&c=3\")\n\t\t})\n\t\to(\"merges query strings with other parameters\", function() {\n\t\t\tvar string = buildPathname(prefix + \"/item/:id?a=1&b=2\", {id: \"foo\", c: 3})\n\n\t\t\to(string).equals(prefix + \"/item/foo?a=1&b=2&c=3\")\n\t\t})\n\t\to(\"consumes template parameters without modifying query string\", function() {\n\t\t\tvar string = buildPathname(prefix + \"/item/:id?a=1&b=2\", {id: \"foo\"})\n\n\t\t\to(string).equals(prefix + \"/item/foo?a=1&b=2\")\n\t\t})\n\t}\n\to.spec(\"absolute\", function() { test(\"\") })\n\to.spec(\"relative\", function() { test(\"..\") })\n\to.spec(\"absolute + domain\", function() { test(\"https://example.com\") })\n\to.spec(\"absolute + `file:`\", function() { test(\"file://\") })\n})\n"
  },
  {
    "path": "pathname/tests/test-compileTemplate.js",
    "content": "\"use strict\"\n\nvar o = require(\"ospec\")\nvar parsePathname = require(\"../../pathname/parse\")\nvar compileTemplate = require(\"../../pathname/compileTemplate\")\n\no.spec(\"compileTemplate\", function() {\n\to(\"checks empty string\", function() {\n\t\tvar data = parsePathname(\"/\")\n\t\to(compileTemplate(\"/\")(data)).equals(true)\n\t\to(data.params).deepEquals({})\n\t})\n\to(\"checks identical match\", function() {\n\t\tvar data = parsePathname(\"/foo\")\n\t\to(compileTemplate(\"/foo\")(data)).equals(true)\n\t\to(data.params).deepEquals({})\n\t})\n\to(\"checks identical mismatch\", function() {\n\t\tvar data = parsePathname(\"/bar\")\n\t\to(compileTemplate(\"/foo\")(data)).equals(false)\n\t\to(data.params).deepEquals({})\n\t})\n\to(\"checks single parameter\", function() {\n\t\tvar data = parsePathname(\"/1\")\n\t\to(compileTemplate(\"/:id\")(data)).equals(true)\n\t\to(data.params).deepEquals({id: \"1\"})\n\t})\n\to(\"checks single variadic parameter\", function() {\n\t\tvar data = parsePathname(\"/some/path\")\n\t\to(compileTemplate(\"/:id...\")(data)).equals(true)\n\t\to(data.params).deepEquals({id: \"some/path\"})\n\t})\n\to(\"checks single parameter with extra match\", function() {\n\t\tvar data = parsePathname(\"/1/foo\")\n\t\to(compileTemplate(\"/:id/foo\")(data)).equals(true)\n\t\to(data.params).deepEquals({id: \"1\"})\n\t})\n\to(\"checks single parameter with extra mismatch\", function() {\n\t\tvar data = parsePathname(\"/1/bar\")\n\t\to(compileTemplate(\"/:id/foo\")(data)).equals(false)\n\t\to(data.params).deepEquals({})\n\t})\n\to(\"checks single variadic parameter with extra match\", function() {\n\t\tvar data = parsePathname(\"/some/path/foo\")\n\t\to(compileTemplate(\"/:id.../foo\")(data)).equals(true)\n\t\to(data.params).deepEquals({id: \"some/path\"})\n\t})\n\to(\"checks single variadic parameter with extra mismatch\", function() {\n\t\tvar data = parsePathname(\"/some/path/bar\")\n\t\to(compileTemplate(\"/:id.../foo\")(data)).equals(false)\n\t\to(data.params).deepEquals({})\n\t})\n\to(\"checks multiple parameters\", function() {\n\t\tvar data = parsePathname(\"/1/2\")\n\t\to(compileTemplate(\"/:id/:name\")(data)).equals(true)\n\t\to(data.params).deepEquals({id: \"1\", name: \"2\"})\n\t})\n\to(\"checks incomplete multiple parameters\", function() {\n\t\tvar data = parsePathname(\"/1\")\n\t\to(compileTemplate(\"/:id/:name\")(data)).equals(false)\n\t\to(data.params).deepEquals({})\n\t})\n\to(\"checks multiple parameters with extra match\", function() {\n\t\tvar data = parsePathname(\"/1/2/foo\")\n\t\to(compileTemplate(\"/:id/:name/foo\")(data)).equals(true)\n\t\to(data.params).deepEquals({id: \"1\", name: \"2\"})\n\t})\n\to(\"checks multiple parameters with extra mismatch\", function() {\n\t\tvar data = parsePathname(\"/1/2/bar\")\n\t\to(compileTemplate(\"/:id/:name/foo\")(data)).equals(false)\n\t\to(data.params).deepEquals({})\n\t})\n\to(\"checks multiple parameters, last variadic, with extra match\", function() {\n\t\tvar data = parsePathname(\"/1/some/path/foo\")\n\t\to(compileTemplate(\"/:id/:name.../foo\")(data)).equals(true)\n\t\to(data.params).deepEquals({id: \"1\", name: \"some/path\"})\n\t})\n\to(\"checks multiple parameters, last variadic, with extra mismatch\", function() {\n\t\tvar data = parsePathname(\"/1/some/path/bar\")\n\t\to(compileTemplate(\"/:id/:name.../foo\")(data)).equals(false)\n\t\to(data.params).deepEquals({})\n\t})\n\to(\"checks multiple separated parameters\", function() {\n\t\tvar data = parsePathname(\"/1/sep/2\")\n\t\to(compileTemplate(\"/:id/sep/:name\")(data)).equals(true)\n\t\to(data.params).deepEquals({id: \"1\", name: \"2\"})\n\t})\n\to(\"checks incomplete multiple separated parameters\", function() {\n\t\tvar data = parsePathname(\"/1\")\n\t\to(compileTemplate(\"/:id/sep/:name\")(data)).equals(false)\n\t\to(data.params).deepEquals({})\n\t\tdata = parsePathname(\"/1/sep\")\n\t\to(compileTemplate(\"/:id/sep/:name\")(data)).equals(false)\n\t\to(data.params).deepEquals({})\n\t})\n\to(\"checks multiple separated parameters missing sep\", function() {\n\t\tvar data = parsePathname(\"/1/2\")\n\t\to(compileTemplate(\"/:id/sep/:name\")(data)).equals(false)\n\t\to(data.params).deepEquals({})\n\t})\n\to(\"checks multiple separated parameters with extra match\", function() {\n\t\tvar data = parsePathname(\"/1/sep/2/foo\")\n\t\to(compileTemplate(\"/:id/sep/:name/foo\")(data)).equals(true)\n\t\to(data.params).deepEquals({id: \"1\", name: \"2\"})\n\t})\n\to(\"checks multiple separated parameters with extra mismatch\", function() {\n\t\tvar data = parsePathname(\"/1/sep/2/bar\")\n\t\to(compileTemplate(\"/:id/sep/:name/foo\")(data)).equals(false)\n\t\to(data.params).deepEquals({})\n\t})\n\to(\"checks multiple separated parameters, last variadic, with extra match\", function() {\n\t\tvar data = parsePathname(\"/1/sep/some/path/foo\")\n\t\to(compileTemplate(\"/:id/sep/:name.../foo\")(data)).equals(true)\n\t\to(data.params).deepEquals({id: \"1\", name: \"some/path\"})\n\t})\n\to(\"checks multiple separated parameters, last variadic, with extra mismatch\", function() {\n\t\tvar data = parsePathname(\"/1/sep/some/path/bar\")\n\t\to(compileTemplate(\"/:id/sep/:name.../foo\")(data)).equals(false)\n\t\to(data.params).deepEquals({})\n\t})\n\to(\"checks multiple parameters + prefix\", function() {\n\t\tvar data = parsePathname(\"/route/1/2\")\n\t\to(compileTemplate(\"/route/:id/:name\")(data)).equals(true)\n\t\to(data.params).deepEquals({id: \"1\", name: \"2\"})\n\t})\n\to(\"checks incomplete multiple parameters + prefix\", function() {\n\t\tvar data = parsePathname(\"/route/1\")\n\t\to(compileTemplate(\"/route/:id/:name\")(data)).equals(false)\n\t\to(data.params).deepEquals({})\n\t})\n\to(\"checks multiple parameters + prefix with extra match\", function() {\n\t\tvar data = parsePathname(\"/route/1/2/foo\")\n\t\to(compileTemplate(\"/route/:id/:name/foo\")(data)).equals(true)\n\t\to(data.params).deepEquals({id: \"1\", name: \"2\"})\n\t})\n\to(\"checks multiple parameters + prefix with extra mismatch\", function() {\n\t\tvar data = parsePathname(\"/route/1/2/bar\")\n\t\to(compileTemplate(\"/route/:id/:name/foo\")(data)).equals(false)\n\t\to(data.params).deepEquals({})\n\t})\n\to(\"checks multiple parameters + prefix, last variadic, with extra match\", function() {\n\t\tvar data = parsePathname(\"/route/1/some/path/foo\")\n\t\to(compileTemplate(\"/route/:id/:name.../foo\")(data)).equals(true)\n\t\to(data.params).deepEquals({id: \"1\", name: \"some/path\"})\n\t})\n\to(\"checks multiple parameters + prefix, last variadic, with extra mismatch\", function() {\n\t\tvar data = parsePathname(\"/route/1/some/path/bar\")\n\t\to(compileTemplate(\"/route/:id/:name.../foo\")(data)).equals(false)\n\t\to(data.params).deepEquals({})\n\t})\n\to(\"checks multiple separated parameters + prefix\", function() {\n\t\tvar data = parsePathname(\"/route/1/sep/2\")\n\t\to(compileTemplate(\"/route/:id/sep/:name\")(data)).equals(true)\n\t\to(data.params).deepEquals({id: \"1\", name: \"2\"})\n\t})\n\to(\"checks incomplete multiple separated parameters + prefix\", function() {\n\t\tvar data = parsePathname(\"/route/1\")\n\t\to(compileTemplate(\"/route/:id/sep/:name\")(data)).equals(false)\n\t\to(data.params).deepEquals({})\n\t\tvar data = parsePathname(\"/route/1/sep\")\n\t\to(compileTemplate(\"/route/:id/sep/:name\")(data)).equals(false)\n\t\to(data.params).deepEquals({})\n\t})\n\to(\"checks multiple separated parameters + prefix missing sep\", function() {\n\t\tvar data = parsePathname(\"/route/1/2\")\n\t\to(compileTemplate(\"/route/:id/sep/:name\")(data)).equals(false)\n\t\to(data.params).deepEquals({})\n\t})\n\to(\"checks multiple separated parameters + prefix with extra match\", function() {\n\t\tvar data = parsePathname(\"/route/1/sep/2/foo\")\n\t\to(compileTemplate(\"/route/:id/sep/:name/foo\")(data)).equals(true)\n\t\to(data.params).deepEquals({id: \"1\", name: \"2\"})\n\t})\n\to(\"checks multiple separated parameters + prefix with extra mismatch\", function() {\n\t\tvar data = parsePathname(\"/route/1/sep/2/bar\")\n\t\to(compileTemplate(\"/route/:id/sep/:name/foo\")(data)).equals(false)\n\t\to(data.params).deepEquals({})\n\t})\n\to(\"checks multiple separated parameters + prefix, last variadic, with extra match\", function() {\n\t\tvar data = parsePathname(\"/route/1/sep/some/path/foo\")\n\t\to(compileTemplate(\"/route/:id/sep/:name.../foo\")(data)).equals(true)\n\t\to(data.params).deepEquals({id: \"1\", name: \"some/path\"})\n\t})\n\to(\"checks multiple separated parameters + prefix, last variadic, with extra mismatch\", function() {\n\t\tvar data = parsePathname(\"/route/1/sep/some/path/bar\")\n\t\to(compileTemplate(\"/route/:id/sep/:name.../foo\")(data)).equals(false)\n\t\to(data.params).deepEquals({})\n\t})\n\to(\"checks query params match\", function() {\n\t\tvar data = parsePathname(\"/route/1?foo=bar\")\n\t\to(compileTemplate(\"/route/:id?foo=bar\")(data)).equals(true)\n\t\to(data.params).deepEquals({id: \"1\", foo: \"bar\"})\n\t})\n\to(\"checks query params mismatch\", function() {\n\t\tvar data = parsePathname(\"/route/1?foo=bar\")\n\t\to(compileTemplate(\"/route/:id?foo=1\")(data)).equals(false)\n\t\to(data.params).deepEquals({foo: \"bar\"})\n\t\to(compileTemplate(\"/route/:id?bar=foo\")(data)).equals(false)\n\t\to(data.params).deepEquals({foo: \"bar\"})\n\t})\n\to(\"checks dot before dot\", function() {\n\t\tvar data = parsePathname(\"/file.test.png/edit\")\n\t\to(compileTemplate(\"/:file.:ext/edit\")(data)).equals(true)\n\t\to(data.params).deepEquals({file: \"file.test\", ext: \"png\"})\n\t})\n\to(\"checks dash before dot\", function() {\n\t\tvar data = parsePathname(\"/file-test.png/edit\")\n\t\to(compileTemplate(\"/:file.:ext/edit\")(data)).equals(true)\n\t\to(data.params).deepEquals({file: \"file-test\", ext: \"png\"})\n\t})\n\to(\"checks dot before dash\", function() {\n\t\tvar data = parsePathname(\"/file.test-png/edit\")\n\t\to(compileTemplate(\"/:file-:ext/edit\")(data)).equals(true)\n\t\to(data.params).deepEquals({file: \"file.test\", ext: \"png\"})\n\t})\n\to(\"checks dash before dash\", function() {\n\t\tvar data = parsePathname(\"/file-test-png/edit\")\n\t\to(compileTemplate(\"/:file-:ext/edit\")(data)).equals(true)\n\t\to(data.params).deepEquals({file: \"file-test\", ext: \"png\"})\n\t})\n})\n"
  },
  {
    "path": "pathname/tests/test-parsePathname.js",
    "content": "\"use strict\"\n\nvar o = require(\"ospec\")\nvar parsePathname = require(\"../../pathname/parse\")\n\no.spec(\"parsePathname\", function() {\n\to(\"parses empty string\", function() {\n\t\tvar data = parsePathname(\"\")\n\t\to(data).deepEquals({\n\t\t\tpath: \"/\",\n\t\t\tparams: {}\n\t\t})\n\t})\n\to(\"parses query at start\", function() {\n\t\tvar data = parsePathname(\"?a=b&c=d\")\n\t\to(data).deepEquals({\n\t\t\tpath: \"/\",\n\t\t\tparams: {a: \"b\", c: \"d\"}\n\t\t})\n\t})\n\to(\"ignores hash at start\", function() {\n\t\tvar data = parsePathname(\"#a=b&c=d\")\n\t\to(data).deepEquals({\n\t\t\tpath: \"/\",\n\t\t\tparams: {}\n\t\t})\n\t})\n\to(\"parses query, ignores hash at start\", function() {\n\t\tvar data = parsePathname(\"?a=1&b=2#c=3&d=4\")\n\t\to(data).deepEquals({\n\t\t\tpath: \"/\",\n\t\t\tparams: {a: \"1\", b: \"2\"}\n\t\t})\n\t})\n\to(\"parses root\", function() {\n\t\tvar data = parsePathname(\"/\")\n\t\to(data).deepEquals({\n\t\t\tpath: \"/\",\n\t\t\tparams: {}\n\t\t})\n\t})\n\to(\"parses root + query at start\", function() {\n\t\tvar data = parsePathname(\"/?a=b&c=d\")\n\t\to(data).deepEquals({\n\t\t\tpath: \"/\",\n\t\t\tparams: {a: \"b\", c: \"d\"}\n\t\t})\n\t})\n\to(\"parses root, ignores hash at start\", function() {\n\t\tvar data = parsePathname(\"/#a=b&c=d\")\n\t\to(data).deepEquals({\n\t\t\tpath: \"/\",\n\t\t\tparams: {}\n\t\t})\n\t})\n\to(\"parses root + query, ignores hash at start\", function() {\n\t\tvar data = parsePathname(\"/?a=1&b=2#c=3&d=4\")\n\t\to(data).deepEquals({\n\t\t\tpath: \"/\",\n\t\t\tparams: {a: \"1\", b: \"2\"}\n\t\t})\n\t})\n\to(\"parses route\", function() {\n\t\tvar data = parsePathname(\"/route/foo\")\n\t\to(data).deepEquals({\n\t\t\tpath: \"/route/foo\",\n\t\t\tparams: {}\n\t\t})\n\t})\n\to(\"parses route + empty query\", function() {\n\t\tvar data = parsePathname(\"/route/foo?\")\n\t\to(data).deepEquals({\n\t\t\tpath: \"/route/foo\",\n\t\t\tparams: {}\n\t\t})\n\t})\n\to(\"parses route + empty hash\", function() {\n\t\tvar data = parsePathname(\"/route/foo?\")\n\t\to(data).deepEquals({\n\t\t\tpath: \"/route/foo\",\n\t\t\tparams: {}\n\t\t})\n\t})\n\to(\"parses route + empty query + empty hash\", function() {\n\t\tvar data = parsePathname(\"/route/foo?#\")\n\t\to(data).deepEquals({\n\t\t\tpath: \"/route/foo\",\n\t\t\tparams: {}\n\t\t})\n\t})\n\to(\"parses route + query\", function() {\n\t\tvar data = parsePathname(\"/route/foo?a=1&b=2\")\n\t\to(data).deepEquals({\n\t\t\tpath: \"/route/foo\",\n\t\t\tparams: {a: \"1\", b: \"2\"}\n\t\t})\n\t})\n\to(\"parses route + hash\", function() {\n\t\tvar data = parsePathname(\"/route/foo?c=3&d=4\")\n\t\to(data).deepEquals({\n\t\t\tpath: \"/route/foo\",\n\t\t\tparams: {c: \"3\", d: \"4\"}\n\t\t})\n\t})\n\to(\"parses route + query, ignores hash\", function() {\n\t\tvar data = parsePathname(\"/route/foo?a=1&b=2#c=3&d=4\")\n\t\to(data).deepEquals({\n\t\t\tpath: \"/route/foo\",\n\t\t\tparams: {a: \"1\", b: \"2\"}\n\t\t})\n\t})\n\to(\"parses route + query, ignores hash with lots of junk slashes\", function() {\n\t\tvar data = parsePathname(\"//route/////foo//?a=1&b=2#c=3&d=4\")\n\t\to(data).deepEquals({\n\t\t\tpath: \"/route/foo/\",\n\t\t\tparams: {a: \"1\", b: \"2\"}\n\t\t})\n\t})\n\to(\"doesn't comprehend protocols\", function() {\n\t\tvar data = parsePathname(\"https://example.com/foo/bar\")\n\t\to(data).deepEquals({\n\t\t\tpath: \"/https:/example.com/foo/bar\",\n\t\t\tparams: {}\n\t\t})\n\t})\n})\n"
  },
  {
    "path": "performance/index.html",
    "content": "<!DOCTYPE html>\n<html>\n\t<head>\n\t\t<meta charset=\"utf-8\">\n\t\t<script src=\"../node_modules/lodash/lodash.js\"></script>\n\t\t<script src=\"../node_modules/benchmark/benchmark.js\"></script>\n\t\t<script src=\"../mithril.js\"></script>\n\t\t<script src=\"test-perf.js\"></script>\n\t</head>\n\t<body>\n\t</body>\n</html>\n"
  },
  {
    "path": "performance/test-perf.js",
    "content": "\"use strict\"\n\n/* Based off of preact's perf tests, so including their MIT license */\n/*\nThe MIT License (MIT)\n\nCopyright (c) 2017 Jason Miller\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n*/\n\n// Note: this tests against the generated bundle in browsers, but it tests\n// against `index.js` in Node. Please do keep that in mind while testing.\n//\n// Mithril.js and Benchmark.js are loaded globally via bundle in the browser, so\n// this doesn't require a CommonJS sham polyfill.\n\n// I add it globally just so it's visible in the tests.\n/* global m, rootElem: true */\n\n// set up browser env on before running tests\nvar isDOM = typeof window !== \"undefined\"\nvar Benchmark\n\nif (isDOM) {\n\tBenchmark = window.Benchmark\n\twindow.rootElem = null\n} else {\n\t/* eslint-disable global-require */\n\tBenchmark = require(\"benchmark\")\n\tglobal.window = require(\"../test-utils/browserMock\")()\n\tglobal.document = window.document\n\t// We're benchmarking renders, not our throttling.\n\tglobal.requestAnimationFrame = function () {\n\t\tthrow new Error(\"This should never be called.\")\n\t}\n\tglobal.m = require(\"../index.js\")\n\tglobal.rootElem = null\n\t/* eslint-enable global-require */\n}\n\nfunction cycleRoot() {\n\tif (rootElem) document.body.removeChild(rootElem)\n\tdocument.body.appendChild(rootElem = document.createElement(\"div\"))\n}\n\n// Initialize benchmark suite\nBenchmark.options.async = true\nBenchmark.options.initCount = 10\nBenchmark.options.minSamples = 40\n\nif (isDOM) {\n\t// Wait long enough for the browser to actually commit the DOM changes to\n\t// the screen before moving on to the next cycle, so things are at least\n\t// reasonably fresh each cycle.\n\tBenchmark.options.delay = 1 / 30 /* frames per second */\n}\n\nvar suite = new Benchmark.Suite(\"Mithril.js perf\", {\n\tonStart: function () {\n\t\tthis.start = Date.now()\n\t},\n\n\tonCycle: function (e) {\n\t\tconsole.log(e.target.toString())\n\t\tcycleRoot()\n\t},\n\n\tonComplete: function () {\n\t\tconsole.log(\"Completed perf tests in \" + (Date.now() - this.start) + \"ms\")\n\t},\n\n\tonError: function (e) {\n\t\tconsole.error(e)\n\t},\n})\n// eslint-disable-next-line no-unused-vars\nvar xsuite = {add: function(name) { console.log(\"skipping \" + name) }}\n\nsuite.add(\"construct large vnode tree\", {\n\tsetup: function () {\n\t\tthis.fields = []\n\n\t\tfor(var i=100; i--;) {\n\t\t\tthis.fields.push((i * 999).toString(36))\n\t\t}\n\t},\n\tfn: function () {\n\t\tm(\".foo.bar[data-foo=bar]\", {p: 2},\n\t\t\tm(\"header\",\n\t\t\t\tm(\"h1.asdf\", \"a \", \"b\", \" c \", 0, \" d\"),\n\t\t\t\tm(\"nav\",\n\t\t\t\t\tm(\"a[href=/foo]\", \"Foo\"),\n\t\t\t\t\tm(\"a[href=/bar]\", \"Bar\")\n\t\t\t\t)\n\t\t\t),\n\t\t\tm(\"main\",\n\t\t\t\tm(\"form\",\n\t\t\t\t\t{onSubmit: function () {}},\n\t\t\t\t\tm(\"input[type=checkbox][checked]\"),\n\t\t\t\t\tm(\"input[type=checkbox]\"),\n\t\t\t\t\tm(\"fieldset\",\n\t\t\t\t\t\tthis.fields.map(function (field) {\n\t\t\t\t\t\t\treturn m(\"label\",\n\t\t\t\t\t\t\t\tfield,\n\t\t\t\t\t\t\t\t\":\",\n\t\t\t\t\t\t\t\tm(\"input\", {placeholder: field})\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t})\n\t\t\t\t\t),\n\t\t\t\t\tm(\"button-bar\",\n\t\t\t\t\t\tm(\"button\",\n\t\t\t\t\t\t\t{style: \"width:10px; height:10px; border:1px solid #FFF;\"},\n\t\t\t\t\t\t\t\"Normal CSS\"\n\t\t\t\t\t\t),\n\t\t\t\t\t\tm(\"button\",\n\t\t\t\t\t\t\t{style: \"top:0 ; right: 20\"},\n\t\t\t\t\t\t\t\"Poor CSS\"\n\t\t\t\t\t\t),\n\t\t\t\t\t\tm(\"button\",\n\t\t\t\t\t\t\t{style: \"invalid-prop:1;padding:1px;font:12px/1.1 arial,sans-serif;\", icon: true},\n\t\t\t\t\t\t\t\"Poorer CSS\"\n\t\t\t\t\t\t),\n\t\t\t\t\t\tm(\"button\",\n\t\t\t\t\t\t\t{style: {margin: 0, padding: \"10px\", overflow: \"visible\"}},\n\t\t\t\t\t\t\t\"Object CSS\"\n\t\t\t\t\t\t)\n\t\t\t\t\t)\n\t\t\t\t)\n\t\t\t)\n\t\t)\n\t},\n})\n\nsuite.add(\"rerender identical vnode\", {\n\tsetup: function () {\n\t\tthis.cached = m(\".foo.bar[data-foo=bar]\", {p: 2},\n\t\t\tm(\"header\",\n\t\t\t\tm(\"h1.asdf\", \"a \", \"b\", \" c \", 0, \" d\"),\n\t\t\t\tm(\"nav\",\n\t\t\t\t\tm(\"a\", {href: \"/foo\"}, \"Foo\"),\n\t\t\t\t\tm(\"a\", {href: \"/bar\"}, \"Bar\")\n\t\t\t\t)\n\t\t\t),\n\t\t\tm(\"main\",\n\t\t\t\tm(\"form\", {onSubmit: function () {}},\n\t\t\t\t\tm(\"input\", {type: \"checkbox\", checked: true}),\n\t\t\t\t\tm(\"input\", {type: \"checkbox\", checked: false}),\n\t\t\t\t\tm(\"fieldset\",\n\t\t\t\t\t\tm(\"label\",\n\t\t\t\t\t\t\tm(\"input\", {type: \"radio\", checked: true})\n\t\t\t\t\t\t),\n\t\t\t\t\t\tm(\"label\",\n\t\t\t\t\t\t\tm(\"input\", {type: \"radio\"})\n\t\t\t\t\t\t)\n\t\t\t\t\t),\n\t\t\t\t\tm(\"button-bar\",\n\t\t\t\t\t\tm(\"button\",\n\t\t\t\t\t\t\t{style: \"width:10px; height:10px; border:1px solid #FFF;\"},\n\t\t\t\t\t\t\t\"Normal CSS\"\n\t\t\t\t\t\t),\n\t\t\t\t\t\tm(\"button\",\n\t\t\t\t\t\t\t{style: \"top:0 ; right: 20\"},\n\t\t\t\t\t\t\t\"Poor CSS\"\n\t\t\t\t\t\t),\n\t\t\t\t\t\tm(\"button\",\n\t\t\t\t\t\t\t{style: \"invalid-prop:1;padding:1px;font:12px/1.1 arial,sans-serif;\", icon: true},\n\t\t\t\t\t\t\t\"Poorer CSS\"\n\t\t\t\t\t\t),\n\t\t\t\t\t\tm(\"button\",\n\t\t\t\t\t\t\t{style: {margin: 0, padding: \"10px\", overflow: \"visible\"}},\n\t\t\t\t\t\t\t\"Object CSS\"\n\t\t\t\t\t\t)\n\t\t\t\t\t)\n\t\t\t\t)\n\t\t\t)\n\t\t)\n\t},\n\tfn: function () {\n\t\tm.render(rootElem, this.cached)\n\t},\n})\n\nsuite.add(\"rerender same tree\", {\n\tfn: function () {\n\t\tm.render(rootElem, m(\".foo.bar[data-foo=bar]\", {p: 2},\n\t\t\tm(\"header\",\n\t\t\t\tm(\"h1.asdf\", \"a \", \"b\", \" c \", 0, \" d\"),\n\t\t\t\tm(\"nav\",\n\t\t\t\t\tm(\"a\", {href: \"/foo\"}, \"Foo\"),\n\t\t\t\t\tm(\"a\", {href: \"/bar\"}, \"Bar\")\n\t\t\t\t)\n\t\t\t),\n\t\t\tm(\"main\",\n\t\t\t\tm(\"form\", {onSubmit: function () {}},\n\t\t\t\t\tm(\"input\", {type: \"checkbox\", checked: true}),\n\t\t\t\t\tm(\"input\", {type: \"checkbox\", checked: false}),\n\t\t\t\t\tm(\"fieldset\",\n\t\t\t\t\t\tm(\"label\",\n\t\t\t\t\t\t\tm(\"input\", {type: \"radio\", checked: true})\n\t\t\t\t\t\t),\n\t\t\t\t\t\tm(\"label\",\n\t\t\t\t\t\t\tm(\"input\", {type: \"radio\"})\n\t\t\t\t\t\t)\n\t\t\t\t\t),\n\t\t\t\t\tm(\"button-bar\",\n\t\t\t\t\t\tm(\"button\",\n\t\t\t\t\t\t\t{style: \"width:10px; height:10px; border:1px solid #FFF;\"},\n\t\t\t\t\t\t\t\"Normal CSS\"\n\t\t\t\t\t\t),\n\t\t\t\t\t\tm(\"button\",\n\t\t\t\t\t\t\t{style: \"top:0 ; right: 20\"},\n\t\t\t\t\t\t\t\"Poor CSS\"\n\t\t\t\t\t\t),\n\t\t\t\t\t\tm(\"button\",\n\t\t\t\t\t\t\t{style: \"invalid-prop:1;padding:1px;font:12px/1.1 arial,sans-serif;\", icon: true},\n\t\t\t\t\t\t\t\"Poorer CSS\"\n\t\t\t\t\t\t),\n\t\t\t\t\t\tm(\"button\",\n\t\t\t\t\t\t\t{style: {margin: 0, padding: \"10px\", overflow: \"visible\"}},\n\t\t\t\t\t\t\t\"Object CSS\"\n\t\t\t\t\t\t)\n\t\t\t\t\t)\n\t\t\t\t)\n\t\t\t)\n\t\t))\n\t},\n})\n\nsuite.add(\"add large nested tree\", {\n\tsetup: function () {\n\t\tvar fields = []\n\n\t\tfor(var i=100; i--;) {\n\t\t\tfields.push((i * 999).toString(36))\n\t\t}\n\n\t\tvar NestedHeader = {\n\t\t\tview: function () {\n\t\t\t\treturn m(\"header\",\n\t\t\t\t\tm(\"h1.asdf\", \"a \", \"b\", \" c \", 0, \" d\"),\n\t\t\t\t\tm(\"nav\",\n\t\t\t\t\t\tm(\"a\", {href: \"/foo\"}, \"Foo\"),\n\t\t\t\t\t\tm(\"a\", {href: \"/bar\"}, \"Bar\")\n\t\t\t\t\t)\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\n\t\tvar NestedForm = {\n\t\t\tview: function () {\n\t\t\t\treturn m(\"form\", {onSubmit: function () {}},\n\t\t\t\t\tm(\"input[type=checkbox][checked]\"),\n\t\t\t\t\tm(\"input[type=checkbox]\", {checked: false}),\n\t\t\t\t\tm(\"fieldset\",\n\t\t\t\t\t\tm(\"label\",\n\t\t\t\t\t\t\tm(\"input[type=radio][checked]\")\n\t\t\t\t\t\t),\n\t\t\t\t\t\tm(\"label\",\n\t\t\t\t\t\t\tm(\"input[type=radio]\")\n\t\t\t\t\t\t)\n\t\t\t\t\t),\n\t\t\t\t\tm(\"fieldset\",\n\t\t\t\t\t\tfields.map(function (field) {\n\t\t\t\t\t\t\treturn m(\"label\",\n\t\t\t\t\t\t\t\tfield,\n\t\t\t\t\t\t\t\t\":\",\n\t\t\t\t\t\t\t\tm(\"input\", {placeholder: field})\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t})\n\t\t\t\t\t),\n\t\t\t\t\tm(NestedButtonBar, null)\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\n\t\tvar NestedButtonBar = {\n\t\t\tview: function () {\n\t\t\t\treturn m(\".button-bar\",\n\t\t\t\t\tm(NestedButton,\n\t\t\t\t\t\t{style: \"width:10px; height:10px; border:1px solid #FFF;\"},\n\t\t\t\t\t\t\"Normal CSS\"\n\t\t\t\t\t),\n\t\t\t\t\tm(NestedButton,\n\t\t\t\t\t\t{style: \"top:0 ; right: 20\"},\n\t\t\t\t\t\t\"Poor CSS\"\n\t\t\t\t\t),\n\t\t\t\t\tm(NestedButton,\n\t\t\t\t\t\t{style: \"invalid-prop:1;padding:1px;font:12px/1.1 arial,sans-serif;\", icon: true},\n\t\t\t\t\t\t\"Poorer CSS\"\n\t\t\t\t\t),\n\t\t\t\t\tm(NestedButton,\n\t\t\t\t\t\t{style: {margin: 0, padding: \"10px\", overflow: \"visible\"}},\n\t\t\t\t\t\t\"Object CSS\"\n\t\t\t\t\t)\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\n\t\tvar NestedButton = {\n\t\t\tview: function (vnode) {\n\t\t\t\treturn m(\"button\", m.censor(vnode.attrs), vnode.children)\n\t\t\t}\n\t\t}\n\n\t\tvar NestedMain = {\n\t\t\tview: function () {\n\t\t\t\treturn m(NestedForm)\n\t\t\t}\n\t\t}\n\n\t\tthis.NestedRoot = {\n\t\t\tview: function () {\n\t\t\t\treturn m(\"div.foo.bar[data-foo=bar]\",\n\t\t\t\t\t{p: 2},\n\t\t\t\t\tm(NestedHeader),\n\t\t\t\t\tm(NestedMain)\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\t},\n\tfn: function () {\n\t\tm.render(rootElem, m(this.NestedRoot))\n\t},\n})\n\nsuite.add(\"mutate styles/properties\", {\n\tsetup: function () {\n\t\tfunction get(obj, i) { return obj[i % obj.length] }\n\t\tvar counter = 0\n\t\tvar classes = [\"foo\", \"foo bar\", \"\", \"baz-bat\", null, \"fooga\", null, null, undefined]\n\t\tvar styles = []\n\t\tvar multivalue = [\"0 1px\", \"0 0 1px 0\", \"0\", \"1px\", \"20px 10px\", \"7em 5px\", \"1px 0 5em 2px\"]\n\t\tvar stylekeys = [\n\t\t\t[\"left\", function (c) { return c % 3 ? c + \"px\" : c }],\n\t\t\t[\"top\", function (c) { return c % 2 ? c + \"px\" : c }],\n\t\t\t[\"margin\", function (c) { return get(multivalue, c).replace(\"1px\", c+\"px\") }],\n\t\t\t[\"padding\", function (c) { return get(multivalue, c) }],\n\t\t\t[\"position\", function (c) { return c%5 ? c%2 ? \"absolute\" : \"relative\" : null }],\n\t\t\t[\"display\", function (c) { return c%10 ? c%2 ? \"block\" : \"inline\" : \"none\" }],\n\t\t\t[\"color\", function (c) { return (\"rgba(\" + (c%255) + \", \" + (255 - c%255) + \", \" + (50+c%150) + \", \" + (c%50/50) + \")\") }],\n\t\t\t[\"border\", function (c) { return c%5 ? (c%10) + \"px \" + (c%2?\"solid\":\"dotted\") + \" \" + stylekeys[6][1](c) : \"\" }]\n\t\t]\n\t\tvar i, j, style, conf\n\n\t\tfor (i=0; i<1000; i++) {\n\t\t\tstyle = {}\n\t\t\tfor (j=0; j<i%10; j++) {\n\t\t\t\tconf = get(stylekeys, ++counter)\n\t\t\t\tstyle[conf[0]] = conf[1](counter)\n\t\t\t}\n\t\t\tstyles[i] = style\n\t\t}\n\n\t\tvar count = 0\n\n\t\tthis.app = function () {\n\t\t\tvar vnodes = []\n\t\t\tfor (var index = ++count, last = index + 300; index < last; index++) {\n\t\t\t\tvnodes.push(\n\t\t\t\t\tm(\"div.booga\",\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tclass: get(classes, index),\n\t\t\t\t\t\t\t\"data-index\": index,\n\t\t\t\t\t\t\ttitle: index.toString(36)\n\t\t\t\t\t\t},\n\t\t\t\t\t\tm(\"input.dooga\", {type: \"checkbox\", checked: index % 3 === 0}),\n\t\t\t\t\t\tm(\"input\", {value: \"test \" + Math.floor(index / 4), disabled: index % 10 ? null : true}),\n\t\t\t\t\t\tm(\"div\", {class: get(classes, index * 11)},\n\t\t\t\t\t\t\tm(\"p\", {style: get(styles, index)}, \"p1\"),\n\t\t\t\t\t\t\tm(\"p\", {style: get(styles, index + 1)}, \"p2\"),\n\t\t\t\t\t\t\tm(\"p\", {style: get(styles, index * 2)}, \"p3\"),\n\t\t\t\t\t\t\tm(\"p.zooga\", {style: get(styles, index * 3 + 1), className: get(classes, index * 7)}, \"p4\")\n\t\t\t\t\t\t)\n\t\t\t\t\t)\n\t\t\t\t)\n\t\t\t}\n\t\t\treturn vnodes\n\t\t}\n\t},\n\tfn: function () {\n\t\tm.render(rootElem, this.app())\n\t},\n})\n\nsuite.add(\"repeated add/removal\", {\n\tsetup: function () {\n\t\tvar RepeatedHeader = {\n\t\t\tview: function () {\n\t\t\t\treturn m(\"header\",\n\t\t\t\t\tm(\"h1.asdf\", \"a \", \"b\", \" c \", 0, \" d\"),\n\t\t\t\t\tm(\"nav\",\n\t\t\t\t\t\tm(\"a\", {href: \"/foo\"}, \"Foo\"),\n\t\t\t\t\t\tm(\"a\", {href: \"/bar\"}, \"Bar\")\n\t\t\t\t\t)\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\n\t\tvar RepeatedForm = {\n\t\t\tview: function () {\n\t\t\t\treturn m(\"form\", {onSubmit: function () {}},\n\t\t\t\t\tm(\"input\", {type: \"checkbox\", checked: true}),\n\t\t\t\t\tm(\"input\", {type: \"checkbox\", checked: false}),\n\t\t\t\t\tm(\"fieldset\",\n\t\t\t\t\t\tm(\"label\",\n\t\t\t\t\t\t\tm(\"input\", {type: \"radio\", checked: true})\n\t\t\t\t\t\t),\n\t\t\t\t\t\tm(\"label\",\n\t\t\t\t\t\t\tm(\"input\", {type: \"radio\"})\n\t\t\t\t\t\t)\n\t\t\t\t\t),\n\t\t\t\t\tm(RepeatedButtonBar, null)\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\n\t\tvar RepeatedButtonBar = {\n\t\t\tview: function () {\n\t\t\t\treturn m(\".button-bar\",\n\t\t\t\t\tm(RepeatedButton,\n\t\t\t\t\t\t{style: \"width:10px; height:10px; border:1px solid #FFF;\"},\n\t\t\t\t\t\t\"Normal CSS\"\n\t\t\t\t\t),\n\t\t\t\t\tm(RepeatedButton,\n\t\t\t\t\t\t{style: \"top:0 ; right: 20\"},\n\t\t\t\t\t\t\"Poor CSS\"\n\t\t\t\t\t),\n\t\t\t\t\tm(RepeatedButton,\n\t\t\t\t\t\t{style: \"invalid-prop:1;padding:1px;font:12px/1.1 arial,sans-serif;\", icon: true},\n\t\t\t\t\t\t\"Poorer CSS\"\n\t\t\t\t\t),\n\t\t\t\t\tm(RepeatedButton,\n\t\t\t\t\t\t{style: {margin: 0, padding: \"10px\", overflow: \"visible\"}},\n\t\t\t\t\t\t\"Object CSS\"\n\t\t\t\t\t)\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\n\t\tvar RepeatedButton = {\n\t\t\tview: function (vnode) {\n\t\t\t\treturn m(\"button\", vnode.attrs, vnode.children)\n\t\t\t}\n\t\t}\n\n\t\tvar RepeatedMain = {\n\t\t\tview: function () {\n\t\t\t\treturn m(RepeatedForm)\n\t\t\t}\n\t\t}\n\n\t\tthis.RepeatedRoot = {\n\t\t\tview: function () {\n\t\t\t\treturn m(\"div.foo.bar[data-foo=bar]\",\n\t\t\t\t\t{p: 2},\n\t\t\t\t\tm(RepeatedHeader, null),\n\t\t\t\t\tm(RepeatedMain, null)\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\t},\n\tfn: function () {\n\t\tm.render(rootElem, [m(this.RepeatedRoot)])\n\t\tm.render(rootElem, [])\n\t},\n})\n\nif (isDOM) {\n\twindow.onload = function () {\n\t\tcycleRoot()\n\t\tsuite.run()\n\t}\n} else {\n\tcycleRoot()\n\tsuite.run()\n}\n"
  },
  {
    "path": "querystring/build.js",
    "content": "\"use strict\"\n\nmodule.exports = function(object) {\n\tif (Object.prototype.toString.call(object) !== \"[object Object]\") return \"\"\n\n\tvar args = []\n\tfor (var key in object) {\n\t\tdestructure(key, object[key])\n\t}\n\n\treturn args.join(\"&\")\n\n\tfunction destructure(key, value) {\n\t\tif (Array.isArray(value)) {\n\t\t\tfor (var i = 0; i < value.length; i++) {\n\t\t\t\tdestructure(key + \"[\" + i + \"]\", value[i])\n\t\t\t}\n\t\t}\n\t\telse if (Object.prototype.toString.call(value) === \"[object Object]\") {\n\t\t\tfor (var i in value) {\n\t\t\t\tdestructure(key + \"[\" + i + \"]\", value[i])\n\t\t\t}\n\t\t}\n\t\telse args.push(encodeURIComponent(key) + (value != null && value !== \"\" ? \"=\" + encodeURIComponent(value) : \"\"))\n\t}\n}\n"
  },
  {
    "path": "querystring/parse.js",
    "content": "\"use strict\"\n\nvar decodeURIComponentSafe = require(\"../util/decodeURIComponentSafe\")\n\nmodule.exports = function(string) {\n\tif (string === \"\" || string == null) return {}\n\tif (string.charAt(0) === \"?\") string = string.slice(1)\n\n\tvar entries = string.split(\"&\"), counters = {}, data = {}\n\tfor (var i = 0; i < entries.length; i++) {\n\t\tvar entry = entries[i].split(\"=\")\n\t\tvar key = decodeURIComponentSafe(entry[0])\n\t\tvar value = entry.length === 2 ? decodeURIComponentSafe(entry[1]) : \"\"\n\n\t\tif (value === \"true\") value = true\n\t\telse if (value === \"false\") value = false\n\n\t\tvar levels = key.split(/\\]\\[?|\\[/)\n\t\tvar cursor = data\n\t\tif (key.indexOf(\"[\") > -1) levels.pop()\n\t\tfor (var j = 0; j < levels.length; j++) {\n\t\t\tvar level = levels[j], nextLevel = levels[j + 1]\n\t\t\tvar isNumber = nextLevel == \"\" || !isNaN(parseInt(nextLevel, 10))\n\t\t\tif (level === \"\") {\n\t\t\t\tvar key = levels.slice(0, j).join()\n\t\t\t\tif (counters[key] == null) {\n\t\t\t\t\tcounters[key] = Array.isArray(cursor) ? cursor.length : 0\n\t\t\t\t}\n\t\t\t\tlevel = counters[key]++\n\t\t\t}\n\t\t\t// Disallow direct prototype pollution\n\t\t\telse if (level === \"__proto__\") break\n\t\t\tif (j === levels.length - 1) cursor[level] = value\n\t\t\telse {\n\t\t\t\t// Read own properties exclusively to disallow indirect\n\t\t\t\t// prototype pollution\n\t\t\t\tvar desc = Object.getOwnPropertyDescriptor(cursor, level)\n\t\t\t\tif (desc != null) desc = desc.value\n\t\t\t\tif (desc == null) cursor[level] = desc = isNumber ? [] : {}\n\t\t\t\tcursor = desc\n\t\t\t}\n\t\t}\n\t}\n\treturn data\n}\n"
  },
  {
    "path": "querystring/tests/test-buildQueryString.js",
    "content": "\"use strict\"\n\nvar o = require(\"ospec\")\nvar buildQueryString = require(\"../../querystring/build\")\n\no.spec(\"buildQueryString\", function() {\n\to(\"handles flat object\", function() {\n\t\tvar string = buildQueryString({a: \"b\", c: 1})\n\n\t\to(string).equals(\"a=b&c=1\")\n\t})\n\to(\"handles escaped values\", function() {\n\t\tvar data = buildQueryString({\";:@&=+$,/?%#\": \";:@&=+$,/?%#\"})\n\n\t\to(data).equals(\"%3B%3A%40%26%3D%2B%24%2C%2F%3F%25%23=%3B%3A%40%26%3D%2B%24%2C%2F%3F%25%23\")\n\t})\n\to(\"handles unicode\", function() {\n\t\tvar data = buildQueryString({\"ö\": \"ö\"})\n\n\t\to(data).equals(\"%C3%B6=%C3%B6\")\n\t})\n\to(\"handles nested object\", function() {\n\t\tvar string = buildQueryString({a: {b: 1, c: 2}})\n\n\t\to(string).equals(\"a%5Bb%5D=1&a%5Bc%5D=2\")\n\t})\n\to(\"handles deep nested object\", function() {\n\t\tvar string = buildQueryString({a: {b: {c: 1, d: 2}}})\n\n\t\to(string).equals(\"a%5Bb%5D%5Bc%5D=1&a%5Bb%5D%5Bd%5D=2\")\n\t})\n\to(\"handles nested array\", function() {\n\t\tvar string = buildQueryString({a: [\"x\", \"y\"]})\n\n\t\to(string).equals(\"a%5B0%5D=x&a%5B1%5D=y\")\n\t})\n\to(\"handles array w/ dupe values\", function() {\n\t\tvar string = buildQueryString({a: [\"x\", \"x\"]})\n\n\t\to(string).equals(\"a%5B0%5D=x&a%5B1%5D=x\")\n\t})\n\to(\"handles deep nested array\", function() {\n\t\tvar string = buildQueryString({a: [[\"x\", \"y\"]]})\n\n\t\to(string).equals(\"a%5B0%5D%5B0%5D=x&a%5B0%5D%5B1%5D=y\")\n\t})\n\to(\"handles deep nested array in object\", function() {\n\t\tvar string = buildQueryString({a: {b: [\"x\", \"y\"]}})\n\n\t\to(string).equals(\"a%5Bb%5D%5B0%5D=x&a%5Bb%5D%5B1%5D=y\")\n\t})\n\to(\"handles deep nested object in array\", function() {\n\t\tvar string = buildQueryString({a: [{b: 1, c: 2}]})\n\n\t\to(string).equals(\"a%5B0%5D%5Bb%5D=1&a%5B0%5D%5Bc%5D=2\")\n\t})\n\to(\"handles date\", function() {\n\t\tvar string = buildQueryString({a: new Date(0)})\n\n\t\to(string).equals(\"a=\" + encodeURIComponent(new Date(0).toString()))\n\t})\n\to(\"turns null into value-less string (like jQuery)\", function() {\n\t\tvar string = buildQueryString({a: null})\n\n\t\to(string).equals(\"a\")\n\t})\n\to(\"turns undefined into value-less string (like jQuery)\", function() {\n\t\tvar string = buildQueryString({a: undefined})\n\n\t\to(string).equals(\"a\")\n\t})\n\to(\"turns empty string into value-less string (like jQuery)\", function() {\n\t\tvar string = buildQueryString({a: \"\"})\n\n\t\to(string).equals(\"a\")\n\t})\n\to(\"handles zero\", function() {\n\t\tvar string = buildQueryString({a: 0})\n\n\t\to(string).equals(\"a=0\")\n\t})\n\to(\"handles false\", function() {\n\t\tvar string = buildQueryString({a: false})\n\n\t\to(string).equals(\"a=false\")\n\t})\n})\n"
  },
  {
    "path": "querystring/tests/test-parseQueryString.js",
    "content": "\"use strict\"\n\nvar o = require(\"ospec\")\nvar parseQueryString = require(\"../../querystring/parse\")\n\no.spec(\"parseQueryString\", function() {\n\to(\"works\", function() {\n\t\tvar data = parseQueryString(\"?aaa=bbb\")\n\t\to(data).deepEquals({aaa: \"bbb\"})\n\t})\n\to(\"parses empty string\", function() {\n\t\tvar data = parseQueryString(\"\")\n\t\to(data).deepEquals({})\n\t})\n\to(\"parses flat object\", function() {\n\t\tvar data = parseQueryString(\"?a=b&c=d\")\n\t\to(data).deepEquals({a: \"b\", c: \"d\"})\n\t})\n\to(\"handles escaped values\", function() {\n\t\tvar data = parseQueryString(\"?%3B%3A%40%26%3D%2B%24%2C%2F%3F%25%23=%3B%3A%40%26%3D%2B%24%2C%2F%3F%25%23\")\n\t\to(data).deepEquals({\";:@&=+$,/?%#\": \";:@&=+$,/?%#\"})\n\t})\n\to(\"handles wrongly escaped values\", function() {\n\t\tvar data = parseQueryString(\"?test=%c5%a1%e8ZM%80%82H\")\n\t\t// decodes \"%c5%a1\" only\n\t\to(data).deepEquals({test: \"š%e8ZM%80%82H\"})\n\t})\n\to(\"handles escaped slashes followed by a number\", function () {\n\t\tvar data = parseQueryString(\"?hello=%2Fen%2F1\")\n\t\to(data.hello).equals(\"/en/1\")\n\t})\n\to(\"handles escaped square brackets\", function() {\n\t\tvar data = parseQueryString(\"?a%5B%5D=b\")\n\t\to(data).deepEquals({\"a\": [\"b\"]})\n\t})\n\to(\"handles escaped unicode\", function() {\n\t\tvar data = parseQueryString(\"?%C3%B6=%C3%B6\")\n\t\to(data).deepEquals({\"ö\": \"ö\"})\n\t})\n\to(\"handles unicode\", function() {\n\t\tvar data = parseQueryString(\"?ö=ö\")\n\t\to(data).deepEquals({\"ö\": \"ö\"})\n\t})\n\to(\"parses without question mark\", function() {\n\t\tvar data = parseQueryString(\"a=b&c=d\")\n\t\to(data).deepEquals({a: \"b\", c: \"d\"})\n\t})\n\to(\"parses nested object\", function() {\n\t\tvar data = parseQueryString(\"a[b]=x&a[c]=y\")\n\t\to(data).deepEquals({a: {b: \"x\", c: \"y\"}})\n\t})\n\to(\"parses deep nested object\", function() {\n\t\tvar data = parseQueryString(\"a[b][c]=x&a[b][d]=y\")\n\t\to(data).deepEquals({a: {b: {c: \"x\", d: \"y\"}}})\n\t})\n\to(\"parses nested array\", function() {\n\t\tvar data = parseQueryString(\"a[0]=x&a[1]=y\")\n\t\to(data).deepEquals({a: [\"x\", \"y\"]})\n\t})\n\to(\"parses deep nested array\", function() {\n\t\tvar data = parseQueryString(\"a[0][0]=x&a[0][1]=y\")\n\t\to(data).deepEquals({a: [[\"x\", \"y\"]]})\n\t})\n\to(\"parses deep nested object in array\", function() {\n\t\tvar data = parseQueryString(\"a[0][c]=x&a[0][d]=y\")\n\t\to(data).deepEquals({a: [{c: \"x\", d: \"y\"}]})\n\t})\n\to(\"parses deep nested array in object\", function() {\n\t\tvar data = parseQueryString(\"a[b][0]=x&a[b][1]=y\")\n\t\to(data).deepEquals({a: {b: [\"x\", \"y\"]}})\n\t})\n\to(\"parses array without index\", function() {\n\t\tvar data = parseQueryString(\"a[]=x&a[]=y&b[]=w&b[]=z\")\n\t\to(data).deepEquals({a: [\"x\", \"y\"], b: [\"w\", \"z\"]})\n\t})\n\to(\"casts booleans\", function() {\n\t\tvar data = parseQueryString(\"a=true&b=false\")\n\t\to(data).deepEquals({a: true, b: false})\n\t})\n\to(\"does not cast numbers\", function() {\n\t\tvar data = parseQueryString(\"a=1&b=-2.3&c=0x10&d=1e2&e=Infinity\")\n\t\to(data).deepEquals({a: \"1\", b: \"-2.3\", c: \"0x10\", d: \"1e2\", e: \"Infinity\"})\n\t})\n\to(\"does not cast NaN\", function() {\n\t\tvar data = parseQueryString(\"a=NaN\")\n\t\to(data.a).equals(\"NaN\")\n\t})\n\to(\"does not casts Date\", function() {\n\t\tvar data = parseQueryString(\"a=1970-01-01\")\n\t\to(typeof data.a).equals(\"string\")\n\t\to(data.a).equals(\"1970-01-01\")\n\t})\n\to(\"does not cast empty string to number\", function() {\n\t\tvar data = parseQueryString(\"a=\")\n\t\to(data).deepEquals({a: \"\"})\n\t})\n\to(\"does not cast void to number\", function() {\n\t\tvar data = parseQueryString(\"a\")\n\t\to(data).deepEquals({a: \"\"})\n\t})\n\to(\"prefers later values\", function() {\n\t\tvar data = parseQueryString(\"a=1&b=2&a=3\")\n\t\to(data).deepEquals({a: \"3\", b: \"2\"})\n\t})\n\to(\"doesn't pollute prototype directly, censors `__proto__`\", function() {\n\t\tvar prev = Object.prototype.toString\n\t\tvar data = parseQueryString(\"a=b&__proto__%5BtoString%5D=123\")\n\t\to(Object.prototype.toString).equals(prev)\n\t\to(data).deepEquals({a: \"b\"})\n\t})\n\to(\"doesn't pollute prototype indirectly, retains `constructor`\", function() {\n\t\tvar prev = Object.prototype.toString\n\t\tvar data = parseQueryString(\"a=b&constructor%5Bprototype%5D%5BtoString%5D=123\")\n\t\to(Object.prototype.toString).equals(prev)\n\t\t// The deep matcher is borked here.\n\t\to(Object.keys(data)).deepEquals([\"a\", \"constructor\"])\n\t\to(data.a).equals(\"b\")\n\t\to(data.constructor).deepEquals({prototype: {toString: \"123\"}})\n\t})\n})\n"
  },
  {
    "path": "redraw.js",
    "content": "\"use strict\"\n\nmodule.exports = require(\"./mount-redraw\").redraw\n"
  },
  {
    "path": "render/cachedAttrsIsStaticMap.js",
    "content": "\"use strict\"\n\nvar emptyAttrs = require(\"./emptyAttrs\")\n\n// This Map manages the following:\n// - Whether an attrs is cached attrs generated by compileSelector().\n// - Whether the cached attrs is \"static\", i.e., does not contain any form attributes.\n// These information will be useful to skip updating attrs in render().\n//\n// Since the attrs used as keys in this map are not released from the selectorCache object,\n// there is no risk of memory leaks. Therefore, Map is used here instead of WeakMap.\nmodule.exports = new Map([[emptyAttrs, true]])\n"
  },
  {
    "path": "render/delayedRemoval.js",
    "content": "\"use strict\"\n\nmodule.exports = new WeakMap\n"
  },
  {
    "path": "render/domFor.js",
    "content": "\"use strict\"\n\nvar delayedRemoval = require(\"./delayedRemoval\")\n\nfunction *domFor(vnode) {\n\t// To avoid unintended mangling of the internal bundler,\n\t// parameter destructuring is not used here.\n\tvar dom = vnode.dom\n\tvar domSize = vnode.domSize\n\tvar generation = delayedRemoval.get(dom)\n\tif (dom != null) do {\n\t\tvar nextSibling = dom.nextSibling\n\n\t\tif (delayedRemoval.get(dom) === generation) {\n\t\t\tyield dom\n\t\t\tdomSize--\n\t\t}\n\n\t\tdom = nextSibling\n\t}\n\twhile (domSize)\n}\n\nmodule.exports = domFor\n"
  },
  {
    "path": "render/emptyAttrs.js",
    "content": "\"use strict\"\n\n// This is an attrs object that is used by default when attrs is undefined or null.\nmodule.exports = {}\n"
  },
  {
    "path": "render/fragment.js",
    "content": "\"use strict\"\n\nvar Vnode = require(\"../render/vnode\")\nvar hyperscriptVnode = require(\"./hyperscriptVnode\")\n\nmodule.exports = function(attrs, ...children) {\n\tvar vnode = hyperscriptVnode(attrs, children)\n\n\tif (vnode.attrs == null) vnode.attrs = {}\n\tvnode.tag = \"[\"\n\tvnode.children = Vnode.normalizeChildren(vnode.children)\n\treturn vnode\n}\n"
  },
  {
    "path": "render/hyperscript.js",
    "content": "\"use strict\"\n\nvar Vnode = require(\"../render/vnode\")\nvar hyperscriptVnode = require(\"./hyperscriptVnode\")\nvar hasOwn = require(\"../util/hasOwn\")\nvar emptyAttrs = require(\"./emptyAttrs\")\nvar cachedAttrsIsStaticMap = require(\"./cachedAttrsIsStaticMap\")\n\nvar selectorParser = /(?:(^|#|\\.)([^#\\.\\[\\]]+))|(\\[(.+?)(?:\\s*=\\s*(\"|'|)((?:\\\\[\"'\\]]|.)*?)\\5)?\\])/g\nvar selectorCache = Object.create(null)\n\nfunction isEmpty(object) {\n\tfor (var key in object) if (hasOwn.call(object, key)) return false\n\treturn true\n}\n\nfunction isFormAttributeKey(key) {\n\treturn key === \"value\" || key === \"checked\" || key === \"selectedIndex\" || key === \"selected\"\n}\n\nfunction compileSelector(selector) {\n\tvar match, tag = \"div\", classes = [], attrs = {}, isStatic = true\n\twhile (match = selectorParser.exec(selector)) {\n\t\tvar type = match[1], value = match[2]\n\t\tif (type === \"\" && value !== \"\") tag = value\n\t\telse if (type === \"#\") attrs.id = value\n\t\telse if (type === \".\") classes.push(value)\n\t\telse if (match[3][0] === \"[\") {\n\t\t\tvar attrValue = match[6]\n\t\t\tif (attrValue) attrValue = attrValue.replace(/\\\\([\"'])/g, \"$1\").replace(/\\\\\\\\/g, \"\\\\\")\n\t\t\tif (match[4] === \"class\") classes.push(attrValue)\n\t\t\telse {\n\t\t\t\tattrs[match[4]] = attrValue === \"\" ? attrValue : attrValue || true\n\t\t\t\tif (isFormAttributeKey(match[4])) isStatic = false\n\t\t\t}\n\t\t}\n\t}\n\tif (classes.length > 0) attrs.className = classes.join(\" \")\n\tif (isEmpty(attrs)) attrs = emptyAttrs\n\telse cachedAttrsIsStaticMap.set(attrs, isStatic)\n\treturn selectorCache[selector] = {tag: tag, attrs: attrs, is: attrs.is}\n}\n\nfunction execSelector(state, vnode) {\n\tvnode.tag = state.tag\n\n\tvar attrs = vnode.attrs\n\tif (attrs == null) {\n\t\tvnode.attrs = state.attrs\n\t\tvnode.is = state.is\n\t\treturn vnode\n\t}\n\n\tif (hasOwn.call(attrs, \"class\")) {\n\t\tif (attrs.class != null) attrs.className = attrs.class\n\t\tattrs.class = null\n\t}\n\n\tif (state.attrs !== emptyAttrs) {\n\t\tvar className = attrs.className\n\t\tattrs = Object.assign({}, state.attrs, attrs)\n\n\t\tif (state.attrs.className != null) attrs.className =\n\t\t\tclassName != null\n\t\t\t\t? String(state.attrs.className) + \" \" + String(className)\n\t\t\t\t: state.attrs.className\n\t}\n\n\t// workaround for #2622 (reorder keys in attrs to set \"type\" first)\n\t// The DOM does things to inputs based on the \"type\", so it needs set first.\n\t// See: https://github.com/MithrilJS/mithril.js/issues/2622\n\tif (state.tag === \"input\" && hasOwn.call(attrs, \"type\")) {\n\t\tattrs = Object.assign({type: attrs.type}, attrs)\n\t}\n\n\t// This reduces the complexity of the evaluation of \"is\" within the render function.\n\tvnode.is = attrs.is\n\n\tvnode.attrs = attrs\n\n\treturn vnode\n}\n\nfunction hyperscript(selector, attrs, ...children) {\n\tif (selector == null || typeof selector !== \"string\" && typeof selector !== \"function\" && typeof selector.view !== \"function\") {\n\t\tthrow Error(\"The selector must be either a string or a component.\");\n\t}\n\n\tvar vnode = hyperscriptVnode(attrs, children)\n\n\tif (typeof selector === \"string\") {\n\t\tvnode.children = Vnode.normalizeChildren(vnode.children)\n\t\tif (selector !== \"[\") return execSelector(selectorCache[selector] || compileSelector(selector), vnode)\n\t}\n\n\tif (vnode.attrs == null) vnode.attrs = {}\n\tvnode.tag = selector\n\treturn vnode\n}\n\nmodule.exports = hyperscript\n"
  },
  {
    "path": "render/hyperscriptVnode.js",
    "content": "\"use strict\"\n\nvar Vnode = require(\"../render/vnode\")\n\n// Note: the processing of variadic parameters is perf-sensitive.\n//\n// In native ES6, it might be preferable to define hyperscript and fragment\n// factories with a final ...args parameter and call hyperscriptVnode(...args),\n// since modern engines can optimize spread calls.\n//\n// However, benchmarks showed this was not faster. As a result, spread is used\n// only in the parameter lists of hyperscript and fragment, while an array is\n// passed to hyperscriptVnode.\nmodule.exports = function(attrs, children) {\n\tif (attrs == null || typeof attrs === \"object\" && attrs.tag == null && !Array.isArray(attrs)) {\n\t\tif (children.length === 1 && Array.isArray(children[0])) children = children[0]\n\t} else {\n\t\tchildren = children.length === 0 && Array.isArray(attrs) ? attrs : [attrs, ...children]\n\t\tattrs = undefined\n\t}\n\n\treturn Vnode(\"\", attrs && attrs.key, attrs, children)\n}\n"
  },
  {
    "path": "render/render.js",
    "content": "\"use strict\"\n\nvar Vnode = require(\"./vnode\")\nvar delayedRemoval = require(\"./delayedRemoval\")\nvar domFor = require(\"./domFor\")\nvar cachedAttrsIsStaticMap = require(\"./cachedAttrsIsStaticMap\")\n\nmodule.exports = function() {\n\tvar nameSpace = {\n\t\tsvg: \"http://www.w3.org/2000/svg\",\n\t\tmath: \"http://www.w3.org/1998/Math/MathML\"\n\t}\n\n\tvar currentRedraw\n\tvar currentRender\n\n\tfunction getDocument(dom) {\n\t\treturn dom.ownerDocument;\n\t}\n\n\tfunction getNameSpace(vnode) {\n\t\treturn vnode.attrs && vnode.attrs.xmlns || nameSpace[vnode.tag]\n\t}\n\n\t//sanity check to discourage people from doing `vnode.state = ...`\n\tfunction checkState(vnode, original) {\n\t\tif (vnode.state !== original) throw new Error(\"'vnode.state' must not be modified.\")\n\t}\n\n\t//Note: the hook is passed as the `this` argument to allow proxying the\n\t//arguments without requiring a full array allocation to do so. It also\n\t//takes advantage of the fact the current `vnode` is the first argument in\n\t//all lifecycle methods.\n\tfunction callHook(vnode) {\n\t\tvar original = vnode.state\n\t\ttry {\n\t\t\treturn this.apply(original, arguments)\n\t\t} finally {\n\t\t\tcheckState(vnode, original)\n\t\t}\n\t}\n\n\t// IE11 (at least) throws an UnspecifiedError when accessing document.activeElement when\n\t// inside an iframe. Catch and swallow this error, and heavy-handidly return null.\n\tfunction activeElement(dom) {\n\t\ttry {\n\t\t\treturn getDocument(dom).activeElement\n\t\t} catch (e) {\n\t\t\treturn null\n\t\t}\n\t}\n\t//create\n\tfunction createNodes(parent, vnodes, start, end, hooks, nextSibling, ns) {\n\t\tfor (var i = start; i < end; i++) {\n\t\t\tvar vnode = vnodes[i]\n\t\t\tif (vnode != null) {\n\t\t\t\tcreateNode(parent, vnode, hooks, ns, nextSibling)\n\t\t\t}\n\t\t}\n\t}\n\tfunction createNode(parent, vnode, hooks, ns, nextSibling) {\n\t\tvar tag = vnode.tag\n\t\tif (typeof tag === \"string\") {\n\t\t\tvnode.state = {}\n\t\t\tif (vnode.attrs != null) initLifecycle(vnode.attrs, vnode, hooks)\n\t\t\tswitch (tag) {\n\t\t\t\tcase \"#\": createText(parent, vnode, nextSibling); break\n\t\t\t\tcase \"<\": createHTML(parent, vnode, ns, nextSibling); break\n\t\t\t\tcase \"[\": createFragment(parent, vnode, hooks, ns, nextSibling); break\n\t\t\t\tdefault: createElement(parent, vnode, hooks, ns, nextSibling)\n\t\t\t}\n\t\t}\n\t\telse createComponent(parent, vnode, hooks, ns, nextSibling)\n\t}\n\tfunction createText(parent, vnode, nextSibling) {\n\t\tvnode.dom = getDocument(parent).createTextNode(vnode.children)\n\t\tinsertDOM(parent, vnode.dom, nextSibling)\n\t}\n\tvar possibleParents = {caption: \"table\", thead: \"table\", tbody: \"table\", tfoot: \"table\", tr: \"tbody\", th: \"tr\", td: \"tr\", colgroup: \"table\", col: \"colgroup\"}\n\tfunction createHTML(parent, vnode, ns, nextSibling) {\n\t\tvar match = vnode.children.match(/^\\s*?<(\\w+)/im) || []\n\t\t// not using the proper parent makes the child element(s) vanish.\n\t\t//     var div = document.createElement(\"div\")\n\t\t//     div.innerHTML = \"<td>i</td><td>j</td>\"\n\t\t//     console.log(div.innerHTML)\n\t\t// --> \"ij\", no <td> in sight.\n\t\tvar temp = getDocument(parent).createElement(possibleParents[match[1]] || \"div\")\n\t\tif (ns === \"http://www.w3.org/2000/svg\") {\n\t\t\ttemp.innerHTML = \"<svg xmlns=\\\"http://www.w3.org/2000/svg\\\">\" + vnode.children + \"</svg>\"\n\t\t\ttemp = temp.firstChild\n\t\t} else {\n\t\t\ttemp.innerHTML = vnode.children\n\t\t}\n\t\tvnode.dom = temp.firstChild\n\t\tvnode.domSize = temp.childNodes.length\n\t\tvar fragment = getDocument(parent).createDocumentFragment()\n\t\tvar child\n\t\twhile (child = temp.firstChild) {\n\t\t\tfragment.appendChild(child)\n\t\t}\n\t\tinsertDOM(parent, fragment, nextSibling)\n\t}\n\tfunction createFragment(parent, vnode, hooks, ns, nextSibling) {\n\t\tvar fragment = getDocument(parent).createDocumentFragment()\n\t\tif (vnode.children != null) {\n\t\t\tvar children = vnode.children\n\t\t\tcreateNodes(fragment, children, 0, children.length, hooks, null, ns)\n\t\t}\n\t\tvnode.dom = fragment.firstChild\n\t\tvnode.domSize = fragment.childNodes.length\n\t\tinsertDOM(parent, fragment, nextSibling)\n\t}\n\tfunction createElement(parent, vnode, hooks, ns, nextSibling) {\n\t\tvar tag = vnode.tag\n\t\tvar attrs = vnode.attrs\n\t\tvar is = vnode.is\n\n\t\tns = getNameSpace(vnode) || ns\n\n\t\tvar element = ns ?\n\t\t\tis ? getDocument(parent).createElementNS(ns, tag, {is: is}) : getDocument(parent).createElementNS(ns, tag) :\n\t\t\tis ? getDocument(parent).createElement(tag, {is: is}) : getDocument(parent).createElement(tag)\n\t\tvnode.dom = element\n\n\t\tif (attrs != null) {\n\t\t\tsetAttrs(vnode, attrs, ns)\n\t\t}\n\n\t\tinsertDOM(parent, element, nextSibling)\n\n\t\tif (!maybeSetContentEditable(vnode)) {\n\t\t\tif (vnode.children != null) {\n\t\t\t\tvar children = vnode.children\n\t\t\t\tcreateNodes(element, children, 0, children.length, hooks, null, ns)\n\t\t\t\tif (vnode.tag === \"select\" && attrs != null) setLateSelectAttrs(vnode, attrs)\n\t\t\t}\n\t\t}\n\t}\n\tfunction initComponent(vnode, hooks) {\n\t\tvar sentinel\n\t\tif (typeof vnode.tag.view === \"function\") {\n\t\t\tvnode.state = Object.create(vnode.tag)\n\t\t\tsentinel = vnode.state.view\n\t\t\tif (sentinel.$$reentrantLock$$ != null) return\n\t\t\tsentinel.$$reentrantLock$$ = true\n\t\t} else {\n\t\t\tvnode.state = void 0\n\t\t\tsentinel = vnode.tag\n\t\t\tif (sentinel.$$reentrantLock$$ != null) return\n\t\t\tsentinel.$$reentrantLock$$ = true\n\t\t\tvnode.state = (vnode.tag.prototype != null && typeof vnode.tag.prototype.view === \"function\") ? new vnode.tag(vnode) : vnode.tag(vnode)\n\t\t}\n\t\tinitLifecycle(vnode.state, vnode, hooks)\n\t\tif (vnode.attrs != null) initLifecycle(vnode.attrs, vnode, hooks)\n\t\tvnode.instance = Vnode.normalize(callHook.call(vnode.state.view, vnode))\n\t\tif (vnode.instance === vnode) throw Error(\"A view cannot return the vnode it received as argument\")\n\t\tsentinel.$$reentrantLock$$ = null\n\t}\n\tfunction createComponent(parent, vnode, hooks, ns, nextSibling) {\n\t\tinitComponent(vnode, hooks)\n\t\tif (vnode.instance != null) {\n\t\t\tcreateNode(parent, vnode.instance, hooks, ns, nextSibling)\n\t\t\tvnode.dom = vnode.instance.dom\n\t\t\tvnode.domSize = vnode.instance.domSize\n\t\t}\n\t\telse {\n\t\t\tvnode.domSize = 0\n\t\t}\n\t}\n\n\t//update\n\t/**\n\t * @param {Element|Fragment} parent - the parent element\n\t * @param {Vnode[] | null} old - the list of vnodes of the last `render()` call for\n\t *                               this part of the tree\n\t * @param {Vnode[] | null} vnodes - as above, but for the current `render()` call.\n\t * @param {Function[]} hooks - an accumulator of post-render hooks (oncreate/onupdate)\n\t * @param {Element | null} nextSibling - the next DOM node if we're dealing with a\n\t *                                       fragment that is not the last item in its\n\t *                                       parent\n\t * @param {'svg' | 'math' | String | null} ns) - the current XML namespace, if any\n\t * @returns void\n\t */\n\t// This function diffs and patches lists of vnodes, both keyed and unkeyed.\n\t//\n\t// We will:\n\t//\n\t// 1. describe its general structure\n\t// 2. focus on the diff algorithm optimizations\n\t// 3. discuss DOM node operations.\n\n\t// ## Overview:\n\t//\n\t// The updateNodes() function:\n\t// - deals with trivial cases\n\t// - determines whether the lists are keyed or unkeyed based on the first non-null node\n\t//   of each list.\n\t// - diffs them and patches the DOM if needed (that's the brunt of the code)\n\t// - manages the leftovers: after diffing, are there:\n\t//   - old nodes left to remove?\n\t// \t - new nodes to insert?\n\t// \t deal with them!\n\t//\n\t// The lists are only iterated over once, with an exception for the nodes in `old` that\n\t// are visited in the fourth part of the diff and in the `removeNodes` loop.\n\n\t// ## Diffing\n\t//\n\t// Reading https://github.com/localvoid/ivi/blob/ddc09d06abaef45248e6133f7040d00d3c6be853/packages/ivi/src/vdom/implementation.ts#L617-L837\n\t// may be good for context on longest increasing subsequence-based logic for moving nodes.\n\t//\n\t// In order to diff keyed lists, one has to\n\t//\n\t// 1) match nodes in both lists, per key, and update them accordingly\n\t// 2) create the nodes present in the new list, but absent in the old one\n\t// 3) remove the nodes present in the old list, but absent in the new one\n\t// 4) figure out what nodes in 1) to move in order to minimize the DOM operations.\n\t//\n\t// To achieve 1) one can create a dictionary of keys => index (for the old list), then iterate\n\t// over the new list and for each new vnode, find the corresponding vnode in the old list using\n\t// the map.\n\t// 2) is achieved in the same step: if a new node has no corresponding entry in the map, it is new\n\t// and must be created.\n\t// For the removals, we actually remove the nodes that have been updated from the old list.\n\t// The nodes that remain in that list after 1) and 2) have been performed can be safely removed.\n\t// The fourth step is a bit more complex and relies on the longest increasing subsequence (LIS)\n\t// algorithm.\n\t//\n\t// the longest increasing subsequence is the list of nodes that can remain in place. Imagine going\n\t// from `1,2,3,4,5` to `4,5,1,2,3` where the numbers are not necessarily the keys, but the indices\n\t// corresponding to the keyed nodes in the old list (keyed nodes `e,d,c,b,a` => `b,a,e,d,c` would\n\t//  match the above lists, for example).\n\t//\n\t// In there are two increasing subsequences: `4,5` and `1,2,3`, the latter being the longest. We\n\t// can update those nodes without moving them, and only call `insertNode` on `4` and `5`.\n\t//\n\t// @localvoid adapted the algo to also support node deletions and insertions (the `lis` is actually\n\t// the longest increasing subsequence *of old nodes still present in the new list*).\n\t//\n\t// It is a general algorithm that is fireproof in all circumstances, but it requires the allocation\n\t// and the construction of a `key => oldIndex` map, and three arrays (one with `newIndex => oldIndex`,\n\t// the `LIS` and a temporary one to create the LIS).\n\t//\n\t// So we cheat where we can: if the tails of the lists are identical, they are guaranteed to be part of\n\t// the LIS and can be updated without moving them.\n\t//\n\t// If two nodes are swapped, they are guaranteed not to be part of the LIS, and must be moved (with\n\t// the exception of the last node if the list is fully reversed).\n\t//\n\t// ## Finding the next sibling.\n\t//\n\t// `updateNode()` and `createNode()` expect a nextSibling parameter to perform DOM operations.\n\t// When the list is being traversed top-down, at any index, the DOM nodes up to the previous\n\t// vnode reflect the content of the new list, whereas the rest of the DOM nodes reflect the old\n\t// list. The next sibling must be looked for in the old list using `getNextSibling(... oldStart + 1 ...)`.\n\t//\n\t// In the other scenarios (swaps, upwards traversal, map-based diff),\n\t// the new vnodes list is traversed upwards. The DOM nodes at the bottom of the list reflect the\n\t// bottom part of the new vnodes list, and we can use the `v.dom`  value of the previous node\n\t// as the next sibling (cached in the `nextSibling` variable).\n\n\n\t// ## DOM node moves\n\t//\n\t// In most scenarios `updateNode()` and `createNode()` perform the DOM operations. However,\n\t// this is not the case if the node moved (second and fourth part of the diff algo). We move\n\t// the old DOM nodes before updateNode runs because it enables us to use the cached `nextSibling`\n\t// variable rather than fetching it using `getNextSibling()`.\n\n\tfunction updateNodes(parent, old, vnodes, hooks, nextSibling, ns) {\n\t\tif (old === vnodes || old == null && vnodes == null) return\n\t\telse if (old == null || old.length === 0) createNodes(parent, vnodes, 0, vnodes.length, hooks, nextSibling, ns)\n\t\telse if (vnodes == null || vnodes.length === 0) removeNodes(parent, old, 0, old.length)\n\t\telse {\n\t\t\tvar isOldKeyed = old[0] != null && old[0].key != null\n\t\t\tvar isKeyed = vnodes[0] != null && vnodes[0].key != null\n\t\t\tvar start = 0, oldStart = 0, o, v\n\t\t\tif (isOldKeyed !== isKeyed) {\n\t\t\t\tremoveNodes(parent, old, 0, old.length)\n\t\t\t\tcreateNodes(parent, vnodes, 0, vnodes.length, hooks, nextSibling, ns)\n\t\t\t} else if (!isKeyed) {\n\t\t\t\t// Don't index past the end of either list (causes deopts).\n\t\t\t\tvar commonLength = old.length < vnodes.length ? old.length : vnodes.length\n\t\t\t\t// Rewind if necessary to the first non-null index on either side.\n\t\t\t\t// We could alternatively either explicitly create or remove nodes when `start !== oldStart`\n\t\t\t\t// but that would be optimizing for sparse lists which are more rare than dense ones.\n\t\t\t\twhile (oldStart < old.length && old[oldStart] == null) oldStart++\n\t\t\t\twhile (start < vnodes.length && vnodes[start] == null) start++\n\t\t\t\tstart = start < oldStart ? start : oldStart\n\t\t\t\tfor (; start < commonLength; start++) {\n\t\t\t\t\to = old[start]\n\t\t\t\t\tv = vnodes[start]\n\t\t\t\t\tif (o === v || o == null && v == null) continue\n\t\t\t\t\telse if (o == null) createNode(parent, v, hooks, ns, getNextSibling(old, start + 1, old.length, nextSibling))\n\t\t\t\t\telse if (v == null) removeNode(parent, o)\n\t\t\t\t\telse updateNode(parent, o, v, hooks, getNextSibling(old, start + 1, old.length, nextSibling), ns)\n\t\t\t\t}\n\t\t\t\tif (old.length > commonLength) removeNodes(parent, old, start, old.length)\n\t\t\t\tif (vnodes.length > commonLength) createNodes(parent, vnodes, start, vnodes.length, hooks, nextSibling, ns)\n\t\t\t} else {\n\t\t\t\t// keyed diff\n\t\t\t\tvar oldEnd = old.length - 1, end = vnodes.length - 1, oe, ve, topSibling\n\n\t\t\t\t// bottom-up\n\t\t\t\twhile (oldEnd >= oldStart && end >= start) {\n\t\t\t\t\toe = old[oldEnd]\n\t\t\t\t\tve = vnodes[end]\n\t\t\t\t\tif (oe.key !== ve.key) break\n\t\t\t\t\tif (oe !== ve) updateNode(parent, oe, ve, hooks, nextSibling, ns)\n\t\t\t\t\tif (ve.dom != null) nextSibling = ve.dom\n\t\t\t\t\toldEnd--, end--\n\t\t\t\t}\n\t\t\t\t// top-down\n\t\t\t\twhile (oldEnd >= oldStart && end >= start) {\n\t\t\t\t\to = old[oldStart]\n\t\t\t\t\tv = vnodes[start]\n\t\t\t\t\tif (o.key !== v.key) break\n\t\t\t\t\toldStart++, start++\n\t\t\t\t\tif (o !== v) updateNode(parent, o, v, hooks, getNextSibling(old, oldStart, oldEnd + 1, nextSibling), ns)\n\t\t\t\t}\n\t\t\t\t// swaps and list reversals\n\t\t\t\twhile (oldEnd >= oldStart && end >= start) {\n\t\t\t\t\tif (start === end) break\n\t\t\t\t\tif (o.key !== ve.key || oe.key !== v.key) break\n\t\t\t\t\ttopSibling = getNextSibling(old, oldStart, oldEnd, nextSibling)\n\t\t\t\t\tmoveDOM(parent, oe, topSibling)\n\t\t\t\t\tif (oe !== v) updateNode(parent, oe, v, hooks, topSibling, ns)\n\t\t\t\t\tif (++start <= --end) moveDOM(parent, o, nextSibling)\n\t\t\t\t\tif (o !== ve) updateNode(parent, o, ve, hooks, nextSibling, ns)\n\t\t\t\t\tif (ve.dom != null) nextSibling = ve.dom\n\t\t\t\t\toldStart++; oldEnd--\n\t\t\t\t\toe = old[oldEnd]\n\t\t\t\t\tve = vnodes[end]\n\t\t\t\t\to = old[oldStart]\n\t\t\t\t\tv = vnodes[start]\n\t\t\t\t}\n\t\t\t\t// bottom up once again\n\t\t\t\twhile (oldEnd >= oldStart && end >= start) {\n\t\t\t\t\tif (oe.key !== ve.key) break\n\t\t\t\t\tif (oe !== ve) updateNode(parent, oe, ve, hooks, nextSibling, ns)\n\t\t\t\t\tif (ve.dom != null) nextSibling = ve.dom\n\t\t\t\t\toldEnd--, end--\n\t\t\t\t\toe = old[oldEnd]\n\t\t\t\t\tve = vnodes[end]\n\t\t\t\t}\n\t\t\t\tif (start > end) removeNodes(parent, old, oldStart, oldEnd + 1)\n\t\t\t\telse if (oldStart > oldEnd) createNodes(parent, vnodes, start, end + 1, hooks, nextSibling, ns)\n\t\t\t\telse {\n\t\t\t\t\t// inspired by ivi https://github.com/ivijs/ivi/ by Boris Kaul\n\t\t\t\t\tvar originalNextSibling = nextSibling, pos = 2147483647, matched = 0\n\t\t\t\t\tvar oldIndices = new Array(end - start + 1).fill(-1)\n\t\t\t\t\tvar map = Object.create(null)\n\t\t\t\t\tfor (var i = start; i <= end; i++) map[vnodes[i].key] = i\n\t\t\t\t\tfor (var i = oldEnd; i >= oldStart; i--) {\n\t\t\t\t\t\toe = old[i]\n\t\t\t\t\t\tvar newIndex = map[oe.key]\n\t\t\t\t\t\tif (newIndex != null) {\n\t\t\t\t\t\t\tpos = (newIndex < pos) ? newIndex : -1 // becomes -1 if nodes were re-ordered\n\t\t\t\t\t\t\toldIndices[newIndex-start] = i\n\t\t\t\t\t\t\tve = vnodes[newIndex]\n\t\t\t\t\t\t\told[i] = null\n\t\t\t\t\t\t\tif (oe !== ve) updateNode(parent, oe, ve, hooks, nextSibling, ns)\n\t\t\t\t\t\t\tif (ve.dom != null) nextSibling = ve.dom\n\t\t\t\t\t\t\tmatched++\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tnextSibling = originalNextSibling\n\t\t\t\t\tif (matched !== oldEnd - oldStart + 1) removeNodes(parent, old, oldStart, oldEnd + 1)\n\t\t\t\t\tif (matched === 0) createNodes(parent, vnodes, start, end + 1, hooks, nextSibling, ns)\n\t\t\t\t\telse {\n\t\t\t\t\t\tif (pos === -1) {\n\t\t\t\t\t\t\t// the indices of the indices of the items that are part of the\n\t\t\t\t\t\t\t// longest increasing subsequence in the oldIndices list\n\t\t\t\t\t\t\tvar lisIndices = makeLisIndices(oldIndices)\n\t\t\t\t\t\t\tvar li = lisIndices.length - 1\n\t\t\t\t\t\t\tfor (var i = end; i >= start; i--) {\n\t\t\t\t\t\t\t\tve = vnodes[i]\n\t\t\t\t\t\t\t\tif (oldIndices[i-start] === -1) createNode(parent, ve, hooks, ns, nextSibling)\n\t\t\t\t\t\t\t\telse {\n\t\t\t\t\t\t\t\t\tif (lisIndices[li] === i - start) li--\n\t\t\t\t\t\t\t\t\telse moveDOM(parent, ve, nextSibling)\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tif (ve.dom != null) nextSibling = ve.dom\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tfor (var i = end; i >= start; i--) {\n\t\t\t\t\t\t\t\tve = vnodes[i]\n\t\t\t\t\t\t\t\tif (oldIndices[i-start] === -1) createNode(parent, ve, hooks, ns, nextSibling)\n\t\t\t\t\t\t\t\tif (ve.dom != null) nextSibling = ve.dom\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tfunction updateNode(parent, old, vnode, hooks, nextSibling, ns) {\n\t\tvar oldTag = old.tag, tag = vnode.tag\n\t\tif (oldTag === tag && old.is === vnode.is) {\n\t\t\tvnode.state = old.state\n\t\t\tvnode.events = old.events\n\t\t\tif (shouldNotUpdate(vnode, old)) return\n\t\t\tif (typeof oldTag === \"string\") {\n\t\t\t\tif (vnode.attrs != null) {\n\t\t\t\t\tupdateLifecycle(vnode.attrs, vnode, hooks)\n\t\t\t\t}\n\t\t\t\tswitch (oldTag) {\n\t\t\t\t\tcase \"#\": updateText(old, vnode); break\n\t\t\t\t\tcase \"<\": updateHTML(parent, old, vnode, ns, nextSibling); break\n\t\t\t\t\tcase \"[\": updateFragment(parent, old, vnode, hooks, nextSibling, ns); break\n\t\t\t\t\tdefault: updateElement(old, vnode, hooks, ns)\n\t\t\t\t}\n\t\t\t}\n\t\t\telse updateComponent(parent, old, vnode, hooks, nextSibling, ns)\n\t\t}\n\t\telse {\n\t\t\tremoveNode(parent, old)\n\t\t\tcreateNode(parent, vnode, hooks, ns, nextSibling)\n\t\t}\n\t}\n\tfunction updateText(old, vnode) {\n\t\tif (old.children.toString() !== vnode.children.toString()) {\n\t\t\told.dom.nodeValue = vnode.children\n\t\t}\n\t\tvnode.dom = old.dom\n\t}\n\tfunction updateHTML(parent, old, vnode, ns, nextSibling) {\n\t\tif (old.children !== vnode.children) {\n\t\t\tremoveDOM(parent, old)\n\t\t\tcreateHTML(parent, vnode, ns, nextSibling)\n\t\t}\n\t\telse {\n\t\t\tvnode.dom = old.dom\n\t\t\tvnode.domSize = old.domSize\n\t\t}\n\t}\n\tfunction updateFragment(parent, old, vnode, hooks, nextSibling, ns) {\n\t\tupdateNodes(parent, old.children, vnode.children, hooks, nextSibling, ns)\n\t\tvar domSize = 0, children = vnode.children\n\t\tvnode.dom = null\n\t\tif (children != null) {\n\t\t\tfor (var i = 0; i < children.length; i++) {\n\t\t\t\tvar child = children[i]\n\t\t\t\tif (child != null && child.dom != null) {\n\t\t\t\t\tif (vnode.dom == null) vnode.dom = child.dom\n\t\t\t\t\tdomSize += child.domSize || 1\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tvnode.domSize = domSize\n\t}\n\tfunction updateElement(old, vnode, hooks, ns) {\n\t\tvar element = vnode.dom = old.dom\n\t\tns = getNameSpace(vnode) || ns\n\n\t\tif (old.attrs != vnode.attrs || (vnode.attrs != null && !cachedAttrsIsStaticMap.get(vnode.attrs))) {\n\t\t\tupdateAttrs(vnode, old.attrs, vnode.attrs, ns)\n\t\t}\n\t\tif (!maybeSetContentEditable(vnode)) {\n\t\t\tupdateNodes(element, old.children, vnode.children, hooks, null, ns)\n\t\t}\n\t}\n\tfunction updateComponent(parent, old, vnode, hooks, nextSibling, ns) {\n\t\tvnode.instance = Vnode.normalize(callHook.call(vnode.state.view, vnode))\n\t\tif (vnode.instance === vnode) throw Error(\"A view cannot return the vnode it received as argument\")\n\t\tupdateLifecycle(vnode.state, vnode, hooks)\n\t\tif (vnode.attrs != null) updateLifecycle(vnode.attrs, vnode, hooks)\n\t\tif (vnode.instance != null) {\n\t\t\tif (old.instance == null) createNode(parent, vnode.instance, hooks, ns, nextSibling)\n\t\t\telse updateNode(parent, old.instance, vnode.instance, hooks, nextSibling, ns)\n\t\t\tvnode.dom = vnode.instance.dom\n\t\t\tvnode.domSize = vnode.instance.domSize\n\t\t}\n\t\telse {\n\t\t\tif (old.instance != null) removeNode(parent, old.instance)\n\t\t\tvnode.domSize = 0\n\t\t}\n\t}\n\t// Lifted from ivi https://github.com/ivijs/ivi/\n\t// takes a list of unique numbers (-1 is special and can\n\t// occur multiple times) and returns an array with the indices\n\t// of the items that are part of the longest increasing\n\t// subsequence\n\tvar lisTemp = []\n\tfunction makeLisIndices(a) {\n\t\tvar result = [0]\n\t\tvar u = 0, v = 0, i = 0\n\t\tvar il = lisTemp.length = a.length\n\t\tfor (var i = 0; i < il; i++) lisTemp[i] = a[i]\n\t\tfor (var i = 0; i < il; ++i) {\n\t\t\tif (a[i] === -1) continue\n\t\t\tvar j = result[result.length - 1]\n\t\t\tif (a[j] < a[i]) {\n\t\t\t\tlisTemp[i] = j\n\t\t\t\tresult.push(i)\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tu = 0\n\t\t\tv = result.length - 1\n\t\t\twhile (u < v) {\n\t\t\t\t// Fast integer average without overflow.\n\t\t\t\t// eslint-disable-next-line no-bitwise\n\t\t\t\tvar c = (u >>> 1) + (v >>> 1) + (u & v & 1)\n\t\t\t\tif (a[result[c]] < a[i]) {\n\t\t\t\t\tu = c + 1\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tv = c\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (a[i] < a[result[u]]) {\n\t\t\t\tif (u > 0) lisTemp[i] = result[u - 1]\n\t\t\t\tresult[u] = i\n\t\t\t}\n\t\t}\n\t\tu = result.length\n\t\tv = result[u - 1]\n\t\twhile (u-- > 0) {\n\t\t\tresult[u] = v\n\t\t\tv = lisTemp[v]\n\t\t}\n\t\tlisTemp.length = 0\n\t\treturn result\n\t}\n\n\tfunction getNextSibling(vnodes, i, end, nextSibling) {\n\t\tfor (; i < end; i++) {\n\t\t\tif (vnodes[i] != null && vnodes[i].dom != null) return vnodes[i].dom\n\t\t}\n\t\treturn nextSibling\n\t}\n\n\t// This handles fragments with zombie children (removed from vdom, but persisted in DOM through onbeforeremove)\n\tfunction moveDOM(parent, vnode, nextSibling) {\n\t\tif (vnode.dom != null) {\n\t\t\tvar target\n\t\t\tif (vnode.domSize == null || vnode.domSize === 1) {\n\t\t\t\t// don't allocate for the common case\n\t\t\t\ttarget = vnode.dom\n\t\t\t} else {\n\t\t\t\ttarget = getDocument(parent).createDocumentFragment()\n\t\t\t\tfor (var dom of domFor(vnode)) target.appendChild(dom)\n\t\t\t}\n\t\t\tinsertDOM(parent, target, nextSibling)\n\t\t}\n\t}\n\n\tfunction insertDOM(parent, dom, nextSibling) {\n\t\tif (nextSibling != null) parent.insertBefore(dom, nextSibling)\n\t\telse parent.appendChild(dom)\n\t}\n\n\tfunction maybeSetContentEditable(vnode) {\n\t\tif (vnode.attrs == null || (\n\t\t\tvnode.attrs.contenteditable == null && // attribute\n\t\t\tvnode.attrs.contentEditable == null // property\n\t\t)) return false\n\t\tvar children = vnode.children\n\t\tif (children != null && children.length === 1 && children[0].tag === \"<\") {\n\t\t\tvar content = children[0].children\n\t\t\tif (vnode.dom.innerHTML !== content) vnode.dom.innerHTML = content\n\t\t}\n\t\telse if (children != null && children.length !== 0) throw new Error(\"Child node of a contenteditable must be trusted.\")\n\t\treturn true\n\t}\n\n\t//remove\n\tfunction removeNodes(parent, vnodes, start, end) {\n\t\tfor (var i = start; i < end; i++) {\n\t\t\tvar vnode = vnodes[i]\n\t\t\tif (vnode != null) removeNode(parent, vnode)\n\t\t}\n\t}\n\tfunction tryBlockRemove(parent, vnode, source, counter) {\n\t\tvar original = vnode.state\n\t\tvar result = callHook.call(source.onbeforeremove, vnode)\n\t\tif (result == null) return\n\n\t\tvar generation = currentRender\n\t\tfor (var dom of domFor(vnode)) delayedRemoval.set(dom, generation)\n\t\tcounter.v++\n\n\t\tPromise.resolve(result).finally(function () {\n\t\t\tcheckState(vnode, original)\n\t\t\ttryResumeRemove(parent, vnode, counter)\n\t\t})\n\t}\n\tfunction tryResumeRemove(parent, vnode, counter) {\n\t\tif (--counter.v === 0) {\n\t\t\tonremove(vnode)\n\t\t\tremoveDOM(parent, vnode)\n\t\t}\n\t}\n\tfunction removeNode(parent, vnode) {\n\t\tvar counter = {v: 1}\n\t\tif (typeof vnode.tag !== \"string\" && typeof vnode.state.onbeforeremove === \"function\") tryBlockRemove(parent, vnode, vnode.state, counter)\n\t\tif (vnode.attrs && typeof vnode.attrs.onbeforeremove === \"function\") tryBlockRemove(parent, vnode, vnode.attrs, counter)\n\t\ttryResumeRemove(parent, vnode, counter)\n\t}\n\tfunction removeDOM(parent, vnode) {\n\t\tif (vnode.dom == null) return\n\t\tif (vnode.domSize == null || vnode.domSize === 1) {\n\t\t\tparent.removeChild(vnode.dom)\n\t\t} else {\n\t\t\tfor (var dom of domFor(vnode)) parent.removeChild(dom)\n\t\t}\n\t}\n\n\tfunction onremove(vnode) {\n\t\tif (typeof vnode.tag !== \"string\" && typeof vnode.state.onremove === \"function\") callHook.call(vnode.state.onremove, vnode)\n\t\tif (vnode.attrs && typeof vnode.attrs.onremove === \"function\") callHook.call(vnode.attrs.onremove, vnode)\n\t\tif (typeof vnode.tag !== \"string\") {\n\t\t\tif (vnode.instance != null) onremove(vnode.instance)\n\t\t} else {\n\t\t\tif (vnode.events != null) vnode.events._ = null\n\t\t\tvar children = vnode.children\n\t\t\tif (Array.isArray(children)) {\n\t\t\t\tfor (var i = 0; i < children.length; i++) {\n\t\t\t\t\tvar child = children[i]\n\t\t\t\t\tif (child != null) onremove(child)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t//attrs\n\tfunction setAttrs(vnode, attrs, ns) {\n\t\tfor (var key in attrs) {\n\t\t\tsetAttr(vnode, key, null, attrs[key], ns)\n\t\t}\n\t}\n\tfunction setAttr(vnode, key, old, value, ns) {\n\t\tif (key === \"key\" || value == null || isLifecycleMethod(key) || (old === value && !isFormAttribute(vnode, key)) && typeof value !== \"object\") return\n\t\tif (key[0] === \"o\" && key[1] === \"n\") return updateEvent(vnode, key, value)\n\t\tif (key.slice(0, 6) === \"xlink:\") vnode.dom.setAttributeNS(\"http://www.w3.org/1999/xlink\", key.slice(6), value)\n\t\telse if (key === \"style\") updateStyle(vnode.dom, old, value)\n\t\telse if (hasPropertyKey(vnode, key, ns)) {\n\t\t\tif (key === \"value\") {\n\t\t\t\t// Only do the coercion if we're actually going to check the value.\n\t\t\t\t/* eslint-disable no-implicit-coercion */\n\t\t\t\t//setting input[value] to same value by typing on focused element moves cursor to end in Chrome\n\t\t\t\t//setting input[type=file][value] to same value causes an error to be generated if it's non-empty\n\t\t\t\t//minlength/maxlength validation isn't performed on script-set values(#2256)\n\t\t\t\tif ((vnode.tag === \"input\" || vnode.tag === \"textarea\") && vnode.dom.value === \"\" + value) return\n\t\t\t\t//setting select[value] to same value while having select open blinks select dropdown in Chrome\n\t\t\t\tif (vnode.tag === \"select\" && old !== null && vnode.dom.value === \"\" + value) return\n\t\t\t\t//setting option[value] to same value while having select open blinks select dropdown in Chrome\n\t\t\t\tif (vnode.tag === \"option\" && old !== null && vnode.dom.value === \"\" + value) return\n\t\t\t\t//setting input[type=file][value] to different value is an error if it's non-empty\n\t\t\t\t// Not ideal, but it at least works around the most common source of uncaught exceptions for now.\n\t\t\t\tif (vnode.tag === \"input\" && vnode.attrs.type === \"file\" && \"\" + value !== \"\") { console.error(\"`value` is read-only on file inputs!\"); return }\n\t\t\t\t/* eslint-enable no-implicit-coercion */\n\t\t\t}\n\t\t\t// If you assign an input type that is not supported by IE 11 with an assignment expression, an error will occur.\n\t\t\tif (vnode.tag === \"input\" && key === \"type\") vnode.dom.setAttribute(key, value)\n\t\t\telse vnode.dom[key] = value\n\t\t} else {\n\t\t\tif (typeof value === \"boolean\") {\n\t\t\t\tif (value) vnode.dom.setAttribute(key, \"\")\n\t\t\t\telse vnode.dom.removeAttribute(key)\n\t\t\t}\n\t\t\telse vnode.dom.setAttribute(key === \"className\" ? \"class\" : key, value)\n\t\t}\n\t}\n\tfunction removeAttr(vnode, key, old, ns) {\n\t\tif (key === \"key\" || old == null || isLifecycleMethod(key)) return\n\t\tif (key[0] === \"o\" && key[1] === \"n\") updateEvent(vnode, key, undefined)\n\t\telse if (key === \"style\") updateStyle(vnode.dom, old, null)\n\t\telse if (\n\t\t\thasPropertyKey(vnode, key, ns)\n\t\t\t&& key !== \"className\"\n\t\t\t&& key !== \"title\" // creates \"null\" as title\n\t\t\t&& !(key === \"value\" && (\n\t\t\t\tvnode.tag === \"option\"\n\t\t\t\t|| vnode.tag === \"select\" && vnode.dom.selectedIndex === -1 && vnode.dom === activeElement(vnode.dom)\n\t\t\t))\n\t\t\t&& !(vnode.tag === \"input\" && key === \"type\")\n\t\t) {\n\t\t\tvnode.dom[key] = null\n\t\t} else {\n\t\t\tvar nsLastIndex = key.indexOf(\":\")\n\t\t\tif (nsLastIndex !== -1) key = key.slice(nsLastIndex + 1)\n\t\t\tif (old !== false) vnode.dom.removeAttribute(key === \"className\" ? \"class\" : key)\n\t\t}\n\t}\n\tfunction setLateSelectAttrs(vnode, attrs) {\n\t\tif (\"value\" in attrs) {\n\t\t\tif(attrs.value === null) {\n\t\t\t\tif (vnode.dom.selectedIndex !== -1) vnode.dom.value = null\n\t\t\t} else {\n\t\t\t\tvar normalized = \"\" + attrs.value // eslint-disable-line no-implicit-coercion\n\t\t\t\tif (vnode.dom.value !== normalized || vnode.dom.selectedIndex === -1) {\n\t\t\t\t\tvnode.dom.value = normalized\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (\"selectedIndex\" in attrs) setAttr(vnode, \"selectedIndex\", null, attrs.selectedIndex, undefined)\n\t}\n\tfunction updateAttrs(vnode, old, attrs, ns) {\n\t\t// Some attributes may NOT be case-sensitive (e.g. data-***),\n\t\t// so removal should be done first to prevent accidental removal for newly setting values.\n\t\tvar val\n\t\tif (old != null) {\n\t\t\tif (old === attrs && !cachedAttrsIsStaticMap.has(attrs)) {\n\t\t\t\tconsole.warn(\"Don't reuse attrs object, use new object for every redraw, this will throw in next major\")\n\t\t\t}\n\t\t\tfor (var key in old) {\n\t\t\t\tif (((val = old[key]) != null) && (attrs == null || attrs[key] == null)) {\n\t\t\t\t\tremoveAttr(vnode, key, val, ns)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (attrs != null) {\n\t\t\tfor (var key in attrs) {\n\t\t\t\tsetAttr(vnode, key, old && old[key], attrs[key], ns)\n\t\t\t}\n\t\t}\n\t}\n\tfunction isFormAttribute(vnode, attr) {\n\t\treturn attr === \"value\" || attr === \"checked\" || attr === \"selectedIndex\" || attr === \"selected\" && (vnode.dom === activeElement(vnode.dom) || vnode.tag === \"option\" && vnode.dom.parentNode === activeElement(vnode.dom))\n\t}\n\tfunction isLifecycleMethod(attr) {\n\t\treturn attr === \"oninit\" || attr === \"oncreate\" || attr === \"onupdate\" || attr === \"onremove\" || attr === \"onbeforeremove\" || attr === \"onbeforeupdate\"\n\t}\n\tfunction hasPropertyKey(vnode, key, ns) {\n\t\t// Filter out namespaced keys\n\t\treturn ns === undefined && (\n\t\t\t// If it's a custom element, just keep it.\n\t\t\tvnode.tag.indexOf(\"-\") > -1 || vnode.is ||\n\t\t\t// If it's a normal element, let's try to avoid a few browser bugs.\n\t\t\tkey !== \"href\" && key !== \"list\" && key !== \"form\" && key !== \"width\" && key !== \"height\"// && key !== \"type\"\n\t\t\t// Defer the property check until *after* we check everything.\n\t\t) && key in vnode.dom\n\t}\n\n\t//style\n\tfunction updateStyle(element, old, style) {\n\t\tif (old === style) {\n\t\t\t// Styles are equivalent, do nothing.\n\t\t} else if (style == null) {\n\t\t\t// New style is missing, just clear it.\n\t\t\telement.style = \"\"\n\t\t} else if (typeof style !== \"object\") {\n\t\t\t// New style is a string, let engine deal with patching.\n\t\t\telement.style = style\n\t\t} else if (old == null || typeof old !== \"object\") {\n\t\t\t// `old` is missing or a string, `style` is an object.\n\t\t\telement.style = \"\"\n\t\t\t// Add new style properties\n\t\t\tfor (var key in style) {\n\t\t\t\tvar value = style[key]\n\t\t\t\tif (value != null) {\n\t\t\t\t\tif (key.includes(\"-\")) element.style.setProperty(key, String(value))\n\t\t\t\t\telse element.style[key] = String(value)\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\t// Both old & new are (different) objects.\n\t\t\t// Remove style properties that no longer exist\n\t\t\t// Style properties may have two cases(dash-case and camelCase),\n\t\t\t// so removal should be done first to prevent accidental removal for newly setting values.\n\t\t\tfor (var key in old) {\n\t\t\t\tif (old[key] != null && style[key] == null) {\n\t\t\t\t\tif (key.includes(\"-\")) element.style.removeProperty(key)\n\t\t\t\t\telse element.style[key] = \"\"\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Update style properties that have changed\n\t\t\tfor (var key in style) {\n\t\t\t\tvar value = style[key]\n\t\t\t\tif (value != null && (value = String(value)) !== String(old[key])) {\n\t\t\t\t\tif (key.includes(\"-\")) element.style.setProperty(key, value)\n\t\t\t\t\telse element.style[key] = value\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// Here's an explanation of how this works:\n\t// 1. The event names are always (by design) prefixed by `on`.\n\t// 2. The EventListener interface accepts either a function or an object\n\t//    with a `handleEvent` method.\n\t// 3. The object does not inherit from `Object.prototype`, to avoid\n\t//    any potential interference with that (e.g. setters).\n\t// 4. The event name is remapped to the handler before calling it.\n\t// 5. In function-based event handlers, `ev.target === this`. We replicate\n\t//    that below.\n\t// 6. In function-based event handlers, `return false` prevents the default\n\t//    action and stops event propagation. We replicate that below.\n\tfunction EventDict() {\n\t\t// Save this, so the current redraw is correctly tracked.\n\t\tthis._ = currentRedraw\n\t}\n\tEventDict.prototype = Object.create(null)\n\tEventDict.prototype.handleEvent = function (ev) {\n\t\tvar handler = this[\"on\" + ev.type]\n\t\tvar result\n\t\tif (typeof handler === \"function\") result = handler.call(ev.currentTarget, ev)\n\t\telse if (typeof handler.handleEvent === \"function\") handler.handleEvent(ev)\n\t\tvar self = this\n\t\tif (self._ != null) {\n\t\t\tif (ev.redraw !== false) (0, self._)()\n\t\t\tif (result != null && typeof result.then === \"function\") {\n\t\t\t\tPromise.resolve(result).then(function () {\n\t\t\t\t\tif (self._ != null && ev.redraw !== false) (0, self._)()\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\t\tif (result === false) {\n\t\t\tev.preventDefault()\n\t\t\tev.stopPropagation()\n\t\t}\n\t}\n\n\t//event\n\tfunction updateEvent(vnode, key, value) {\n\t\tif (vnode.events != null) {\n\t\t\tvnode.events._ = currentRedraw\n\t\t\tif (vnode.events[key] === value) return\n\t\t\tif (value != null && (typeof value === \"function\" || typeof value === \"object\")) {\n\t\t\t\tif (vnode.events[key] == null) vnode.dom.addEventListener(key.slice(2), vnode.events, false)\n\t\t\t\tvnode.events[key] = value\n\t\t\t} else {\n\t\t\t\tif (vnode.events[key] != null) vnode.dom.removeEventListener(key.slice(2), vnode.events, false)\n\t\t\t\tvnode.events[key] = undefined\n\t\t\t}\n\t\t} else if (value != null && (typeof value === \"function\" || typeof value === \"object\")) {\n\t\t\tvnode.events = new EventDict()\n\t\t\tvnode.dom.addEventListener(key.slice(2), vnode.events, false)\n\t\t\tvnode.events[key] = value\n\t\t}\n\t}\n\n\t//lifecycle\n\tfunction initLifecycle(source, vnode, hooks) {\n\t\tif (typeof source.oninit === \"function\") callHook.call(source.oninit, vnode)\n\t\tif (typeof source.oncreate === \"function\") hooks.push(callHook.bind(source.oncreate, vnode))\n\t}\n\tfunction updateLifecycle(source, vnode, hooks) {\n\t\tif (typeof source.onupdate === \"function\") hooks.push(callHook.bind(source.onupdate, vnode))\n\t}\n\tfunction shouldNotUpdate(vnode, old) {\n\t\tdo {\n\t\t\tif (vnode.attrs != null && typeof vnode.attrs.onbeforeupdate === \"function\") {\n\t\t\t\tvar force = callHook.call(vnode.attrs.onbeforeupdate, vnode, old)\n\t\t\t\tif (force !== undefined && !force) break\n\t\t\t}\n\t\t\tif (typeof vnode.tag !== \"string\" && typeof vnode.state.onbeforeupdate === \"function\") {\n\t\t\t\tvar force = callHook.call(vnode.state.onbeforeupdate, vnode, old)\n\t\t\t\tif (force !== undefined && !force) break\n\t\t\t}\n\t\t\treturn false\n\t\t} while (false); // eslint-disable-line no-constant-condition\n\t\tvnode.dom = old.dom\n\t\tvnode.domSize = old.domSize\n\t\tvnode.instance = old.instance\n\t\t// One would think having the actual latest attributes would be ideal,\n\t\t// but it doesn't let us properly diff based on our current internal\n\t\t// representation. We have to save not only the old DOM info, but also\n\t\t// the attributes used to create it, as we diff *that*, not against the\n\t\t// DOM directly (with a few exceptions in `setAttr`). And, of course, we\n\t\t// need to save the children and text as they are conceptually not\n\t\t// unlike special \"attributes\" internally.\n\t\tvnode.attrs = old.attrs\n\t\tvnode.children = old.children\n\t\tvnode.text = old.text\n\t\treturn true\n\t}\n\n\tvar currentDOM\n\n\treturn function(dom, vnodes, redraw) {\n\t\tif (!dom) throw new TypeError(\"DOM element being rendered to does not exist.\")\n\t\tif (currentDOM != null && dom.contains(currentDOM)) {\n\t\t\tthrow new TypeError(\"Node is currently being rendered to and thus is locked.\")\n\t\t}\n\t\tvar prevRedraw = currentRedraw\n\t\tvar prevDOM = currentDOM\n\t\tvar hooks = []\n\t\tvar active = activeElement(dom)\n\t\tvar namespace = dom.namespaceURI\n\n\t\tcurrentDOM = dom\n\t\tcurrentRedraw = typeof redraw === \"function\" ? redraw : undefined\n\t\tcurrentRender = {}\n\t\ttry {\n\t\t\t// First time rendering into a node clears it out\n\t\t\tif (dom.vnodes == null) dom.textContent = \"\"\n\t\t\tvnodes = Vnode.normalizeChildren(Array.isArray(vnodes) ? vnodes : [vnodes])\n\t\t\tupdateNodes(dom, dom.vnodes, vnodes, hooks, null, namespace === \"http://www.w3.org/1999/xhtml\" ? undefined : namespace)\n\t\t\tdom.vnodes = vnodes\n\t\t\t// `document.activeElement` can return null: https://html.spec.whatwg.org/multipage/interaction.html#dom-document-activeelement\n\t\t\tif (active != null && activeElement(dom) !== active && typeof active.focus === \"function\") active.focus()\n\t\t\tfor (var i = 0; i < hooks.length; i++) hooks[i]()\n\t\t} finally {\n\t\t\tcurrentRedraw = prevRedraw\n\t\t\tcurrentDOM = prevDOM\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "render/tests/.eslintrc.js",
    "content": "\"use strict\"\n\nmodule.exports = {\n\t\"extends\": \"../../.eslintrc.js\",\n\t\"env\": {\n\t\t\"browser\": null,\n\t\t\"node\": true,\n\t\t\"es2022\": true,\n\t},\n\t\"parserOptions\": {\n\t\t\"ecmaVersion\": 2022,\n\t},\n\t\"rules\": {\n\t\t\"no-process-env\": \"off\",\n\t},\n};\n"
  },
  {
    "path": "render/tests/manual/case-handling.html",
    "content": "<!DOCTYPE html>\n<html>\n    <head>\n        <meta charset=\"utf-8\">\n    </head>\n    <body>\n        <p>This is a test for special case-handling of attribute and style properties. (#2988).</p>\n        <p>Open your browser's Developer Console and follow these steps:</p>\n        <ol>\n            <li>Check the background color of the \"foo\" below.</li>\n            <ul>\n                <li>If it is light green, it is correct. The style has been updated properly.</li>\n                <li>If it is red or yellow, the style has not been updated properly.</li>\n            </ul>\n            <li>Check the logs displayed in the console.</li>\n            <ul>\n                <li>If the attribute has been updated correctly, you should see the following message: \"If you see this message, the update process is correct.\"</li>\n                <li>If \"null\" is displayed, the attribute has not been updated properly.</li>\n            </ul>\n        </ol>\n\n        <div id=\"root\" style=\"background-color: red;\"></div>\n        <script src=\"../../../mithril.js\"></script>\n        <script>\n            // data-*** is NOT case-sensitive\n            // style properties have two cases (camelCase and dash-case)\n            var a = m(\"div#a\", {\"data-sampleId\": \"If you see this message, something is wrong.\", style: {backgroundColor: \"yellow\"}}, \"foo\")\n            var b = m(\"div#a\", {\"data-sampleid\": \"If you see this message, the update process is correct.\", style: {\"background-color\": \"lightgreen\"}}, \"foo\")\n\n            // background color is yellow\n            m.render(document.getElementById(\"root\"), a)\n\n            // background color is lightgreen?\n            m.render(document.getElementById(\"root\"), b)\n\n            // data-sampleid is \"If you see this message, the update process is correct.\"?\n            console.log(document.querySelector(\"#a\").getAttribute(\"data-sampleid\"))\n        </script>\n    </body>\n</html>\n"
  },
  {
    "path": "render/tests/manual/iframe.html",
    "content": "<!DOCTYPE html>\n<html>\n    <head>\n        <meta charset=\"utf-8\">\n    </head>\n    <body>\n        <div id=\"root\"></div>\n        <script src=\"../../../mithril.js\"></script>\n        <script>\n            var count = 0\n\n            var Button = {\n                view: function() {\n                    return m(\n                        \"button\",\n                        {onclick: function() { count += 1 }},\n                        \"Inside the iframe: \" + count)\n                }\n            }\n\n            m.mount(document.getElementById(\"root\"), Button)\n        </script>\n    </body>\n</html>\n"
  },
  {
    "path": "render/tests/manual/index.html",
    "content": "<html>\n    <body>\n        Various parent website content.\n        There should be a clickable button below, which is inside an iframe containing a Mithril.js app:\n        <div>\n            <iframe src=\"./iframe.html\"></iframe>\n        </div>\n    </body>\n</html>\n"
  },
  {
    "path": "render/tests/manual/minlength-input.html",
    "content": "<!DOCTYPE html>\n<html>\n    <head>\n        <meta charset=\"utf-8\">\n    </head>\n    <body>\n        This is minlength validity test (#2256).\n        Open your browser's Developer Console and follow these steps:\n        <ol>\n            <li>Type any (1 or 2) characters in the input field.</li>\n            <li>Click “submit.”</li>\n            <li>Click “submit” again.</li>\n            <li>Check the logs displayed in the console.</li>\n        </ol>\n\n        <div id=\"root\"></div>\n        <script src=\"../../../mithril.js\"></script>\n        <script>\n            let input, value;\n\n            m.mount(document.getElementById(\"root\"), {\n                view: () => [\n                    input = m(\"input[type=text]\", {\n                        value,\n                        minLength: 4,\n                        required: true,\n                        oninput(e) {\n                            value = e.target.value;\n                            check();\n                        }\n                    }),\n                    m(\"button\", {\n                        onclick(e) {\n                            console.log(\"click\");\n                            check();\n                        }\n                    }, \"submit\")\n                ],\n            });\n\n            function check() {\n                console.log(`tooShort: ${input.dom.validity.tooShort}`,\n                    `valueMissing: ${input.dom.validity.valueMissing}`,\n                    `checkValidity: ${input.dom.checkValidity()}`\n                );\n            }\n        </script>\n    </body>\n</html>\n"
  },
  {
    "path": "render/tests/manual/minlength-textarea.html",
    "content": "<!DOCTYPE html>\n<html>\n    <head>\n        <meta charset=\"utf-8\">\n    </head>\n    <body>\n        This is minlength validity test (#2256).\n        Open your browser's Developer Console and follow these steps:\n        <ol>\n            <li>Type any (1 or 2) characters in the textarea field.</li>\n            <li>Click “submit.”</li>\n            <li>Click “submit” again.</li>\n            <li>Check the logs displayed in the console.</li>\n        </ol>\n\n        <div id=\"root\"></div>\n        <script src=\"../../../mithril.js\"></script>\n        <script>\n            let input, value;\n\n            m.mount(document.getElementById(\"root\"), {\n                view: () => [\n                    input = m(\"textarea\", {\n                        value,\n                        minLength: 4,\n                        required: true,\n                        oninput(e) {\n                            value = e.target.value;\n                            check();\n                        }\n                    }),\n                    m(\"button\", {\n                        onclick(e) {\n                            console.log(\"click\");\n                            check();\n                        }\n                    }, \"submit\")\n                ],\n            });\n\n            function check() {\n                console.log(`tooShort: ${input.dom.validity.tooShort}`,\n                    `valueMissing: ${input.dom.validity.valueMissing}`,\n                    `checkValidity: ${input.dom.checkValidity()}`\n                );\n            }\n        </script>\n    </body>\n</html>\n"
  },
  {
    "path": "render/tests/test-attributes.js",
    "content": "\"use strict\"\n\nvar o = require(\"ospec\")\nvar domMock = require(\"../../test-utils/domMock\")\nvar vdom = require(\"../../render/render\")\nvar m = require(\"../../render/hyperscript\")\nvar trust = require(\"../../render/trust\")\n\no.spec(\"attributes\", function() {\n\tvar $window, root, render\n\to.beforeEach(function() {\n\t\t$window = domMock()\n\t\troot = $window.document.body\n\t\trender = vdom($window)\n\t})\n\to.spec(\"basics\", function() {\n\t\to(\"works (create/update/remove)\", function() {\n\n\t\t\tvar a = m(\"div\")\n\t\t\tvar b = m(\"div\", {id: \"test\"})\n\t\t\tvar c = m(\"div\")\n\n\t\t\trender(root, a);\n\n\t\t\to(a.dom.hasAttribute(\"id\")).equals(false)\n\n\t\t\trender(root, b);\n\n\t\t\to(b.dom.getAttribute(\"id\")).equals(\"test\")\n\n\t\t\trender(root, c);\n\n\t\t\to(c.dom.hasAttribute(\"id\")).equals(false)\n\t\t})\n\t\to(\"undefined attr is equivalent to a lack of attr\", function() {\n\t\t\tvar a = m(\"div\", {id: undefined})\n\t\t\tvar b = m(\"div\", {id: \"test\"})\n\t\t\tvar c = m(\"div\", {id: undefined})\n\n\t\t\trender(root, a);\n\n\t\t\to(a.dom.hasAttribute(\"id\")).equals(false)\n\n\t\t\trender(root, b);\n\n\t\t\to(b.dom.hasAttribute(\"id\")).equals(true)\n\t\t\to(b.dom.getAttribute(\"id\")).equals(\"test\")\n\n\t\t\t// #1804\n\t\t\trender(root, c);\n\n\t\t\to(c.dom.hasAttribute(\"id\")).equals(false)\n\t\t})\n\t})\n\to.spec(\"customElements\", function(){\n\n\t\to(\"when vnode is customElement without property, custom setAttribute called\", function(){\n\t\t\tvar f = $window.document.createElement\n\t\t\tvar spies = []\n\n\t\t\t$window.document.createElement = function(tag, is){\n\t\t\t\tvar el = f(tag, is)\n\t\t\t\tvar spy = o.spy(el.setAttribute)\n\t\t\t\tel.setAttribute = spy\n\t\t\t\tspies.push(spy)\n\t\t\t\tspy.elem = el\n\t\t\t\treturn el\n\t\t\t}\n\n\t\t\trender(root, [\n\t\t\t\tm(\"input\", {value: \"hello\"}),\n\t\t\t\tm(\"input\", {value: \"hello\"}),\n\t\t\t\tm(\"input\", {value: \"hello\"}),\n\t\t\t\tm(\"custom-element\", {custom: \"x\"}),\n\t\t\t\tm(\"input\", {is: \"something-special\", custom: \"x\"}),\n\t\t\t\tm(\"custom-element\", {is: \"something-special\", custom: \"x\"})\n\t\t\t])\n\n\t\t\to(spies[1].callCount).equals(0)\n\t\t\to(spies[0].callCount).equals(0)\n\t\t\to(spies[2].callCount).equals(0)\n\t\t\to(spies[3].calls).deepEquals([{this: spies[3].elem, args: [\"custom\", \"x\"]}])\n\t\t\to(spies[4].calls).deepEquals([{this: spies[4].elem, args: [\"is\", \"something-special\"]}, {this: spies[4].elem, args: [\"custom\", \"x\"]}])\n\t\t\to(spies[5].calls).deepEquals([{this: spies[5].elem, args: [\"is\", \"something-special\"]}, {this: spies[5].elem, args: [\"custom\", \"x\"]}])\n\t\t})\n\n\t\to(\"when vnode is customElement with property, custom setAttribute not called\", function(){\n\t\t\tvar f = $window.document.createElement\n\t\t\tvar spies = []\n\t\t\tvar getters = []\n\t\t\tvar setters = []\n\n\t\t\t$window.document.createElement = function(tag, is){\n\t\t\t\tvar el = f(tag, is)\n\t\t\t\tvar spy = o.spy(el.setAttribute)\n\t\t\t\tel.setAttribute = spy\n\t\t\t\tspies.push(spy)\n\t\t\t\tspy.elem = el\n\t\t\t\tif (tag === \"custom-element\" || is && is.is === \"something-special\") {\n\t\t\t\t\tvar custom = \"foo\"\n\t\t\t\t\tvar getter, setter\n\t\t\t\t\tObject.defineProperty(el, \"custom\", {\n\t\t\t\t\t\tconfigurable: true,\n\t\t\t\t\t\tenumerable: true,\n\t\t\t\t\t\tget: getter = o.spy(function () { return custom }),\n\t\t\t\t\t\tset: setter = o.spy(function (value) { custom = value })\n\t\t\t\t\t})\n\t\t\t\t\tgetters.push(getter)\n\t\t\t\t\tsetters.push(setter)\n\t\t\t\t}\n\t\t\t\treturn el\n\t\t\t}\n\n\t\t\trender(root, [\n\t\t\t\tm(\"input\", {value: \"hello\"}),\n\t\t\t\tm(\"input\", {value: \"hello\"}),\n\t\t\t\tm(\"input\", {value: \"hello\"}),\n\t\t\t\tm(\"custom-element\", {custom: \"x\"}),\n\t\t\t\tm(\"input\", {is: \"something-special\", custom: \"x\"}),\n\t\t\t\tm(\"custom-element\", {is: \"something-special\", custom: \"x\"})\n\t\t\t])\n\n\t\t\to(spies[0].callCount).equals(0)\n\t\t\to(spies[1].callCount).equals(0)\n\t\t\to(spies[2].callCount).equals(0)\n\t\t\to(spies[3].callCount).equals(0)\n\t\t\to(spies[4].callCount).equals(1) // setAttribute(\"is\", \"something-special\") is called\n\t\t\to(spies[5].callCount).equals(1) // setAttribute(\"is\", \"something-special\") is called\n\t\t\to(getters[0].callCount).equals(0)\n\t\t\to(getters[1].callCount).equals(0)\n\t\t\to(getters[2].callCount).equals(0)\n\t\t\to(setters[0].calls).deepEquals([{this: spies[3].elem, args: [\"x\"]}])\n\t\t\to(setters[1].calls).deepEquals([{this: spies[4].elem, args: [\"x\"]}])\n\t\t\to(setters[2].calls).deepEquals([{this: spies[5].elem, args: [\"x\"]}])\n\t\t})\n\n\t})\n\to.spec(\"input readonly\", function() {\n\t\to(\"when input readonly is true, attribute is present\", function() {\n\t\t\tvar a = m(\"input\", {readonly: true})\n\n\t\t\trender(root, a)\n\n\t\t\to(a.dom.attributes[\"readonly\"].value).equals(\"\")\n\t\t})\n\t\to(\"when input readonly is false, attribute is not present\", function() {\n\t\t\tvar a = m(\"input\", {readonly: false})\n\n\t\t\trender(root, a)\n\n\t\t\to(a.dom.attributes[\"readonly\"]).equals(undefined)\n\t\t})\n\t})\n\to.spec(\"input checked\", function() {\n\t\to(\"when input checked is true, attribute is not present\", function() {\n\t\t\tvar a = m(\"input\", {checked: true})\n\n\t\t\trender(root, a)\n\n\t\t\to(a.dom.checked).equals(true)\n\t\t\to(a.dom.attributes[\"checked\"]).equals(undefined)\n\t\t})\n\t\to(\"when input checked is false, attribute is not present\", function() {\n\t\t\tvar a = m(\"input\", {checked: false})\n\n\t\t\trender(root, a)\n\n\t\t\to(a.dom.checked).equals(false)\n\t\t\to(a.dom.attributes[\"checked\"]).equals(undefined)\n\t\t})\n\t\to(\"after input checked is changed by 3rd party, it can still be changed by render\", function() {\n\t\t\tvar a = m(\"input\", {checked: false})\n\t\t\tvar b = m(\"input\", {checked: true})\n\n\t\t\trender(root, a)\n\n\t\t\ta.dom.checked = true //setting the javascript property makes the value no longer track the state of the attribute\n\t\t\ta.dom.checked = false\n\n\t\t\trender(root, b)\n\n\t\t\to(a.dom.checked).equals(true)\n\t\t\to(a.dom.attributes[\"checked\"]).equals(undefined)\n\t\t})\n\t})\n\to.spec(\"input.value\", function() {\n\t\to(\"can be set as text\", function() {\n\t\t\tvar a = m(\"input\", {value: \"test\"})\n\n\t\t\trender(root, a);\n\n\t\t\to(a.dom.value).equals(\"test\")\n\t\t})\n\t\to(\"a lack of attribute removes `value`\", function() {\n\t\t\tvar a = m(\"input\")\n\t\t\tvar b = m(\"input\", {value: \"test\"})\n\t\t\tvar c = m(\"input\")\n\n\t\t\trender(root, a)\n\n\t\t\to(a.dom.value).equals(\"\")\n\n\t\t\trender(root, b)\n\n\t\t\to(a.dom.value).equals(\"test\")\n\n\t\t\t// https://github.com/MithrilJS/mithril.js/issues/1804#issuecomment-304521235\n\t\t\trender(root, c)\n\n\t\t\to(a.dom.value).equals(\"\")\n\t\t})\n\t\to(\"can be set as number\", function() {\n\t\t\tvar a = m(\"input\", {value: 1})\n\n\t\t\trender(root, a);\n\n\t\t\to(a.dom.value).equals(\"1\")\n\t\t})\n\t\to(\"null becomes the empty string\", function() {\n\t\t\tvar a = m(\"input\", {value: null})\n\t\t\tvar b = m(\"input\", {value: \"test\"})\n\t\t\tvar c = m(\"input\", {value: null})\n\n\t\t\trender(root, a);\n\n\t\t\to(a.dom.value).equals(\"\")\n\t\t\to(a.dom.getAttribute(\"value\")).equals(null)\n\n\t\t\trender(root, b);\n\n\t\t\to(b.dom.value).equals(\"test\")\n\t\t\to(b.dom.getAttribute(\"value\")).equals(null)\n\n\t\t\trender(root, c);\n\n\t\t\to(c.dom.value).equals(\"\")\n\t\t\to(c.dom.getAttribute(\"value\")).equals(null)\n\t\t})\n\t\to(\"'' and 0 are different values\", function() {\n\t\t\tvar a = m(\"input\", {value: 0})\n\t\t\tvar b = m(\"input\", {value: \"\"})\n\t\t\tvar c = m(\"input\", {value: 0})\n\n\t\t\trender(root, a);\n\n\t\t\to(a.dom.value).equals(\"0\")\n\n\t\t\trender(root, b);\n\n\t\t\to(b.dom.value).equals(\"\")\n\n\t\t\t// #1595 redux\n\t\t\trender(root, c);\n\n\t\t\to(c.dom.value).equals(\"0\")\n\t\t})\n\t\to(\"isn't set when equivalent to the previous value and focused\", function() {\n\t\t\tvar $window = domMock({spy: o.spy})\n\t\t\tvar root = $window.document.body\n\t\t\tvar render = vdom($window)\n\n\t\t\tvar a =m(\"input\")\n\t\t\tvar b = m(\"input\", {value: \"1\"})\n\t\t\tvar c = m(\"input\", {value: \"1\"})\n\t\t\tvar d = m(\"input\", {value: 1})\n\t\t\tvar e = m(\"input\", {value: 2})\n\n\t\t\trender(root, a)\n\t\t\tvar spies = $window.__getSpies(a.dom)\n\t\t\ta.dom.focus()\n\n\t\t\to(spies.valueSetter.callCount).equals(0)\n\n\t\t\trender(root, b)\n\n\t\t\to(b.dom.value).equals(\"1\")\n\t\t\to(spies.valueSetter.callCount).equals(1)\n\n\t\t\trender(root, c)\n\n\t\t\to(c.dom.value).equals(\"1\")\n\t\t\to(spies.valueSetter.callCount).equals(1)\n\n\t\t\trender(root, d)\n\n\t\t\to(d.dom.value).equals(\"1\")\n\t\t\to(spies.valueSetter.callCount).equals(1)\n\n\t\t\trender(root, e)\n\n\t\t\to(d.dom.value).equals(\"2\")\n\t\t\to(spies.valueSetter.callCount).equals(2)\n\t\t})\n\t})\n\to.spec(\"input.type\", function() {\n\t\to(\"the input.type setter is never used\", function() {\n\t\t\tvar $window = domMock({spy: o.spy})\n\t\t\tvar root = $window.document.body\n\t\t\tvar render = vdom($window)\n\n\t\t\tvar a = m(\"input\", {type: \"radio\"})\n\t\t\tvar b = m(\"input\", {type: \"text\"})\n\t\t\tvar c = m(\"input\")\n\n\t\t\trender(root, a)\n\t\t\tvar spies = $window.__getSpies(a.dom)\n\n\t\t\to(spies.typeSetter.callCount).equals(0)\n\t\t\to(a.dom.getAttribute(\"type\")).equals(\"radio\")\n\n\t\t\trender(root, b)\n\n\t\t\to(spies.typeSetter.callCount).equals(0)\n\t\t\to(b.dom.getAttribute(\"type\")).equals(\"text\")\n\n\t\t\trender(root, c)\n\n\t\t\to(spies.typeSetter.callCount).equals(0)\n\t\t\to(c.dom.hasAttribute(\"type\")).equals(false)\n\t\t})\n\t})\n\to.spec(\"textarea.value\", function() {\n\t\to(\"can be removed by not passing a value\", function() {\n\t\t\tvar a = m(\"textarea\", {value:\"x\"})\n\t\t\tvar b = m(\"textarea\")\n\n\t\t\trender(root, a)\n\n\t\t\to(a.dom.value).equals(\"x\")\n\n\t\t\t// https://github.com/MithrilJS/mithril.js/issues/1804#issuecomment-304521235\n\t\t\trender(root, b)\n\n\t\t\to(b.dom.value).equals(\"\")\n\t\t})\n\t\to(\"isn't set when equivalent to the previous value and focused\", function() {\n\t\t\tvar $window = domMock({spy: o.spy})\n\t\t\tvar root = $window.document.body\n\t\t\tvar render = vdom($window)\n\n\t\t\tvar a = m(\"textarea\")\n\t\t\tvar b = m(\"textarea\", {value: \"1\"})\n\t\t\tvar c = m(\"textarea\", {value: \"1\"})\n\t\t\tvar d = m(\"textarea\", {value: 1})\n\t\t\tvar e = m(\"textarea\", {value: 2})\n\n\t\t\trender(root, a)\n\t\t\tvar spies = $window.__getSpies(a.dom)\n\t\t\ta.dom.focus()\n\n\t\t\to(spies.valueSetter.callCount).equals(0)\n\n\t\t\trender(root, b)\n\n\t\t\to(b.dom.value).equals(\"1\")\n\t\t\to(spies.valueSetter.callCount).equals(1)\n\n\t\t\trender(root, c)\n\n\t\t\to(c.dom.value).equals(\"1\")\n\t\t\to(spies.valueSetter.callCount).equals(1)\n\n\t\t\trender(root, d)\n\n\t\t\to(d.dom.value).equals(\"1\")\n\t\t\to(spies.valueSetter.callCount).equals(1)\n\n\t\t\trender(root, e)\n\n\t\t\to(d.dom.value).equals(\"2\")\n\t\t\to(spies.valueSetter.callCount).equals(2)\n\t\t})\n\t})\n\to.spec(\"link href\", function() {\n\t\to(\"when link href is true, attribute is present\", function() {\n\t\t\tvar a = m(\"a\", {href: true})\n\n\t\t\trender(root, a)\n\n\t\t\to(a.dom.attributes[\"href\"]).notEquals(undefined)\n\t\t})\n\t\to(\"when link href is false, attribute is not present\", function() {\n\t\t\tvar a = m(\"a\", {href: false})\n\n\t\t\trender(root, a)\n\n\t\t\to(a.dom.attributes[\"href\"]).equals(undefined)\n\t\t})\n\t})\n\to.spec(\"canvas width and height\", function() {\n\t\to(\"uses attribute API\", function() {\n\t\t\tvar canvas = m(\"canvas\", {width: \"100%\"})\n\n\t\t\trender(root, canvas)\n\n\t\t\to(canvas.dom.attributes[\"width\"].value).equals(\"100%\")\n\t\t\to(canvas.dom.width).equals(100)\n\t\t})\n\t})\n\to.spec(\"svg\", function() {\n\t\to(\"when className is specified then it should be added as a class\", function() {\n\t\t\tvar a = m(\"svg\", {className: \"test\"})\n\n\t\t\trender(root, a);\n\n\t\t\to(a.dom.attributes[\"class\"].value).equals(\"test\")\n\t\t})\n\t\t/* eslint-disable no-script-url */\n\t\to(\"handles xlink:href\", function() {\n\t\t\tvar vnode = m(\"svg\", {ns: \"http://www.w3.org/2000/svg\"},\n\t\t\t\tm(\"a\", {ns: \"http://www.w3.org/2000/svg\", \"xlink:href\": \"javascript:;\"})\n\t\t\t)\n\t\t\trender(root, vnode)\n\n\t\t\to(vnode.dom.nodeName).equals(\"svg\")\n\t\t\to(vnode.dom.firstChild.attributes[\"href\"].value).equals(\"javascript:;\")\n\t\t\to(vnode.dom.firstChild.attributes[\"href\"].namespaceURI).equals(\"http://www.w3.org/1999/xlink\")\n\n\t\t\tvnode = m(\"svg\", {ns: \"http://www.w3.org/2000/svg\"},\n\t\t\t\tm(\"a\", {ns: \"http://www.w3.org/2000/svg\"})\n\t\t\t)\n\t\t\trender(root, vnode)\n\n\t\t\to(vnode.dom.nodeName).equals(\"svg\")\n\t\t\to(\"href\" in vnode.dom.firstChild.attributes).equals(false)\n\t\t})\n\t\t/* eslint-enable no-script-url */\n\t})\n\to.spec(\"option.value\", function() {\n\t\to(\"can be set as text\", function() {\n\t\t\tvar a = m(\"option\", {value: \"test\"})\n\n\t\t\trender(root, a);\n\n\t\t\to(a.dom.value).equals(\"test\")\n\t\t})\n\t\to(\"can be set as number\", function() {\n\t\t\tvar a = m(\"option\", {value: 1})\n\n\t\t\trender(root, a);\n\n\t\t\to(a.dom.value).equals(\"1\")\n\t\t})\n\t\to(\"null removes the attribute\", function() {\n\t\t\tvar a = m(\"option\", {value: null})\n\t\t\tvar b = m(\"option\", {value: \"test\"})\n\t\t\tvar c = m(\"option\", {value: null})\n\n\t\t\trender(root, a);\n\n\t\t\to(a.dom.value).equals(\"\")\n\t\t\to(a.dom.hasAttribute(\"value\")).equals(false)\n\n\t\t\trender(root, b);\n\n\t\t\to(b.dom.value).equals(\"test\")\n\t\t\to(b.dom.getAttribute(\"value\")).equals(\"test\")\n\n\t\t\trender(root, c);\n\n\t\t\to(c.dom.value).equals(\"\")\n\t\t\to(c.dom.hasAttribute(\"value\")).equals(false)\n\t\t})\n\t\to(\"'' and 0 are different values\", function() {\n\t\t\tvar a = m(\"option\", {value: 0}, \"\")\n\t\t\tvar b = m(\"option\", {value: \"\"}, \"\")\n\t\t\tvar c = m(\"option\", {value: 0}, \"\")\n\n\t\t\trender(root, a);\n\n\t\t\to(a.dom.value).equals(\"0\")\n\n\t\t\trender(root, b);\n\n\t\t\to(a.dom.value).equals(\"\")\n\n\t\t\t// #1595 redux\n\t\t\trender(root, c);\n\n\t\t\to(c.dom.value).equals(\"0\")\n\t\t})\n\t\to(\"isn't set when equivalent to the previous value\", function() {\n\t\t\tvar $window = domMock({spy: o.spy})\n\t\t\tvar root = $window.document.body\n\t\t\tvar render = vdom($window)\n\n\t\t\tvar a = m(\"option\")\n\t\t\tvar b = m(\"option\", {value: \"1\"})\n\t\t\tvar c = m(\"option\", {value: \"1\"})\n\t\t\tvar d = m(\"option\", {value: 1})\n\t\t\tvar e = m(\"option\", {value: 2})\n\n\t\t\trender(root, a)\n\t\t\tvar spies = $window.__getSpies(a.dom)\n\n\t\t\to(spies.valueSetter.callCount).equals(0)\n\n\t\t\trender(root, b)\n\n\t\t\to(b.dom.value).equals(\"1\")\n\t\t\to(spies.valueSetter.callCount).equals(1)\n\n\t\t\trender(root, c)\n\n\t\t\to(c.dom.value).equals(\"1\")\n\t\t\to(spies.valueSetter.callCount).equals(1)\n\n\t\t\trender(root, d)\n\n\t\t\to(d.dom.value).equals(\"1\")\n\t\t\to(spies.valueSetter.callCount).equals(1)\n\n\t\t\trender(root, e)\n\n\t\t\to(d.dom.value).equals(\"2\")\n\t\t\to(spies.valueSetter.callCount).equals(2)\n\t\t})\n\t})\n\to.spec(\"select.value\", function() {\n\t\tfunction makeSelect(value) {\n\t\t\tvar attrs = (arguments.length === 0) ? {} : {value: value}\n\t\t\treturn m(\"select\", attrs,\n\t\t\t\tm(\"option\", {value: \"1\"}),\n\t\t\t\tm(\"option\", {value: \"2\"}),\n\t\t\t\tm(\"option\", {value: \"a\"}),\n\t\t\t\tm(\"option\", {value: \"0\"}),\n\t\t\t\tm(\"option\", {value: \"\"})\n\t\t\t)\n\t\t}\n\t\to(\"render select options with `selected` (#1916)\", function() {\n\t\t\tvar select1 = m(\"select\", [m(\"option\"), m(\"option\")])\n\t\t\tvar select2 = m(\"select\", [m(\"option\", {selected: false}), m(\"option\", {selected: true})])\n\t\t\tvar select3 = m(\"select\", [m(\"option\", {selected: false}), m(\"option\", {selected: true})])\n\t\t\tvar select4 = m(\"select\", [m(\"option\", {selected: false}), m(\"option\", {selected: true})])\n\t\t\tvar select5 = m(\"select\", [m(\"option\", {selected: true}), m(\"option\", {selected: false})])\n\t\t\tvar select6 = m(\"select\", [m(\"option\", {selected: true}), m(\"option\", {selected: false})])\n\t\t\tvar select7 = m(\"select\", [m(\"option\", {selected: true}), m(\"option\", {selected: false})])\n\n\t\t\t// selected: [undefined,undefined]\n\t\t\t// DomMock can't set/read `option.selected` when the option doesn't have a `select` parent,\n\t\t\t// so call render() without `option.selected` first.\n\t\t\trender(root, select1)\n\t\t\tvar el = root.firstChild\n\t\t\to(el.selectedIndex).equals(0)\n\t\t\to(el.childNodes[0].selected).equals(true)\n\t\t\to(el.childNodes[1].selected).equals(false)\n\n\t\t\t// selected: [undefined,undefined] -> [false,true] (changed -> update by render)\n\t\t\trender(root, select2)\n\t\t\to(el.selectedIndex).equals(1)\n\t\t\to(el.childNodes[0].selected).equals(false)\n\t\t\to(el.childNodes[1].selected).equals(true)\n\n\t\t\t// selected: [false,true] -> [false,true] (unchanged, not focused -> not update by render)\n\t\t\tel.selectedIndex = 0 // set 0 without render\n\t\t\trender(root, select3)\n\t\t\to(el.selectedIndex).equals(0) // unchanged\n\t\t\to(el.childNodes[0].selected).equals(true)\n\t\t\to(el.childNodes[1].selected).equals(false)\n\n\t\t\t// selected: [false,true] -> [false,true] (unchanged, focused -> update by render)\n\t\t\tel.focus()\n\t\t\trender(root, select4)\n\t\t\to(el.selectedIndex).equals(1)\n\t\t\to(el.childNodes[0].selected).equals(false)\n\t\t\to(el.childNodes[1].selected).equals(true)\n\n\t\t\t// selected: [false,true] -> [true,false] (changed -> update by render)\n\t\t\trender(root, select5)\n\t\t\to(el.selectedIndex).equals(0)\n\t\t\to(el.childNodes[0].selected).equals(true)\n\t\t\to(el.childNodes[1].selected).equals(false)\n\t\t\t\n\t\t\t// selected: [true,false] -> [true,false] (unchanged, not focused -> not update by render)\n\t\t\tel.selectedIndex = 1 // set 1 without render\n\t\t\troot.focus()\n\t\t\trender(root, select6)\n\t\t\to(el.selectedIndex).equals(1) // unchanged\n\t\t\to(el.childNodes[0].selected).equals(false)\n\t\t\to(el.childNodes[1].selected).equals(true)\n\n\t\t\t// selected: [true,false] -> [true,false] (unchanged, focused -> update by render)\n\t\t\tel.focus()\n\t\t\trender(root, select7)\n\t\t\to(el.selectedIndex).equals(0)\n\t\t\to(el.childNodes[0].selected).equals(true)\n\t\t\to(el.childNodes[1].selected).equals(false)\n\t\t})\n\t\to(\"can be set as text\", function() {\n\t\t\tvar a = makeSelect()\n\t\t\tvar b = makeSelect(\"2\")\n\t\t\tvar c = makeSelect(\"a\")\n\n\t\t\trender(root, a)\n\n\t\t\to(a.dom.value).equals(\"1\")\n\t\t\to(a.dom.selectedIndex).equals(0)\n\n\t\t\trender(root, b)\n\n\t\t\to(b.dom.value).equals(\"2\")\n\t\t\to(b.dom.selectedIndex).equals(1)\n\n\t\t\trender(root, c)\n\n\t\t\to(c.dom.value).equals(\"a\")\n\t\t\to(c.dom.selectedIndex).equals(2)\n\t\t})\n\t\to(\"setting null unsets the value\", function() {\n\t\t\tvar a = makeSelect(null)\n\n\t\t\trender(root, a)\n\n\t\t\to(a.dom.value).equals(\"\")\n\t\t\to(a.dom.selectedIndex).equals(-1)\n\t\t})\n\t\to(\"values are type converted\", function() {\n\t\t\tvar a = makeSelect(1)\n\t\t\tvar b = makeSelect(2)\n\n\t\t\trender(root, a)\n\n\t\t\to(a.dom.value).equals(\"1\")\n\t\t\to(a.dom.selectedIndex).equals(0)\n\n\t\t\trender(root, b)\n\n\t\t\to(b.dom.value).equals(\"2\")\n\t\t\to(b.dom.selectedIndex).equals(1)\n\t\t})\n\t\to(\"'' and 0 are different values when focused\", function() {\n\t\t\tvar a = makeSelect(\"\")\n\t\t\tvar b = makeSelect(0)\n\n\t\t\trender(root, a)\n\t\t\ta.dom.focus()\n\n\t\t\to(a.dom.value).equals(\"\")\n\n\t\t\t// #1595 redux\n\t\t\trender(root, b)\n\n\t\t\to(b.dom.value).equals(\"0\")\n\t\t})\n\t\to(\"'' and null are different values when focused\", function() {\n\t\t\tvar a = makeSelect(\"\")\n\t\t\tvar b = makeSelect(null)\n\t\t\tvar c = makeSelect(\"\")\n\n\t\t\trender(root, a)\n\t\t\ta.dom.focus()\n\n\t\t\to(a.dom.value).equals(\"\")\n\t\t\to(a.dom.selectedIndex).equals(4)\n\n\t\t\trender(root, b)\n\n\t\t\to(b.dom.value).equals(\"\")\n\t\t\to(b.dom.selectedIndex).equals(-1)\n\n\t\t\trender(root, c)\n\n\t\t\to(c.dom.value).equals(\"\")\n\t\t\to(c.dom.selectedIndex).equals(4)\n\t\t})\n\t\to(\"updates with the same value do not re-set the attribute if the select has focus\", function() {\n\t\t\tvar $window = domMock({spy: o.spy})\n\t\t\tvar root = $window.document.body\n\t\t\tvar render = vdom($window)\n\n\t\t\tvar a = makeSelect()\n\t\t\tvar b = makeSelect(\"1\")\n\t\t\tvar c = makeSelect(1)\n\t\t\tvar d = makeSelect(\"2\")\n\n\t\t\trender(root, a)\n\t\t\tvar spies = $window.__getSpies(a.dom)\n\t\t\ta.dom.focus()\n\n\t\t\to(spies.valueSetter.callCount).equals(0)\n\t\t\to(a.dom.value).equals(\"1\")\n\n\t\t\trender(root, b)\n\n\t\t\to(spies.valueSetter.callCount).equals(0)\n\t\t\to(b.dom.value).equals(\"1\")\n\n\t\t\trender(root, c)\n\n\t\t\to(spies.valueSetter.callCount).equals(0)\n\t\t\to(c.dom.value).equals(\"1\")\n\n\t\t\trender(root, d)\n\n\t\t\to(spies.valueSetter.callCount).equals(1)\n\t\t\to(d.dom.value).equals(\"2\")\n\t\t})\n\t})\n\to.spec(\"contenteditable throws on untrusted children\", function() {\n\t\to(\"including elements\", function() {\n\t\t\tvar div = m(\"div\", {contenteditable: true}, m(\"script\", {src: \"http://evil.com\"}))\n\t\t\tvar succeeded = false\n\n\t\t\ttry {\n\t\t\t\trender(root, div)\n\n\t\t\t\tsucceeded = true\n\t\t\t}\n\t\t\tcatch(e){/* ignore */}\n\n\t\t\to(succeeded).equals(false)\n\t\t})\n\t\to(\"tolerating empty children\", function() {\n\t\t\tvar div = m(\"div\", {contenteditable: true})\n\t\t\tvar succeeded = false\n\n\t\t\ttry {\n\t\t\t\trender(root, div)\n\n\t\t\t\tsucceeded = true\n\t\t\t}\n\t\t\tcatch(e){/* ignore */}\n\n\t\t\to(succeeded).equals(true)\n\t\t})\n\t\to(\"tolerating trusted content\", function() {\n\t\t\tvar div = m(\"div\", {contenteditable: true}, trust(\"<a></a>\"))\n\t\t\tvar succeeded = false\n\n\t\t\ttry {\n\t\t\t\trender(root, div)\n\n\t\t\t\tsucceeded = true\n\t\t\t}\n\t\t\tcatch(e){/* ignore */}\n\n\t\t\to(succeeded).equals(true)\n\t\t})\n\t})\n\to.spec(\"mutate attr object\", function() {\n\t\to(\"warn when reusing attrs object\", function() {\n\t\t\tconst _consoleWarn = console.warn\n\t\t\tconsole.warn = o.spy()\n\n\t\t\tconst attrs = {className: \"on\"}\n\t\t\trender(root, {tag: \"input\", attrs})\n\n\t\t\tattrs.className = \"off\"\n\t\t\trender(root, {tag: \"input\", attrs})\n\n\t\t\to(console.warn.callCount).equals(1)\n\t\t\to(console.warn.args[0]).equals(\"Don't reuse attrs object, use new object for every redraw, this will throw in next major\")\n\n\t\t\tconsole.warn = _consoleWarn\n\t\t})\n\t})\n})\n"
  },
  {
    "path": "render/tests/test-component.js",
    "content": "\"use strict\"\n\nvar o = require(\"ospec\")\nvar components = require(\"../../test-utils/components\")\nvar domMock = require(\"../../test-utils/domMock\")\nvar vdom = require(\"../../render/render\")\nvar m = require(\"../../render/hyperscript\")\n\no.spec(\"component\", function() {\n\tvar $window, root, render\n\to.beforeEach(function() {\n\t\t$window = domMock()\n\t\troot = $window.document.createElement(\"div\")\n\n\t\trender = vdom($window)\n\t})\n\n\tcomponents.forEach(function(cmp){\n\t\to.spec(cmp.kind, function(){\n\t\t\tvar createComponent = cmp.create\n\n\t\t\to.spec(\"basics\", function() {\n\t\t\t\to(\"works\", function() {\n\t\t\t\t\tvar component = createComponent({\n\t\t\t\t\t\tview: function() {\n\t\t\t\t\t\t\treturn m(\"div\", {id: \"a\"}, \"b\")\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\t\t\t\t\tvar node = m(component)\n\n\t\t\t\t\trender(root, node)\n\n\t\t\t\t\to(root.firstChild.nodeName).equals(\"DIV\")\n\t\t\t\t\to(root.firstChild.attributes[\"id\"].value).equals(\"a\")\n\t\t\t\t\to(root.firstChild.firstChild.nodeValue).equals(\"b\")\n\t\t\t\t})\n\t\t\t\to(\"receives arguments\", function() {\n\t\t\t\t\tvar component = createComponent({\n\t\t\t\t\t\tview: function(vnode) {\n\t\t\t\t\t\t\treturn m(\"div\", vnode.attrs, vnode.children)\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\t\t\t\t\tvar node = m(component, {id: \"a\"}, \"b\")\n\n\t\t\t\t\trender(root, node)\n\n\t\t\t\t\to(root.firstChild.nodeName).equals(\"DIV\")\n\t\t\t\t\to(root.firstChild.attributes[\"id\"].value).equals(\"a\")\n\t\t\t\t\to(root.firstChild.firstChild.nodeValue).equals(\"b\")\n\t\t\t\t})\n\t\t\t\to(\"updates\", function() {\n\t\t\t\t\tvar component = createComponent({\n\t\t\t\t\t\tview: function(vnode) {\n\t\t\t\t\t\t\treturn m(\"div\", vnode.attrs, vnode.children)\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\t\t\t\t\trender(root, [m(component, {id: \"a\"}, \"b\")])\n\t\t\t\t\trender(root, [m(component, {id: \"c\"}, \"d\")])\n\n\t\t\t\t\to(root.firstChild.nodeName).equals(\"DIV\")\n\t\t\t\t\to(root.firstChild.attributes[\"id\"].value).equals(\"c\")\n\t\t\t\t\to(root.firstChild.firstChild.nodeValue).equals(\"d\")\n\t\t\t\t})\n\t\t\t\to(\"updates root from null\", function() {\n\t\t\t\t\tvar visible = false\n\t\t\t\t\tvar component = createComponent({\n\t\t\t\t\t\tview: function() {\n\t\t\t\t\t\t\treturn visible ? m(\"div\") : null\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\t\t\t\t\trender(root, m(component))\n\t\t\t\t\tvisible = true\n\t\t\t\t\trender(root, m(component))\n\n\t\t\t\t\to(root.firstChild.nodeName).equals(\"DIV\")\n\t\t\t\t})\n\t\t\t\to(\"updates root from primitive\", function() {\n\t\t\t\t\tvar visible = false\n\t\t\t\t\tvar component = createComponent({\n\t\t\t\t\t\tview: function() {\n\t\t\t\t\t\t\treturn visible ? m(\"div\") : false\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\t\t\t\t\trender(root, m(component))\n\t\t\t\t\tvisible = true\n\t\t\t\t\trender(root, m(component))\n\n\t\t\t\t\to(root.firstChild.nodeName).equals(\"DIV\")\n\t\t\t\t})\n\t\t\t\to(\"updates root to null\", function() {\n\t\t\t\t\tvar visible = true\n\t\t\t\t\tvar component = createComponent({\n\t\t\t\t\t\tview: function() {\n\t\t\t\t\t\t\treturn visible ? m(\"div\") : null\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\t\t\t\t\trender(root, m(component))\n\t\t\t\t\tvisible = false\n\t\t\t\t\trender(root, m(component))\n\n\t\t\t\t\to(root.childNodes.length).equals(0)\n\t\t\t\t})\n\t\t\t\to(\"updates root to primitive\", function() {\n\t\t\t\t\tvar visible = true\n\t\t\t\t\tvar component = createComponent({\n\t\t\t\t\t\tview: function() {\n\t\t\t\t\t\t\treturn visible ? m(\"div\") : false\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\t\t\t\t\trender(root, m(component))\n\t\t\t\t\tvisible = false\n\t\t\t\t\trender(root, m(component))\n\n\t\t\t\t\to(root.childNodes.length).equals(0)\n\t\t\t\t})\n\t\t\t\to(\"updates root from null to null\", function() {\n\t\t\t\t\tvar component = createComponent({\n\t\t\t\t\t\tview: function() {\n\t\t\t\t\t\t\treturn null\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\t\t\t\t\trender(root, m(component))\n\t\t\t\t\trender(root, m(component))\n\n\t\t\t\t\to(root.childNodes.length).equals(0)\n\t\t\t\t})\n\t\t\t\to(\"removes\", function() {\n\t\t\t\t\tvar component = createComponent({\n\t\t\t\t\t\tview: function() {\n\t\t\t\t\t\t\treturn m(\"div\")\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\t\t\t\t\tvar div = m(\"div\", {key: 2})\n\t\t\t\t\trender(root, [m(component, {key: 1}), div])\n\t\t\t\t\trender(root, div)\n\n\t\t\t\t\to(root.childNodes.length).equals(1)\n\t\t\t\t\to(root.firstChild).equals(div.dom)\n\t\t\t\t})\n\t\t\t\to(\"svg works when creating across component boundary\", function() {\n\t\t\t\t\tvar component = createComponent({\n\t\t\t\t\t\tview: function() {\n\t\t\t\t\t\t\treturn m(\"g\")\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\t\t\t\t\trender(root, m(\"svg\", m(component)))\n\n\t\t\t\t\to(root.firstChild.firstChild.namespaceURI).equals(\"http://www.w3.org/2000/svg\")\n\t\t\t\t})\n\t\t\t\to(\"svg works when updating across component boundary\", function() {\n\t\t\t\t\tvar component = createComponent({\n\t\t\t\t\t\tview: function() {\n\t\t\t\t\t\t\treturn m(\"g\")\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\t\t\t\t\trender(root, m(\"svg\", m(component)))\n\t\t\t\t\trender(root, m(\"svg\", m(component)))\n\n\t\t\t\t\to(root.firstChild.firstChild.namespaceURI).equals(\"http://www.w3.org/2000/svg\")\n\t\t\t\t})\n\t\t\t})\n\t\t\to.spec(\"return value\", function() {\n\t\t\t\to(\"can return fragments\", function() {\n\t\t\t\t\tvar component = createComponent({\n\t\t\t\t\t\tview: function() {\n\t\t\t\t\t\t\treturn [\n\t\t\t\t\t\t\t\tm(\"label\"),\n\t\t\t\t\t\t\t\tm(\"input\"),\n\t\t\t\t\t\t\t]\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\t\t\t\t\trender(root, m(component))\n\n\t\t\t\t\to(root.childNodes.length).equals(2)\n\t\t\t\t\to(root.childNodes[0].nodeName).equals(\"LABEL\")\n\t\t\t\t\to(root.childNodes[1].nodeName).equals(\"INPUT\")\n\t\t\t\t})\n\t\t\t\to(\"can return string\", function() {\n\t\t\t\t\tvar component = createComponent({\n\t\t\t\t\t\tview: function() {\n\t\t\t\t\t\t\treturn \"a\"\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\t\t\t\t\trender(root, m(component))\n\n\t\t\t\t\to(root.firstChild.nodeType).equals(3)\n\t\t\t\t\to(root.firstChild.nodeValue).equals(\"a\")\n\t\t\t\t})\n\t\t\t\to(\"can return falsy string\", function() {\n\t\t\t\t\tvar component = createComponent({\n\t\t\t\t\t\tview: function() {\n\t\t\t\t\t\t\treturn \"\"\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\t\t\t\t\trender(root, m(component))\n\n\t\t\t\t\to(root.firstChild.nodeType).equals(3)\n\t\t\t\t\to(root.firstChild.nodeValue).equals(\"\")\n\t\t\t\t})\n\t\t\t\to(\"can return number\", function() {\n\t\t\t\t\tvar component = createComponent({\n\t\t\t\t\t\tview: function() {\n\t\t\t\t\t\t\treturn 1\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\t\t\t\t\trender(root, m(component))\n\n\t\t\t\t\to(root.firstChild.nodeType).equals(3)\n\t\t\t\t\to(root.firstChild.nodeValue).equals(\"1\")\n\t\t\t\t})\n\t\t\t\to(\"can return falsy number\", function() {\n\t\t\t\t\tvar component = createComponent({\n\t\t\t\t\t\tview: function() {\n\t\t\t\t\t\t\treturn 0\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\t\t\t\t\trender(root, m(component))\n\n\t\t\t\t\to(root.firstChild.nodeType).equals(3)\n\t\t\t\t\to(root.firstChild.nodeValue).equals(\"0\")\n\t\t\t\t})\n\t\t\t\to(\"can return `true`\", function() {\n\t\t\t\t\tvar component = createComponent({\n\t\t\t\t\t\tview: function() {\n\t\t\t\t\t\t\treturn true\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\t\t\t\t\trender(root, m(component))\n\n\t\t\t\t\to(root.childNodes.length).equals(0)\n\t\t\t\t})\n\t\t\t\to(\"can return `false`\", function() {\n\t\t\t\t\tvar component = createComponent({\n\t\t\t\t\t\tview: function() {\n\t\t\t\t\t\t\treturn false\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\t\t\t\t\trender(root, m(component))\n\n\t\t\t\t\to(root.childNodes.length).equals(0)\n\t\t\t\t})\n\t\t\t\to(\"can return null\", function() {\n\t\t\t\t\tvar component = createComponent({\n\t\t\t\t\t\tview: function() {\n\t\t\t\t\t\t\treturn null\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\t\t\t\t\trender(root, m(component))\n\n\t\t\t\t\to(root.childNodes.length).equals(0)\n\t\t\t\t})\n\t\t\t\to(\"can return undefined\", function() {\n\t\t\t\t\tvar component = createComponent({\n\t\t\t\t\t\tview: function() {\n\t\t\t\t\t\t\treturn undefined\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\t\t\t\t\trender(root, m(component))\n\n\t\t\t\t\to(root.childNodes.length).equals(0)\n\t\t\t\t})\n\t\t\t\to(\"throws a custom error if it returns itself when created\", function() {\n\t\t\t\t\t// A view that returns its vnode would otherwise trigger an infinite loop\n\t\t\t\t\tvar threw = false\n\t\t\t\t\tvar component = createComponent({\n\t\t\t\t\t\tview: function(vnode) {\n\t\t\t\t\t\t\treturn vnode\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\t\t\t\t\ttry {\n\t\t\t\t\t\trender(root, m(component))\n\t\t\t\t\t}\n\t\t\t\t\tcatch (e) {\n\t\t\t\t\t\tthrew = true\n\t\t\t\t\t\to(e instanceof Error).equals(true)\n\t\t\t\t\t\t// Call stack exception is a RangeError\n\t\t\t\t\t\to(e instanceof RangeError).equals(false)\n\t\t\t\t\t}\n\t\t\t\t\to(threw).equals(true)\n\t\t\t\t})\n\t\t\t\to(\"throws a custom error if it returns itself when updated\", function() {\n\t\t\t\t\t// A view that returns its vnode would otherwise trigger an infinite loop\n\t\t\t\t\tvar threw = false\n\t\t\t\t\tvar init = true\n\t\t\t\t\tvar oninit = o.spy()\n\t\t\t\t\tvar component = createComponent({\n\t\t\t\t\t\toninit: oninit,\n\t\t\t\t\t\tview: function(vnode) {\n\t\t\t\t\t\t\tif (init) return init = false\n\t\t\t\t\t\t\telse return vnode\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\t\t\t\t\trender(root, m(component))\n\n\t\t\t\t\to(root.childNodes.length).equals(0)\n\n\t\t\t\t\ttry {\n\t\t\t\t\t\trender(root, m(component))\n\t\t\t\t\t}\n\t\t\t\t\tcatch (e) {\n\t\t\t\t\t\tthrew = true\n\t\t\t\t\t\to(e instanceof Error).equals(true)\n\t\t\t\t\t\t// Call stack exception is a RangeError\n\t\t\t\t\t\to(e instanceof RangeError).equals(false)\n\t\t\t\t\t}\n\t\t\t\t\to(threw).equals(true)\n\t\t\t\t\to(oninit.callCount).equals(1)\n\t\t\t\t})\n\t\t\t\to(\"can update when returning fragments\", function() {\n\t\t\t\t\tvar component = createComponent({\n\t\t\t\t\t\tview: function() {\n\t\t\t\t\t\t\treturn [\n\t\t\t\t\t\t\t\tm(\"label\"),\n\t\t\t\t\t\t\t\tm(\"input\"),\n\t\t\t\t\t\t\t]\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\t\t\t\t\trender(root, m(component))\n\t\t\t\t\trender(root, m(component))\n\n\t\t\t\t\to(root.childNodes.length).equals(2)\n\t\t\t\t\to(root.childNodes[0].nodeName).equals(\"LABEL\")\n\t\t\t\t\to(root.childNodes[1].nodeName).equals(\"INPUT\")\n\t\t\t\t})\n\t\t\t\to(\"can update when returning primitive\", function() {\n\t\t\t\t\tvar component = createComponent({\n\t\t\t\t\t\tview: function() {\n\t\t\t\t\t\t\treturn \"a\"\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\t\t\t\t\trender(root, m(component))\n\t\t\t\t\trender(root, m(component))\n\n\t\t\t\t\to(root.firstChild.nodeType).equals(3)\n\t\t\t\t\to(root.firstChild.nodeValue).equals(\"a\")\n\t\t\t\t})\n\t\t\t\to(\"can update when returning null\", function() {\n\t\t\t\t\tvar component = createComponent({\n\t\t\t\t\t\tview: function() {\n\t\t\t\t\t\t\treturn null\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\t\t\t\t\trender(root, m(component))\n\t\t\t\t\trender(root, m(component))\n\n\t\t\t\t\to(root.childNodes.length).equals(0)\n\t\t\t\t})\n\t\t\t\to(\"can remove when returning fragments\", function() {\n\t\t\t\t\tvar component = createComponent({\n\t\t\t\t\t\tview: function() {\n\t\t\t\t\t\t\treturn [\n\t\t\t\t\t\t\t\tm(\"label\"),\n\t\t\t\t\t\t\t\tm(\"input\"),\n\t\t\t\t\t\t\t]\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\t\t\t\t\tvar div = m(\"div\", {key: 2})\n\t\t\t\t\trender(root, [m(component, {key: 1}), div])\n\n\t\t\t\t\trender(root, [m(\"div\", {key: 2})])\n\n\t\t\t\t\to(root.childNodes.length).equals(1)\n\t\t\t\t\to(root.firstChild).equals(div.dom)\n\t\t\t\t})\n\t\t\t\to(\"can remove when returning primitive\", function() {\n\t\t\t\t\tvar component = createComponent({\n\t\t\t\t\t\tview: function() {\n\t\t\t\t\t\t\treturn \"a\"\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\t\t\t\t\tvar div = m(\"div\", {key: 2})\n\t\t\t\t\trender(root, [m(component, {key: 1}), div])\n\n\t\t\t\t\trender(root, [m(\"div\", {key: 2})])\n\n\t\t\t\t\to(root.childNodes.length).equals(1)\n\t\t\t\t\to(root.firstChild).equals(div.dom)\n\t\t\t\t})\n\t\t\t})\n\t\t\to.spec(\"lifecycle\", function() {\n\t\t\t\to(\"calls oninit\", function() {\n\t\t\t\t\tvar called = 0\n\t\t\t\t\tvar component = createComponent({\n\t\t\t\t\t\toninit: function(vnode) {\n\t\t\t\t\t\t\tcalled++\n\n\t\t\t\t\t\t\to(vnode.tag).equals(component)\n\t\t\t\t\t\t\to(vnode.dom).equals(undefined)\n\t\t\t\t\t\t\to(root.childNodes.length).equals(0)\n\t\t\t\t\t\t},\n\t\t\t\t\t\tview: function() {\n\t\t\t\t\t\t\treturn m(\"div\", {id: \"a\"}, \"b\")\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\n\t\t\t\t\trender(root, m(component))\n\n\t\t\t\t\to(called).equals(1)\n\t\t\t\t\to(root.firstChild.nodeName).equals(\"DIV\")\n\t\t\t\t\to(root.firstChild.attributes[\"id\"].value).equals(\"a\")\n\t\t\t\t\to(root.firstChild.firstChild.nodeValue).equals(\"b\")\n\t\t\t\t})\n\t\t\t\to(\"calls oninit when returning fragment\", function() {\n\t\t\t\t\tvar called = 0\n\t\t\t\t\tvar component = createComponent({\n\t\t\t\t\t\toninit: function(vnode) {\n\t\t\t\t\t\t\tcalled++\n\n\t\t\t\t\t\t\to(vnode.tag).equals(component)\n\t\t\t\t\t\t\to(vnode.dom).equals(undefined)\n\t\t\t\t\t\t\to(root.childNodes.length).equals(0)\n\t\t\t\t\t\t},\n\t\t\t\t\t\tview: function() {\n\t\t\t\t\t\t\treturn [m(\"div\", {id: \"a\"}, \"b\")]\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\n\t\t\t\t\trender(root, m(component))\n\n\t\t\t\t\to(called).equals(1)\n\t\t\t\t\to(root.firstChild.nodeName).equals(\"DIV\")\n\t\t\t\t\to(root.firstChild.attributes[\"id\"].value).equals(\"a\")\n\t\t\t\t\to(root.firstChild.firstChild.nodeValue).equals(\"b\")\n\t\t\t\t})\n\t\t\t\to(\"calls oninit before view\", function() {\n\t\t\t\t\tvar viewCalled = false\n\t\t\t\t\tvar component = createComponent({\n\t\t\t\t\t\tview: function() {\n\t\t\t\t\t\t\tviewCalled = true\n\t\t\t\t\t\t\treturn m(\"div\", {id: \"a\"}, \"b\")\n\t\t\t\t\t\t},\n\t\t\t\t\t\toninit: function() {\n\t\t\t\t\t\t\to(viewCalled).equals(false)\n\t\t\t\t\t\t},\n\t\t\t\t\t})\n\n\t\t\t\t\trender(root, m(component))\n\t\t\t\t})\n\t\t\t\to(\"does not calls oninit on redraw\", function() {\n\t\t\t\t\tvar init = o.spy()\n\t\t\t\t\tvar component = createComponent({\n\t\t\t\t\t\tview: function() {\n\t\t\t\t\t\t\treturn m(\"div\", {id: \"a\"}, \"b\")\n\t\t\t\t\t\t},\n\t\t\t\t\t\toninit: init,\n\t\t\t\t\t})\n\n\t\t\t\t\tfunction view() {\n\t\t\t\t\t\treturn m(component)\n\t\t\t\t\t}\n\n\t\t\t\t\trender(root, view())\n\t\t\t\t\trender(root, view())\n\n\t\t\t\t\to(init.callCount).equals(1)\n\t\t\t\t})\n\t\t\t\to(\"calls oncreate\", function() {\n\t\t\t\t\tvar called = 0\n\t\t\t\t\tvar component = createComponent({\n\t\t\t\t\t\toncreate: function(vnode) {\n\t\t\t\t\t\t\tcalled++\n\n\t\t\t\t\t\t\to(vnode.dom).notEquals(undefined)\n\t\t\t\t\t\t\to(vnode.dom).equals(root.firstChild)\n\t\t\t\t\t\t\to(root.childNodes.length).equals(1)\n\t\t\t\t\t\t},\n\t\t\t\t\t\tview: function() {\n\t\t\t\t\t\t\treturn m(\"div\", {id: \"a\"}, \"b\")\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\n\t\t\t\t\trender(root, m(component))\n\n\t\t\t\t\to(called).equals(1)\n\t\t\t\t\to(root.firstChild.nodeName).equals(\"DIV\")\n\t\t\t\t\to(root.firstChild.attributes[\"id\"].value).equals(\"a\")\n\t\t\t\t\to(root.firstChild.firstChild.nodeValue).equals(\"b\")\n\t\t\t\t})\n\t\t\t\to(\"does not calls oncreate on redraw\", function() {\n\t\t\t\t\tvar create = o.spy()\n\t\t\t\t\tvar component = createComponent({\n\t\t\t\t\t\tview: function() {\n\t\t\t\t\t\t\treturn m(\"div\", {id: \"a\"}, \"b\")\n\t\t\t\t\t\t},\n\t\t\t\t\t\toncreate: create,\n\t\t\t\t\t})\n\n\t\t\t\t\tfunction view() {\n\t\t\t\t\t\treturn m(component)\n\t\t\t\t\t}\n\n\t\t\t\t\trender(root, view())\n\t\t\t\t\trender(root, view())\n\n\t\t\t\t\to(create.callCount).equals(1)\n\t\t\t\t})\n\t\t\t\to(\"calls oncreate when returning fragment\", function() {\n\t\t\t\t\tvar called = 0\n\t\t\t\t\tvar component = createComponent({\n\t\t\t\t\t\toncreate: function(vnode) {\n\t\t\t\t\t\t\tcalled++\n\n\t\t\t\t\t\t\to(vnode.dom).notEquals(undefined)\n\t\t\t\t\t\t\to(vnode.dom).equals(root.firstChild)\n\t\t\t\t\t\t\to(root.childNodes.length).equals(1)\n\t\t\t\t\t\t},\n\t\t\t\t\t\tview: function() {\n\t\t\t\t\t\t\treturn m(\"div\", {id: \"a\"}, \"b\")\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\n\t\t\t\t\trender(root, m(component))\n\n\t\t\t\t\to(called).equals(1)\n\t\t\t\t\to(root.firstChild.nodeName).equals(\"DIV\")\n\t\t\t\t\to(root.firstChild.attributes[\"id\"].value).equals(\"a\")\n\t\t\t\t\to(root.firstChild.firstChild.nodeValue).equals(\"b\")\n\t\t\t\t})\n\t\t\t\to(\"calls onupdate\", function() {\n\t\t\t\t\tvar called = 0\n\t\t\t\t\tvar component = createComponent({\n\t\t\t\t\t\tonupdate: function(vnode) {\n\t\t\t\t\t\t\tcalled++\n\n\t\t\t\t\t\t\to(vnode.dom).notEquals(undefined)\n\t\t\t\t\t\t\to(vnode.dom).equals(root.firstChild)\n\t\t\t\t\t\t\to(root.childNodes.length).equals(1)\n\t\t\t\t\t\t},\n\t\t\t\t\t\tview: function() {\n\t\t\t\t\t\t\treturn m(\"div\", {id: \"a\"}, \"b\")\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\n\t\t\t\t\trender(root, m(component))\n\n\t\t\t\t\to(called).equals(0)\n\n\t\t\t\t\trender(root, m(component))\n\n\t\t\t\t\to(called).equals(1)\n\t\t\t\t\to(root.firstChild.nodeName).equals(\"DIV\")\n\t\t\t\t\to(root.firstChild.attributes[\"id\"].value).equals(\"a\")\n\t\t\t\t\to(root.firstChild.firstChild.nodeValue).equals(\"b\")\n\t\t\t\t})\n\t\t\t\to(\"calls onupdate when returning fragment\", function() {\n\t\t\t\t\tvar called = 0\n\t\t\t\t\tvar component = createComponent({\n\t\t\t\t\t\tonupdate: function(vnode) {\n\t\t\t\t\t\t\tcalled++\n\n\t\t\t\t\t\t\to(vnode.dom).notEquals(undefined)\n\t\t\t\t\t\t\to(vnode.dom).equals(root.firstChild)\n\t\t\t\t\t\t\to(root.childNodes.length).equals(1)\n\t\t\t\t\t\t},\n\t\t\t\t\t\tview: function() {\n\t\t\t\t\t\t\treturn [m(\"div\", {id: \"a\"}, \"b\")]\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\n\t\t\t\t\trender(root, m(component))\n\n\t\t\t\t\to(called).equals(0)\n\n\t\t\t\t\trender(root, m(component))\n\n\t\t\t\t\to(called).equals(1)\n\t\t\t\t\to(root.firstChild.nodeName).equals(\"DIV\")\n\t\t\t\t\to(root.firstChild.attributes[\"id\"].value).equals(\"a\")\n\t\t\t\t\to(root.firstChild.firstChild.nodeValue).equals(\"b\")\n\t\t\t\t})\n\t\t\t\to(\"calls onremove\", function() {\n\t\t\t\t\tvar called = 0\n\t\t\t\t\tvar component = createComponent({\n\t\t\t\t\t\tonremove: function(vnode) {\n\t\t\t\t\t\t\tcalled++\n\n\t\t\t\t\t\t\to(vnode.dom).notEquals(undefined)\n\t\t\t\t\t\t\to(vnode.dom).equals(root.firstChild)\n\t\t\t\t\t\t\to(root.childNodes.length).equals(1)\n\t\t\t\t\t\t},\n\t\t\t\t\t\tview: function() {\n\t\t\t\t\t\t\treturn m(\"div\", {id: \"a\"}, \"b\")\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\n\t\t\t\t\trender(root, m(component))\n\n\t\t\t\t\to(called).equals(0)\n\n\t\t\t\t\trender(root, [])\n\n\t\t\t\t\to(called).equals(1)\n\t\t\t\t\to(root.childNodes.length).equals(0)\n\t\t\t\t})\n\t\t\t\to(\"calls onremove when returning fragment\", function() {\n\t\t\t\t\tvar called = 0\n\t\t\t\t\tvar component = createComponent({\n\t\t\t\t\t\tonremove: function(vnode) {\n\t\t\t\t\t\t\tcalled++\n\n\t\t\t\t\t\t\to(vnode.dom).notEquals(undefined)\n\t\t\t\t\t\t\to(vnode.dom).equals(root.firstChild)\n\t\t\t\t\t\t\to(root.childNodes.length).equals(1)\n\t\t\t\t\t\t},\n\t\t\t\t\t\tview: function() {\n\t\t\t\t\t\t\treturn [m(\"div\", {id: \"a\"}, \"b\")]\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\n\t\t\t\t\trender(root, m(component))\n\n\t\t\t\t\to(called).equals(0)\n\n\t\t\t\t\trender(root, [])\n\n\t\t\t\t\to(called).equals(1)\n\t\t\t\t\to(root.childNodes.length).equals(0)\n\t\t\t\t})\n\t\t\t\to(\"calls onbeforeremove\", function() {\n\t\t\t\t\tvar called = 0\n\t\t\t\t\tvar component = createComponent({\n\t\t\t\t\t\tonbeforeremove: function(vnode) {\n\t\t\t\t\t\t\tcalled++\n\n\t\t\t\t\t\t\to(vnode.dom).notEquals(undefined)\n\t\t\t\t\t\t\to(vnode.dom).equals(root.firstChild)\n\t\t\t\t\t\t\to(root.childNodes.length).equals(1)\n\t\t\t\t\t\t},\n\t\t\t\t\t\tview: function() {\n\t\t\t\t\t\t\treturn m(\"div\", {id: \"a\"}, \"b\")\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\n\t\t\t\t\trender(root, m(component))\n\n\t\t\t\t\to(called).equals(0)\n\n\t\t\t\t\trender(root, [])\n\n\t\t\t\t\to(called).equals(1)\n\t\t\t\t\to(root.childNodes.length).equals(0)\n\t\t\t\t})\n\t\t\t\to(\"calls onbeforeremove when returning fragment\", function() {\n\t\t\t\t\tvar called = 0\n\t\t\t\t\tvar component = createComponent({\n\t\t\t\t\t\tonbeforeremove: function(vnode) {\n\t\t\t\t\t\t\tcalled++\n\n\t\t\t\t\t\t\to(vnode.dom).notEquals(undefined)\n\t\t\t\t\t\t\to(vnode.dom).equals(root.firstChild)\n\t\t\t\t\t\t\to(root.childNodes.length).equals(1)\n\t\t\t\t\t\t},\n\t\t\t\t\t\tview: function() {\n\t\t\t\t\t\t\treturn [m(\"div\", {id: \"a\"}, \"b\")]\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\n\t\t\t\t\trender(root, m(component))\n\n\t\t\t\t\to(called).equals(0)\n\n\t\t\t\t\trender(root, [])\n\n\t\t\t\t\to(called).equals(1)\n\t\t\t\t\to(root.childNodes.length).equals(0)\n\t\t\t\t})\n\t\t\t\to(\"does not recycle when there's an onupdate\", function() {\n\t\t\t\t\tvar component = createComponent({\n\t\t\t\t\t\tonupdate: function() {},\n\t\t\t\t\t\tview: function() {\n\t\t\t\t\t\t\treturn m(\"div\")\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\t\t\t\t\tvar vnode = m(component, {key: 1})\n\t\t\t\t\tvar updated = m(component, {key: 1})\n\n\t\t\t\t\trender(root, vnode)\n\t\t\t\t\trender(root, [])\n\t\t\t\t\trender(root, updated)\n\n\t\t\t\t\to(vnode.dom).notEquals(updated.dom)\n\t\t\t\t})\n\t\t\t\to(\"lifecycle timing megatest (for a single component)\", function() {\n\t\t\t\t\tvar methods = {\n\t\t\t\t\t\tview: o.spy(function() {\n\t\t\t\t\t\t\treturn \"\"\n\t\t\t\t\t\t})\n\t\t\t\t\t}\n\t\t\t\t\tvar attrs = {}\n\t\t\t\t\tvar hooks = [\n\t\t\t\t\t\t\"oninit\", \"oncreate\", \"onbeforeupdate\",\n\t\t\t\t\t\t\"onupdate\", \"onbeforeremove\", \"onremove\"\n\t\t\t\t\t]\n\t\t\t\t\thooks.forEach(function(hook) {\n\t\t\t\t\t\tif (hook === \"onbeforeupdate\") {\n\t\t\t\t\t\t\t// the component's `onbeforeupdate` is called after the `attrs`' one\n\t\t\t\t\t\t\tattrs[hook] = o.spy(function() {\n\t\t\t\t\t\t\t\to(attrs[hook].callCount).equals(methods[hook].callCount + 1)(hook)\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\tmethods[hook] = o.spy(function() {\n\t\t\t\t\t\t\t\to(attrs[hook].callCount).equals(methods[hook].callCount)(hook)\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t// the other component hooks are called before the `attrs` ones\n\t\t\t\t\t\t\tmethods[hook] = o.spy(function() {\n\t\t\t\t\t\t\t\to(attrs[hook].callCount).equals(methods[hook].callCount - 1)(hook)\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\tattrs[hook] = o.spy(function() {\n\t\t\t\t\t\t\t\to(attrs[hook].callCount).equals(methods[hook].callCount)(hook)\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\n\t\t\t\t\tvar component = createComponent(methods)\n\n\t\t\t\t\to(methods.view.callCount).equals(0)\n\t\t\t\t\to(methods.oninit.callCount).equals(0)\n\t\t\t\t\to(methods.oncreate.callCount).equals(0)\n\t\t\t\t\to(methods.onbeforeupdate.callCount).equals(0)\n\t\t\t\t\to(methods.onupdate.callCount).equals(0)\n\t\t\t\t\to(methods.onbeforeremove.callCount).equals(0)\n\t\t\t\t\to(methods.onremove.callCount).equals(0)\n\n\t\t\t\t\thooks.forEach(function(hook) {\n\t\t\t\t\t\to(attrs[hook].callCount).equals(methods[hook].callCount)(hook)\n\t\t\t\t\t})\n\n\t\t\t\t\trender(root, [m(component, attrs)])\n\n\t\t\t\t\to(methods.view.callCount).equals(1)\n\t\t\t\t\to(methods.oninit.callCount).equals(1)\n\t\t\t\t\to(methods.oncreate.callCount).equals(1)\n\t\t\t\t\to(methods.onbeforeupdate.callCount).equals(0)\n\t\t\t\t\to(methods.onupdate.callCount).equals(0)\n\t\t\t\t\to(methods.onbeforeremove.callCount).equals(0)\n\t\t\t\t\to(methods.onremove.callCount).equals(0)\n\n\t\t\t\t\thooks.forEach(function(hook) {\n\t\t\t\t\t\to(attrs[hook].callCount).equals(methods[hook].callCount)(hook)\n\t\t\t\t\t})\n\n\t\t\t\t\trender(root, [m(component, attrs)])\n\n\t\t\t\t\to(methods.view.callCount).equals(2)\n\t\t\t\t\to(methods.oninit.callCount).equals(1)\n\t\t\t\t\to(methods.oncreate.callCount).equals(1)\n\t\t\t\t\to(methods.onbeforeupdate.callCount).equals(1)\n\t\t\t\t\to(methods.onupdate.callCount).equals(1)\n\t\t\t\t\to(methods.onbeforeremove.callCount).equals(0)\n\t\t\t\t\to(methods.onremove.callCount).equals(0)\n\n\t\t\t\t\thooks.forEach(function(hook) {\n\t\t\t\t\t\to(attrs[hook].callCount).equals(methods[hook].callCount)(hook)\n\t\t\t\t\t})\n\n\t\t\t\t\trender(root, [])\n\n\t\t\t\t\to(methods.view.callCount).equals(2)\n\t\t\t\t\to(methods.oninit.callCount).equals(1)\n\t\t\t\t\to(methods.oncreate.callCount).equals(1)\n\t\t\t\t\to(methods.onbeforeupdate.callCount).equals(1)\n\t\t\t\t\to(methods.onupdate.callCount).equals(1)\n\t\t\t\t\to(methods.onbeforeremove.callCount).equals(1)\n\t\t\t\t\to(methods.onremove.callCount).equals(1)\n\n\t\t\t\t\thooks.forEach(function(hook) {\n\t\t\t\t\t\to(attrs[hook].callCount).equals(methods[hook].callCount)(hook)\n\t\t\t\t\t})\n\t\t\t\t})\n\t\t\t\to(\"hook state and arguments validation\", function(){\n\t\t\t\t\tvar methods = {\n\t\t\t\t\t\tview: o.spy(function(vnode) {\n\t\t\t\t\t\t\to(this).equals(vnode.state)\n\t\t\t\t\t\t\treturn \"\"\n\t\t\t\t\t\t})\n\t\t\t\t\t}\n\t\t\t\t\tvar attrs = {}\n\t\t\t\t\tvar hooks = [\n\t\t\t\t\t\t\"oninit\", \"oncreate\", \"onbeforeupdate\",\n\t\t\t\t\t\t\"onupdate\", \"onbeforeremove\", \"onremove\"\n\t\t\t\t\t]\n\t\t\t\t\thooks.forEach(function(hook) {\n\t\t\t\t\t\tattrs[hook] = o.spy(function(vnode){\n\t\t\t\t\t\t\to(this).equals(vnode.state)(hook)\n\t\t\t\t\t\t})\n\t\t\t\t\t\tmethods[hook] = o.spy(function(vnode){\n\t\t\t\t\t\t\to(this).equals(vnode.state)\n\t\t\t\t\t\t})\n\t\t\t\t\t})\n\n\t\t\t\t\tvar component = createComponent(methods)\n\n\t\t\t\t\trender(root, [m(component, attrs)])\n\t\t\t\t\trender(root, [m(component, attrs)])\n\t\t\t\t\trender(root, [])\n\n\t\t\t\t\thooks.forEach(function(hook) {\n\t\t\t\t\t\to(attrs[hook].this).equals(methods.view.this)(hook)\n\t\t\t\t\t\to(methods[hook].this).equals(methods.view.this)(hook)\n\t\t\t\t\t})\n\n\t\t\t\t\to(methods.view.args.length).equals(1)\n\t\t\t\t\to(methods.oninit.args.length).equals(1)\n\t\t\t\t\to(methods.oncreate.args.length).equals(1)\n\t\t\t\t\to(methods.onbeforeupdate.args.length).equals(2)\n\t\t\t\t\to(methods.onupdate.args.length).equals(1)\n\t\t\t\t\to(methods.onbeforeremove.args.length).equals(1)\n\t\t\t\t\to(methods.onremove.args.length).equals(1)\n\n\t\t\t\t\thooks.forEach(function(hook) {\n\t\t\t\t\t\to(methods[hook].args.length).equals(attrs[hook].args.length)(hook)\n\t\t\t\t\t})\n\t\t\t\t})\n\t\t\t\to(\"no recycling occurs (was: recycled components get a fresh state)\", function() {\n\t\t\t\t\tvar step = 0\n\t\t\t\t\tvar firstState\n\t\t\t\t\tvar view = o.spy(function(vnode) {\n\t\t\t\t\t\tif (step === 0) {\n\t\t\t\t\t\t\tfirstState = vnode.state\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\to(vnode.state).notEquals(firstState)\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn m(\"div\")\n\t\t\t\t\t})\n\t\t\t\t\tvar component = createComponent({view: view})\n\n\t\t\t\t\trender(root, [m(\"div\", m(component, {key: 1}))])\n\t\t\t\t\tvar child = root.firstChild.firstChild\n\t\t\t\t\trender(root, [])\n\t\t\t\t\tstep = 1\n\t\t\t\t\trender(root, [m(\"div\", m(component, {key: 1}))])\n\n\t\t\t\t\to(child).notEquals(root.firstChild.firstChild) // this used to be a recycling pool test\n\t\t\t\t\to(view.callCount).equals(2)\n\t\t\t\t})\n\t\t\t})\n\t\t\to.spec(\"state\", function() {\n\t\t\t\to(\"initializes state\", function() {\n\t\t\t\t\tvar data = {a: 1}\n\t\t\t\t\tvar component = createComponent(createComponent({\n\t\t\t\t\t\tdata: data,\n\t\t\t\t\t\toninit: init,\n\t\t\t\t\t\tview: function() {\n\t\t\t\t\t\t\treturn \"\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}))\n\n\t\t\t\t\trender(root, m(component))\n\n\t\t\t\t\tfunction init(vnode) {\n\t\t\t\t\t\to(vnode.state.data).equals(data)\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t\to(\"state proxies to the component object/prototype\", function() {\n\t\t\t\t\tvar body = {a: 1}\n\t\t\t\t\tvar data = [body]\n\t\t\t\t\tvar component = createComponent(createComponent({\n\t\t\t\t\t\tdata: data,\n\t\t\t\t\t\toninit: init,\n\t\t\t\t\t\tview: function() {\n\t\t\t\t\t\t\treturn \"\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}))\n\n\t\t\t\t\trender(root, m(component))\n\n\t\t\t\t\tfunction init(vnode) {\n\t\t\t\t\t\to(vnode.state.data).equals(data)\n\t\t\t\t\t\to(vnode.state.data[0]).equals(body)\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t})\n\t\t\to.spec(\"vnode.domSize (vnode.instance == null)\", function() {\n\t\t\t\to(\"create and update\", function() {\n\t\t\t\t\tvar component = createComponent({\n\t\t\t\t\t\tview: function() {return null}\n\t\t\t\t\t})\n\t\t\t\t\t// create\n\t\t\t\t\tvar v1 = m(component)\n\t\t\t\t\trender(root, v1)\n\t\t\t\t\to(v1.domSize).equals(0)\n\t\t\t\t\to(v1.instance).equals(null)\n\t\t\t\t\t// update\n\t\t\t\t\tvar v2 = m(component)\n\t\t\t\t\trender(root, v2)\n\t\t\t\t\to(v2.domSize).equals(0)\n\t\t\t\t\to(v2.instance).equals(null)\n\t\t\t\t})\n\t\t\t\to(\"remove instance\", function() {\n\t\t\t\t\tvar v\n\t\t\t\t\tvar component = createComponent({\n\t\t\t\t\t\tview: function() {return v}\n\t\t\t\t\t})\n\t\t\t\t\t// create (return element vnode)\n\t\t\t\t\tv = m(\"a\")\n\t\t\t\t\tvar v1 = m(component)\n\t\t\t\t\trender(root, v1)\n\t\t\t\t\to(v1.instance.tag).equals(\"a\")\n\t\t\t\t\to(v1.domSize).equals(undefined)\n\t\t\t\t\to(v1.instance.domSize).equals(undefined)\n\t\t\t\t\t// remove instance (return null)\n\t\t\t\t\tv = null\n\t\t\t\t\tvar v2 = m(component)\n\t\t\t\t\trender(root, v2)\n\t\t\t\t\to(v2.domSize).equals(0)\n\t\t\t\t\to(v2.instance).equals(null)\n\t\t\t\t})\n\t\t\t\to(\"nested component\", function() {\n\t\t\t\t\tvar childComponent = createComponent({\n\t\t\t\t\t\tview: function() {return null}\n\t\t\t\t\t})\n\t\t\t\t\tvar parentComponent = createComponent({\n\t\t\t\t\t\tview: function() {return m(childComponent)}\n\t\t\t\t\t})\n\t\t\t\t\t// create\n\t\t\t\t\tvar v1 = m(parentComponent)\n\t\t\t\t\trender(root, v1)\n\t\t\t\t\to(v1.domSize).equals(0)\n\t\t\t\t\to(v1.instance.domSize).equals(0)\n\t\t\t\t\to(v1.instance.instance).equals(null)\n\t\t\t\t\t// update\n\t\t\t\t\tvar v2 = m(parentComponent)\n\t\t\t\t\trender(root, v2)\n\t\t\t\t\to(v2.domSize).equals(0)\n\t\t\t\t\to(v2.instance.domSize).equals(0)\n\t\t\t\t\to(v2.instance.instance).equals(null)\n\t\t\t\t})\n\t\t\t})\n\t\t\to.spec(\"vnode.domSize equals vnode.instance.domSize (vnode.instance != null)\", function() {\n\t\t\t\to(\"text\", function() {\n\t\t\t\t\tvar component = createComponent({\n\t\t\t\t\t\tview: function() {return \"text\"}\n\t\t\t\t\t})\n\t\t\t\t\t// create\n\t\t\t\t\tvar v1 = m(component)\n\t\t\t\t\trender(root, v1)\n\t\t\t\t\to(v1.instance.tag).equals(\"#\")\n\t\t\t\t\to(v1.domSize).equals(undefined)\n\t\t\t\t\to(v1.instance.domSize).equals(undefined)\n\t\t\t\t\to(v1.dom).equals(v1.instance.dom)\n\t\t\t\t\t// update\n\t\t\t\t\tvar v2 = m(component)\n\t\t\t\t\trender(root, v2)\n\t\t\t\t\to(v2.instance.tag).equals(\"#\")\n\t\t\t\t\to(v2.domSize).equals(undefined)\n\t\t\t\t\to(v2.instance.domSize).equals(undefined)\n\t\t\t\t\to(v2.dom).equals(v2.instance.dom)\n\t\t\t\t})\n\t\t\t\to(\"element\", function() {\n\t\t\t\t\tvar component = createComponent({\n\t\t\t\t\t\tview: function() {return m(\"a\")}\n\t\t\t\t\t})\n\t\t\t\t\t// create\n\t\t\t\t\tvar v1 = m(component)\n\t\t\t\t\trender(root, v1)\n\t\t\t\t\to(v1.instance.tag).equals(\"a\")\n\t\t\t\t\to(v1.domSize).equals(undefined)\n\t\t\t\t\to(v1.instance.domSize).equals(undefined)\n\t\t\t\t\to(v1.dom).equals(v1.instance.dom)\n\t\t\t\t\t// update\n\t\t\t\t\tvar v2 = m(component)\n\t\t\t\t\trender(root, v2)\n\t\t\t\t\to(v2.instance.tag).equals(\"a\")\n\t\t\t\t\to(v2.domSize).equals(undefined)\n\t\t\t\t\to(v2.instance.domSize).equals(undefined)\n\t\t\t\t\to(v2.dom).equals(v2.instance.dom)\n\t\t\t\t})\n\t\t\t\to(\"trust(0)\", function() {\n\t\t\t\t\tvar component = createComponent({\n\t\t\t\t\t\tview: function() {return m.trust(\"\")}\n\t\t\t\t\t})\n\t\t\t\t\t// create\n\t\t\t\t\tvar v1 = m(component)\n\t\t\t\t\trender(root, v1)\n\t\t\t\t\to(v1.instance.tag).equals(\"<\")\n\t\t\t\t\to(v1.domSize).equals(0)\n\t\t\t\t\to(v1.instance.domSize).equals(0)\n\t\t\t\t\to(v1.dom).equals(v1.instance.dom)\n\t\t\t\t\t// update\n\t\t\t\t\tvar v2 = m(component)\n\t\t\t\t\trender(root, v2)\n\t\t\t\t\to(v2.instance.tag).equals(\"<\")\n\t\t\t\t\to(v2.domSize).equals(0)\n\t\t\t\t\to(v2.instance.domSize).equals(0)\n\t\t\t\t\to(v2.dom).equals(v2.instance.dom)\n\t\t\t\t})\n\t\t\t\to(\"trust(1)\", function() {\n\t\t\t\t\tvar component = createComponent({\n\t\t\t\t\t\tview: function() {return m.trust(\"<a></a>\")}\n\t\t\t\t\t})\n\t\t\t\t\t// create\n\t\t\t\t\tvar v1 = m(component)\n\t\t\t\t\trender(root, v1)\n\t\t\t\t\to(v1.instance.tag).equals(\"<\")\n\t\t\t\t\to(v1.domSize).equals(1)\n\t\t\t\t\to(v1.instance.domSize).equals(1)\n\t\t\t\t\to(v1.dom).equals(v1.instance.dom)\n\t\t\t\t\t// update\n\t\t\t\t\tvar v2 = m(component)\n\t\t\t\t\trender(root, v2)\n\t\t\t\t\to(v2.instance.tag).equals(\"<\")\n\t\t\t\t\to(v2.domSize).equals(1)\n\t\t\t\t\to(v2.instance.domSize).equals(1)\n\t\t\t\t\to(v2.dom).equals(v2.instance.dom)\n\t\t\t\t})\n\t\t\t\to(\"trust(2)\", function() {\n\t\t\t\t\tvar component = createComponent({\n\t\t\t\t\t\tview: function() {return m.trust(\"<a></a><b></b>\")}\n\t\t\t\t\t})\n\t\t\t\t\t// create\n\t\t\t\t\tvar v1 = m(component)\n\t\t\t\t\trender(root, v1)\n\t\t\t\t\to(v1.instance.tag).equals(\"<\")\n\t\t\t\t\to(v1.domSize).equals(2)\n\t\t\t\t\to(v1.instance.domSize).equals(2)\n\t\t\t\t\to(v1.dom).equals(v1.instance.dom)\n\t\t\t\t\t// update\n\t\t\t\t\tvar v2 = m(component)\n\t\t\t\t\trender(root, v2)\n\t\t\t\t\to(v2.instance.tag).equals(\"<\")\n\t\t\t\t\to(v2.domSize).equals(2)\n\t\t\t\t\to(v2.instance.domSize).equals(2)\n\t\t\t\t\to(v2.dom).equals(v2.instance.dom)\n\t\t\t\t})\n\t\t\t\to(\"fragment(0)\", function() {\n\t\t\t\t\tvar component = createComponent({\n\t\t\t\t\t\tview: function() {return []}\n\t\t\t\t\t})\n\t\t\t\t\t// create\n\t\t\t\t\tvar v1 = m(component)\n\t\t\t\t\trender(root, v1)\n\t\t\t\t\to(v1.instance.tag).equals(\"[\")\n\t\t\t\t\to(v1.domSize).equals(0)\n\t\t\t\t\to(v1.instance.domSize).equals(0)\n\t\t\t\t\to(v1.dom).equals(v1.instance.dom)\n\t\t\t\t\t// update\n\t\t\t\t\tvar v2 = m(component)\n\t\t\t\t\trender(root, v2)\n\t\t\t\t\to(v2.instance.tag).equals(\"[\")\n\t\t\t\t\to(v2.domSize).equals(0)\n\t\t\t\t\to(v2.instance.domSize).equals(0)\n\t\t\t\t\to(v2.dom).equals(v2.instance.dom)\n\t\t\t\t})\n\t\t\t\to(\"fragment(1)\", function() {\n\t\t\t\t\tvar component = createComponent({\n\t\t\t\t\t\tview: function() {return [m(\"a\")]}\n\t\t\t\t\t})\n\t\t\t\t\t// create\n\t\t\t\t\tvar v1 = m(component)\n\t\t\t\t\trender(root, v1)\n\t\t\t\t\to(v1.instance.tag).equals(\"[\")\n\t\t\t\t\to(v1.domSize).equals(1)\n\t\t\t\t\to(v1.instance.domSize).equals(1)\n\t\t\t\t\to(v1.dom).equals(v1.instance.dom)\n\t\t\t\t\t// update\n\t\t\t\t\tvar v2 = m(component)\n\t\t\t\t\trender(root, v2)\n\t\t\t\t\to(v2.instance.tag).equals(\"[\")\n\t\t\t\t\to(v2.domSize).equals(1)\n\t\t\t\t\to(v2.instance.domSize).equals(1)\n\t\t\t\t\to(v2.dom).equals(v2.instance.dom)\n\t\t\t\t})\n\t\t\t\to(\"fragment(2)\", function() {\n\t\t\t\t\tvar component = createComponent({\n\t\t\t\t\t\tview: function() {return [m(\"a\"), m(\"b\")]}\n\t\t\t\t\t})\n\t\t\t\t\t// create\n\t\t\t\t\tvar v1 = m(component)\n\t\t\t\t\trender(root, v1)\n\t\t\t\t\to(v1.instance.tag).equals(\"[\")\n\t\t\t\t\to(v1.domSize).equals(2)\n\t\t\t\t\to(v1.instance.domSize).equals(2)\n\t\t\t\t\to(v1.dom).equals(v1.instance.dom)\n\t\t\t\t\t// update\n\t\t\t\t\tvar v2 = m(component)\n\t\t\t\t\trender(root, v2)\n\t\t\t\t\to(v2.instance.tag).equals(\"[\")\n\t\t\t\t\to(v2.domSize).equals(2)\n\t\t\t\t\to(v2.instance.domSize).equals(2)\n\t\t\t\t\to(v2.dom).equals(v2.instance.dom)\n\t\t\t\t})\n\t\t\t\to(\"nested component\", function() {\n\t\t\t\t\tvar childComponent = createComponent({\n\t\t\t\t\t\tview: function() {return [m(\"a\"), m(\"b\")]}\n\t\t\t\t\t})\n\t\t\t\t\tvar parentComponent = createComponent({\n\t\t\t\t\t\tview: function() {return m(childComponent)}\n\t\t\t\t\t})\n\t\t\t\t\t// create\n\t\t\t\t\tvar v1 = m(parentComponent)\n\t\t\t\t\trender(root, v1)\n\t\t\t\t\to(v1.instance.instance.tag).equals(\"[\")\n\t\t\t\t\to(v1.domSize).equals(2)\n\t\t\t\t\to(v1.instance.domSize).equals(2)\n\t\t\t\t\to(v1.instance.instance.domSize).equals(2)\n\t\t\t\t\to(v1.dom).equals(v1.instance.dom)\n\t\t\t\t\to(v1.instance.dom).equals(v1.instance.instance.dom)\n\t\t\t\t\t// update\n\t\t\t\t\tvar v2 = m(parentComponent)\n\t\t\t\t\trender(root, v2)\n\t\t\t\t\to(v2.instance.instance.tag).equals(\"[\")\n\t\t\t\t\to(v2.domSize).equals(2)\n\t\t\t\t\to(v2.instance.domSize).equals(2)\n\t\t\t\t\to(v2.instance.instance.domSize).equals(2)\n\t\t\t\t\to(v2.dom).equals(v2.instance.dom)\n\t\t\t\t\to(v2.instance.dom).equals(v2.instance.instance.dom)\n\t\t\t\t})\n\t\t\t})\n\t\t})\n\t})\n\to.spec(\"Tests specific to certain component kinds\", function() {\n\t\to.spec(\"state\", function() {\n\t\t\to(\"POJO\", function() {\n\t\t\t\tvar data = {}\n\t\t\t\tvar component = {\n\t\t\t\t\tdata: data,\n\t\t\t\t\toninit: init,\n\t\t\t\t\tview: function() {\n\t\t\t\t\t\treturn \"\"\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\trender(root, m(component))\n\n\t\t\t\tfunction init(vnode) {\n\t\t\t\t\to(vnode.state.data).equals(data)\n\n\t\t\t\t\t//inherits state via prototype\n\t\t\t\t\tcomponent.x = 1\n\t\t\t\t\to(vnode.state.x).equals(1)\n\t\t\t\t}\n\t\t\t})\n\t\t\to(\"Constructible\", function() {\n\t\t\t\tvar oninit = o.spy()\n\t\t\t\tvar component = o.spy(function(vnode){\n\t\t\t\t\to(vnode.state).equals(undefined)\n\t\t\t\t\to(oninit.callCount).equals(0)\n\t\t\t\t})\n\t\t\t\tvar view = o.spy(function(){\n\t\t\t\t\to(this instanceof component).equals(true)\n\t\t\t\t\treturn \"\"\n\t\t\t\t})\n\t\t\t\tcomponent.prototype.view = view\n\t\t\t\tcomponent.prototype.oninit = oninit\n\n\t\t\t\trender(root, [m(component, {oninit: oninit})])\n\t\t\t\trender(root, [m(component, {oninit: oninit})])\n\t\t\t\trender(root, [])\n\n\t\t\t\to(component.callCount).equals(1)\n\t\t\t\to(oninit.callCount).equals(2)\n\t\t\t\to(view.callCount).equals(2)\n\t\t\t})\n\t\t\to(\"Closure\", function() {\n\t\t\t\tvar state\n\t\t\t\tvar oninit = o.spy()\n\t\t\t\tvar view = o.spy(function() {\n\t\t\t\t\to(this).equals(state)\n\t\t\t\t\treturn \"\"\n\t\t\t\t})\n\t\t\t\tvar component = o.spy(function(vnode) {\n\t\t\t\t\to(vnode.state).equals(undefined)\n\t\t\t\t\to(oninit.callCount).equals(0)\n\t\t\t\t\treturn state = {\n\t\t\t\t\t\tview: view\n\t\t\t\t\t}\n\t\t\t\t})\n\n\t\t\t\trender(root, [m(component, {oninit: oninit})])\n\t\t\t\trender(root, [m(component, {oninit: oninit})])\n\t\t\t\trender(root, [])\n\n\t\t\t\to(component.callCount).equals(1)\n\t\t\t\to(oninit.callCount).equals(1)\n\t\t\t\to(view.callCount).equals(2)\n\t\t\t})\n\t\t})\n\t})\n})\n"
  },
  {
    "path": "render/tests/test-createElement.js",
    "content": "\"use strict\"\n\nvar o = require(\"ospec\")\nvar domMock = require(\"../../test-utils/domMock\")\nvar vdom = require(\"../../render/render\")\nvar m = require(\"../../render/hyperscript\")\n\no.spec(\"createElement\", function() {\n\tvar $window, root, render\n\to.beforeEach(function() {\n\t\t$window = domMock()\n\t\troot = $window.document.createElement(\"div\")\n\t\trender = vdom($window)\n\t})\n\n\to(\"creates element\", function() {\n\t\tvar vnode = m(\"div\")\n\t\trender(root, vnode)\n\n\t\to(vnode.dom.nodeName).equals(\"DIV\")\n\t})\n\to(\"creates attr\", function() {\n\t\tvar vnode = m(\"div\", {id: \"a\", title: \"b\"})\n\t\trender(root, vnode)\n\n\t\to(vnode.dom.nodeName).equals(\"DIV\")\n\t\to(vnode.dom.attributes[\"id\"].value).equals(\"a\")\n\t\to(vnode.dom.attributes[\"title\"].value).equals(\"b\")\n\t})\n\to(\"creates style\", function() {\n\t\tvar vnode = m(\"div\", {style: {backgroundColor: \"red\"}})\n\t\trender(root, vnode)\n\n\t\to(vnode.dom.nodeName).equals(\"DIV\")\n\t\to(vnode.dom.style.backgroundColor).equals(\"red\")\n\t})\n\to(\"allows css vars in style\", function() {\n\t\tvar vnode = m(\"div\", {style: {\"--css-var\": \"red\"}})\n\t\trender(root, vnode)\n\n\t\to(vnode.dom.style[\"--css-var\"]).equals(\"red\")\n\t})\n\to(\"allows css vars in style with uppercase letters\", function() {\n\t\tvar vnode = m(\"div\", {style: {\"--cssVar\": \"red\"}})\n\t\trender(root, vnode)\n\n\t\to(vnode.dom.style[\"--cssVar\"]).equals(\"red\")\n\t})\n\to(\"creates children\", function() {\n\t\tvar vnode = m(\"div\", m(\"a\"), m(\"b\"))\n\t\trender(root, vnode)\n\n\t\to(vnode.dom.nodeName).equals(\"DIV\")\n\t\to(vnode.dom.childNodes.length).equals(2)\n\t\to(vnode.dom.childNodes[0].nodeName).equals(\"A\")\n\t\to(vnode.dom.childNodes[1].nodeName).equals(\"B\")\n\t})\n\to(\"creates attrs and children\", function() {\n\t\tvar vnode = m(\"div\", {id: \"a\", title: \"b\"}, m(\"a\"), m(\"b\"))\n\t\trender(root, vnode)\n\n\t\to(vnode.dom.nodeName).equals(\"DIV\")\n\t\to(vnode.dom.attributes[\"id\"].value).equals(\"a\")\n\t\to(vnode.dom.attributes[\"title\"].value).equals(\"b\")\n\t\to(vnode.dom.childNodes.length).equals(2)\n\t\to(vnode.dom.childNodes[0].nodeName).equals(\"A\")\n\t\to(vnode.dom.childNodes[1].nodeName).equals(\"B\")\n\t})\n\t/* eslint-disable no-script-url */\n\to(\"creates svg\", function() {\n\t\tvar vnode = m(\"svg\",\n\t\t\tm(\"a\", {\"xlink:href\": \"javascript:;\"}),\n\t\t\tm(\"foreignObject\", m(\"body\", {xmlns: \"http://www.w3.org/1999/xhtml\"}))\n\t\t)\n\t\trender(root, vnode)\n\n\t\to(vnode.dom.nodeName).equals(\"svg\")\n\t\to(vnode.dom.namespaceURI).equals(\"http://www.w3.org/2000/svg\")\n\t\to(vnode.dom.firstChild.nodeName).equals(\"a\")\n\t\to(vnode.dom.firstChild.namespaceURI).equals(\"http://www.w3.org/2000/svg\")\n\t\to(vnode.dom.firstChild.attributes[\"href\"].value).equals(\"javascript:;\")\n\t\to(vnode.dom.firstChild.attributes[\"href\"].namespaceURI).equals(\"http://www.w3.org/1999/xlink\")\n\t\to(vnode.dom.childNodes[1].nodeName).equals(\"foreignObject\")\n\t\to(vnode.dom.childNodes[1].firstChild.nodeName).equals(\"body\")\n\t\to(vnode.dom.childNodes[1].firstChild.namespaceURI).equals(\"http://www.w3.org/1999/xhtml\")\n\t})\n\t/* eslint-enable no-script-url */\n\to(\"sets attributes correctly for svg\", function() {\n\t\tvar vnode = m(\"svg\", {viewBox: \"0 0 100 100\"})\n\t\trender(root, vnode)\n\n\t\to(vnode.dom.attributes[\"viewBox\"].value).equals(\"0 0 100 100\")\n\t})\n\to(\"creates mathml\", function() {\n\t\tvar vnode = m(\"math\", m(\"mrow\"))\n\t\trender(root, vnode)\n\n\t\to(vnode.dom.nodeName).equals(\"math\")\n\t\to(vnode.dom.namespaceURI).equals(\"http://www.w3.org/1998/Math/MathML\")\n\t\to(vnode.dom.firstChild.nodeName).equals(\"mrow\")\n\t\to(vnode.dom.firstChild.namespaceURI).equals(\"http://www.w3.org/1998/Math/MathML\")\n\t})\n})\n"
  },
  {
    "path": "render/tests/test-createFragment.js",
    "content": "\"use strict\"\n\nvar o = require(\"ospec\")\nvar domMock = require(\"../../test-utils/domMock\")\nvar vdom = require(\"../../render/render\")\nvar m = require(\"../../render/hyperscript\")\nvar fragment = require(\"../../render/fragment\")\n\no.spec(\"createFragment\", function() {\n\tvar $window, root, render\n\to.beforeEach(function() {\n\t\t$window = domMock()\n\t\troot = $window.document.createElement(\"div\")\n\t\trender = vdom($window)\n\t})\n\n\to(\"creates fragment\", function() {\n\t\tvar vnode = fragment(m(\"a\"))\n\t\trender(root, vnode)\n\n\t\to(vnode.dom.nodeName).equals(\"A\")\n\t\to(vnode.domSize).equals(1)\n\t})\n\to(\"handles empty fragment\", function() {\n\t\tvar vnode = fragment()\n\t\trender(root, vnode)\n\n\t\to(vnode.dom).equals(null)\n\t\to(vnode.domSize).equals(0)\n\t})\n\to(\"handles childless fragment\", function() {\n\t\tvar vnode = fragment()\n\t\trender(root, vnode)\n\n\t\to(vnode.dom).equals(null)\n\t\to(vnode.domSize).equals(0)\n\t})\n\to(\"handles multiple children\", function() {\n\t\tvar vnode = fragment(m(\"a\"), m(\"b\"))\n\t\trender(root, vnode)\n\n\t\to(vnode.domSize).equals(2)\n\t\to(vnode.dom.nodeName).equals(\"A\")\n\t\to(vnode.dom.nextSibling.nodeName).equals(\"B\")\n\t})\n\to(\"handles td\", function() {\n\t\tvar vnode = fragment(m(\"td\"))\n\t\trender(root, vnode)\n\n\t\to(vnode.dom).notEquals(null)\n\t\to(vnode.dom.nodeName).equals(\"TD\")\n\t\to(vnode.domSize).equals(1)\n\t})\n})\n"
  },
  {
    "path": "render/tests/test-createHTML.js",
    "content": "\"use strict\"\n\nvar o = require(\"ospec\")\nvar domMock = require(\"../../test-utils/domMock\")\nvar vdom = require(\"../../render/render\")\nvar m = require(\"../../render/hyperscript\")\nvar trust = require(\"../../render/trust\")\n\no.spec(\"createHTML\", function() {\n\tvar $window, root, render\n\to.beforeEach(function() {\n\t\t$window = domMock()\n\t\troot = $window.document.createElement(\"div\")\n\t\trender = vdom($window)\n\t})\n\n\to(\"creates HTML\", function() {\n\t\tvar vnode = trust(\"<a></a>\")\n\t\trender(root, vnode)\n\n\t\to(vnode.dom.nodeName).equals(\"A\")\n\t\to(vnode.domSize).equals(1)\n\t})\n\to(\"creates text HTML\", function() {\n\t\tvar vnode = trust(\"a\")\n\t\trender(root, vnode)\n\n\t\to(vnode.dom.nodeValue).equals(\"a\")\n\t\to(vnode.domSize).equals(1)\n\t})\n\to(\"handles empty HTML\", function() {\n\t\tvar vnode = trust(\"\")\n\t\trender(root, vnode)\n\n\t\to(vnode.dom).equals(null)\n\t\to(vnode.domSize).equals(0)\n\t})\n\to(\"handles multiple children in HTML\", function() {\n\t\tvar vnode = trust(\"<a></a><b></b>\")\n\t\trender(root, vnode)\n\n\t\to(vnode.domSize).equals(2)\n\t\to(vnode.dom.nodeName).equals(\"A\")\n\t\to(vnode.dom.nextSibling.nodeName).equals(\"B\")\n\t})\n\to(\"handles valid html tags\", function() {\n\t\t//FIXME body,head,html,frame,frameset are not supported\n\t\t//FIXME keygen is broken in Firefox\n\t\tvar tags = [\"a\", \"abbr\", \"acronym\", \"address\", \"applet\", \"area\", \"article\", \"aside\", \"audio\", \"b\", \"base\", \"basefont\", \"bdi\", \"bdo\", \"big\", \"blockquote\", /*\"body\",*/ \"br\", \"button\", \"canvas\", \"caption\", \"center\", \"cite\", \"code\", \"col\", \"colgroup\", \"datalist\", \"dd\", \"del\", \"details\", \"dfn\", \"dialog\", \"dir\", \"div\", \"dl\", \"dt\", \"em\", \"embed\", \"fieldset\", \"figcaption\", \"figure\", \"font\", \"footer\", \"form\", /*\"frame\", \"frameset\",*/ \"h1\", \"h2\", \"h3\", \"h4\", \"h5\", \"h6\", /*\"head\",*/ \"header\", \"hr\", /*\"html\",*/ \"i\", \"iframe\", \"img\", \"input\", \"ins\", \"kbd\", /*\"keygen\", */\"label\", \"legend\", \"li\", \"link\", \"main\", \"map\", \"mark\", \"menu\", \"menuitem\", \"meta\", \"meter\", \"nav\", \"noframes\", \"noscript\", \"object\", \"ol\", \"optgroup\", \"option\", \"output\", \"p\", \"param\", \"pre\", \"progress\", \"q\", \"rp\", \"rt\", \"ruby\", \"s\", \"samp\", \"script\", \"section\", \"select\", \"small\", \"source\", \"span\", \"strike\", \"strong\", \"style\", \"sub\", \"summary\", \"sup\", \"table\", \"tbody\", \"td\", \"textarea\", \"tfoot\", \"th\", \"thead\", \"time\", \"title\", \"tr\", \"track\", \"tt\", \"u\", \"ul\", \"var\", \"video\", \"wbr\"]\n\n\t\ttags.forEach(function(tag) {\n\t\t\tvar vnode = trust(\"<\" + tag + \" />\")\n\t\t\trender(root, vnode)\n\n\t\t\to(vnode.dom.nodeName).equals(tag.toUpperCase())\n\t\t})\n\t})\n\to(\"creates SVG\", function() {\n\t\tvar vnode = trust(\"<g></g>\")\n\t\trender(root, m(\"svg\", vnode))\n\n\t\to(vnode.dom.nodeName).equals(\"g\")\n\t\to(vnode.dom.namespaceURI).equals(\"http://www.w3.org/2000/svg\")\n\t\to(vnode.domSize).equals(1)\n\t})\n\to(\"creates text SVG\", function() {\n\t\tvar vnode = trust(\"a\")\n\t\trender(root, m(\"svg\", vnode))\n\n\t\to(vnode.dom.nodeValue).equals(\"a\")\n\t\to(vnode.domSize).equals(1)\n\t})\n\to(\"handles empty SVG\", function() {\n\t\tvar vnode = trust(\"\")\n\t\trender(root, m(\"svg\", vnode))\n\n\t\to(vnode.dom).equals(null)\n\t\to(vnode.domSize).equals(0)\n\t})\n\to(\"handles multiple children in SVG\", function() {\n\t\tvar vnode = trust(\"<g></g><text></text>\")\n\t\trender(root, m(\"svg\", vnode))\n\n\t\to(vnode.domSize).equals(2)\n\t\to(vnode.dom.nodeName).equals(\"g\")\n\t\to(vnode.dom.namespaceURI).equals(\"http://www.w3.org/2000/svg\")\n\t\to(vnode.dom.nextSibling.nodeName).equals(\"text\")\n\t\to(vnode.dom.nextSibling.namespaceURI).equals(\"http://www.w3.org/2000/svg\")\n\t})\n\to(\"creates the dom correctly with a contenteditable parent\", function() {\n\t\tvar div = m(\"div\", {contenteditable: true}, trust(\"<a></a>\"))\n\n\t\trender(root, div)\n\t\tvar tags = []\n\t\tfor (var i = 0; i < div.dom.childNodes.length; i++) {\n\t\t\ttags.push(div.dom.childNodes[i].nodeName)\n\t\t}\n\t\to(tags).deepEquals([\"A\"])\n\t})\n})\n"
  },
  {
    "path": "render/tests/test-createNodes.js",
    "content": "\"use strict\"\n\nvar o = require(\"ospec\")\nvar domMock = require(\"../../test-utils/domMock\")\nvar vdom = require(\"../../render/render\")\nvar m = require(\"../../render/hyperscript\")\nvar fragment = require(\"../../render/fragment\")\nvar trust = require(\"../../render/trust\")\n\no.spec(\"createNodes\", function() {\n\tvar $window, root, render\n\to.beforeEach(function() {\n\t\t$window = domMock()\n\t\troot = $window.document.createElement(\"div\")\n\t\trender = vdom($window)\n\t})\n\n\to(\"creates nodes\", function() {\n\t\tvar vnodes = [\n\t\t\tm(\"a\"),\n\t\t\t\"b\",\n\t\t\ttrust(\"c\"),\n\t\t\tfragment(\"d\"),\n\t\t]\n\t\trender(root, vnodes)\n\n\t\to(root.childNodes.length).equals(4)\n\t\to(root.childNodes[0].nodeName).equals(\"A\")\n\t\to(root.childNodes[1].nodeValue).equals(\"b\")\n\t\to(root.childNodes[2].nodeValue).equals(\"c\")\n\t\to(root.childNodes[3].nodeValue).equals(\"d\")\n\t})\n\to(\"ignores null\", function() {\n\t\tvar vnodes = [\n\t\t\tm(\"a\"),\n\t\t\t\"b\",\n\t\t\tnull,\n\t\t\ttrust(\"c\"),\n\t\t\tfragment(\"d\"),\n\t\t]\n\t\trender(root, vnodes)\n\n\t\to(root.childNodes.length).equals(4)\n\t\to(root.childNodes[0].nodeName).equals(\"A\")\n\t\to(root.childNodes[1].nodeValue).equals(\"b\")\n\t\to(root.childNodes[2].nodeValue).equals(\"c\")\n\t\to(root.childNodes[3].nodeValue).equals(\"d\")\n\t})\n\to(\"ignores undefined\", function() {\n\t\tvar vnodes = [\n\t\t\tm(\"a\"),\n\t\t\t\"b\",\n\t\t\tundefined,\n\t\t\ttrust(\"c\"),\n\t\t\tfragment(\"d\"),\n\t\t]\n\t\trender(root, vnodes)\n\n\t\to(root.childNodes.length).equals(4)\n\t\to(root.childNodes[0].nodeName).equals(\"A\")\n\t\to(root.childNodes[1].nodeValue).equals(\"b\")\n\t\to(root.childNodes[2].nodeValue).equals(\"c\")\n\t\to(root.childNodes[3].nodeValue).equals(\"d\")\n\t})\n})\n"
  },
  {
    "path": "render/tests/test-createText.js",
    "content": "\"use strict\"\n\nvar o = require(\"ospec\")\nvar domMock = require(\"../../test-utils/domMock\")\nvar vdom = require(\"../../render/render\")\n\no.spec(\"createText\", function() {\n\tvar $window, root, render\n\to.beforeEach(function() {\n\t\t$window = domMock()\n\t\troot = $window.document.createElement(\"div\")\n\t\trender = vdom($window)\n\t})\n\n\to(\"creates string\", function() {\n\t\tvar vnode = \"a\"\n\t\trender(root, vnode)\n\n\t\to(root.firstChild.nodeName).equals(\"#text\")\n\t\to(root.firstChild.nodeValue).equals(\"a\")\n\t})\n\to(\"creates falsy string\", function() {\n\t\tvar vnode = \"\"\n\t\trender(root, vnode)\n\n\t\to(root.firstChild.nodeName).equals(\"#text\")\n\t\to(root.firstChild.nodeValue).equals(\"\")\n\t})\n\to(\"creates number\", function() {\n\t\tvar vnode = 1\n\t\trender(root, vnode)\n\n\t\to(root.firstChild.nodeName).equals(\"#text\")\n\t\to(root.firstChild.nodeValue).equals(\"1\")\n\t})\n\to(\"creates falsy number\", function() {\n\t\tvar vnode = 0\n\t\trender(root, vnode)\n\n\t\to(root.firstChild.nodeName).equals(\"#text\")\n\t\to(root.firstChild.nodeValue).equals(\"0\")\n\t})\n\to(\"ignores true boolean\", function() {\n\t\tvar vnode = true\n\t\trender(root, vnode)\n\n\t\to(root.childNodes.length).equals(0)\n\t})\n\to(\"creates false boolean\", function() {\n\t\tvar vnode = false\n\t\trender(root, vnode)\n\n\t\to(root.childNodes.length).equals(0)\n\t})\n\to(\"creates spaces\", function() {\n\t\tvar vnode = \"   \"\n\t\trender(root, vnode)\n\n\t\to(root.firstChild.nodeName).equals(\"#text\")\n\t\to(root.firstChild.nodeValue).equals(\"   \")\n\t})\n\to(\"ignores html\", function() {\n\t\tvar vnode = \"<a></a>&trade;\"\n\t\trender(root, vnode)\n\n\t\to(root.firstChild.nodeName).equals(\"#text\")\n\t\to(root.firstChild.nodeValue).equals(\"<a></a>&trade;\")\n\t})\n})\n"
  },
  {
    "path": "render/tests/test-domFor.js",
    "content": "\"use strict\"\n\nconst o = require(\"ospec\")\nconst callAsync = require(\"../../test-utils/callAsync\")\nconst components = require(\"../../test-utils/components\")\nconst domMock = require(\"../../test-utils/domMock\")\nconst vdom = require(\"../../render/render\")\nconst m = require(\"../../render/hyperscript\")\nconst fragment = require(\"../../render/fragment\")\nconst domFor = require(\"../../render/domFor\")\n\no.spec(\"domFor(vnode)\", function() {\n\tlet $window, root, render\n\to.beforeEach(function() {\n\t\t$window = domMock()\n\t\troot = $window.document.createElement(\"div\")\n\t\trender = vdom($window)\n\t})\n\to(\"works for simple vnodes\", function() {\n\t\trender(root, m(\"div\", {oncreate(vnode){\n\t\t\tlet n = 0\n\t\t\tfor (const dom of domFor(vnode)) {\n\t\t\t\to(dom).equals(root.firstChild)\n\t\t\t\to(++n).equals(1)\n\t\t\t}\n\t\t}}))\n\t})\n\to(\"works for fragments\", function () {\n\t\trender(root, fragment({\n\t\t\toncreate(vnode){\n\t\t\t\tlet n = 0\n\t\t\t\tfor (const dom of domFor(vnode)) {\n\t\t\t\t\to(dom).equals(root.childNodes[n])\n\t\t\t\t\tn++\n\t\t\t\t}\n\t\t\t\to(n).equals(2)\n\t\t\t}\n\t\t}, [\n\t\t\tm(\"a\"),\n\t\t\tm(\"b\")\n\t\t]))\n\t})\n\to(\"works in fragments with children that have delayed removal\", function() {\n\t\tfunction oncreate(vnode){\n\t\t\to(root.childNodes.length).equals(3)\n\t\t\to(root.childNodes[0].nodeName).equals(\"A\")\n\t\t\to(root.childNodes[1].nodeName).equals(\"B\")\n\t\t\to(root.childNodes[2].nodeName).equals(\"C\")\n\n\t\t\tconst iter = domFor(vnode)\n\t\t\to(iter.next()).deepEquals({done:false, value: root.childNodes[0]})\n\t\t\to(iter.next()).deepEquals({done:false, value: root.childNodes[1]})\n\t\t\to(iter.next()).deepEquals({done:false, value: root.childNodes[2]})\n\t\t\to(iter.next().done).deepEquals(true)\n\t\t\to(root.childNodes.length).equals(3)\n\t\t}\n\t\tfunction onupdate(vnode) {\n\t\t\t// the b node is still present in the DOM\n\t\t\to(root.childNodes.length).equals(3)\n\t\t\to(root.childNodes[0].nodeName).equals(\"A\")\n\t\t\to(root.childNodes[1].nodeName).equals(\"B\")\n\t\t\to(root.childNodes[2].nodeName).equals(\"C\")\n\n\t\t\tconst iter = domFor(vnode)\n\t\t\to(iter.next()).deepEquals({done:false, value: root.childNodes[0]})\n\t\t\to(iter.next()).deepEquals({done:false, value: root.childNodes[2]})\n\t\t\to(iter.next().done).deepEquals(true)\n\t\t\to(root.childNodes.length).equals(3)\n\t\t}\n\n\t\trender(root, fragment(\n\t\t\t{oncreate, onupdate},\n\t\t\t[\n\t\t\t\tm(\"a\"),\n\t\t\t\tm(\"b\", {onbeforeremove(){return {then(){}, finally(){}}}}),\n\t\t\t\tm(\"c\")\n\t\t\t]\n\t\t))\n\t\trender(root, fragment(\n\t\t\t{oncreate, onupdate},\n\t\t\t[\n\t\t\t\tm(\"a\"),\n\t\t\t\tnull,\n\t\t\t\tm(\"c\"),\n\t\t\t]\n\t\t))\n\n\t})\n\to(\"works in onbeforeremove and onremove\", function (done) {\n\t\tconst onbeforeremove = o.spy(function onbeforeremove(vnode){\n\t\t\to(root.childNodes.length).equals(1)\n\t\t\to(root.childNodes[0].nodeName).equals(\"A\")\n\t\t\tconst iter = domFor(vnode)\n\t\t\to(iter.next()).deepEquals({done:false, value: root.childNodes[0]})\n\t\t\to(iter.next().done).deepEquals(true)\n\t\t\to(root.childNodes.length).equals(1)\n\t\t\treturn {then(resolve){resolve()}}\n\t\t})\n\t\tconst onremove = o.spy(function onremove(vnode){\n\t\t\to(root.childNodes.length).equals(1)\n\t\t\to(root.childNodes[0].nodeName).equals(\"A\")\n\t\t\tconst iter = domFor(vnode)\n\t\t\to(iter.next()).deepEquals({done:false, value: root.childNodes[0]})\n\t\t\to(iter.next().done).deepEquals(true)\n\t\t\to(root.childNodes.length).equals(1)\n\t\t})\n\t\trender(root, [m(\"a\", {onbeforeremove, onremove})])\n\t\trender(root, [])\n\n\t\to(onbeforeremove.callCount).equals(1)\n\t\to(onremove.callCount).equals(0)\n\t\tcallAsync(function(){\n\t\t\to(onremove.callCount).equals(1)\n\t\t\tdone()\n\t\t})\n\t})\n\to(\"works multiple vnodes with onbeforeremove (#3007, 1/6, BCA)\", function (done) {\n\t\tlet thenCBA, thenCBB, thenCBC\n\t\tconst onbeforeremoveA = o.spy(function onbeforeremove(){\n\t\t\treturn {then(resolve){thenCBA = resolve}}\n\t\t})\n\t\tconst onbeforeremoveB = o.spy(function onbeforeremove(){\n\t\t\treturn {then(resolve){thenCBB = resolve}}\n\t\t})\n\t\tconst onbeforeremoveC = o.spy(function onbeforeremove(){\n\t\t\treturn {then(resolve){thenCBC = resolve}}\n\t\t})\n\t\t// to avoid updating internal nodes only, vnodes have key attributes\n\t\tconst A = fragment({key: 1, onbeforeremove: onbeforeremoveA}, [m(\"a1\"), m(\"a2\")])\n\t\tconst B = fragment({key: 2, onbeforeremove: onbeforeremoveB}, [m(\"b1\"), m(\"b2\")])\n\t\tconst C = fragment({key: 3, onbeforeremove: onbeforeremoveC}, [m(\"c1\"), m(\"c2\")])\n\n\t\trender(root, [A])\n\t\to(onbeforeremoveA.callCount).equals(0)\n\t\to(onbeforeremoveB.callCount).equals(0)\n\t\to(onbeforeremoveC.callCount).equals(0)\n\n\t\trender(root, [B])\n\t\to(onbeforeremoveA.callCount).equals(1)\n\t\to(onbeforeremoveB.callCount).equals(0)\n\t\to(onbeforeremoveC.callCount).equals(0)\n\n\t\trender(root, [C])\n\t\to(onbeforeremoveA.callCount).equals(1)\n\t\to(onbeforeremoveB.callCount).equals(1)\n\t\to(onbeforeremoveC.callCount).equals(0)\n\n\t\trender(root, [])\n\t\to(onbeforeremoveA.callCount).equals(1)\n\t\to(onbeforeremoveB.callCount).equals(1)\n\t\to(onbeforeremoveC.callCount).equals(1)\n\n\t\t// not resolved\n\t\to(root.childNodes.length).equals(6)\n\t\to(root.childNodes[0].nodeName).equals(\"A1\")\n\t\to(root.childNodes[1].nodeName).equals(\"A2\")\n\t\to(root.childNodes[2].nodeName).equals(\"B1\")\n\t\to(root.childNodes[3].nodeName).equals(\"B2\")\n\t\to(root.childNodes[4].nodeName).equals(\"C1\")\n\t\to(root.childNodes[5].nodeName).equals(\"C2\")\n\n\t\tconst iterA = domFor(A)\n\t\to(iterA.next().value.nodeName).equals(\"A1\")\n\t\to(iterA.next().value.nodeName).equals(\"A2\")\n\t\to(iterA.next().done).deepEquals(true)\n\n\t\tconst iterB = domFor(B)\n\t\to(iterB.next().value.nodeName).equals(\"B1\")\n\t\to(iterB.next().value.nodeName).equals(\"B2\")\n\t\to(iterB.next().done).deepEquals(true)\n\n\t\tconst iterC = domFor(C)\n\t\to(iterC.next().value.nodeName).equals(\"C1\")\n\t\to(iterC.next().value.nodeName).equals(\"C2\")\n\t\to(iterC.next().done).deepEquals(true)\n\n\t\tcallAsync(function(){\n\t\t\t// not resolved yet\n\t\t\to(root.childNodes.length).equals(6)\n\t\t\to(root.childNodes[0].nodeName).equals(\"A1\")\n\t\t\to(root.childNodes[1].nodeName).equals(\"A2\")\n\t\t\to(root.childNodes[2].nodeName).equals(\"B1\")\n\t\t\to(root.childNodes[3].nodeName).equals(\"B2\")\n\t\t\to(root.childNodes[4].nodeName).equals(\"C1\")\n\t\t\to(root.childNodes[5].nodeName).equals(\"C2\")\n\t\n\t\t\tconst iterA = domFor(A)\n\t\t\to(iterA.next().value.nodeName).equals(\"A1\")\n\t\t\to(iterA.next().value.nodeName).equals(\"A2\")\n\t\t\to(iterA.next().done).deepEquals(true)\n\t\n\t\t\tconst iterB = domFor(B)\n\t\t\to(iterB.next().value.nodeName).equals(\"B1\")\n\t\t\to(iterB.next().value.nodeName).equals(\"B2\")\n\t\t\to(iterB.next().done).deepEquals(true)\n\t\n\t\t\tconst iterC = domFor(C)\n\t\t\to(iterC.next().value.nodeName).equals(\"C1\")\n\t\t\to(iterC.next().value.nodeName).equals(\"C2\")\n\t\t\to(iterC.next().done).deepEquals(true)\n\n\t\t\t// resolve B\n\t\t\tthenCBB()\n\t\t\tcallAsync(function(){\n\t\t\t\to(root.childNodes.length).equals(4)\n\t\t\t\to(root.childNodes[0].nodeName).equals(\"A1\")\n\t\t\t\to(root.childNodes[1].nodeName).equals(\"A2\")\n\t\t\t\to(root.childNodes[2].nodeName).equals(\"C1\")\n\t\t\t\to(root.childNodes[3].nodeName).equals(\"C2\")\n\n\t\t\t\tconst iterA = domFor(A)\n\t\t\t\to(iterA.next().value.nodeName).equals(\"A1\")\n\t\t\t\to(iterA.next().value.nodeName).equals(\"A2\")\n\t\t\t\to(iterA.next().done).deepEquals(true)\n\n\t\t\t\tconst iterC = domFor(C)\n\t\t\t\to(iterC.next().value.nodeName).equals(\"C1\")\n\t\t\t\to(iterC.next().value.nodeName).equals(\"C2\")\n\t\t\t\to(iterC.next().done).deepEquals(true)\n\n\t\t\t\t// resolve C\n\t\t\t\tthenCBC()\n\t\t\t\tcallAsync(function(){\n\t\t\t\t\to(root.childNodes.length).equals(2)\n\t\t\t\t\to(root.childNodes[0].nodeName).equals(\"A1\")\n\t\t\t\t\to(root.childNodes[1].nodeName).equals(\"A2\")\n\n\t\t\t\t\tconst iterA = domFor(A)\n\t\t\t\t\to(iterA.next().value.nodeName).equals(\"A1\")\n\t\t\t\t\to(iterA.next().value.nodeName).equals(\"A2\")\n\t\t\t\t\to(iterA.next().done).deepEquals(true)\n\n\t\t\t\t\t// resolve A\n\t\t\t\t\tthenCBA()\n\t\t\t\t\tcallAsync(function(){\n\t\t\t\t\t\to(root.childNodes.length).equals(0)\n\t\t\t\t\t\tdone()\n\t\t\t\t\t})\n\t\t\t\t})\n\t\t\t})\n\t\t})\n\t})\n\to(\"works multiple vnodes with onbeforeremove (#3007, 2/6, CAB)\", function (done) {\n\t\tlet thenCBA, thenCBB, thenCBC\n\t\tconst onbeforeremoveA = o.spy(function onbeforeremove(){\n\t\t\treturn {then(resolve){thenCBA = resolve}}\n\t\t})\n\t\tconst onbeforeremoveB = o.spy(function onbeforeremove(){\n\t\t\treturn {then(resolve){thenCBB = resolve}}\n\t\t})\n\t\tconst onbeforeremoveC = o.spy(function onbeforeremove(){\n\t\t\treturn {then(resolve){thenCBC = resolve}}\n\t\t})\n\t\t// to avoid updating internal nodes only, vnodes have key attributes\n\t\tconst A = fragment({key: 1, onbeforeremove: onbeforeremoveA}, [m(\"a1\"), m(\"a2\")])\n\t\tconst B = fragment({key: 2, onbeforeremove: onbeforeremoveB}, [m(\"b1\"), m(\"b2\")])\n\t\tconst C = fragment({key: 3, onbeforeremove: onbeforeremoveC}, [m(\"c1\"), m(\"c2\")])\n\n\t\trender(root, [A])\n\t\to(onbeforeremoveA.callCount).equals(0)\n\t\to(onbeforeremoveB.callCount).equals(0)\n\t\to(onbeforeremoveC.callCount).equals(0)\n\n\t\trender(root, [B])\n\t\to(onbeforeremoveA.callCount).equals(1)\n\t\to(onbeforeremoveB.callCount).equals(0)\n\t\to(onbeforeremoveC.callCount).equals(0)\n\n\t\trender(root, [C])\n\t\to(onbeforeremoveA.callCount).equals(1)\n\t\to(onbeforeremoveB.callCount).equals(1)\n\t\to(onbeforeremoveC.callCount).equals(0)\n\n\t\trender(root, [])\n\t\to(onbeforeremoveA.callCount).equals(1)\n\t\to(onbeforeremoveB.callCount).equals(1)\n\t\to(onbeforeremoveC.callCount).equals(1)\n\n\t\t// not resolved\n\t\to(root.childNodes.length).equals(6)\n\t\to(root.childNodes[0].nodeName).equals(\"A1\")\n\t\to(root.childNodes[1].nodeName).equals(\"A2\")\n\t\to(root.childNodes[2].nodeName).equals(\"B1\")\n\t\to(root.childNodes[3].nodeName).equals(\"B2\")\n\t\to(root.childNodes[4].nodeName).equals(\"C1\")\n\t\to(root.childNodes[5].nodeName).equals(\"C2\")\n\n\t\tconst iterA = domFor(A)\n\t\to(iterA.next().value.nodeName).equals(\"A1\")\n\t\to(iterA.next().value.nodeName).equals(\"A2\")\n\t\to(iterA.next().done).deepEquals(true)\n\n\t\tconst iterB = domFor(B)\n\t\to(iterB.next().value.nodeName).equals(\"B1\")\n\t\to(iterB.next().value.nodeName).equals(\"B2\")\n\t\to(iterB.next().done).deepEquals(true)\n\n\t\tconst iterC = domFor(C)\n\t\to(iterC.next().value.nodeName).equals(\"C1\")\n\t\to(iterC.next().value.nodeName).equals(\"C2\")\n\t\to(iterC.next().done).deepEquals(true)\n\n\t\tcallAsync(function(){\n\t\t\t// not resolved yet\n\t\t\to(root.childNodes.length).equals(6)\n\t\t\to(root.childNodes[0].nodeName).equals(\"A1\")\n\t\t\to(root.childNodes[1].nodeName).equals(\"A2\")\n\t\t\to(root.childNodes[2].nodeName).equals(\"B1\")\n\t\t\to(root.childNodes[3].nodeName).equals(\"B2\")\n\t\t\to(root.childNodes[4].nodeName).equals(\"C1\")\n\t\t\to(root.childNodes[5].nodeName).equals(\"C2\")\n\t\n\t\t\tconst iterA = domFor(A)\n\t\t\to(iterA.next().value.nodeName).equals(\"A1\")\n\t\t\to(iterA.next().value.nodeName).equals(\"A2\")\n\t\t\to(iterA.next().done).deepEquals(true)\n\t\n\t\t\tconst iterB = domFor(B)\n\t\t\to(iterB.next().value.nodeName).equals(\"B1\")\n\t\t\to(iterB.next().value.nodeName).equals(\"B2\")\n\t\t\to(iterB.next().done).deepEquals(true)\n\t\n\t\t\tconst iterC = domFor(C)\n\t\t\to(iterC.next().value.nodeName).equals(\"C1\")\n\t\t\to(iterC.next().value.nodeName).equals(\"C2\")\n\t\t\to(iterC.next().done).deepEquals(true)\n\n\t\t\t// resolve C\n\t\t\tthenCBC()\n\t\t\tcallAsync(function(){\n\t\t\t\to(root.childNodes.length).equals(4)\n\t\t\t\to(root.childNodes[0].nodeName).equals(\"A1\")\n\t\t\t\to(root.childNodes[1].nodeName).equals(\"A2\")\n\t\t\t\to(root.childNodes[2].nodeName).equals(\"B1\")\n\t\t\t\to(root.childNodes[3].nodeName).equals(\"B2\")\n\n\t\t\t\tconst iterB = domFor(B)\n\t\t\t\to(iterB.next().value.nodeName).equals(\"B1\")\n\t\t\t\to(iterB.next().value.nodeName).equals(\"B2\")\n\t\t\t\to(iterB.next().done).deepEquals(true)\n\n\t\t\t\tconst iterA = domFor(A)\n\t\t\t\to(iterA.next().value.nodeName).equals(\"A1\")\n\t\t\t\to(iterA.next().value.nodeName).equals(\"A2\")\n\t\t\t\to(iterA.next().done).deepEquals(true)\n\n\t\t\t\t// resolve A\n\t\t\t\tthenCBA()\n\t\t\t\tcallAsync(function(){\n\t\t\t\t\to(root.childNodes.length).equals(2)\n\t\t\t\t\to(root.childNodes[0].nodeName).equals(\"B1\")\n\t\t\t\t\to(root.childNodes[1].nodeName).equals(\"B2\")\n\n\t\t\t\t\tconst iterB = domFor(B)\n\t\t\t\t\to(iterB.next().value.nodeName).equals(\"B1\")\n\t\t\t\t\to(iterB.next().value.nodeName).equals(\"B2\")\n\t\t\t\t\to(iterB.next().done).deepEquals(true)\n\n\t\t\t\t\t// resolve B\n\t\t\t\t\tthenCBB()\n\t\t\t\t\tcallAsync(function(){\n\t\t\t\t\t\to(root.childNodes.length).equals(0)\n\t\t\t\t\t\tdone()\n\t\t\t\t\t})\n\t\t\t\t})\n\t\t\t})\n\t\t})\n\t})\n\to(\"works multiple vnodes with onbeforeremove (#3007, 3/6, ABC)\", function (done) {\n\t\tlet thenCBA, thenCBB, thenCBC\n\t\tconst onbeforeremoveA = o.spy(function onbeforeremove(){\n\t\t\treturn {then(resolve){thenCBA = resolve}}\n\t\t})\n\t\tconst onbeforeremoveB = o.spy(function onbeforeremove(){\n\t\t\treturn {then(resolve){thenCBB = resolve}}\n\t\t})\n\t\tconst onbeforeremoveC = o.spy(function onbeforeremove(){\n\t\t\treturn {then(resolve){thenCBC = resolve}}\n\t\t})\n\t\t// to avoid updating internal nodes only, vnodes have key attributes\n\t\tconst A = fragment({key: 1, onbeforeremove: onbeforeremoveA}, [m(\"a1\"), m(\"a2\")])\n\t\tconst B = fragment({key: 2, onbeforeremove: onbeforeremoveB}, [m(\"b1\"), m(\"b2\")])\n\t\tconst C = fragment({key: 3, onbeforeremove: onbeforeremoveC}, [m(\"c1\"), m(\"c2\")])\n\n\t\trender(root, [A])\n\t\to(onbeforeremoveA.callCount).equals(0)\n\t\to(onbeforeremoveB.callCount).equals(0)\n\t\to(onbeforeremoveC.callCount).equals(0)\n\n\t\trender(root, [B])\n\t\to(onbeforeremoveA.callCount).equals(1)\n\t\to(onbeforeremoveB.callCount).equals(0)\n\t\to(onbeforeremoveC.callCount).equals(0)\n\n\t\trender(root, [C])\n\t\to(onbeforeremoveA.callCount).equals(1)\n\t\to(onbeforeremoveB.callCount).equals(1)\n\t\to(onbeforeremoveC.callCount).equals(0)\n\n\t\trender(root, [])\n\t\to(onbeforeremoveA.callCount).equals(1)\n\t\to(onbeforeremoveB.callCount).equals(1)\n\t\to(onbeforeremoveC.callCount).equals(1)\n\n\t\t// not resolved\n\t\to(root.childNodes.length).equals(6)\n\t\to(root.childNodes[0].nodeName).equals(\"A1\")\n\t\to(root.childNodes[1].nodeName).equals(\"A2\")\n\t\to(root.childNodes[2].nodeName).equals(\"B1\")\n\t\to(root.childNodes[3].nodeName).equals(\"B2\")\n\t\to(root.childNodes[4].nodeName).equals(\"C1\")\n\t\to(root.childNodes[5].nodeName).equals(\"C2\")\n\n\t\tconst iterA = domFor(A)\n\t\to(iterA.next().value.nodeName).equals(\"A1\")\n\t\to(iterA.next().value.nodeName).equals(\"A2\")\n\t\to(iterA.next().done).deepEquals(true)\n\n\t\tconst iterB = domFor(B)\n\t\to(iterB.next().value.nodeName).equals(\"B1\")\n\t\to(iterB.next().value.nodeName).equals(\"B2\")\n\t\to(iterB.next().done).deepEquals(true)\n\n\t\tconst iterC = domFor(C)\n\t\to(iterC.next().value.nodeName).equals(\"C1\")\n\t\to(iterC.next().value.nodeName).equals(\"C2\")\n\t\to(iterC.next().done).deepEquals(true)\n\n\t\tcallAsync(function(){\n\t\t\t// not resolved yet\n\t\t\to(root.childNodes.length).equals(6)\n\t\t\to(root.childNodes[0].nodeName).equals(\"A1\")\n\t\t\to(root.childNodes[1].nodeName).equals(\"A2\")\n\t\t\to(root.childNodes[2].nodeName).equals(\"B1\")\n\t\t\to(root.childNodes[3].nodeName).equals(\"B2\")\n\t\t\to(root.childNodes[4].nodeName).equals(\"C1\")\n\t\t\to(root.childNodes[5].nodeName).equals(\"C2\")\n\t\n\t\t\tconst iterA = domFor(A)\n\t\t\to(iterA.next().value.nodeName).equals(\"A1\")\n\t\t\to(iterA.next().value.nodeName).equals(\"A2\")\n\t\t\to(iterA.next().done).deepEquals(true)\n\t\n\t\t\tconst iterB = domFor(B)\n\t\t\to(iterB.next().value.nodeName).equals(\"B1\")\n\t\t\to(iterB.next().value.nodeName).equals(\"B2\")\n\t\t\to(iterB.next().done).deepEquals(true)\n\t\n\t\t\tconst iterC = domFor(C)\n\t\t\to(iterC.next().value.nodeName).equals(\"C1\")\n\t\t\to(iterC.next().value.nodeName).equals(\"C2\")\n\t\t\to(iterC.next().done).deepEquals(true)\n\n\t\t\t// resolve A\n\t\t\tthenCBA()\n\t\t\tcallAsync(function(){\n\t\t\t\to(root.childNodes.length).equals(4)\n\t\t\t\to(root.childNodes[0].nodeName).equals(\"B1\")\n\t\t\t\to(root.childNodes[1].nodeName).equals(\"B2\")\n\t\t\t\to(root.childNodes[2].nodeName).equals(\"C1\")\n\t\t\t\to(root.childNodes[3].nodeName).equals(\"C2\")\n\n\t\t\t\tconst iterB = domFor(B)\n\t\t\t\to(iterB.next().value.nodeName).equals(\"B1\")\n\t\t\t\to(iterB.next().value.nodeName).equals(\"B2\")\n\t\t\t\to(iterB.next().done).deepEquals(true)\n\n\t\t\t\tconst iterC = domFor(C)\n\t\t\t\to(iterC.next().value.nodeName).equals(\"C1\")\n\t\t\t\to(iterC.next().value.nodeName).equals(\"C2\")\n\t\t\t\to(iterC.next().done).deepEquals(true)\n\n\t\t\t\t// resolve B\n\t\t\t\tthenCBB()\n\t\t\t\tcallAsync(function(){\n\t\t\t\t\to(root.childNodes.length).equals(2)\n\t\t\t\t\to(root.childNodes[0].nodeName).equals(\"C1\")\n\t\t\t\t\to(root.childNodes[1].nodeName).equals(\"C2\")\n\t\t\t\t\t\n\t\t\t\t\tconst iterC = domFor(C)\n\t\t\t\t\to(iterC.next().value.nodeName).equals(\"C1\")\n\t\t\t\t\to(iterC.next().value.nodeName).equals(\"C2\")\n\t\t\t\t\to(iterC.next().done).deepEquals(true)\n\n\t\t\t\t\t// resolve C\n\t\t\t\t\tthenCBC()\n\t\t\t\t\tcallAsync(function(){\n\t\t\t\t\t\to(root.childNodes.length).equals(0)\n\t\t\t\t\t\tdone()\n\t\t\t\t\t})\n\t\t\t\t})\n\t\t\t})\n\t\t})\n\t})\n\to(\"works multiple vnodes with onbeforeremove (#3007, 4/6, ACB)\", function (done) {\n\t\tlet thenCBA, thenCBB, thenCBC\n\t\tconst onbeforeremoveA = o.spy(function onbeforeremove(){\n\t\t\treturn {then(resolve){thenCBA = resolve}}\n\t\t})\n\t\tconst onbeforeremoveB = o.spy(function onbeforeremove(){\n\t\t\treturn {then(resolve){thenCBB = resolve}}\n\t\t})\n\t\tconst onbeforeremoveC = o.spy(function onbeforeremove(){\n\t\t\treturn {then(resolve){thenCBC = resolve}}\n\t\t})\n\t\t// to avoid updating internal nodes only, vnodes have key attributes\n\t\tconst A = fragment({key: 1, onbeforeremove: onbeforeremoveA}, [m(\"a1\"), m(\"a2\")])\n\t\tconst B = fragment({key: 2, onbeforeremove: onbeforeremoveB}, [m(\"b1\"), m(\"b2\")])\n\t\tconst C = fragment({key: 3, onbeforeremove: onbeforeremoveC}, [m(\"c1\"), m(\"c2\")])\n\n\t\trender(root, [A])\n\t\to(onbeforeremoveA.callCount).equals(0)\n\t\to(onbeforeremoveB.callCount).equals(0)\n\t\to(onbeforeremoveC.callCount).equals(0)\n\n\t\trender(root, [B])\n\t\to(onbeforeremoveA.callCount).equals(1)\n\t\to(onbeforeremoveB.callCount).equals(0)\n\t\to(onbeforeremoveC.callCount).equals(0)\n\n\t\trender(root, [C])\n\t\to(onbeforeremoveA.callCount).equals(1)\n\t\to(onbeforeremoveB.callCount).equals(1)\n\t\to(onbeforeremoveC.callCount).equals(0)\n\n\t\trender(root, [])\n\t\to(onbeforeremoveA.callCount).equals(1)\n\t\to(onbeforeremoveB.callCount).equals(1)\n\t\to(onbeforeremoveC.callCount).equals(1)\n\n\t\t// not resolved\n\t\to(root.childNodes.length).equals(6)\n\t\to(root.childNodes[0].nodeName).equals(\"A1\")\n\t\to(root.childNodes[1].nodeName).equals(\"A2\")\n\t\to(root.childNodes[2].nodeName).equals(\"B1\")\n\t\to(root.childNodes[3].nodeName).equals(\"B2\")\n\t\to(root.childNodes[4].nodeName).equals(\"C1\")\n\t\to(root.childNodes[5].nodeName).equals(\"C2\")\n\n\t\tconst iterA = domFor(A)\n\t\to(iterA.next().value.nodeName).equals(\"A1\")\n\t\to(iterA.next().value.nodeName).equals(\"A2\")\n\t\to(iterA.next().done).deepEquals(true)\n\n\t\tconst iterB = domFor(B)\n\t\to(iterB.next().value.nodeName).equals(\"B1\")\n\t\to(iterB.next().value.nodeName).equals(\"B2\")\n\t\to(iterB.next().done).deepEquals(true)\n\n\t\tconst iterC = domFor(C)\n\t\to(iterC.next().value.nodeName).equals(\"C1\")\n\t\to(iterC.next().value.nodeName).equals(\"C2\")\n\t\to(iterC.next().done).deepEquals(true)\n\n\t\tcallAsync(function(){\n\t\t\t// not resolved yet\n\t\t\to(root.childNodes.length).equals(6)\n\t\t\to(root.childNodes[0].nodeName).equals(\"A1\")\n\t\t\to(root.childNodes[1].nodeName).equals(\"A2\")\n\t\t\to(root.childNodes[2].nodeName).equals(\"B1\")\n\t\t\to(root.childNodes[3].nodeName).equals(\"B2\")\n\t\t\to(root.childNodes[4].nodeName).equals(\"C1\")\n\t\t\to(root.childNodes[5].nodeName).equals(\"C2\")\n\t\n\t\t\tconst iterA = domFor(A)\n\t\t\to(iterA.next().value.nodeName).equals(\"A1\")\n\t\t\to(iterA.next().value.nodeName).equals(\"A2\")\n\t\t\to(iterA.next().done).deepEquals(true)\n\t\n\t\t\tconst iterB = domFor(B)\n\t\t\to(iterB.next().value.nodeName).equals(\"B1\")\n\t\t\to(iterB.next().value.nodeName).equals(\"B2\")\n\t\t\to(iterB.next().done).deepEquals(true)\n\t\n\t\t\tconst iterC = domFor(C)\n\t\t\to(iterC.next().value.nodeName).equals(\"C1\")\n\t\t\to(iterC.next().value.nodeName).equals(\"C2\")\n\t\t\to(iterC.next().done).deepEquals(true)\n\n\t\t\t// resolve A\n\t\t\tthenCBA()\n\t\t\tcallAsync(function(){\n\t\t\t\to(root.childNodes.length).equals(4)\n\t\t\t\to(root.childNodes[0].nodeName).equals(\"B1\")\n\t\t\t\to(root.childNodes[1].nodeName).equals(\"B2\")\n\t\t\t\to(root.childNodes[2].nodeName).equals(\"C1\")\n\t\t\t\to(root.childNodes[3].nodeName).equals(\"C2\")\n\n\t\t\t\tconst iterB = domFor(B)\n\t\t\t\to(iterB.next().value.nodeName).equals(\"B1\")\n\t\t\t\to(iterB.next().value.nodeName).equals(\"B2\")\n\t\t\t\to(iterB.next().done).deepEquals(true)\n\n\t\t\t\tconst iterC = domFor(C)\n\t\t\t\to(iterC.next().value.nodeName).equals(\"C1\")\n\t\t\t\to(iterC.next().value.nodeName).equals(\"C2\")\n\t\t\t\to(iterC.next().done).deepEquals(true)\n\n\t\t\t\t// resolve C\n\t\t\t\tthenCBC()\n\t\t\t\tcallAsync(function(){\n\t\t\t\t\to(root.childNodes.length).equals(2)\n\t\t\t\t\to(root.childNodes[0].nodeName).equals(\"B1\")\n\t\t\t\t\to(root.childNodes[1].nodeName).equals(\"B2\")\n\t\t\t\t\t\n\t\t\t\t\tconst iterC = domFor(B)\n\t\t\t\t\to(iterC.next().value.nodeName).equals(\"B1\")\n\t\t\t\t\to(iterC.next().value.nodeName).equals(\"B2\")\n\t\t\t\t\to(iterC.next().done).deepEquals(true)\n\n\t\t\t\t\t// resolve B\n\t\t\t\t\tthenCBB()\n\t\t\t\t\tcallAsync(function(){\n\t\t\t\t\t\to(root.childNodes.length).equals(0)\n\t\t\t\t\t\tdone()\n\t\t\t\t\t})\n\t\t\t\t})\n\t\t\t})\n\t\t})\n\t})\n\to(\"works multiple vnodes with onbeforeremove (#3007, 5/6, BAC)\", function (done) {\n\t\tlet thenCBA, thenCBB, thenCBC\n\t\tconst onbeforeremoveA = o.spy(function onbeforeremove(){\n\t\t\treturn {then(resolve){thenCBA = resolve}}\n\t\t})\n\t\tconst onbeforeremoveB = o.spy(function onbeforeremove(){\n\t\t\treturn {then(resolve){thenCBB = resolve}}\n\t\t})\n\t\tconst onbeforeremoveC = o.spy(function onbeforeremove(){\n\t\t\treturn {then(resolve){thenCBC = resolve}}\n\t\t})\n\t\t// to avoid updating internal nodes only, vnodes have key attributes\n\t\tconst A = fragment({key: 1, onbeforeremove: onbeforeremoveA}, [m(\"a1\"), m(\"a2\")])\n\t\tconst B = fragment({key: 2, onbeforeremove: onbeforeremoveB}, [m(\"b1\"), m(\"b2\")])\n\t\tconst C = fragment({key: 3, onbeforeremove: onbeforeremoveC}, [m(\"c1\"), m(\"c2\")])\n\n\t\trender(root, [A])\n\t\to(onbeforeremoveA.callCount).equals(0)\n\t\to(onbeforeremoveB.callCount).equals(0)\n\t\to(onbeforeremoveC.callCount).equals(0)\n\n\t\trender(root, [B])\n\t\to(onbeforeremoveA.callCount).equals(1)\n\t\to(onbeforeremoveB.callCount).equals(0)\n\t\to(onbeforeremoveC.callCount).equals(0)\n\n\t\trender(root, [C])\n\t\to(onbeforeremoveA.callCount).equals(1)\n\t\to(onbeforeremoveB.callCount).equals(1)\n\t\to(onbeforeremoveC.callCount).equals(0)\n\n\t\trender(root, [])\n\t\to(onbeforeremoveA.callCount).equals(1)\n\t\to(onbeforeremoveB.callCount).equals(1)\n\t\to(onbeforeremoveC.callCount).equals(1)\n\n\t\t// not resolved\n\t\to(root.childNodes.length).equals(6)\n\t\to(root.childNodes[0].nodeName).equals(\"A1\")\n\t\to(root.childNodes[1].nodeName).equals(\"A2\")\n\t\to(root.childNodes[2].nodeName).equals(\"B1\")\n\t\to(root.childNodes[3].nodeName).equals(\"B2\")\n\t\to(root.childNodes[4].nodeName).equals(\"C1\")\n\t\to(root.childNodes[5].nodeName).equals(\"C2\")\n\n\t\tconst iterA = domFor(A)\n\t\to(iterA.next().value.nodeName).equals(\"A1\")\n\t\to(iterA.next().value.nodeName).equals(\"A2\")\n\t\to(iterA.next().done).deepEquals(true)\n\n\t\tconst iterB = domFor(B)\n\t\to(iterB.next().value.nodeName).equals(\"B1\")\n\t\to(iterB.next().value.nodeName).equals(\"B2\")\n\t\to(iterB.next().done).deepEquals(true)\n\n\t\tconst iterC = domFor(C)\n\t\to(iterC.next().value.nodeName).equals(\"C1\")\n\t\to(iterC.next().value.nodeName).equals(\"C2\")\n\t\to(iterC.next().done).deepEquals(true)\n\n\t\tcallAsync(function(){\n\t\t\t// not resolved yet\n\t\t\to(root.childNodes.length).equals(6)\n\t\t\to(root.childNodes[0].nodeName).equals(\"A1\")\n\t\t\to(root.childNodes[1].nodeName).equals(\"A2\")\n\t\t\to(root.childNodes[2].nodeName).equals(\"B1\")\n\t\t\to(root.childNodes[3].nodeName).equals(\"B2\")\n\t\t\to(root.childNodes[4].nodeName).equals(\"C1\")\n\t\t\to(root.childNodes[5].nodeName).equals(\"C2\")\n\t\n\t\t\tconst iterA = domFor(A)\n\t\t\to(iterA.next().value.nodeName).equals(\"A1\")\n\t\t\to(iterA.next().value.nodeName).equals(\"A2\")\n\t\t\to(iterA.next().done).deepEquals(true)\n\t\n\t\t\tconst iterB = domFor(B)\n\t\t\to(iterB.next().value.nodeName).equals(\"B1\")\n\t\t\to(iterB.next().value.nodeName).equals(\"B2\")\n\t\t\to(iterB.next().done).deepEquals(true)\n\t\n\t\t\tconst iterC = domFor(C)\n\t\t\to(iterC.next().value.nodeName).equals(\"C1\")\n\t\t\to(iterC.next().value.nodeName).equals(\"C2\")\n\t\t\to(iterC.next().done).deepEquals(true)\n\n\t\t\t// resolve B\n\t\t\tthenCBB()\n\t\t\tcallAsync(function(){\n\t\t\t\to(root.childNodes.length).equals(4)\n\t\t\t\to(root.childNodes[0].nodeName).equals(\"A1\")\n\t\t\t\to(root.childNodes[1].nodeName).equals(\"A2\")\n\t\t\t\to(root.childNodes[2].nodeName).equals(\"C1\")\n\t\t\t\to(root.childNodes[3].nodeName).equals(\"C2\")\n\n\t\t\t\tconst iterB = domFor(A)\n\t\t\t\to(iterB.next().value.nodeName).equals(\"A1\")\n\t\t\t\to(iterB.next().value.nodeName).equals(\"A2\")\n\t\t\t\to(iterB.next().done).deepEquals(true)\n\n\t\t\t\tconst iterC = domFor(C)\n\t\t\t\to(iterC.next().value.nodeName).equals(\"C1\")\n\t\t\t\to(iterC.next().value.nodeName).equals(\"C2\")\n\t\t\t\to(iterC.next().done).deepEquals(true)\n\n\t\t\t\t// resolve A\n\t\t\t\tthenCBA()\n\t\t\t\tcallAsync(function(){\n\t\t\t\t\to(root.childNodes.length).equals(2)\n\t\t\t\t\to(root.childNodes[0].nodeName).equals(\"C1\")\n\t\t\t\t\to(root.childNodes[1].nodeName).equals(\"C2\")\n\t\t\t\t\t\n\t\t\t\t\tconst iterC = domFor(C)\n\t\t\t\t\to(iterC.next().value.nodeName).equals(\"C1\")\n\t\t\t\t\to(iterC.next().value.nodeName).equals(\"C2\")\n\t\t\t\t\to(iterC.next().done).deepEquals(true)\n\n\t\t\t\t\t// resolve C\n\t\t\t\t\tthenCBC()\n\t\t\t\t\tcallAsync(function(){\n\t\t\t\t\t\to(root.childNodes.length).equals(0)\n\t\t\t\t\t\tdone()\n\t\t\t\t\t})\n\t\t\t\t})\n\t\t\t})\n\t\t})\n\t})\n\to(\"works multiple vnodes with onbeforeremove (#3007, 6/6, CBA)\", function (done) {\n\t\tlet thenCBA, thenCBB, thenCBC\n\t\tconst onbeforeremoveA = o.spy(function onbeforeremove(){\n\t\t\treturn {then(resolve){thenCBA = resolve}}\n\t\t})\n\t\tconst onbeforeremoveB = o.spy(function onbeforeremove(){\n\t\t\treturn {then(resolve){thenCBB = resolve}}\n\t\t})\n\t\tconst onbeforeremoveC = o.spy(function onbeforeremove(){\n\t\t\treturn {then(resolve){thenCBC = resolve}}\n\t\t})\n\t\t// to avoid updating internal nodes only, vnodes have key attributes\n\t\tconst A = fragment({key: 1, onbeforeremove: onbeforeremoveA}, [m(\"a1\"), m(\"a2\")])\n\t\tconst B = fragment({key: 2, onbeforeremove: onbeforeremoveB}, [m(\"b1\"), m(\"b2\")])\n\t\tconst C = fragment({key: 3, onbeforeremove: onbeforeremoveC}, [m(\"c1\"), m(\"c2\")])\n\n\t\trender(root, [A])\n\t\to(onbeforeremoveA.callCount).equals(0)\n\t\to(onbeforeremoveB.callCount).equals(0)\n\t\to(onbeforeremoveC.callCount).equals(0)\n\n\t\trender(root, [B])\n\t\to(onbeforeremoveA.callCount).equals(1)\n\t\to(onbeforeremoveB.callCount).equals(0)\n\t\to(onbeforeremoveC.callCount).equals(0)\n\n\t\trender(root, [C])\n\t\to(onbeforeremoveA.callCount).equals(1)\n\t\to(onbeforeremoveB.callCount).equals(1)\n\t\to(onbeforeremoveC.callCount).equals(0)\n\n\t\trender(root, [])\n\t\to(onbeforeremoveA.callCount).equals(1)\n\t\to(onbeforeremoveB.callCount).equals(1)\n\t\to(onbeforeremoveC.callCount).equals(1)\n\n\t\t// not resolved\n\t\to(root.childNodes.length).equals(6)\n\t\to(root.childNodes[0].nodeName).equals(\"A1\")\n\t\to(root.childNodes[1].nodeName).equals(\"A2\")\n\t\to(root.childNodes[2].nodeName).equals(\"B1\")\n\t\to(root.childNodes[3].nodeName).equals(\"B2\")\n\t\to(root.childNodes[4].nodeName).equals(\"C1\")\n\t\to(root.childNodes[5].nodeName).equals(\"C2\")\n\n\t\tconst iterA = domFor(A)\n\t\to(iterA.next().value.nodeName).equals(\"A1\")\n\t\to(iterA.next().value.nodeName).equals(\"A2\")\n\t\to(iterA.next().done).deepEquals(true)\n\n\t\tconst iterB = domFor(B)\n\t\to(iterB.next().value.nodeName).equals(\"B1\")\n\t\to(iterB.next().value.nodeName).equals(\"B2\")\n\t\to(iterB.next().done).deepEquals(true)\n\n\t\tconst iterC = domFor(C)\n\t\to(iterC.next().value.nodeName).equals(\"C1\")\n\t\to(iterC.next().value.nodeName).equals(\"C2\")\n\t\to(iterC.next().done).deepEquals(true)\n\n\t\tcallAsync(function(){\n\t\t\t// not resolved yet\n\t\t\to(root.childNodes.length).equals(6)\n\t\t\to(root.childNodes[0].nodeName).equals(\"A1\")\n\t\t\to(root.childNodes[1].nodeName).equals(\"A2\")\n\t\t\to(root.childNodes[2].nodeName).equals(\"B1\")\n\t\t\to(root.childNodes[3].nodeName).equals(\"B2\")\n\t\t\to(root.childNodes[4].nodeName).equals(\"C1\")\n\t\t\to(root.childNodes[5].nodeName).equals(\"C2\")\n\t\n\t\t\tconst iterA = domFor(A)\n\t\t\to(iterA.next().value.nodeName).equals(\"A1\")\n\t\t\to(iterA.next().value.nodeName).equals(\"A2\")\n\t\t\to(iterA.next().done).deepEquals(true)\n\t\n\t\t\tconst iterB = domFor(B)\n\t\t\to(iterB.next().value.nodeName).equals(\"B1\")\n\t\t\to(iterB.next().value.nodeName).equals(\"B2\")\n\t\t\to(iterB.next().done).deepEquals(true)\n\t\n\t\t\tconst iterC = domFor(C)\n\t\t\to(iterC.next().value.nodeName).equals(\"C1\")\n\t\t\to(iterC.next().value.nodeName).equals(\"C2\")\n\t\t\to(iterC.next().done).deepEquals(true)\n\n\t\t\t// resolve C\n\t\t\tthenCBC()\n\t\t\tcallAsync(function(){\n\t\t\t\to(root.childNodes.length).equals(4)\n\t\t\t\to(root.childNodes[0].nodeName).equals(\"A1\")\n\t\t\t\to(root.childNodes[1].nodeName).equals(\"A2\")\n\t\t\t\to(root.childNodes[2].nodeName).equals(\"B1\")\n\t\t\t\to(root.childNodes[3].nodeName).equals(\"B2\")\n\n\t\t\t\tconst iterB = domFor(A)\n\t\t\t\to(iterB.next().value.nodeName).equals(\"A1\")\n\t\t\t\to(iterB.next().value.nodeName).equals(\"A2\")\n\t\t\t\to(iterB.next().done).deepEquals(true)\n\n\t\t\t\tconst iterC = domFor(B)\n\t\t\t\to(iterC.next().value.nodeName).equals(\"B1\")\n\t\t\t\to(iterC.next().value.nodeName).equals(\"B2\")\n\t\t\t\to(iterC.next().done).deepEquals(true)\n\n\t\t\t\t// resolve B\n\t\t\t\tthenCBB()\n\t\t\t\tcallAsync(function(){\n\t\t\t\t\to(root.childNodes.length).equals(2)\n\t\t\t\t\to(root.childNodes[0].nodeName).equals(\"A1\")\n\t\t\t\t\to(root.childNodes[1].nodeName).equals(\"A2\")\n\t\t\t\t\t\n\t\t\t\t\tconst iterC = domFor(A)\n\t\t\t\t\to(iterC.next().value.nodeName).equals(\"A1\")\n\t\t\t\t\to(iterC.next().value.nodeName).equals(\"A2\")\n\t\t\t\t\to(iterC.next().done).deepEquals(true)\n\n\t\t\t\t\t// resolve A\n\t\t\t\t\tthenCBA()\n\t\t\t\t\tcallAsync(function(){\n\t\t\t\t\t\to(root.childNodes.length).equals(0)\n\t\t\t\t\t\tdone()\n\t\t\t\t\t})\n\t\t\t\t})\n\t\t\t})\n\t\t})\n\t})\n\tcomponents.forEach(function(cmp){\n\t\tconst {kind, create: createComponent} = cmp\n\t\to.spec(kind, function(){\n\t\t\to(\"works for components that return one element\", function() {\n\t\t\t\tconst C = createComponent({\n\t\t\t\t\tview(){return m(\"div\")},\n\t\t\t\t\toncreate(vnode){\n\t\t\t\t\t\tlet n = 0\n\t\t\t\t\t\tfor (const dom of domFor(vnode)) {\n\t\t\t\t\t\t\to(dom).equals(root.firstChild)\n\t\t\t\t\t\t\to(++n).equals(1)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t\trender(root, m(C))\n\t\t\t})\n\t\t\to(\"works for components that return fragments\", function () {\n\t\t\t\tconst oncreate = o.spy(function oncreate(vnode){\n\t\t\t\t\to(root.childNodes.length).equals(3)\n\t\t\t\t\to(root.childNodes[0].nodeName).equals(\"A\")\n\t\t\t\t\to(root.childNodes[1].nodeName).equals(\"B\")\n\t\t\t\t\to(root.childNodes[2].nodeName).equals(\"C\")\n\n\t\t\t\t\tconst iter = domFor(vnode)\n\t\t\t\t\to(iter.next()).deepEquals({done:false, value: root.childNodes[0]})\n\t\t\t\t\to(iter.next()).deepEquals({done:false, value: root.childNodes[1]})\n\t\t\t\t\to(iter.next()).deepEquals({done:false, value: root.childNodes[2]})\n\t\t\t\t\to(iter.next().done).deepEquals(true)\n\t\t\t\t\to(root.childNodes.length).equals(3)\n\t\t\t\t})\n\t\t\t\tconst C = createComponent({\n\t\t\t\t\tview({children}){return children},\n\t\t\t\t\toncreate\n\t\t\t\t})\n\t\t\t\trender(root, m(C, [\n\t\t\t\t\tm(\"a\"),\n\t\t\t\t\tm(\"b\"),\n\t\t\t\t\tm(\"c\")\n\t\t\t\t]))\n\t\t\t\to(oncreate.callCount).equals(1)\n\t\t\t})\n\t\t\to(\"works for components that return fragments with delayed removal\", function () {\n\t\t\t\tconst onbeforeremove = o.spy(function onbeforeremove(){return {then(){}, finally(){}}})\n\t\t\t\tconst oncreate = o.spy(function oncreate(vnode){\n\t\t\t\t\to(root.childNodes.length).equals(3)\n\t\t\t\t\to(root.childNodes[0].nodeName).equals(\"A\")\n\t\t\t\t\to(root.childNodes[1].nodeName).equals(\"B\")\n\t\t\t\t\to(root.childNodes[2].nodeName).equals(\"C\")\n\n\t\t\t\t\tconst iter = domFor(vnode)\n\t\t\t\t\to(iter.next()).deepEquals({done:false, value: root.childNodes[0]})\n\t\t\t\t\to(iter.next()).deepEquals({done:false, value: root.childNodes[1]})\n\t\t\t\t\to(iter.next()).deepEquals({done:false, value: root.childNodes[2]})\n\t\t\t\t\to(iter.next().done).deepEquals(true)\n\t\t\t\t\to(root.childNodes.length).equals(3)\n\t\t\t\t})\n\t\t\t\tconst onupdate = o.spy(function onupdate(vnode) {\n\t\t\t\t\to(root.childNodes.length).equals(3)\n\t\t\t\t\to(root.childNodes[0].nodeName).equals(\"A\")\n\t\t\t\t\to(root.childNodes[1].nodeName).equals(\"B\")\n\t\t\t\t\to(root.childNodes[2].nodeName).equals(\"C\")\n\n\t\t\t\t\tconst iter = domFor(vnode)\n\n\t\t\t\t\to(iter.next()).deepEquals({done:false, value: root.childNodes[0]})\n\t\t\t\t\to(iter.next()).deepEquals({done:false, value: root.childNodes[2]})\n\t\t\t\t\to(iter.next().done).deepEquals(true)\n\t\t\t\t\to(root.childNodes.length).equals(3)\n\t\t\t\t})\n\t\t\t\tconst C = createComponent({\n\t\t\t\t\tview({children}){return children},\n\t\t\t\t\toncreate,\n\t\t\t\t\tonupdate\n\t\t\t\t})\n\t\t\t\trender(root, m(C, [\n\t\t\t\t\tm(\"a\"),\n\t\t\t\t\tm(\"b\", {onbeforeremove}),\n\t\t\t\t\tm(\"c\")\n\t\t\t\t]))\n\t\t\t\trender(root, m(C, [\n\t\t\t\t\tm(\"a\"),\n\t\t\t\t\tnull,\n\t\t\t\t\tm(\"c\")\n\t\t\t\t]))\n\t\t\t\to(oncreate.callCount).equals(1)\n\t\t\t\to(onupdate.callCount).equals(1)\n\t\t\t\to(onbeforeremove.callCount).equals(1)\n\t\t\t})\n\t\t\to(\"works in state.onbeforeremove and attrs.onbeforeremove\", function () {\n\t\t\t\tconst onbeforeremove = o.spy(function onbeforeremove(vnode){\n\t\t\t\t\to(root.childNodes.length).equals(3)\n\t\t\t\t\to(root.childNodes[0].nodeName).equals(\"A\")\n\t\t\t\t\to(root.childNodes[1].nodeName).equals(\"B\")\n\t\t\t\t\to(root.childNodes[2].nodeName).equals(\"C\")\n\t\t\t\t\tconst iter = domFor(vnode)\n\t\t\t\t\to(iter.next()).deepEquals({done:false, value: root.childNodes[0]})\n\t\t\t\t\to(iter.next()).deepEquals({done:false, value: root.childNodes[1]})\n\t\t\t\t\to(iter.next()).deepEquals({done:false, value: root.childNodes[2]})\n\t\t\t\t\to(iter.next().done).deepEquals(true)\n\t\t\t\t\to(root.childNodes.length).equals(3)\n\t\t\t\t\treturn {then(){}, finally(){}}\n\t\t\t\t})\n\t\t\t\tconst C = createComponent({\n\t\t\t\t\tview({children}){return children},\n\t\t\t\t\tonbeforeremove\n\t\t\t\t})\n\t\t\t\trender(root, m(C, {onbeforeremove}, [\n\t\t\t\t\tm(\"a\"),\n\t\t\t\t\tm(\"b\"),\n\t\t\t\t\tm(\"c\")\n\t\t\t\t]))\n\t\t\t\trender(root, [])\n\n\t\t\t\to(onbeforeremove.callCount).equals(2)\n\t\t\t})\n\t\t})\n\t})\n})"
  },
  {
    "path": "render/tests/test-event.js",
    "content": "\"use strict\"\n\nvar o = require(\"ospec\")\nvar callAsync = require(\"../../test-utils/callAsync\")\nvar domMock = require(\"../../test-utils/domMock\")\nvar vdom = require(\"../../render/render\")\nvar m = require(\"../../render/hyperscript\")\n\no.spec(\"event\", function() {\n\tvar $window, root, redraw, render, reallyRender\n\to.beforeEach(function() {\n\t\t$window = domMock()\n\t\troot = $window.document.body\n\t\tredraw = o.spy()\n\t\treallyRender = vdom($window)\n\t\trender = function(dom, vnode) {\n\t\t\treturn reallyRender(dom, vnode, redraw)\n\t\t}\n\t})\n\n\tfunction eventSpy(fn) {\n\t\tfunction spy(e) {\n\t\t\tspy.calls.push({\n\t\t\t\tthis: this, type: e.type,\n\t\t\t\ttarget: e.target, currentTarget: e.currentTarget,\n\t\t\t})\n\t\t\tif (fn) return fn.apply(this, arguments)\n\t\t}\n\t\tspy.calls = []\n\t\treturn spy\n\t}\n\n\to(\"handles onclick\", function() {\n\t\tvar spyDiv = eventSpy()\n\t\tvar spyParent = eventSpy()\n\t\tvar div = m(\"div\", {onclick: spyDiv})\n\t\tvar parent = m(\"div\", {onclick: spyParent}, div)\n\t\tvar e = $window.document.createEvent(\"MouseEvents\")\n\t\te.initEvent(\"click\", true, true)\n\n\t\trender(root, parent)\n\t\tdiv.dom.dispatchEvent(e)\n\n\t\to(spyDiv.calls.length).equals(1)\n\t\to(spyDiv.calls[0].this).equals(div.dom)\n\t\to(spyDiv.calls[0].type).equals(\"click\")\n\t\to(spyDiv.calls[0].target).equals(div.dom)\n\t\to(spyDiv.calls[0].currentTarget).equals(div.dom)\n\t\to(spyParent.calls.length).equals(1)\n\t\to(spyParent.calls[0].this).equals(parent.dom)\n\t\to(spyParent.calls[0].type).equals(\"click\")\n\t\to(spyParent.calls[0].target).equals(div.dom)\n\t\to(spyParent.calls[0].currentTarget).equals(parent.dom)\n\t\to(redraw.callCount).equals(2)\n\t\to(redraw.this).equals(undefined)\n\t\to(redraw.args.length).equals(0)\n\t\to(e.defaultPrevented).equals(false)\n\t})\n\n\to(\"handles onclick returning false\", function() {\n\t\tvar spyDiv = eventSpy(function() { return false })\n\t\tvar spyParent = eventSpy()\n\t\tvar div = m(\"div\", {onclick: spyDiv})\n\t\tvar parent = m(\"div\", {onclick: spyParent}, div)\n\t\tvar e = $window.document.createEvent(\"MouseEvents\")\n\t\te.initEvent(\"click\", true, true)\n\n\t\trender(root, parent)\n\t\tdiv.dom.dispatchEvent(e)\n\n\t\to(spyDiv.calls.length).equals(1)\n\t\to(spyDiv.calls[0].this).equals(div.dom)\n\t\to(spyDiv.calls[0].type).equals(\"click\")\n\t\to(spyDiv.calls[0].target).equals(div.dom)\n\t\to(spyDiv.calls[0].currentTarget).equals(div.dom)\n\t\to(spyParent.calls.length).equals(0)\n\t\to(redraw.callCount).equals(1)\n\t\to(redraw.this).equals(undefined)\n\t\to(redraw.args.length).equals(0)\n\t\to(e.defaultPrevented).equals(true)\n\t})\n\n\to(\"handles click EventListener object\", function() {\n\t\tvar spyDiv = eventSpy()\n\t\tvar spyParent = eventSpy()\n\t\tvar listenerDiv = {handleEvent: spyDiv}\n\t\tvar listenerParent = {handleEvent: spyParent}\n\t\tvar div = m(\"div\", {onclick: listenerDiv})\n\t\tvar parent = m(\"div\", {onclick: listenerParent}, div)\n\t\tvar e = $window.document.createEvent(\"MouseEvents\")\n\t\te.initEvent(\"click\", true, true)\n\n\t\trender(root, parent)\n\t\tdiv.dom.dispatchEvent(e)\n\n\t\to(spyDiv.calls.length).equals(1)\n\t\to(spyDiv.calls[0].this).equals(listenerDiv)\n\t\to(spyDiv.calls[0].type).equals(\"click\")\n\t\to(spyDiv.calls[0].target).equals(div.dom)\n\t\to(spyDiv.calls[0].currentTarget).equals(div.dom)\n\t\to(spyParent.calls.length).equals(1)\n\t\to(spyParent.calls[0].this).equals(listenerParent)\n\t\to(spyParent.calls[0].type).equals(\"click\")\n\t\to(spyParent.calls[0].target).equals(div.dom)\n\t\to(spyParent.calls[0].currentTarget).equals(parent.dom)\n\t\to(redraw.callCount).equals(2)\n\t\to(redraw.this).equals(undefined)\n\t\to(redraw.args.length).equals(0)\n\t\to(e.defaultPrevented).equals(false)\n\t})\n\n\to(\"handles click EventListener object returning false\", function() {\n\t\tvar spyDiv = eventSpy(function() { return false })\n\t\tvar spyParent = eventSpy()\n\t\tvar listenerDiv = {handleEvent: spyDiv}\n\t\tvar listenerParent = {handleEvent: spyParent}\n\t\tvar div = m(\"div\", {onclick: listenerDiv})\n\t\tvar parent = m(\"div\", {onclick: listenerParent}, div)\n\t\tvar e = $window.document.createEvent(\"MouseEvents\")\n\t\te.initEvent(\"click\", true, true)\n\n\t\trender(root, parent)\n\t\tdiv.dom.dispatchEvent(e)\n\n\t\to(spyDiv.calls.length).equals(1)\n\t\to(spyDiv.calls[0].this).equals(listenerDiv)\n\t\to(spyDiv.calls[0].type).equals(\"click\")\n\t\to(spyDiv.calls[0].target).equals(div.dom)\n\t\to(spyDiv.calls[0].currentTarget).equals(div.dom)\n\t\to(spyParent.calls.length).equals(1)\n\t\to(spyParent.calls[0].this).equals(listenerParent)\n\t\to(spyParent.calls[0].type).equals(\"click\")\n\t\to(spyParent.calls[0].target).equals(div.dom)\n\t\to(spyParent.calls[0].currentTarget).equals(parent.dom)\n\t\to(redraw.callCount).equals(2)\n\t\to(redraw.this).equals(undefined)\n\t\to(redraw.args.length).equals(0)\n\t\to(e.defaultPrevented).equals(false)\n\t})\n\n\to(\"removes event\", function() {\n\t\tvar spy = o.spy()\n\t\tvar vnode = m(\"a\", {onclick: spy})\n\t\tvar updated = m(\"a\")\n\n\t\trender(root, vnode)\n\t\trender(root, updated)\n\n\t\tvar e = $window.document.createEvent(\"MouseEvents\")\n\t\te.initEvent(\"click\", true, true)\n\t\tvnode.dom.dispatchEvent(e)\n\n\t\to(spy.callCount).equals(0)\n\t})\n\n\to(\"removes event when null\", function() {\n\t\tvar spy = o.spy()\n\t\tvar vnode = m(\"a\", {onclick: spy})\n\t\tvar updated = m(\"a\", {onclick: null})\n\n\t\trender(root, vnode)\n\t\trender(root, updated)\n\n\t\tvar e = $window.document.createEvent(\"MouseEvents\")\n\t\te.initEvent(\"click\", true, true)\n\t\tvnode.dom.dispatchEvent(e)\n\n\t\to(spy.callCount).equals(0)\n\t})\n\n\to(\"removes event when undefined\", function() {\n\t\tvar spy = o.spy()\n\t\tvar vnode = m(\"a\", {onclick: spy})\n\t\tvar updated = m(\"a\", {onclick: undefined})\n\n\t\trender(root, vnode)\n\t\trender(root, updated)\n\n\t\tvar e = $window.document.createEvent(\"MouseEvents\")\n\t\te.initEvent(\"click\", true, true)\n\t\tvnode.dom.dispatchEvent(e)\n\n\t\to(spy.callCount).equals(0)\n\t})\n\n\to(\"removes event added via addEventListener when null\", function() {\n\t\tvar spy = o.spy()\n\t\tvar vnode = m(\"a\", {ontouchstart: spy})\n\t\tvar updated = m(\"a\", {ontouchstart: null})\n\n\t\trender(root, vnode)\n\t\trender(root, updated)\n\n\t\tvar e = $window.document.createEvent(\"TouchEvents\")\n\t\te.initEvent(\"touchstart\", true, true)\n\t\tvnode.dom.dispatchEvent(e)\n\n\t\to(spy.callCount).equals(0)\n\t})\n\n\to(\"removes event added via addEventListener\", function() {\n\t\tvar spy = o.spy()\n\t\tvar vnode = m(\"a\", {ontouchstart: spy})\n\t\tvar updated = m(\"a\")\n\n\t\trender(root, vnode)\n\t\trender(root, updated)\n\n\t\tvar e = $window.document.createEvent(\"TouchEvents\")\n\t\te.initEvent(\"touchstart\", true, true)\n\t\tvnode.dom.dispatchEvent(e)\n\n\t\to(spy.callCount).equals(0)\n\t})\n\n\to(\"removes event added via addEventListener when undefined\", function() {\n\t\tvar spy = o.spy()\n\t\tvar vnode = m(\"a\", {ontouchstart: spy})\n\t\tvar updated = m(\"a\", {ontouchstart: undefined})\n\n\t\trender(root, vnode)\n\t\trender(root, updated)\n\n\t\tvar e = $window.document.createEvent(\"TouchEvents\")\n\t\te.initEvent(\"touchstart\", true, true)\n\t\tvnode.dom.dispatchEvent(e)\n\n\t\to(spy.callCount).equals(0)\n\t})\n\n\to(\"removes EventListener object\", function() {\n\t\tvar spy = o.spy()\n\t\tvar listener = {handleEvent: spy}\n\t\tvar vnode = m(\"a\", {onclick: listener})\n\t\tvar updated = m(\"a\")\n\n\t\trender(root, vnode)\n\t\trender(root, updated)\n\n\t\tvar e = $window.document.createEvent(\"MouseEvents\")\n\t\te.initEvent(\"click\", true, true)\n\t\tvnode.dom.dispatchEvent(e)\n\n\t\to(spy.callCount).equals(0)\n\t})\n\n\to(\"removes EventListener object when null\", function() {\n\t\tvar spy = o.spy()\n\t\tvar listener = {handleEvent: spy}\n\t\tvar vnode = m(\"a\", {onclick: listener})\n\t\tvar updated = m(\"a\", {onclick: null})\n\n\t\trender(root, vnode)\n\t\trender(root, updated)\n\n\t\tvar e = $window.document.createEvent(\"MouseEvents\")\n\t\te.initEvent(\"click\", true, true)\n\t\tvnode.dom.dispatchEvent(e)\n\n\t\to(spy.callCount).equals(0)\n\t})\n\n\to(\"removes EventListener object when undefined\", function() {\n\t\tvar spy = o.spy()\n\t\tvar listener = {handleEvent: spy}\n\t\tvar vnode = m(\"a\", {onclick: listener})\n\t\tvar updated = m(\"a\", {onclick: undefined})\n\n\t\trender(root, vnode)\n\t\trender(root, updated)\n\n\t\tvar e = $window.document.createEvent(\"MouseEvents\")\n\t\te.initEvent(\"click\", true, true)\n\t\tvnode.dom.dispatchEvent(e)\n\n\t\to(spy.callCount).equals(0)\n\t})\n\n\to(\"fires onclick only once after redraw\", function() {\n\t\tvar spy = o.spy()\n\t\tvar div = m(\"div\", {id: \"a\", onclick: spy})\n\t\tvar updated = m(\"div\", {id: \"b\", onclick: spy})\n\t\tvar e = $window.document.createEvent(\"MouseEvents\")\n\t\te.initEvent(\"click\", true, true)\n\n\t\trender(root, div)\n\t\trender(root, updated)\n\t\tdiv.dom.dispatchEvent(e)\n\n\t\to(spy.callCount).equals(1)\n\t\to(spy.this).equals(div.dom)\n\t\to(spy.args[0].type).equals(\"click\")\n\t\to(spy.args[0].target).equals(div.dom)\n\t\to(redraw.callCount).equals(1)\n\t\to(redraw.this).equals(undefined)\n\t\to(redraw.args.length).equals(0)\n\t\to(div.dom).equals(updated.dom)\n\t\to(div.dom.attributes[\"id\"].value).equals(\"b\")\n\t})\n\n\to(\"fires click EventListener object only once after redraw\", function() {\n\t\tvar spy = o.spy()\n\t\tvar listener = {handleEvent: spy}\n\t\tvar div = m(\"div\", {id: \"a\", onclick: listener})\n\t\tvar updated = m(\"div\", {id: \"b\", onclick: listener})\n\t\tvar e = $window.document.createEvent(\"MouseEvents\")\n\t\te.initEvent(\"click\", true, true)\n\n\t\trender(root, div)\n\t\trender(root, updated)\n\t\tdiv.dom.dispatchEvent(e)\n\n\t\to(spy.callCount).equals(1)\n\t\to(spy.this).equals(listener)\n\t\to(spy.args[0].type).equals(\"click\")\n\t\to(spy.args[0].target).equals(div.dom)\n\t\to(redraw.callCount).equals(1)\n\t\to(redraw.this).equals(undefined)\n\t\to(redraw.args.length).equals(0)\n\t\to(div.dom).equals(updated.dom)\n\t\to(div.dom.attributes[\"id\"].value).equals(\"b\")\n\t})\n\n\to(\"handles ontransitionend\", function() {\n\t\tvar spy = o.spy()\n\t\tvar div = m(\"div\", {ontransitionend: spy})\n\t\tvar e = $window.document.createEvent(\"HTMLEvents\")\n\t\te.initEvent(\"transitionend\", true, true)\n\n\t\trender(root, div)\n\t\tdiv.dom.dispatchEvent(e)\n\n\t\to(spy.callCount).equals(1)\n\t\to(spy.this).equals(div.dom)\n\t\to(spy.args[0].type).equals(\"transitionend\")\n\t\to(spy.args[0].target).equals(div.dom)\n\t\to(redraw.callCount).equals(1)\n\t\to(redraw.this).equals(undefined)\n\t\to(redraw.args.length).equals(0)\n\t})\n\n\to(\"handles transitionend EventListener object\", function() {\n\t\tvar spy = o.spy()\n\t\tvar listener = {handleEvent: spy}\n\t\tvar div = m(\"div\", {ontransitionend: listener})\n\t\tvar e = $window.document.createEvent(\"HTMLEvents\")\n\t\te.initEvent(\"transitionend\", true, true)\n\n\t\trender(root, div)\n\t\tdiv.dom.dispatchEvent(e)\n\n\t\to(spy.callCount).equals(1)\n\t\to(spy.this).equals(listener)\n\t\to(spy.args[0].type).equals(\"transitionend\")\n\t\to(spy.args[0].target).equals(div.dom)\n\t\to(redraw.callCount).equals(1)\n\t\to(redraw.this).equals(undefined)\n\t\to(redraw.args.length).equals(0)\n\t})\n\n\to(\"handles changed spy\", function() {\n\t\tvar div1 = m(\"div\", {ontransitionend: function() {}})\n\n\t\treallyRender(root, [div1], redraw)\n\t\tvar e = $window.document.createEvent(\"HTMLEvents\")\n\t\te.initEvent(\"transitionend\", true, true)\n\t\tdiv1.dom.dispatchEvent(e)\n\n\t\to(redraw.callCount).equals(1)\n\t\to(redraw.this).equals(undefined)\n\t\to(redraw.args.length).equals(0)\n\n\t\tvar replacementRedraw = o.spy()\n\t\tvar div2 = m(\"div\", {ontransitionend: function() {}})\n\n\t\treallyRender(root, [div2], replacementRedraw)\n\t\tvar e = $window.document.createEvent(\"HTMLEvents\")\n\t\te.initEvent(\"transitionend\", true, true)\n\t\tdiv2.dom.dispatchEvent(e)\n\n\t\to(redraw.callCount).equals(1)\n\t\to(redraw.this).equals(undefined)\n\t\to(redraw.args.length).equals(0)\n\t\to(replacementRedraw.callCount).equals(1)\n\t\to(replacementRedraw.this).equals(undefined)\n\t\to(replacementRedraw.args.length).equals(0)\n\t})\n\to(\"async function\", function(done) {\n\t\tvar div = m(\"div\", {onclick: async function () {}})\n\t\tvar e = $window.document.createEvent(\"MouseEvents\")\n\t\te.initEvent(\"click\", true, true)\n\n\t\trender(root, div)\n\t\tdiv.dom.dispatchEvent(e)\n\n\t\to(redraw.callCount).equals(1)\n\t\to(redraw.this).equals(undefined)\n\t\to(redraw.args.length).equals(0)\n\n\t\tcallAsync(function() {\n\t\t\to(redraw.callCount).equals(2)\n\t\t\to(redraw.this).equals(undefined)\n\t\t\to(redraw.args.length).equals(0)\n\n\t\t\tdone()\n\t\t})\n\t})\n\to(\"async function (await Promise)\", function(done) {\n\t\tvar thenCB\n\t\tvar div = m(\"div\", {onclick: async function () {\n\t\t\tawait new Promise(function(resolve){thenCB = resolve})\n\t\t}})\n\t\tvar e = $window.document.createEvent(\"MouseEvents\")\n\t\te.initEvent(\"click\", true, true)\n\n\t\trender(root, div)\n\t\tdiv.dom.dispatchEvent(e)\n\n\t\to(redraw.callCount).equals(1)\n\t\to(redraw.this).equals(undefined)\n\t\to(redraw.args.length).equals(0)\n\n\t\tcallAsync(function() {\n\t\t\t// not resolved yet\n\t\t\to(redraw.callCount).equals(1)\n\n\t\t\t// resolve\n\t\t\tthenCB()\n\t\t\tcallAsync(function() {\n\t\t\t\to(redraw.callCount).equals(2)\n\t\t\t\to(redraw.this).equals(undefined)\n\t\t\t\to(redraw.args.length).equals(0)\n\n\t\t\t\tdone()\n\t\t\t})\n\t\t})\n\t})\n\to(\"async function (await thenable)\", function(done) {\n\t\tvar thenCB\n\t\tvar div = m(\"div\", {onclick: async function () {\n\t\t\tawait {then(resolve){thenCB = resolve}}\n\t\t}})\n\t\tvar e = $window.document.createEvent(\"MouseEvents\")\n\t\te.initEvent(\"click\", true, true)\n\n\t\trender(root, div)\n\t\tdiv.dom.dispatchEvent(e)\n\n\t\to(redraw.callCount).equals(1)\n\t\to(redraw.this).equals(undefined)\n\t\to(redraw.args.length).equals(0)\n\n\t\tcallAsync(function() {\n\t\t\t// not resolved yet\n\t\t\to(redraw.callCount).equals(1)\n\n\t\t\t// resolve\n\t\t\tthenCB()\n\t\t\tcallAsync(function() {\n\t\t\t\to(redraw.callCount).equals(2)\n\t\t\t\to(redraw.this).equals(undefined)\n\t\t\t\to(redraw.args.length).equals(0)\n\n\t\t\t\tdone()\n\t\t\t})\n\t\t})\n\t})\n\to(\"return Promise\", function(done) {\n\t\tvar thenCB\n\t\tvar div = m(\"div\", {onclick: function () {\n\t\t\treturn new Promise(function(resolve){thenCB = resolve})\n\t\t}})\n\t\tvar e = $window.document.createEvent(\"MouseEvents\")\n\t\te.initEvent(\"click\", true, true)\n\n\t\trender(root, div)\n\t\tdiv.dom.dispatchEvent(e)\n\n\t\to(redraw.callCount).equals(1)\n\t\to(redraw.this).equals(undefined)\n\t\to(redraw.args.length).equals(0)\n\n\t\tcallAsync(function() {\n\t\t\t// not resolved yet\n\t\t\to(redraw.callCount).equals(1)\n\n\t\t\t// resolve\n\t\t\tthenCB()\n\t\t\tcallAsync(function() {\n\t\t\t\to(redraw.callCount).equals(2)\n\t\t\t\to(redraw.this).equals(undefined)\n\t\t\t\to(redraw.args.length).equals(0)\n\n\t\t\t\tdone()\n\t\t\t})\n\t\t})\n\t})\n\to(\"return thenable\", function(done) {\n\t\tvar thenCB\n\t\tvar div = m(\"div\", {onclick: function () {\n\t\t\treturn {then(resolve){thenCB = resolve}}\n\t\t}})\n\t\tvar e = $window.document.createEvent(\"MouseEvents\")\n\t\te.initEvent(\"click\", true, true)\n\n\t\trender(root, div)\n\t\tdiv.dom.dispatchEvent(e)\n\n\t\to(redraw.callCount).equals(1)\n\t\to(redraw.this).equals(undefined)\n\t\to(redraw.args.length).equals(0)\n\n\t\tcallAsync(function() {\n\t\t\t// not resolved yet\n\t\t\to(redraw.callCount).equals(1)\n\n\t\t\t// resolve\n\t\t\tthenCB()\n\t\t\tcallAsync(function() {\n\t\t\t\to(redraw.callCount).equals(2)\n\t\t\t\to(redraw.this).equals(undefined)\n\t\t\t\to(redraw.args.length).equals(0)\n\n\t\t\t\tdone()\n\t\t\t})\n\t\t})\n\t})\n\to.spec(\"do not asynchronous redraw when returned Promise is rejected\", function() {\n\t\tvar error\n\t\to.beforeEach(function(){\n\t\t\terror = console.error\n\t\t})\n\t\to.afterEach(function(){\n\t\t\tconsole.error = error\n\t\t})\n\t\to(\"async function (throw Error)\", function(done) {\n\t\t\tvar consoleSpy = o.spy()\n\t\t\tconsole.error = consoleSpy\n\n\t\t\tvar div = m(\"div\", {onclick: async function () {throw Error(\"error\")}})\n\t\t\tvar e = $window.document.createEvent(\"MouseEvents\")\n\t\t\te.initEvent(\"click\", true, true)\n\t\n\t\t\trender(root, div)\n\t\t\tdiv.dom.dispatchEvent(e)\n\t\n\t\t\t// sync redraw\n\t\t\to(redraw.callCount).equals(1)\n\t\t\to(redraw.this).equals(undefined)\n\t\t\to(redraw.args.length).equals(0)\n\t\n\t\t\tcallAsync(function() {\n\t\t\t\t// do not async redraw\n\t\t\t\to(redraw.callCount).equals(1)\n\n\t\t\t\t// called console.error\n\t\t\t\to(consoleSpy.callCount).equals(1)\n\t\t\t\tdone()\n\t\t\t})\n\t\t})\n\t\to(\"async function (await Promise, reject)\", function(done) {\n\t\t\tvar consoleSpy = o.spy()\n\t\t\tconsole.error = consoleSpy\n\n\t\t\tvar rejectCB\n\t\t\tvar div = m(\"div\", {onclick: async function () {\n\t\t\t\tawait new Promise(function(_, reject){rejectCB = reject})\n\t\t\t}})\n\t\t\tvar e = $window.document.createEvent(\"MouseEvents\")\n\t\t\te.initEvent(\"click\", true, true)\n\n\t\t\trender(root, div)\n\t\t\tdiv.dom.dispatchEvent(e)\n\n\t\t\t// sync redraw\n\t\t\to(redraw.callCount).equals(1)\n\t\t\to(redraw.this).equals(undefined)\n\t\t\to(redraw.args.length).equals(0)\n\n\t\t\tcallAsync(function() {\n\t\t\t\t// not resolved yet\n\t\t\t\to(redraw.callCount).equals(1)\n\n\t\t\t\t// reject\n\t\t\t\trejectCB(\"error\")\n\t\t\t\tcallAsync(function() {\n\t\t\t\t\t// do not async redraw\n\t\t\t\t\to(redraw.callCount).equals(1)\n\n\t\t\t\t\t// called console.error\n\t\t\t\t\to(consoleSpy.callCount).equals(1)\n\t\t\t\t\tdone()\n\t\t\t\t})\n\t\t\t})\n\t\t})\n\t\to(\"async function (await thenable, reject)\", function(done) {\n\t\t\tvar consoleSpy = o.spy()\n\t\t\tconsole.error = consoleSpy\n\n\t\t\tvar rejectCB\n\t\t\tvar div = m(\"div\", {onclick: async function () {\n\t\t\t\tawait {then(_, reject){rejectCB = reject}}\n\t\t\t}})\n\t\t\tvar e = $window.document.createEvent(\"MouseEvents\")\n\t\t\te.initEvent(\"click\", true, true)\n\n\t\t\trender(root, div)\n\t\t\tdiv.dom.dispatchEvent(e)\n\n\t\t\t// sync redraw\n\t\t\to(redraw.callCount).equals(1)\n\t\t\to(redraw.this).equals(undefined)\n\t\t\to(redraw.args.length).equals(0)\n\n\t\t\tcallAsync(function() {\n\t\t\t\t// not resolved yet\n\t\t\t\to(redraw.callCount).equals(1)\n\n\t\t\t\t// reject\n\t\t\t\trejectCB(\"error\")\n\t\t\t\tcallAsync(function() {\n\t\t\t\t\t// do not async redraw\n\t\t\t\t\to(redraw.callCount).equals(1)\n\n\t\t\t\t\t// called console.error\n\t\t\t\t\to(consoleSpy.callCount).equals(1)\n\t\t\t\t\tdone()\n\t\t\t\t})\n\t\t\t})\n\t\t})\n\t\to(\"async function (await Promise, throw Error)\", function(done) {\n\t\t\tvar consoleSpy = o.spy()\n\t\t\tconsole.error = consoleSpy\n\n\t\t\tvar thenCB\n\t\t\tvar div = m(\"div\", {onclick: async function () {\n\t\t\t\tawait new Promise(function(resolve){thenCB = resolve})\n\t\t\t\tthrow Error(\"error\")\n\t\t\t}})\n\t\t\tvar e = $window.document.createEvent(\"MouseEvents\")\n\t\t\te.initEvent(\"click\", true, true)\n\n\t\t\trender(root, div)\n\t\t\tdiv.dom.dispatchEvent(e)\n\n\t\t\t// sync redraw\n\t\t\to(redraw.callCount).equals(1)\n\t\t\to(redraw.this).equals(undefined)\n\t\t\to(redraw.args.length).equals(0)\n\n\t\t\tcallAsync(function() {\n\t\t\t\t// not resolved yet\n\t\t\t\to(redraw.callCount).equals(1)\n\n\t\t\t\t// resolve (and throw Error)\n\t\t\t\tthenCB()\n\t\t\t\tcallAsync(function() {\n\t\t\t\t\t// do not async redraw\n\t\t\t\t\to(redraw.callCount).equals(1)\n\n\t\t\t\t\t// called console.error\n\t\t\t\t\to(consoleSpy.callCount).equals(1)\n\t\t\t\t\tdone()\n\t\t\t\t})\n\t\t\t})\n\t\t})\n\t\to(\"async function (await thenable, throw Error)\", function(done) {\n\t\t\tvar consoleSpy = o.spy()\n\t\t\tconsole.error = consoleSpy\n\n\t\t\tvar thenCB\n\t\t\tvar div = m(\"div\", {onclick: async function () {\n\t\t\t\tawait {then(resolve){thenCB = resolve}}\n\t\t\t\tthrow Error(\"error\")\n\t\t\t}})\n\t\t\tvar e = $window.document.createEvent(\"MouseEvents\")\n\t\t\te.initEvent(\"click\", true, true)\n\n\t\t\trender(root, div)\n\t\t\tdiv.dom.dispatchEvent(e)\n\n\t\t\t// sync redraw\n\t\t\to(redraw.callCount).equals(1)\n\t\t\to(redraw.this).equals(undefined)\n\t\t\to(redraw.args.length).equals(0)\n\n\t\t\tcallAsync(function() {\n\t\t\t\t// not resolved yet\n\t\t\t\to(redraw.callCount).equals(1)\n\n\t\t\t\t// resolve (and throw Error)\n\t\t\t\tthenCB()\n\t\t\t\tcallAsync(function() {\n\t\t\t\t\t// do not async redraw\n\t\t\t\t\to(redraw.callCount).equals(1)\n\n\t\t\t\t\t// called console.error\n\t\t\t\t\to(consoleSpy.callCount).equals(1)\n\t\t\t\t\tdone()\n\t\t\t\t})\n\t\t\t})\n\t\t})\n\t\to(\"return Promise (reject)\", function(done) {\n\t\t\tvar consoleSpy = o.spy()\n\t\t\tconsole.error = consoleSpy\n\n\t\t\tvar rejectCB\n\t\t\tvar div = m(\"div\", {onclick: function () {\n\t\t\t\treturn new Promise(function(_, reject){rejectCB = reject})\n\t\t\t}})\n\t\t\tvar e = $window.document.createEvent(\"MouseEvents\")\n\t\t\te.initEvent(\"click\", true, true)\n\n\t\t\trender(root, div)\n\t\t\tdiv.dom.dispatchEvent(e)\n\n\t\t\t// sync redraw\n\t\t\to(redraw.callCount).equals(1)\n\t\t\to(redraw.this).equals(undefined)\n\t\t\to(redraw.args.length).equals(0)\n\n\t\t\tcallAsync(function() {\n\t\t\t\t// not resolved yet\n\t\t\t\to(redraw.callCount).equals(1)\n\n\t\t\t\t// reject\n\t\t\t\trejectCB(\"error\")\n\t\t\t\tcallAsync(function() {\n\t\t\t\t\t// do not async redraw\n\t\t\t\t\to(redraw.callCount).equals(1)\n\n\t\t\t\t\t// called console.error\n\t\t\t\t\to(consoleSpy.callCount).equals(1)\n\t\t\t\t\tdone()\n\t\t\t\t})\n\t\t\t})\n\t\t})\n\t\to(\"return thenable (reject)\", function(done) {\n\t\t\tvar consoleSpy = o.spy()\n\t\t\tconsole.error = consoleSpy\n\n\t\t\tvar rejectCB\n\t\t\tvar div = m(\"div\", {onclick: function () {\n\t\t\t\treturn {then(_, reject){rejectCB = reject}}\n\t\t\t}})\n\t\t\tvar e = $window.document.createEvent(\"MouseEvents\")\n\t\t\te.initEvent(\"click\", true, true)\n\n\t\t\trender(root, div)\n\t\t\tdiv.dom.dispatchEvent(e)\n\n\t\t\t// sync redraw\n\t\t\to(redraw.callCount).equals(1)\n\t\t\to(redraw.this).equals(undefined)\n\t\t\to(redraw.args.length).equals(0)\n\n\t\t\tcallAsync(function() {\n\t\t\t\t// not resolved yet\n\t\t\t\to(redraw.callCount).equals(1)\n\n\t\t\t\t// resolve\n\t\t\t\trejectCB(\"error\")\n\t\t\t\tcallAsync(function() {\n\t\t\t\t\t// do not async redraw\n\t\t\t\t\to(redraw.callCount).equals(1)\n\n\t\t\t\t\t// called console.error\n\t\t\t\t\to(consoleSpy.callCount).equals(1)\n\t\t\t\t\tdone()\n\t\t\t\t})\n\t\t\t})\n\t\t})\n\t})\n\to(\"async function (event.redraw = false)\", function(done) {\n\t\tvar spy = o.spy()\n\t\tvar div = m(\"div\", {onclick: async function (ev) {\n\t\t\t// set event.redraw = false to prevent redraws\n\t\t\tev.redraw = false\n\t\t\tspy()\n\t\t}})\n\t\tvar e = $window.document.createEvent(\"MouseEvents\")\n\t\te.initEvent(\"click\", true, true)\n\n\t\trender(root, div)\n\n\t\t// event listener has not yet been called\n\t\to(spy.callCount).equals(0)\n\n\t\tdiv.dom.dispatchEvent(e)\n\n\t\t// event listener called but not redraw\n\t\to(spy.callCount).equals(1)\n\t\to(redraw.callCount).equals(0)\n\n\t\tcallAsync(function() {\n\t\t\to(spy.callCount).equals(1)\n\t\t\to(redraw.callCount).equals(0)\n\n\t\t\tdone()\n\t\t})\n\t})\n\to(\"async function (event.redraw = false, await Promise)\", function(done) {\n\t\tvar thenCB\n\t\tvar spy = o.spy(function(resolve){thenCB = resolve})\n\t\tvar div = m(\"div\", {onclick: async function (ev) {\n\t\t\t// set event.redraw = false to prevent redraws\n\t\t\tev.redraw = false\n\t\t\tawait new Promise(spy)\n\t\t}})\n\t\tvar e = $window.document.createEvent(\"MouseEvents\")\n\t\te.initEvent(\"click\", true, true)\n\n\t\trender(root, div)\n\n\t\t// event listener has not yet been called\n\t\to(spy.callCount).equals(0)\n\t\to(thenCB).equals(undefined)\n\n\t\tdiv.dom.dispatchEvent(e)\n\n\t\t// event listener called but not redraw\n\t\to(spy.callCount).equals(1)\n\t\to(thenCB).notEquals(undefined)\n\t\to(redraw.callCount).equals(0)\n\n\t\tcallAsync(function() {\n\t\t\t// not resolved yet\n\t\t\to(spy.callCount).equals(1)\n\t\t\to(redraw.callCount).equals(0)\n\n\t\t\t// resolve\n\t\t\tthenCB()\n\t\t\tcallAsync(function() {\n\t\t\t\to(spy.callCount).equals(1)\n\t\t\t\to(redraw.callCount).equals(0)\n\n\t\t\t\tdone()\n\t\t\t})\n\t\t})\n\t})\n\to(\"async function (await Promise, event.redraw = false)\", function(done) {\n\t\tvar thenCB\n\t\tvar div = m(\"div\", {onclick: async function (ev) {\n\t\t\tawait new Promise(function(resolve){thenCB = resolve})\n\t\t\t// set event.redraw = false to prevent additional redraw\n\t\t\tev.redraw = false\n\t\t}})\n\t\tvar e = $window.document.createEvent(\"MouseEvents\")\n\t\te.initEvent(\"click\", true, true)\n\n\t\trender(root, div)\n\t\tdiv.dom.dispatchEvent(e)\n\n\t\to(redraw.callCount).equals(1)\n\t\to(redraw.this).equals(undefined)\n\t\to(redraw.args.length).equals(0)\n\n\t\tcallAsync(function() {\n\t\t\t// not resolved yet\n\t\t\to(redraw.callCount).equals(1)\n\n\t\t\t// resolve\n\t\t\tthenCB()\n\t\t\tcallAsync(function() {\n\t\t\t\to(redraw.callCount).equals(1)\n\n\t\t\t\tdone()\n\t\t\t})\n\t\t})\n\t})\n\to(\"async function (event.redraw = false, await Promise, event.redraw = true)\", function(done) {\n\t\tvar thenCB\n\t\tvar spy = o.spy(function(resolve){thenCB = resolve})\n\t\tvar div = m(\"div\", {onclick: async function (ev) {\n\t\t\t// set event.redraw = false to prevent sync redraw\n\t\t\tev.redraw = false\n\t\t\tawait new Promise(spy)\n\t\t\t// set event.redraw = true to enable async redraw\n\t\t\tev.redraw = true\n\t\t}})\n\t\tvar e = $window.document.createEvent(\"MouseEvents\")\n\t\te.initEvent(\"click\", true, true)\n\n\t\trender(root, div)\n\n\t\t// event listener has not yet been called\n\t\to(spy.callCount).equals(0)\n\t\to(thenCB).equals(undefined)\n\n\t\tdiv.dom.dispatchEvent(e)\n\n\t\t// event listener called but not redraw\n\t\to(spy.callCount).equals(1)\n\t\to(thenCB).notEquals(undefined)\n\t\to(redraw.callCount).equals(0)\n\t\tcallAsync(function() {\n\t\t\t// not resolved yet\n\t\t\to(spy.callCount).equals(1)\n\t\t\to(redraw.callCount).equals(0)\n\n\t\t\t// resolve\n\t\t\tthenCB()\n\t\t\tcallAsync(function() {\n\t\t\t\to(spy.callCount).equals(1)\n\t\t\t\to(redraw.callCount).equals(1)\n\t\t\t\to(redraw.this).equals(undefined)\n\t\t\t\to(redraw.args.length).equals(0)\n\n\t\t\t\tdone()\n\t\t\t})\n\t\t})\n\t})\n\to(\"async function (multiple await)\", function(done) {\n\t\tvar thenCB1, thenCB2\n\t\tvar div = m(\"div\", {onclick: async function () {\n\t\t\tawait new Promise(function(resolve){thenCB1 = resolve})\n\t\t\tawait new Promise(function(resolve){thenCB2 = resolve})\n\t\t}})\n\t\tvar e = $window.document.createEvent(\"MouseEvents\")\n\t\te.initEvent(\"click\", true, true)\n\n\t\trender(root, div)\n\t\tdiv.dom.dispatchEvent(e)\n\n\t\to(redraw.callCount).equals(1)\n\t\to(redraw.this).equals(undefined)\n\t\to(redraw.args.length).equals(0)\n\n\t\tcallAsync(function() {\n\t\t\to(redraw.callCount).equals(1)\n\n\t\t\t// resolve 1\n\t\t\tthenCB1()\n\t\t\tcallAsync(function() {\n\t\t\t\to(redraw.callCount).equals(1)\n\n\t\t\t\t// resolve 2\n\t\t\t\tthenCB2()\n\t\t\t\tcallAsync(function() {\n\t\t\t\t\to(redraw.callCount).equals(2)\n\t\t\t\t\to(redraw.this).equals(undefined)\n\t\t\t\t\to(redraw.args.length).equals(0)\n\n\t\t\t\t\tdone()\n\t\t\t\t})\n\t\t\t})\n\t\t})\n\t})\n\to(\"avoid sync redraw after removal\", function() {\n\t\tvar spy = o.spy()\n\t\tvar div = m(\"div\", {onclick: spy})\n\t\tvar e = $window.document.createEvent(\"MouseEvents\")\n\t\te.initEvent(\"click\", true, true)\n\n\t\trender(root, div)\n\t\t// remove div\n\t\trender(root, [])\n\n\t\t// event listener has not yet been called\n\t\to(spy.callCount).equals(0)\n\n\t\tdiv.dom.dispatchEvent(e)\n\n\t\t// event listener called but not redraw\n\t\to(spy.callCount).equals(1)\n\t\to(redraw.callCount).equals(0)\n\t})\n\to(\"avoid async redraw after removal\", function(done) {\n\t\tvar thenCB\n\t\tvar spy = o.spy(function(resolve){thenCB = resolve})\n\t\tvar div = m(\"div\", {onclick: async function () {\n\t\t\tawait new Promise(spy)\n\t\t}})\n\t\tvar e = $window.document.createEvent(\"MouseEvents\")\n\t\te.initEvent(\"click\", true, true)\n\n\t\trender(root, div)\n\n\t\t// event listener has not yet been called\n\t\to(spy.callCount).equals(0)\n\t\to(thenCB).equals(undefined)\n\n\t\tdiv.dom.dispatchEvent(e)\n\n\t\t// event listener called\n\t\to(spy.callCount).equals(1)\n\t\to(thenCB).notEquals(undefined)\n\n\t\to(redraw.callCount).equals(1)\n\t\to(redraw.this).equals(undefined)\n\t\to(redraw.args.length).equals(0)\n\n\t\tcallAsync(function() {\n\t\t\t// not resolved yet\n\t\t\to(spy.callCount).equals(1)\n\t\t\to(redraw.callCount).equals(1)\n\n\t\t\t// remove div\n\t\t\trender(root, [])\n\n\t\t\t// resolve\n\t\t\tthenCB()\n\t\t\tcallAsync(function() {\n\t\t\t\to(spy.callCount).equals(1)\n\t\t\t\to(redraw.callCount).equals(1)\n\n\t\t\t\tdone()\n\t\t\t})\n\t\t})\n\t})\n})\n"
  },
  {
    "path": "render/tests/test-fragment.js",
    "content": "\"use strict\"\n\nvar o = require(\"ospec\")\nvar fragment = require(\"../../render/fragment\")\nvar m = require(\"../../render/hyperscript\")\n\nfunction fragmentStr() {\n\tvar args = [].slice.call(arguments);\n\targs.unshift(\"[\");\n\treturn m.apply(null, args)\n}\n\nfunction runTest(name, fragment) {\n\to.spec(name, function() {\n\t\to(\"works\", function() {\n\t\t\tvar attrs = {foo: 5}\n\t\t\tvar child = m(\"p\")\n\t\t\tvar frag = fragment(attrs, child)\n\n\t\t\to(frag.tag).equals(\"[\")\n\n\t\t\to(Array.isArray(frag.children)).equals(true)\n\t\t\to(frag.children.length).equals(1)\n\t\t\to(frag.children[0]).equals(child)\n\n\t\t\to(frag.attrs).equals(attrs)\n\n\t\t\to(frag.key).equals(undefined)\n\t\t})\n\t\to(\"supports keys\", function() {\n\t\t\tvar attrs = {key: 7}\n\t\t\tvar frag = fragment(attrs, [])\n\t\t\to(frag.tag).equals(\"[\")\n\n\t\t\to(Array.isArray(frag.children)).equals(true)\n\t\t\to(frag.children.length).equals(0)\n\n\t\t\to(frag.attrs).equals(attrs)\n\t\t\to(frag.attrs.key).equals(7)\n\n\t\t\to(frag.key).equals(7)\n\t\t})\n\t\to.spec(\"children with no attrs\", function() {\n\t\t\to(\"handles string single child\", function() {\n\t\t\t\tvar vnode = fragment([\"a\"])\n\n\t\t\t\to(vnode.attrs).deepEquals({})\n\t\t\t\to(vnode.children[0].tag).equals(\"#\")\n\t\t\t\to(vnode.children[0].children).equals(\"a\")\n\t\t\t})\n\t\t\to(\"handles falsy string single child\", function() {\n\t\t\t\tvar vnode = fragment([\"\"])\n\n\t\t\t\to(vnode.attrs).deepEquals({})\n\t\t\t\to(vnode.children[0].tag).equals(\"#\")\n\t\t\t\to(vnode.children[0].children).equals(\"\")\n\t\t\t})\n\t\t\to(\"handles number single child\", function() {\n\t\t\t\tvar vnode = fragment([1])\n\n\t\t\t\to(vnode.attrs).deepEquals({})\n\t\t\t\to(vnode.children[0].tag).equals(\"#\")\n\t\t\t\to(vnode.children[0].children).equals(\"1\")\n\t\t\t})\n\t\t\to(\"handles falsy number single child\", function() {\n\t\t\t\tvar vnode = fragment([0])\n\n\t\t\t\to(vnode.attrs).deepEquals({})\n\t\t\t\to(vnode.children[0].tag).equals(\"#\")\n\t\t\t\to(vnode.children[0].children).equals(\"0\")\n\t\t\t})\n\t\t\to(\"handles boolean single child\", function() {\n\t\t\t\tvar vnode = fragment([true])\n\n\t\t\t\to(vnode.attrs).deepEquals({})\n\t\t\t\to(vnode.children).deepEquals([null])\n\t\t\t})\n\t\t\to(\"handles falsy boolean single child\", function() {\n\t\t\t\tvar vnode = fragment([false])\n\n\t\t\t\to(vnode.attrs).deepEquals({})\n\t\t\t\to(vnode.children).deepEquals([null])\n\t\t\t})\n\t\t\to(\"handles null single child\", function() {\n\t\t\t\tvar vnode = fragment([null])\n\n\t\t\t\to(vnode.attrs).deepEquals({})\n\t\t\t\to(vnode.children[0]).equals(null)\n\t\t\t})\n\t\t\to(\"handles undefined single child\", function() {\n\t\t\t\tvar vnode = fragment([undefined])\n\n\t\t\t\to(vnode.attrs).deepEquals({})\n\t\t\t\to(vnode.children).deepEquals([null])\n\t\t\t})\n\t\t\to(\"handles multiple string children\", function() {\n\t\t\t\tvar vnode = fragment([\"\", \"a\"])\n\n\t\t\t\to(vnode.attrs).deepEquals({})\n\t\t\t\to(vnode.children[0].tag).equals(\"#\")\n\t\t\t\to(vnode.children[0].children).equals(\"\")\n\t\t\t\to(vnode.children[1].tag).equals(\"#\")\n\t\t\t\to(vnode.children[1].children).equals(\"a\")\n\t\t\t})\n\t\t\to(\"handles multiple number children\", function() {\n\t\t\t\tvar vnode = fragment([0, 1])\n\n\t\t\t\to(vnode.attrs).deepEquals({})\n\t\t\t\to(vnode.children[0].tag).equals(\"#\")\n\t\t\t\to(vnode.children[0].children).equals(\"0\")\n\t\t\t\to(vnode.children[1].tag).equals(\"#\")\n\t\t\t\to(vnode.children[1].children).equals(\"1\")\n\t\t\t})\n\t\t\to(\"handles multiple boolean children\", function() {\n\t\t\t\tvar vnode = fragment([false, true])\n\n\t\t\t\to(vnode.attrs).deepEquals({})\n\t\t\t\to(vnode.children).deepEquals([null, null])\n\t\t\t})\n\t\t\to(\"handles multiple null/undefined child\", function() {\n\t\t\t\tvar vnode = fragment([null, undefined])\n\n\t\t\t\to(vnode.attrs).deepEquals({})\n\t\t\t\to(vnode.children).deepEquals([null, null])\n\t\t\t})\n\t\t\to(\"handles falsy number single child without attrs\", function() {\n\t\t\t\tvar vnode = fragment(0)\n\n\t\t\t\to(vnode.attrs).deepEquals({})\n\t\t\t\to(vnode.children[0].tag).equals(\"#\")\n\t\t\t\to(vnode.children[0].children).equals(\"0\")\n\t\t\t})\n\t\t})\n\t\to.spec(\"children with attrs\", function() {\n\t\t\to(\"handles string single child\", function() {\n\t\t\t\tvar vnode = fragment({}, [\"a\"])\n\n\t\t\t\to(vnode.attrs).deepEquals({})\n\t\t\t\to(vnode.children[0].tag).equals(\"#\")\n\t\t\t\to(vnode.children[0].children).equals(\"a\")\n\t\t\t})\n\t\t\to(\"handles falsy string single child\", function() {\n\t\t\t\tvar vnode = fragment({}, [\"\"])\n\n\t\t\t\to(vnode.attrs).deepEquals({})\n\t\t\t\to(vnode.children[0].tag).equals(\"#\")\n\t\t\t\to(vnode.children[0].children).equals(\"\")\n\t\t\t})\n\t\t\to(\"handles number single child\", function() {\n\t\t\t\tvar vnode = fragment({}, [1])\n\n\t\t\t\to(vnode.attrs).deepEquals({})\n\t\t\t\to(vnode.children[0].tag).equals(\"#\")\n\t\t\t\to(vnode.children[0].children).equals(\"1\")\n\t\t\t})\n\t\t\to(\"handles falsy number single child\", function() {\n\t\t\t\tvar vnode = fragment({}, [0])\n\n\t\t\t\to(vnode.attrs).deepEquals({})\n\t\t\t\to(vnode.children[0].tag).equals(\"#\")\n\t\t\t\to(vnode.children[0].children).equals(\"0\")\n\t\t\t})\n\t\t\to(\"handles boolean single child\", function() {\n\t\t\t\tvar vnode = fragment({}, [true])\n\n\t\t\t\to(vnode.attrs).deepEquals({})\n\t\t\t\to(vnode.children).deepEquals([null])\n\t\t\t})\n\t\t\to(\"handles falsy boolean single child\", function() {\n\t\t\t\tvar vnode = fragment({}, [false])\n\n\t\t\t\to(vnode.attrs).deepEquals({})\n\t\t\t\to(vnode.children).deepEquals([null])\n\t\t\t})\n\t\t\to(\"handles null single child\", function() {\n\t\t\t\tvar vnode = fragment({}, [null])\n\n\t\t\t\to(vnode.attrs).deepEquals({})\n\t\t\t\to(vnode.children).deepEquals([null])\n\t\t\t})\n\t\t\to(\"handles undefined single child\", function() {\n\t\t\t\tvar vnode = fragment({}, [undefined])\n\n\t\t\t\to(vnode.attrs).deepEquals({})\n\t\t\t\to(vnode.children).deepEquals([null])\n\t\t\t})\n\t\t\to(\"handles multiple string children\", function() {\n\t\t\t\tvar vnode = fragment({}, [\"\", \"a\"])\n\n\t\t\t\to(vnode.attrs).deepEquals({})\n\t\t\t\to(vnode.children[0].tag).equals(\"#\")\n\t\t\t\to(vnode.children[0].children).equals(\"\")\n\t\t\t\to(vnode.children[1].tag).equals(\"#\")\n\t\t\t\to(vnode.children[1].children).equals(\"a\")\n\t\t\t})\n\t\t\to(\"handles multiple number children\", function() {\n\t\t\t\tvar vnode = fragment({}, [0, 1])\n\n\t\t\t\to(vnode.attrs).deepEquals({})\n\t\t\t\to(vnode.children[0].tag).equals(\"#\")\n\t\t\t\to(vnode.children[0].children).equals(\"0\")\n\t\t\t\to(vnode.children[1].tag).equals(\"#\")\n\t\t\t\to(vnode.children[1].children).equals(\"1\")\n\t\t\t})\n\t\t\to(\"handles multiple boolean children\", function() {\n\t\t\t\tvar vnode = fragment({}, [false, true])\n\n\t\t\t\to(vnode.attrs).deepEquals({})\n\t\t\t\to(vnode.children).deepEquals([null, null])\n\t\t\t})\n\t\t\to(\"handles multiple null/undefined child\", function() {\n\t\t\t\tvar vnode = fragment({}, [null, undefined])\n\n\t\t\t\to(vnode.attrs).deepEquals({})\n\t\t\t\to(vnode.children).deepEquals([null, null])\n\t\t\t})\n\t\t})\n\t})\n}\n\nrunTest(\"fragment\", fragment);\nrunTest(\"fragment-string-selector\", fragmentStr);\n"
  },
  {
    "path": "render/tests/test-hyperscript.js",
    "content": "\"use strict\"\n\nvar o = require(\"ospec\")\nvar m = require(\"../../render/hyperscript\")\n\no.spec(\"hyperscript\", function() {\n\to.spec(\"selector\", function() {\n\t\to(\"throws on null selector\", function(done) {\n\t\t\ttry {m(null)} catch(e) {done()}\n\t\t})\n\t\to(\"throws on non-string selector w/o a view property\", function(done) {\n\t\t\ttry {m({})} catch(e) {done()}\n\t\t})\n\t\to(\"handles tag in selector\", function() {\n\t\t\tvar vnode = m(\"a\")\n\n\t\t\to(vnode.tag).equals(\"a\")\n\t\t})\n\t\to(\"class and className normalization\", function(){\n\t\t\to(m(\"a\", {\n\t\t\t\tclass: null\n\t\t\t}).attrs).deepEquals({\n\t\t\t\tclass: null\n\t\t\t})\n\t\t\to(m(\"a\", {\n\t\t\t\tclass: undefined\n\t\t\t}).attrs).deepEquals({\n\t\t\t\tclass: null\n\t\t\t})\n\t\t\to(m(\"a\", {\n\t\t\t\tclass: false\n\t\t\t}).attrs).deepEquals({\n\t\t\t\tclass: null,\n\t\t\t\tclassName: false\n\t\t\t})\n\t\t\to(m(\"a\", {\n\t\t\t\tclass: true\n\t\t\t}).attrs).deepEquals({\n\t\t\t\tclass: null,\n\t\t\t\tclassName: true\n\t\t\t})\n\t\t\to(m(\"a.x\", {\n\t\t\t\tclass: null\n\t\t\t}).attrs).deepEquals({\n\t\t\t\tclass: null,\n\t\t\t\tclassName: \"x\"\n\t\t\t})\n\t\t\to(m(\"a.x\", {\n\t\t\t\tclass: undefined\n\t\t\t}).attrs).deepEquals({\n\t\t\t\tclass: null,\n\t\t\t\tclassName: \"x\"\n\t\t\t})\n\t\t\to(m(\"a.x\", {\n\t\t\t\tclass: false\n\t\t\t}).attrs).deepEquals({\n\t\t\t\tclass: null,\n\t\t\t\tclassName: \"x false\"\n\t\t\t})\n\t\t\to(m(\"a.x\", {\n\t\t\t\tclass: true\n\t\t\t}).attrs).deepEquals({\n\t\t\t\tclass: null,\n\t\t\t\tclassName: \"x true\"\n\t\t\t})\n\t\t\to(m(\"a\", {\n\t\t\t\tclassName: null\n\t\t\t}).attrs).deepEquals({\n\t\t\t\tclassName: null\n\t\t\t})\n\t\t\to(m(\"a\", {\n\t\t\t\tclassName: undefined\n\t\t\t}).attrs).deepEquals({\n\t\t\t\tclassName: undefined\n\t\t\t})\n\t\t\to(m(\"a\", {\n\t\t\t\tclassName: false\n\t\t\t}).attrs).deepEquals({\n\t\t\t\tclassName: false\n\t\t\t})\n\t\t\to(m(\"a\", {\n\t\t\t\tclassName: true\n\t\t\t}).attrs).deepEquals({\n\t\t\t\tclassName: true\n\t\t\t})\n\t\t\to(m(\"a.x\", {\n\t\t\t\tclassName: null\n\t\t\t}).attrs).deepEquals({\n\t\t\t\tclassName: \"x\"\n\t\t\t})\n\t\t\to(m(\"a.x\", {\n\t\t\t\tclassName: undefined\n\t\t\t}).attrs).deepEquals({\n\t\t\t\tclassName: \"x\"\n\t\t\t})\n\t\t\to(m(\"a.x\", {\n\t\t\t\tclassName: false\n\t\t\t}).attrs).deepEquals({\n\t\t\t\tclassName: \"x false\"\n\t\t\t})\n\t\t\to(m(\"a.x\", {\n\t\t\t\tclassName: true\n\t\t\t}).attrs).deepEquals({\n\t\t\t\tclassName: \"x true\"\n\t\t\t})\n\t\t})\n\t\to(\"handles class in selector\", function() {\n\t\t\tvar vnode = m(\".a\")\n\n\t\t\to(vnode.tag).equals(\"div\")\n\t\t\to(vnode.attrs.className).equals(\"a\")\n\t\t})\n\t\to(\"handles many classes in selector\", function() {\n\t\t\tvar vnode = m(\".a.b.c\")\n\n\t\t\to(vnode.tag).equals(\"div\")\n\t\t\to(vnode.attrs.className).equals(\"a b c\")\n\t\t})\n\t\to(\"handles id in selector\", function() {\n\t\t\tvar vnode = m(\"#a\")\n\n\t\t\to(vnode.tag).equals(\"div\")\n\t\t\to(vnode.attrs.id).equals(\"a\")\n\t\t})\n\t\to(\"handles attr in selector\", function() {\n\t\t\tvar vnode = m(\"[a=b]\")\n\n\t\t\to(vnode.tag).equals(\"div\")\n\t\t\to(vnode.attrs.a).equals(\"b\")\n\t\t})\n\t\to(\"handles many attrs in selector\", function() {\n\t\t\tvar vnode = m(\"[a=b][c=d]\")\n\n\t\t\to(vnode.tag).equals(\"div\")\n\t\t\to(vnode.attrs.a).equals(\"b\")\n\t\t\to(vnode.attrs.c).equals(\"d\")\n\t\t})\n\t\to(\"handles attr w/ spaces in selector\", function() {\n\t\t\tvar vnode = m(\"[a = b]\")\n\n\t\t\to(vnode.tag).equals(\"div\")\n\t\t\to(vnode.attrs.a).equals(\"b\")\n\t\t})\n\t\to(\"handles attr w/ quotes in selector\", function() {\n\t\t\tvar vnode = m(\"[a='b']\")\n\n\t\t\to(vnode.tag).equals(\"div\")\n\t\t\to(vnode.attrs.a).equals(\"b\")\n\t\t})\n\t\to(\"handles attr w/ quoted square bracket\", function() {\n\t\t\tvar vnode = m(\"[x][a='[b]'].c\")\n\n\t\t\to(vnode.tag).equals(\"div\")\n\t\t\to(vnode.attrs.x).equals(true)\n\t\t\to(vnode.attrs.a).equals(\"[b]\")\n\t\t\to(vnode.attrs.className).equals(\"c\")\n\t\t})\n\t\to(\"handles attr w/ unmatched square bracket\", function() {\n\t\t\tvar vnode = m(\"[a=']'].c\")\n\n\t\t\to(vnode.tag).equals(\"div\")\n\t\t\to(vnode.attrs.a).equals(\"]\")\n\t\t\to(vnode.attrs.className).equals(\"c\")\n\t\t})\n\t\to(\"handles attr w/ quoted square bracket and quote\", function() {\n\t\t\tvar vnode = m(\"[a='[b\\\"\\\\']'].c\") // `[a='[b\"\\']']`\n\n\t\t\to(vnode.tag).equals(\"div\")\n\t\t\to(vnode.attrs.a).equals(\"[b\\\"']\") // `[b\"']`\n\t\t\to(vnode.attrs.className).equals(\"c\")\n\t\t})\n\t\to(\"handles attr w/ quoted square containing escaped square bracket\", function() {\n\t\t\tvar vnode = m(\"[a='[\\\\]]'].c\") // `[a='[\\]]']`\n\n\t\t\to(vnode.tag).equals(\"div\")\n\t\t\to(vnode.attrs.a).equals(\"[\\\\]]\") // `[\\]]`\n\t\t\to(vnode.attrs.className).equals(\"c\")\n\t\t})\n\t\to(\"handles attr w/ backslashes\", function() {\n\t\t\tvar vnode = m(\"[a='\\\\\\\\'].c\") // `[a='\\\\']`\n\n\t\t\to(vnode.tag).equals(\"div\")\n\t\t\to(vnode.attrs.a).equals(\"\\\\\")\n\t\t\to(vnode.attrs.className).equals(\"c\")\n\t\t})\n\t\to(\"handles attr w/ quotes and spaces in selector\", function() {\n\t\t\tvar vnode = m(\"[a = 'b']\")\n\n\t\t\to(vnode.tag).equals(\"div\")\n\t\t\to(vnode.attrs.a).equals(\"b\")\n\t\t})\n\t\to(\"handles many attr w/ quotes and spaces in selector\", function() {\n\t\t\tvar vnode = m(\"[a = 'b'][c = 'd']\")\n\n\t\t\to(vnode.tag).equals(\"div\")\n\t\t\to(vnode.attrs.a).equals(\"b\")\n\t\t\to(vnode.attrs.c).equals(\"d\")\n\t\t})\n\t\to(\"handles tag, class, attrs in selector\", function() {\n\t\t\tvar vnode = m(\"a.b[c = 'd']\")\n\n\t\t\to(vnode.tag).equals(\"a\")\n\t\t\to(vnode.attrs.className).equals(\"b\")\n\t\t\to(vnode.attrs.c).equals(\"d\")\n\t\t})\n\t\to(\"handles tag, mixed classes, attrs in selector\", function() {\n\t\t\tvar vnode = m(\"a.b[c = 'd'].e[f = 'g']\")\n\n\t\t\to(vnode.tag).equals(\"a\")\n\t\t\to(vnode.attrs.className).equals(\"b e\")\n\t\t\to(vnode.attrs.c).equals(\"d\")\n\t\t\to(vnode.attrs.f).equals(\"g\")\n\t\t})\n\t\to(\"handles attr without value\", function() {\n\t\t\tvar vnode = m(\"[a]\")\n\n\t\t\to(vnode.tag).equals(\"div\")\n\t\t\to(vnode.attrs.a).equals(true)\n\t\t})\n\t\to(\"handles explicit empty string value for input\", function() {\n\t\t\tvar vnode = m('input[value=\"\"]')\n\n\t\t\to(vnode.tag).equals(\"input\")\n\t\t\to(vnode.attrs.value).equals(\"\")\n\t\t})\n\t\to(\"handles explicit empty string value for option\", function() {\n\t\t\tvar vnode = m('option[value=\"\"]')\n\n\t\t\to(vnode.tag).equals(\"option\")\n\t\t\to(vnode.attrs.value).equals(\"\")\n\t\t})\n\t})\n\to.spec(\"attrs\", function() {\n\t\to(\"handles string attr\", function() {\n\t\t\tvar vnode = m(\"div\", {a: \"b\"})\n\n\t\t\to(vnode.tag).equals(\"div\")\n\t\t\to(vnode.attrs.a).equals(\"b\")\n\t\t})\n\t\to(\"handles falsy string attr\", function() {\n\t\t\tvar vnode = m(\"div\", {a: \"\"})\n\n\t\t\to(vnode.tag).equals(\"div\")\n\t\t\to(vnode.attrs.a).equals(\"\")\n\t\t})\n\t\to(\"handles number attr\", function() {\n\t\t\tvar vnode = m(\"div\", {a: 1})\n\n\t\t\to(vnode.tag).equals(\"div\")\n\t\t\to(vnode.attrs.a).equals(1)\n\t\t})\n\t\to(\"handles falsy number attr\", function() {\n\t\t\tvar vnode = m(\"div\", {a: 0})\n\n\t\t\to(vnode.tag).equals(\"div\")\n\t\t\to(vnode.attrs.a).equals(0)\n\t\t})\n\t\to(\"handles boolean attr\", function() {\n\t\t\tvar vnode = m(\"div\", {a: true})\n\n\t\t\to(vnode.tag).equals(\"div\")\n\t\t\to(vnode.attrs.a).equals(true)\n\t\t})\n\t\to(\"handles falsy boolean attr\", function() {\n\t\t\tvar vnode = m(\"div\", {a: false})\n\n\t\t\to(vnode.tag).equals(\"div\")\n\t\t\to(vnode.attrs.a).equals(false)\n\t\t})\n\t\to(\"handles only key in attrs\", function() {\n\t\t\tvar vnode = m(\"div\", {key:\"a\"})\n\n\t\t\to(vnode.tag).equals(\"div\")\n\t\t\to(vnode.attrs).deepEquals({key:\"a\"})\n\t\t\to(vnode.key).equals(\"a\")\n\t\t})\n\t\to(\"handles many attrs\", function() {\n\t\t\tvar vnode = m(\"div\", {a: \"b\", c: \"d\"})\n\n\t\t\to(vnode.tag).equals(\"div\")\n\t\t\to(vnode.attrs.a).equals(\"b\")\n\t\t\to(vnode.attrs.c).equals(\"d\")\n\t\t})\n\t\to(\"handles className attrs property\", function() {\n\t\t\tvar vnode = m(\"div\", {className: \"a\"})\n\n\t\t\to(vnode.attrs.className).equals(\"a\")\n\t\t})\n\t\to(\"handles 'class' as a verbose attribute declaration\", function() {\n\t\t\tvar vnode = m(\"[class=a]\")\n\n\t\t\to(vnode.attrs.className).equals(\"a\")\n\t\t})\n\t\to(\"handles merging classes w/ class property\", function() {\n\t\t\tvar vnode = m(\".a\", {class: \"b\"})\n\n\t\t\to(vnode.attrs.className).equals(\"a b\")\n\t\t})\n\t\to(\"handles merging classes w/ className property\", function() {\n\t\t\tvar vnode = m(\".a\", {className: \"b\"})\n\n\t\t\to(vnode.attrs.className).equals(\"a b\")\n\t\t})\n\t})\n\to.spec(\"custom element attrs\", function() {\n\t\to(\"handles string attr\", function() {\n\t\t\tvar vnode = m(\"custom-element\", {a: \"b\"})\n\n\t\t\to(vnode.tag).equals(\"custom-element\")\n\t\t\to(vnode.attrs.a).equals(\"b\")\n\t\t})\n\t\to(\"handles falsy string attr\", function() {\n\t\t\tvar vnode = m(\"custom-element\", {a: \"\"})\n\n\t\t\to(vnode.tag).equals(\"custom-element\")\n\t\t\to(vnode.attrs.a).equals(\"\")\n\t\t})\n\t\to(\"handles number attr\", function() {\n\t\t\tvar vnode = m(\"custom-element\", {a: 1})\n\n\t\t\to(vnode.tag).equals(\"custom-element\")\n\t\t\to(vnode.attrs.a).equals(1)\n\t\t})\n\t\to(\"handles falsy number attr\", function() {\n\t\t\tvar vnode = m(\"custom-element\", {a: 0})\n\n\t\t\to(vnode.tag).equals(\"custom-element\")\n\t\t\to(vnode.attrs.a).equals(0)\n\t\t})\n\t\to(\"handles boolean attr\", function() {\n\t\t\tvar vnode = m(\"custom-element\", {a: true})\n\n\t\t\to(vnode.tag).equals(\"custom-element\")\n\t\t\to(vnode.attrs.a).equals(true)\n\t\t})\n\t\to(\"handles falsy boolean attr\", function() {\n\t\t\tvar vnode = m(\"custom-element\", {a: false})\n\n\t\t\to(vnode.tag).equals(\"custom-element\")\n\t\t\to(vnode.attrs.a).equals(false)\n\t\t})\n\t\to(\"handles only key in attrs\", function() {\n\t\t\tvar vnode = m(\"custom-element\", {key:\"a\"})\n\n\t\t\to(vnode.tag).equals(\"custom-element\")\n\t\t\to(vnode.attrs).deepEquals({key:\"a\"})\n\t\t\to(vnode.key).equals(\"a\")\n\t\t})\n\t\to(\"handles many attrs\", function() {\n\t\t\tvar vnode = m(\"custom-element\", {a: \"b\", c: \"d\"})\n\n\t\t\to(vnode.tag).equals(\"custom-element\")\n\t\t\to(vnode.attrs.a).equals(\"b\")\n\t\t\to(vnode.attrs.c).equals(\"d\")\n\t\t})\n\t\to(\"handles className attrs property\", function() {\n\t\t\tvar vnode = m(\"custom-element\", {className: \"a\"})\n\n\t\t\to(vnode.attrs.className).equals(\"a\")\n\t\t})\n\t\to(\"casts className using toString like browsers\", function() {\n\t\t\tconst className = {\n\t\t\t\tvalueOf: () => \".valueOf\",\n\t\t\t\ttoString: () => \"toString\"\n\t\t\t}\n\t\t\tvar vnode = m(\"custom-element\" + className, {className: className})\n\n\t\t\to(vnode.attrs.className).equals(\"valueOf toString\")\n\t\t})\n\t})\n\to.spec(\"children\", function() {\n\t\to(\"handles string single child\", function() {\n\t\t\tvar vnode = m(\"div\", {}, [\"a\"])\n\n\t\t\to(vnode.children[0].children).equals(\"a\")\n\t\t})\n\t\to(\"handles falsy string single child\", function() {\n\t\t\tvar vnode = m(\"div\", {}, [\"\"])\n\n\t\t\to(vnode.children[0].children).equals(\"\")\n\t\t})\n\t\to(\"handles number single child\", function() {\n\t\t\tvar vnode = m(\"div\", {}, [1])\n\n\t\t\to(vnode.children[0].children).equals(\"1\")\n\t\t})\n\t\to(\"handles falsy number single child\", function() {\n\t\t\tvar vnode = m(\"div\", {}, [0])\n\n\t\t\to(vnode.children[0].children).equals(\"0\")\n\t\t})\n\t\to(\"handles boolean single child\", function() {\n\t\t\tvar vnode = m(\"div\", {}, [true])\n\n\t\t\to(vnode.children).deepEquals([null])\n\t\t})\n\t\to(\"handles falsy boolean single child\", function() {\n\t\t\tvar vnode = m(\"div\", {}, [false])\n\n\t\t\to(vnode.children).deepEquals([null])\n\t\t})\n\t\to(\"handles null single child\", function() {\n\t\t\tvar vnode = m(\"div\", {}, [null])\n\n\t\t\to(vnode.children).deepEquals([null])\n\t\t})\n\t\to(\"handles undefined single child\", function() {\n\t\t\tvar vnode = m(\"div\", {}, [undefined])\n\n\t\t\to(vnode.children).deepEquals([null])\n\t\t})\n\t\to(\"handles multiple string children\", function() {\n\t\t\tvar vnode = m(\"div\", {}, [\"\", \"a\"])\n\n\t\t\to(vnode.children[0].tag).equals(\"#\")\n\t\t\to(vnode.children[0].children).equals(\"\")\n\t\t\to(vnode.children[1].tag).equals(\"#\")\n\t\t\to(vnode.children[1].children).equals(\"a\")\n\t\t})\n\t\to(\"handles multiple number children\", function() {\n\t\t\tvar vnode = m(\"div\", {}, [0, 1])\n\n\t\t\to(vnode.children[0].tag).equals(\"#\")\n\t\t\to(vnode.children[0].children).equals(\"0\")\n\t\t\to(vnode.children[1].tag).equals(\"#\")\n\t\t\to(vnode.children[1].children).equals(\"1\")\n\t\t})\n\t\to(\"handles multiple boolean children\", function() {\n\t\t\tvar vnode = m(\"div\", {}, [false, true])\n\n\t\t\to(vnode.children).deepEquals([null, null])\n\t\t})\n\t\to(\"handles multiple null/undefined child\", function() {\n\t\t\tvar vnode = m(\"div\", {}, [null, undefined])\n\n\t\t\to(vnode.children).deepEquals([null, null])\n\t\t})\n\t\to(\"handles falsy number single child without attrs\", function() {\n\t\t\tvar vnode = m(\"div\", 0)\n\n\t\t\to(vnode.children[0].children).equals(\"0\")\n\t\t})\n\t})\n\to.spec(\"permutations\", function() {\n\t\to(\"handles null attr and children\", function() {\n\t\t\tvar vnode = m(\"div\", null, [m(\"a\"), m(\"b\")])\n\n\t\t\to(vnode.children.length).equals(2)\n\t\t\to(vnode.children[0].tag).equals(\"a\")\n\t\t\to(vnode.children[1].tag).equals(\"b\")\n\t\t})\n\t\to(\"handles null attr and child unwrapped\", function() {\n\t\t\tvar vnode = m(\"div\", null, m(\"a\"))\n\n\t\t\to(vnode.children.length).equals(1)\n\t\t\to(vnode.children[0].tag).equals(\"a\")\n\t\t})\n\t\to(\"handles null attr and children unwrapped\", function() {\n\t\t\tvar vnode = m(\"div\", null, m(\"a\"), m(\"b\"))\n\n\t\t\to(vnode.children.length).equals(2)\n\t\t\to(vnode.children[0].tag).equals(\"a\")\n\t\t\to(vnode.children[1].tag).equals(\"b\")\n\t\t})\n\t\to(\"handles attr and children\", function() {\n\t\t\tvar vnode = m(\"div\", {a: \"b\"}, [m(\"i\"), m(\"s\")])\n\n\t\t\to(vnode.attrs.a).equals(\"b\")\n\t\t\to(vnode.children[0].tag).equals(\"i\")\n\t\t\to(vnode.children[1].tag).equals(\"s\")\n\t\t})\n\t\to(\"handles attr and child unwrapped\", function() {\n\t\t\tvar vnode = m(\"div\", {a: \"b\"}, m(\"i\"))\n\n\t\t\to(vnode.attrs.a).equals(\"b\")\n\t\t\to(vnode.children[0].tag).equals(\"i\")\n\t\t})\n\t\to(\"handles attr and children unwrapped\", function() {\n\t\t\tvar vnode = m(\"div\", {a: \"b\"}, m(\"i\"), m(\"s\"))\n\n\t\t\to(vnode.attrs.a).equals(\"b\")\n\t\t\to(vnode.children[0].tag).equals(\"i\")\n\t\t\to(vnode.children[1].tag).equals(\"s\")\n\t\t})\n\t\to(\"handles attr and text children\", function() {\n\t\t\tvar vnode = m(\"div\", {a: \"b\"}, [\"c\", \"d\"])\n\n\t\t\to(vnode.attrs.a).equals(\"b\")\n\t\t\to(vnode.children[0].tag).equals(\"#\")\n\t\t\to(vnode.children[0].children).equals(\"c\")\n\t\t\to(vnode.children[1].tag).equals(\"#\")\n\t\t\to(vnode.children[1].children).equals(\"d\")\n\t\t})\n\t\to(\"handles attr and single string text child\", function() {\n\t\t\tvar vnode = m(\"div\", {a: \"b\"}, [\"c\"])\n\n\t\t\to(vnode.attrs.a).equals(\"b\")\n\t\t\to(vnode.children[0].children).equals(\"c\")\n\t\t})\n\t\to(\"handles attr and single falsy string text child\", function() {\n\t\t\tvar vnode = m(\"div\", {a: \"b\"}, [\"\"])\n\n\t\t\to(vnode.attrs.a).equals(\"b\")\n\t\t\to(vnode.children[0].children).equals(\"\")\n\t\t})\n\t\to(\"handles attr and single number text child\", function() {\n\t\t\tvar vnode = m(\"div\", {a: \"b\"}, [1])\n\n\t\t\to(vnode.attrs.a).equals(\"b\")\n\t\t\to(vnode.children[0].children).equals(\"1\")\n\t\t})\n\t\to(\"handles attr and single falsy number text child\", function() {\n\t\t\tvar vnode = m(\"div\", {a: \"b\"}, [0])\n\n\t\t\to(vnode.attrs.a).equals(\"b\")\n\t\t\to(vnode.children[0].children).equals(\"0\")\n\t\t})\n\t\to(\"handles attr and single boolean text child\", function() {\n\t\t\tvar vnode = m(\"div\", {a: \"b\"}, [true])\n\n\t\t\to(vnode.attrs.a).equals(\"b\")\n\t\t\to(vnode.children).deepEquals([null])\n\t\t})\n\t\to(\"handles attr and single falsy boolean text child\", function() {\n\t\t\tvar vnode = m(\"div\", {a: \"b\"}, [0])\n\n\t\t\to(vnode.attrs.a).equals(\"b\")\n\t\t\to(vnode.children[0].children).equals(\"0\")\n\t\t})\n\t\to(\"handles attr and single false boolean text child\", function() {\n\t\t\tvar vnode = m(\"div\", {a: \"b\"}, [false])\n\n\t\t\to(vnode.attrs.a).equals(\"b\")\n\t\t\to(vnode.children).deepEquals([null])\n\t\t})\n\t\to(\"handles attr and single text child unwrapped\", function() {\n\t\t\tvar vnode = m(\"div\", {a: \"b\"}, \"c\")\n\n\t\t\to(vnode.attrs.a).equals(\"b\")\n\t\t\to(vnode.children[0].children).equals(\"c\")\n\t\t})\n\t\to(\"handles attr and text children unwrapped\", function() {\n\t\t\tvar vnode = m(\"div\", {a: \"b\"}, \"c\", \"d\")\n\n\t\t\to(vnode.attrs.a).equals(\"b\")\n\t\t\to(vnode.children[0].tag).equals(\"#\")\n\t\t\to(vnode.children[0].children).equals(\"c\")\n\t\t\to(vnode.children[1].tag).equals(\"#\")\n\t\t\to(vnode.children[1].children).equals(\"d\")\n\t\t})\n\t\to(\"handles children without attr\", function() {\n\t\t\tvar vnode = m(\"div\", [m(\"i\"), m(\"s\")])\n\n\t\t\to(vnode.attrs).deepEquals({})\n\t\t\to(vnode.children[0].tag).equals(\"i\")\n\t\t\to(vnode.children[1].tag).equals(\"s\")\n\t\t})\n\t\to(\"handles child without attr unwrapped\", function() {\n\t\t\tvar vnode = m(\"div\", m(\"i\"))\n\n\t\t\to(vnode.attrs).deepEquals({})\n\t\t\to(vnode.children[0].tag).equals(\"i\")\n\t\t})\n\t\to(\"handles children without attr unwrapped\", function() {\n\t\t\tvar vnode = m(\"div\", m(\"i\"), m(\"s\"))\n\n\t\t\to(vnode.attrs).deepEquals({})\n\t\t\to(vnode.children[0].tag).equals(\"i\")\n\t\t\to(vnode.children[1].tag).equals(\"s\")\n\t\t})\n\t\to(\"handles children without attr (fragment)\", function() {\n\t\t\tvar vnode = m(\"[\", [m(\"i\"), m(\"s\")])\n\n\t\t\to(vnode.attrs).deepEquals({})\n\t\t\to(vnode.children[0].tag).equals(\"i\")\n\t\t\to(vnode.children[1].tag).equals(\"s\")\n\t\t})\n\t\to(\"handles child without attr unwrapped (fragment)\", function() {\n\t\t\tvar vnode = m(\"[\", m(\"i\"))\n\n\t\t\to(vnode.attrs).deepEquals({})\n\t\t\to(vnode.children[0].tag).equals(\"i\")\n\t\t})\n\t\to(\"handles children without attr unwrapped (fragment)\", function() {\n\t\t\tvar vnode = m(\"[\", m(\"i\"), m(\"s\"))\n\n\t\t\to(vnode.attrs).deepEquals({})\n\t\t\to(vnode.children[0].tag).equals(\"i\")\n\t\t\to(vnode.children[1].tag).equals(\"s\")\n\t\t})\n\t\to(\"handles shared attrs\", function() {\n\t\t\tvar attrs = {a: \"b\"}\n\n\t\t\tvar nodeA = m(\".a\", attrs)\n\t\t\tvar nodeB = m(\".b\", attrs)\n\n\t\t\to(nodeA.attrs.className).equals(\"a\")\n\t\t\to(nodeA.attrs.a).equals(\"b\")\n\n\t\t\to(nodeB.attrs.className).equals(\"b\")\n\t\t\to(nodeB.attrs.a).equals(\"b\")\n\t\t})\n\t\to(\"handles shared empty attrs (#2821)\", function() {\n\t\t\tvar attrs = {}\n\n\t\t\tvar nodeA = m(\".a\", attrs)\n\t\t\tvar nodeB = m(\".b\", attrs)\n\n\t\t\to(nodeA.attrs.className).equals(\"a\")\n\t\t\to(nodeB.attrs.className).equals(\"b\")\n\t\t})\n\t\to(\"doesnt modify passed attributes object\", function() {\n\t\t\tvar attrs = {a: \"b\"}\n\t\t\tm(\".a\", attrs)\n\t\t\to(attrs).deepEquals({a: \"b\"})\n\t\t})\n\t\to(\"non-nullish attr takes precedence over selector\", function() {\n\t\t\to(m(\"[a=b]\", {a: \"c\"}).attrs).deepEquals({a: \"c\"})\n\t\t})\n\t\to(\"null attr takes precedence over selector\", function() {\n\t\t\to(m(\"[a=b]\", {a: null}).attrs).deepEquals({a: null})\n\t\t})\n\t\to(\"undefined attr takes precedence over selector\", function() {\n\t\t\to(m(\"[a=b]\", {a: undefined}).attrs).deepEquals({a: undefined})\n\t\t})\n\t\to(\"handles fragment children without attr unwrapped\", function() {\n\t\t\tvar vnode = m(\"div\", [m(\"i\")], [m(\"s\")])\n\n\t\t\to(vnode.children[0].tag).equals(\"[\")\n\t\t\to(vnode.children[0].children[0].tag).equals(\"i\")\n\t\t\to(vnode.children[1].tag).equals(\"[\")\n\t\t\to(vnode.children[1].children[0].tag).equals(\"s\")\n\t\t})\n\t\to(\"handles children with nested array\", function() {\n\t\t\tvar vnode = m(\"div\", [[m(\"i\"), m(\"s\")]])\n\n\t\t\to(vnode.children[0].tag).equals(\"[\")\n\t\t\to(vnode.children[0].children[0].tag).equals(\"i\")\n\t\t\to(vnode.children[0].children[1].tag).equals(\"s\")\n\t\t})\n\t\to(\"handles children with deeply nested array\", function() {\n\t\t\tvar vnode = m(\"div\", [[[m(\"i\"), m(\"s\")]]])\n\n\t\t\to(vnode.children[0].tag).equals(\"[\")\n\t\t\to(vnode.children[0].children[0].tag).equals(\"[\")\n\t\t\to(vnode.children[0].children[0].children[0].tag).equals(\"i\")\n\t\t\to(vnode.children[0].children[0].children[1].tag).equals(\"s\")\n\t\t})\n\t})\n\to.spec(\"components\", function() {\n\t\to(\"works with POJOs\", function() {\n\t\t\tvar component = {\n\t\t\t\tview: function() {}\n\t\t\t}\n\t\t\tvar vnode = m(component, {id: \"a\"}, \"b\")\n\n\t\t\to(vnode.tag).equals(component)\n\t\t\to(vnode.attrs.id).equals(\"a\")\n\t\t\to(vnode.children.length).equals(1)\n\t\t\to(vnode.children[0]).equals(\"b\")\n\t\t})\n\t\to(\"works with constructibles\", function() {\n\t\t\tvar component = o.spy()\n\t\t\tcomponent.prototype.view = function() {}\n\n\t\t\tvar vnode = m(component, {id: \"a\"}, \"b\")\n\n\t\t\to(component.callCount).equals(0)\n\n\t\t\to(vnode.tag).equals(component)\n\t\t\to(vnode.attrs.id).equals(\"a\")\n\t\t\to(vnode.children.length).equals(1)\n\t\t\to(vnode.children[0]).equals(\"b\")\n\t\t})\n\t\to(\"works with closures\", function () {\n\t\t\tvar component = o.spy()\n\n\t\t\tvar vnode = m(component, {id: \"a\"}, \"b\")\n\n\t\t\to(component.callCount).equals(0)\n\n\t\t\to(vnode.tag).equals(component)\n\t\t\to(vnode.attrs.id).equals(\"a\")\n\t\t\to(vnode.children.length).equals(1)\n\t\t\to(vnode.children[0]).equals(\"b\")\n\t\t})\n\t\to(\"works with POJOs (without attrs)\", function() {\n\t\t\tvar component = {\n\t\t\t\tview: function() {}\n\t\t\t}\n\t\t\tvar vnode = m(component, \"b\")\n\n\t\t\to(vnode.tag).equals(component)\n\t\t\to(vnode.attrs).deepEquals({})\n\t\t\to(vnode.children.length).equals(1)\n\t\t\to(vnode.children[0]).equals(\"b\")\n\t\t})\n\t\to(\"works with constructibles (without attrs)\", function() {\n\t\t\tvar component = o.spy()\n\t\t\tcomponent.prototype.view = function() {}\n\n\t\t\tvar vnode = m(component, \"b\")\n\n\t\t\to(component.callCount).equals(0)\n\n\t\t\to(vnode.tag).equals(component)\n\t\t\to(vnode.attrs).deepEquals({})\n\t\t\to(vnode.children.length).equals(1)\n\t\t\to(vnode.children[0]).equals(\"b\")\n\t\t})\n\t\to(\"works with closures (without attrs)\", function () {\n\t\t\tvar component = o.spy()\n\n\t\t\tvar vnode = m(component, \"b\")\n\n\t\t\to(component.callCount).equals(0)\n\n\t\t\to(vnode.tag).equals(component)\n\t\t\to(vnode.attrs).deepEquals({})\n\t\t\to(vnode.children.length).equals(1)\n\t\t\to(vnode.children[0]).equals(\"b\")\n\t\t})\n\t})\n})\n"
  },
  {
    "path": "render/tests/test-input.js",
    "content": "\"use strict\"\n\nvar o = require(\"ospec\")\nvar domMock = require(\"../../test-utils/domMock\")\nvar vdom = require(\"../../render/render\")\nvar m = require(\"../../render/hyperscript\")\n\no.spec(\"form inputs\", function() {\n\tvar $window, root, render\n\to.beforeEach(function() {\n\t\t$window = domMock()\n\t\trender = vdom($window)\n\t\troot = $window.document.createElement(\"div\")\n\t\t$window.document.body.appendChild(root)\n\t})\n\to.afterEach(function() {\n\t\twhile (root.firstChild) root.removeChild(root.firstChild)\n\t\troot.vnodes = null\n\t})\n\n\to.spec(\"input\", function() {\n\t\to(\"maintains focus after move\", function() {\n\t\t\tvar input = m(\"input\", {key: 1})\n\t\t\tvar a = m(\"a\", {key: 2})\n\t\t\tvar b = m(\"b\", {key: 3})\n\n\t\t\trender(root, [input, a, b])\n\t\t\tinput.dom.focus()\n\t\t\trender(root, [a, input, b])\n\n\t\t\to($window.document.activeElement).equals(input.dom)\n\t\t})\n\n\t\to(\"maintains focus when changed manually in hook\", function() {\n\t\t\tvar input = m(\"input\", {oncreate: function() {\n\t\t\t\tinput.dom.focus();\n\t\t\t}});\n\n\t\t\trender(root, input)\n\n\t\t\to($window.document.activeElement).equals(input.dom)\n\t\t})\n\n\t\to(\"syncs input value if DOM value differs from vdom value\", function() {\n\t\t\tvar input = m(\"input\", {value: \"aaa\", oninput: function() {}})\n\t\t\tvar updated = m(\"input\", {value: \"aaa\", oninput: function() {}})\n\n\t\t\trender(root, input)\n\n\t\t\t//simulate user typing\n\t\t\tvar e = $window.document.createEvent(\"KeyboardEvent\")\n\t\t\te.initEvent(\"input\", true, true)\n\t\t\tinput.dom.focus()\n\t\t\tinput.dom.value += \"a\"\n\t\t\tinput.dom.dispatchEvent(e)\n\n\t\t\t//re-render may use same vdom value as previous render call\n\t\t\trender(root, updated)\n\n\t\t\to(updated.dom.value).equals(\"aaa\")\n\t\t})\n\n\t\to(\"clear element value if vdom value is set to undefined (aka removed)\", function() {\n\t\t\tvar input = m(\"input\", {value: \"aaa\", oninput: function() {}})\n\t\t\tvar updated = m(\"input\", {value: undefined, oninput: function() {}})\n\n\t\t\trender(root, input)\n\t\t\trender(root, updated)\n\n\t\t\to(updated.dom.value).equals(\"\")\n\t\t})\n\n\t\to(\"syncs input checked attribute if DOM value differs from vdom value\", function() {\n\t\t\tvar input = m(\"input\", {type: \"checkbox\", checked: true, onclick: function() {}})\n\t\t\tvar updated = m(\"input\", {type: \"checkbox\", checked: true, onclick: function() {}})\n\n\t\t\trender(root, input)\n\n\t\t\t//simulate user clicking checkbox\n\t\t\tvar e = $window.document.createEvent(\"MouseEvents\")\n\t\t\te.initEvent(\"click\", true, true)\n\t\t\tinput.dom.focus()\n\t\t\tinput.dom.dispatchEvent(e)\n\n\t\t\t//re-render may use same vdom value as previous render call\n\t\t\trender(root, updated)\n\n\t\t\to(updated.dom.checked).equals(true)\n\t\t})\n\n\t\to(\"syncs file input value attribute if DOM value differs from vdom value and is empty\", function() {\n\t\t\tvar input = m(\"input\", {type: \"file\", value: \"\", onclick: function() {}})\n\t\t\tvar updated = m(\"input\", {type: \"file\", value: \"\", onclick: function() {}})\n\t\t\tvar spy = o.spy()\n\t\t\tvar error = console.error\n\n\t\t\trender(root, input)\n\n\t\t\tinput.dom.value = \"test.png\"\n\n\t\t\ttry {\n\t\t\t\tconsole.error = spy\n\t\t\t\trender(root, updated)\n\t\t\t} finally {\n\t\t\t\tconsole.error = error\n\t\t\t}\n\n\t\t\to(updated.dom.value).equals(\"\")\n\t\t\to(spy.callCount).equals(0)\n\t\t})\n\n\t\to(\"warns and ignores file input value attribute if DOM value differs from vdom value and is non-empty\", function() {\n\t\t\tvar input = m(\"input\", {type: \"file\", value: \"\", onclick: function() {}})\n\t\t\tvar updated = m(\"input\", {type: \"file\", value: \"other.png\", onclick: function() {}})\n\t\t\tvar spy = o.spy()\n\t\t\tvar error = console.error\n\n\t\t\trender(root, input)\n\n\t\t\tinput.dom.value = \"test.png\"\n\n\t\t\ttry {\n\t\t\t\tconsole.error = spy\n\t\t\t\trender(root, updated)\n\t\t\t} finally {\n\t\t\t\tconsole.error = error\n\t\t\t}\n\n\t\t\to(updated.dom.value).equals(\"test.png\")\n\t\t\to(spy.callCount).equals(1)\n\t\t})\n\n\t\to(\"retains file input value attribute if DOM value is the same as vdom value and is non-empty\", function() {\n\t\t\tvar $window = domMock(o)\n\t\t\tvar render = vdom($window)\n\t\t\tvar root = $window.document.createElement(\"div\")\n\t\t\t$window.document.body.appendChild(root)\n\t\t\tvar input = m(\"input\", {type: \"file\", value: \"\", onclick: function() {}})\n\t\t\tvar updated1 = m(\"input\", {type: \"file\", value: \"test.png\", onclick: function() {}})\n\t\t\tvar updated2 = m(\"input\", {type: \"file\", value: \"test.png\", onclick: function() {}})\n\t\t\tvar spy = o.spy()\n\t\t\tvar error = console.error\n\n\t\t\trender(root, input)\n\n\t\t\t// Verify our assumptions about the outer element state\n\t\t\to($window.__getSpies(input.dom).valueSetter.callCount).equals(0)\n\t\t\tinput.dom.value = \"test.png\"\n\t\t\to($window.__getSpies(input.dom).valueSetter.callCount).equals(1)\n\n\t\t\ttry {\n\t\t\t\tconsole.error = spy\n\t\t\t\trender(root, updated1)\n\t\t\t} finally {\n\t\t\t\tconsole.error = error\n\t\t\t}\n\n\t\t\to(updated1.dom.value).equals(\"test.png\")\n\t\t\to(spy.callCount).equals(0)\n\t\t\to($window.__getSpies(updated1.dom).valueSetter.callCount).equals(1)\n\n\t\t\ttry {\n\t\t\t\tconsole.error = spy\n\t\t\t\trender(root, updated2)\n\t\t\t} finally {\n\t\t\t\tconsole.error = error\n\t\t\t}\n\n\t\t\to(updated2.dom.value).equals(\"test.png\")\n\t\t\to(spy.callCount).equals(0)\n\t\t\to($window.__getSpies(updated2.dom).valueSetter.callCount).equals(1)\n\t\t})\n\t})\n\n\to.spec(\"select\", function() {\n\t\to(\"select works without attributes\", function() {\n\t\t\tvar select = m(\"select\",\n\t\t\t\tm(\"option\", {value: \"a\"}, \"aaa\")\n\t\t\t)\n\n\t\t\trender(root, select)\n\n\t\t\to(select.dom.value).equals(\"a\")\n\t\t\to(select.dom.selectedIndex).equals(0)\n\t\t})\n\n\t\to(\"select option can have empty string value\", function() {\n\t\t\tvar select = m(\"select\",\n\t\t\t\tm(\"option\", {value: \"\"}, \"aaa\")\n\t\t\t)\n\n\t\t\trender(root, select)\n\n\t\t\to(select.dom.firstChild.value).equals(\"\")\n\t\t})\n\n\t\to(\"option value defaults to textContent unless explicitly set\", function() {\n\t\t\tvar select = m(\"select\",\n\t\t\t\tm(\"option\", \"aaa\")\n\t\t\t)\n\n\t\t\trender(root, select)\n\n\t\t\to(select.dom.firstChild.value).equals(\"aaa\")\n\t\t\to(select.dom.value).equals(\"aaa\")\n\n\t\t\t//test that value changes when content changes\n\t\t\tselect = m(\"select\",\n\t\t\t\tm(\"option\", \"bbb\")\n\t\t\t)\n\n\t\t\trender(root, select)\n\n\t\t\to(select.dom.firstChild.value).equals(\"bbb\")\n\t\t\to(select.dom.value).equals(\"bbb\")\n\n\t\t\t//test that value can be set to \"\" in subsequent render\n\t\t\tselect = m(\"select\",\n\t\t\t\tm(\"option\", {value: \"\"}, \"aaa\")\n\t\t\t)\n\n\t\t\trender(root, select)\n\n\t\t\to(select.dom.firstChild.value).equals(\"\")\n\t\t\to(select.dom.value).equals(\"\")\n\n\t\t\t//test that value reverts to textContent when value omitted\n\t\t\tselect = m(\"select\",\n\t\t\t\tm(\"option\", \"aaa\")\n\t\t\t)\n\n\t\t\trender(root, select)\n\n\t\t\to(select.dom.firstChild.value).equals(\"aaa\")\n\t\t\to(select.dom.value).equals(\"aaa\")\n\t\t})\n\n\t\to(\"select yields invalid value without children\", function() {\n\t\t\tvar select = m(\"select\", {value: \"a\"})\n\n\t\t\trender(root, select)\n\n\t\t\to(select.dom.value).equals(\"\")\n\t\t\to(select.dom.selectedIndex).equals(-1)\n\t\t})\n\n\t\to(\"select value is set correctly on first render\", function() {\n\t\t\tvar select = m(\"select\", {value: \"b\"},\n\t\t\t\tm(\"option\", {value: \"a\"}, \"aaa\"),\n\t\t\t\tm(\"option\", {value: \"b\"}, \"bbb\"),\n\t\t\t\tm(\"option\", {value: \"c\"}, \"ccc\")\n\t\t\t)\n\n\t\t\trender(root, select)\n\n\t\t\to(select.dom.value).equals(\"b\")\n\t\t\to(select.dom.selectedIndex).equals(1)\n\t\t})\n\n\t\to(\"syncs select value if DOM value differs from vdom value\", function() {\n\t\t\tfunction makeSelect() {\n\t\t\t\treturn m(\"select\", {value: \"b\"},\n\t\t\t\t\tm(\"option\", {value: \"a\"}, \"aaa\"),\n\t\t\t\t\tm(\"option\", {value: \"b\"}, \"bbb\"),\n\t\t\t\t\tm(\"option\", {value: \"c\"}, \"ccc\")\n\t\t\t\t)\n\t\t\t}\n\n\t\t\trender(root, makeSelect())\n\n\t\t\t//simulate user selecting option\n\t\t\troot.firstChild.value = \"c\"\n\t\t\troot.firstChild.focus()\n\n\t\t\t//re-render may use same vdom value as previous render call\n\t\t\trender(root, makeSelect())\n\n\t\t\to(root.firstChild.value).equals(\"b\")\n\t\t\to(root.firstChild.selectedIndex).equals(1)\n\t\t})\n\t})\n})\n"
  },
  {
    "path": "render/tests/test-normalize.js",
    "content": "\"use strict\"\n\nvar o = require(\"ospec\")\nvar Vnode = require(\"../../render/vnode\")\n\no.spec(\"normalize\", function() {\n\to(\"normalizes array into fragment\", function() {\n\t\tvar node = Vnode.normalize([])\n\n\t\to(node.tag).equals(\"[\")\n\t\to(node.children.length).equals(0)\n\t})\n\to(\"normalizes nested array into fragment\", function() {\n\t\tvar node = Vnode.normalize([[]])\n\n\t\to(node.tag).equals(\"[\")\n\t\to(node.children.length).equals(1)\n\t\to(node.children[0].tag).equals(\"[\")\n\t\to(node.children[0].children.length).equals(0)\n\t})\n\to(\"normalizes string into text node\", function() {\n\t\tvar node = Vnode.normalize(\"a\")\n\n\t\to(node.tag).equals(\"#\")\n\t\to(node.children).equals(\"a\")\n\t})\n\to(\"normalizes falsy string into text node\", function() {\n\t\tvar node = Vnode.normalize(\"\")\n\n\t\to(node.tag).equals(\"#\")\n\t\to(node.children).equals(\"\")\n\t})\n\to(\"normalizes number into text node\", function() {\n\t\tvar node = Vnode.normalize(1)\n\n\t\to(node.tag).equals(\"#\")\n\t\to(node.children).equals(\"1\")\n\t})\n\to(\"normalizes falsy number into text node\", function() {\n\t\tvar node = Vnode.normalize(0)\n\n\t\to(node.tag).equals(\"#\")\n\t\to(node.children).equals(\"0\")\n\t})\n\to(\"normalizes `true` to `null`\", function() {\n\t\tvar node = Vnode.normalize(true)\n\n\t\to(node).equals(null)\n\t})\n\to(\"normalizes `false` to `null`\", function() {\n\t\tvar node = Vnode.normalize(false)\n\n\t\to(node).equals(null)\n\t})\n})\n"
  },
  {
    "path": "render/tests/test-normalizeChildren.js",
    "content": "\"use strict\"\n\nvar o = require(\"ospec\")\nvar Vnode = require(\"../../render/vnode\")\n\no.spec(\"normalizeChildren\", function() {\n\to(\"normalizes arrays into fragments\", function() {\n\t\tvar children = Vnode.normalizeChildren([[]])\n\n\t\to(children[0].tag).equals(\"[\")\n\t\to(children[0].children.length).equals(0)\n\t})\n\to(\"normalizes strings into text nodes\", function() {\n\t\tvar children = Vnode.normalizeChildren([\"a\"])\n\n\t\to(children[0].tag).equals(\"#\")\n\t\to(children[0].children).equals(\"a\")\n\t})\n\to(\"normalizes `false` values into `null`s\", function() {\n\t\tvar children = Vnode.normalizeChildren([false])\n\n\t\to(children[0]).equals(null)\n\t})\n\to(\"allows all keys\", function() {\n\t\tvar children = Vnode.normalizeChildren([\n\t\t\t{key: 1},\n\t\t\t{key: 2},\n\t\t])\n\n\t\to(children).deepEquals([{key: 1}, {key: 2}])\n\t})\n\to(\"allows no keys\", function() {\n\t\tvar children = Vnode.normalizeChildren([\n\t\t\t{data: 1},\n\t\t\t{data: 2},\n\t\t])\n\n\t\to(children).deepEquals([{data: 1}, {data: 2}])\n\t})\n\to(\"disallows mixed keys, starting with key\", function() {\n\t\to(function() {\n\t\t\tVnode.normalizeChildren([\n\t\t\t\t{key: 1},\n\t\t\t\t{data: 2},\n\t\t\t])\n\t\t}).throws(TypeError)\n\t})\n\to(\"disallows mixed keys, starting with no key\", function() {\n\t\to(function() {\n\t\t\tVnode.normalizeChildren([\n\t\t\t\t{data: 1},\n\t\t\t\t{key: 2},\n\t\t\t])\n\t\t}).throws(TypeError)\n\t})\n\to(\"disallows mixed keys, ending with null\", function() {\n\t\to(function() {\n\t\t\tVnode.normalizeChildren([\n\t\t\t\t{key: 1},\n\t\t\t\tnull,\n\t\t\t])\n\t\t}).throws(TypeError)\n\t})\n\to(\"disallows mixed keys, starting with null\", function() {\n\t\to(function() {\n\t\t\tVnode.normalizeChildren([\n\t\t\t\tnull,\n\t\t\t\t{key: 2},\n\t\t\t])\n\t\t}).throws(TypeError)\n\t})\n\to(\"disallows mixed keys, ending with undefined\", function() {\n\t\to(function() {\n\t\t\tVnode.normalizeChildren([\n\t\t\t\t{key: 1},\n\t\t\t\tundefined,\n\t\t\t])\n\t\t}).throws(TypeError)\n\t})\n\to(\"disallows mixed keys, starting with undefined\", function() {\n\t\to(function() {\n\t\t\tVnode.normalizeChildren([\n\t\t\t\tundefined,\n\t\t\t\t{key: 2},\n\t\t\t])\n\t\t}).throws(TypeError)\n\t})\n\to(\"disallows mixed keys, ending with false\", function() {\n\t\to(function() {\n\t\t\tVnode.normalizeChildren([\n\t\t\t\t{key: 1},\n\t\t\t\tfalse,\n\t\t\t])\n\t\t}).throws(TypeError)\n\t})\n\to(\"disallows mixed keys, starting with false\", function() {\n\t\to(function() {\n\t\t\tVnode.normalizeChildren([\n\t\t\t\tfalse,\n\t\t\t\t{key: 2},\n\t\t\t])\n\t\t}).throws(TypeError)\n\t})\n\to(\"disallows mixed keys, ending with true\", function() {\n\t\to(function() {\n\t\t\tVnode.normalizeChildren([\n\t\t\t\t{key: 1},\n\t\t\t\ttrue,\n\t\t\t])\n\t\t}).throws(TypeError)\n\t})\n\to(\"disallows mixed keys, starting with true\", function() {\n\t\to(function() {\n\t\t\tVnode.normalizeChildren([\n\t\t\t\ttrue,\n\t\t\t\t{key: 2},\n\t\t\t])\n\t\t}).throws(TypeError)\n\t})\n})\n"
  },
  {
    "path": "render/tests/test-normalizeComponentChildren.js",
    "content": "\"use strict\"\n\nvar o = require(\"ospec\")\nvar m = require(\"../../render/hyperscript\")\nvar domMock = require(\"../../test-utils/domMock\")\nvar vdom = require(\"../../render/render\")\n\no.spec(\"component children\", function () {\n\tvar $window = domMock()\n\tvar root = $window.document.createElement(\"div\")\n\tvar render = vdom($window)\n\n\to.spec(\"component children\", function () {\n\t\tvar component = {\n\t\t\tview: function (vnode) {\n\t\t\t\treturn vnode.children\n\t\t\t}\n\t\t}\n\n\t\tvar vnode = m(component, \"a\")\n\n\t\trender(root, vnode)\n\n\t\to(\"are not normalized on ingestion\", function () {\n\t\t\to(vnode.children[0]).equals(\"a\")\n\t\t})\n\n\t\to(\"are normalized upon view interpolation\", function () {\n\t\t\to(vnode.instance.children.length).equals(1)\n\t\t\to(vnode.instance.children[0].tag).equals(\"#\")\n\t\t\to(vnode.instance.children[0].children).equals(\"a\")\n\t\t})\n\t})\n})\n"
  },
  {
    "path": "render/tests/test-onbeforeremove.js",
    "content": "\"use strict\"\n\nvar o = require(\"ospec\")\nvar callAsync = require(\"../../test-utils/callAsync\")\nvar components = require(\"../../test-utils/components\")\nvar domMock = require(\"../../test-utils/domMock\")\nvar vdom = require(\"../../render/render\")\nvar m = require(\"../../render/hyperscript\")\nvar fragment = require(\"../../render/fragment\")\n\no.spec(\"onbeforeremove\", function() {\n\tvar $window, root, render\n\to.beforeEach(function() {\n\t\t$window = domMock()\n\t\troot = $window.document.createElement(\"div\")\n\t\trender = vdom($window)\n\t})\n\n\to(\"does not call onbeforeremove when creating\", function() {\n\t\tvar create = o.spy()\n\t\tvar vnode = m(\"div\", {onbeforeremove: create})\n\n\t\trender(root, vnode)\n\n\t\to(create.callCount).equals(0)\n\t})\n\to(\"does not call onbeforeremove when updating\", function() {\n\t\tvar create = o.spy()\n\t\tvar update = o.spy()\n\t\tvar vnode = m(\"div\", {onbeforeremove: create})\n\t\tvar updated = m(\"div\", {onbeforeremove: update})\n\n\t\trender(root, vnode)\n\t\trender(root, updated)\n\n\t\to(create.callCount).equals(0)\n\t\to(update.callCount).equals(0)\n\t})\n\to(\"calls onbeforeremove when removing element\", function(done) {\n\t\tvar vnode = m(\"div\", {onbeforeremove: remove})\n\n\t\trender(root, vnode)\n\t\trender(root, [])\n\n\t\tfunction remove(node) {\n\t\t\to(node).equals(vnode)\n\t\t\to(this).equals(vnode.state)\n\t\t\to(this != null && typeof this === \"object\").equals(true)\n\t\t\to(root.childNodes.length).equals(1)\n\t\t\to(root.firstChild).equals(vnode.dom)\n\n\t\t\tcallAsync(function() {\n\t\t\t\to(root.childNodes.length).equals(0)\n\n\t\t\t\tdone()\n\t\t\t})\n\t\t}\n\t})\n\to(\"calls onbeforeremove when removing fragment\", function(done) {\n\t\tvar vnode = fragment({onbeforeremove: remove}, m(\"div\"))\n\n\t\trender(root, vnode)\n\t\trender(root, [])\n\n\t\tfunction remove(node) {\n\t\t\to(node).equals(vnode)\n\t\t\to(root.childNodes.length).equals(1)\n\t\t\to(root.firstChild).equals(vnode.dom)\n\n\t\t\tcallAsync(function() {\n\t\t\t\to(root.childNodes.length).equals(0)\n\n\t\t\t\tdone()\n\t\t\t})\n\t\t}\n\t})\n\to(\"calls onremove after onbeforeremove resolves\", function(done) {\n\t\tvar spy = o.spy()\n\t\tvar vnode = fragment({onbeforeremove: onbeforeremove, onremove: spy}, \"a\")\n\n\t\trender(root, vnode)\n\t\trender(root, [])\n\n\t\tfunction onbeforeremove(node) {\n\t\t\to(node).equals(vnode)\n\t\t\to(root.childNodes.length).equals(1)\n\t\t\to(root.firstChild).equals(vnode.dom)\n\t\t\to(spy.callCount).equals(0)\n\n\t\t\tcallAsync(function() {\n\t\t\t\to(root.childNodes.length).equals(0)\n\t\t\t\to(spy.callCount).equals(1)\n\n\t\t\t\tdone()\n\t\t\t})\n\t\t}\n\t})\n\to(\"does not set onbeforeremove as an event handler\", function() {\n\t\tvar remove = o.spy()\n\t\tvar vnode = m(\"div\", {onbeforeremove: remove})\n\n\t\trender(root, vnode)\n\n\t\to(vnode.dom.onbeforeremove).equals(undefined)\n\t\to(vnode.dom.attributes[\"onbeforeremove\"]).equals(undefined)\n\t})\n\to(\"does not leave elements out of order during removal\", function(done) {\n\t\tvar remove = function() {return Promise.resolve()}\n\t\tvar vnodes = [m(\"div\", {key: 1, onbeforeremove: remove}, \"1\"), m(\"div\", {key: 2, onbeforeremove: remove}, \"2\")]\n\t\tvar updated = m(\"div\", {key: 2, onbeforeremove: remove}, \"2\")\n\n\t\trender(root, vnodes)\n\t\trender(root, updated)\n\n\t\to(root.childNodes.length).equals(2)\n\t\to(root.firstChild.firstChild.nodeValue).equals(\"1\")\n\n\t\tcallAsync(function() {\n\t\t\to(root.childNodes.length).equals(1)\n\t\t\to(root.firstChild.firstChild.nodeValue).equals(\"2\")\n\n\t\t\tdone()\n\t\t})\n\t})\n\to(\"handles thenable objecs (#2592)\", function(done) {\n\t\tvar remove = function() {return {then: function(resolve) {resolve()}}}\n\t\tvar vnodes = m(\"div\", {key: 1, onbeforeremove: remove}, \"a\")\n\t\tvar updated = []\n\n\t\trender(root, vnodes)\n\t\trender(root, updated)\n\n\t\to(root.childNodes.length).equals(1)\n\t\to(root.firstChild.firstChild.nodeValue).equals(\"a\")\n\n\t\tcallAsync(function() {\n\t\t\to(root.childNodes.length).equals(0)\n\n\t\t\tdone()\n\t\t})\n\t})\n\tcomponents.forEach(function(cmp){\n\t\to.spec(cmp.kind, function(){\n\t\t\tvar createComponent = cmp.create\n\t\t\to(\"finalizes the remove phase asynchronously when promise is returned synchronously from both attrs- and tag.onbeforeremove\", function(done) {\n\t\t\t\tvar onremove = o.spy()\n\t\t\t\tvar onbeforeremove = function(){return Promise.resolve()}\n\t\t\t\tvar component = createComponent({\n\t\t\t\t\tonbeforeremove: onbeforeremove,\n\t\t\t\t\tonremove: onremove,\n\t\t\t\t\tview: function() {},\n\t\t\t\t})\n\t\t\t\trender(root, m(component, {onbeforeremove: onbeforeremove, onremove: onremove}))\n\t\t\t\trender(root, [])\n\t\t\t\tcallAsync(function() {\n\t\t\t\t\to(onremove.callCount).equals(2) // once for `tag`, once for `attrs`\n\t\t\t\t\tdone()\n\t\t\t\t})\n\t\t\t})\n\t\t\to(\"awaits promise resolution before removing the node\", function(done) {\n\t\t\t\tvar view = o.spy()\n\t\t\t\tvar onremove = o.spy()\n\t\t\t\tvar onbeforeremove = function(){return new Promise(function(resolve){callAsync(resolve)})}\n\t\t\t\tvar component = createComponent({\n\t\t\t\t\tonbeforeremove: onbeforeremove,\n\t\t\t\t\tonremove: onremove,\n\t\t\t\t\tview: view,\n\t\t\t\t})\n\t\t\t\trender(root, m(component))\n\t\t\t\trender(root, [])\n\n\t\t\t\to(onremove.callCount).equals(0)\n\t\t\t\tcallAsync(function(){\n\t\t\t\t\tcallAsync(function() {\n\t\t\t\t\t\to(onremove.callCount).equals(1)\n\t\t\t\t\t\tdone()\n\t\t\t\t\t})\n\t\t\t\t})\n\t\t\t})\n\t\t})\n\t})\n})\n"
  },
  {
    "path": "render/tests/test-onbeforeupdate.js",
    "content": "\"use strict\"\n\nvar o = require(\"ospec\")\nvar components = require(\"../../test-utils/components\")\nvar domMock = require(\"../../test-utils/domMock\")\nvar vdom = require(\"../../render/render\")\nvar m = require(\"../../render/hyperscript\")\nvar fragment = require(\"../../render/fragment\")\n\no.spec(\"onbeforeupdate\", function() {\n\tvar $window, root, render\n\to.beforeEach(function() {\n\t\t$window = domMock()\n\t\troot = $window.document.createElement(\"div\")\n\t\trender = vdom($window)\n\t})\n\n\to(\"prevents update in element\", function() {\n\t\tvar onbeforeupdate = function() {return false}\n\t\tvar vnode = m(\"div\", {id: \"a\", onbeforeupdate: onbeforeupdate})\n\t\tvar updated = m(\"div\", {id: \"b\", onbeforeupdate: onbeforeupdate})\n\n\t\trender(root, vnode)\n\t\trender(root, updated)\n\n\t\to(root.firstChild.attributes[\"id\"].value).equals(\"a\")\n\t})\n\n\to(\"prevents update in fragment\", function() {\n\t\tvar onbeforeupdate = function() {return false}\n\t\tvar vnode = fragment({onbeforeupdate: onbeforeupdate}, \"a\")\n\t\tvar updated = fragment({onbeforeupdate: onbeforeupdate}, \"b\")\n\n\t\trender(root, vnode)\n\t\trender(root, updated)\n\n\t\to(root.firstChild.nodeValue).equals(\"a\")\n\t})\n\n\to(\"does not prevent update if returning true\", function() {\n\t\tvar onbeforeupdate = function() {return true}\n\t\tvar vnode = m(\"div\", {id: \"a\", onbeforeupdate: onbeforeupdate})\n\t\tvar updated = m(\"div\", {id: \"b\", onbeforeupdate: onbeforeupdate})\n\n\t\trender(root, vnode)\n\t\trender(root, updated)\n\n\t\to(root.firstChild.attributes[\"id\"].value).equals(\"b\")\n\t})\n\n\to(\"accepts arguments for comparison\", function() {\n\t\tvar count = 0\n\t\tvar vnode = m(\"div\", {id: \"a\", onbeforeupdate: onbeforeupdate})\n\t\tvar updated = m(\"div\", {id: \"b\", onbeforeupdate: onbeforeupdate})\n\n\t\trender(root, vnode)\n\t\trender(root, updated)\n\n\t\tfunction onbeforeupdate(vnode, old) {\n\t\t\tcount++\n\n\t\t\to(old.attrs.id).equals(\"a\")\n\t\t\to(vnode.attrs.id).equals(\"b\")\n\n\t\t\treturn old.attrs.id !== vnode.attrs.id\n\t\t}\n\n\t\to(count).equals(1)\n\t\to(root.firstChild.attributes[\"id\"].value).equals(\"b\")\n\t})\n\n\to(\"is not called on creation\", function() {\n\t\tvar count = 0\n\t\tvar vnode = m(\"div\", {id: \"a\", onbeforeupdate: onbeforeupdate})\n\n\t\trender(root, vnode)\n\n\t\tfunction onbeforeupdate() {\n\t\t\tcount++\n\t\t\treturn true\n\t\t}\n\n\t\to(count).equals(0)\n\t})\n\n\to(\"is called only once on update\", function() {\n\t\tvar count = 0\n\t\tvar vnode = m(\"div\", {id: \"a\", onbeforeupdate: onbeforeupdate})\n\t\tvar updated = m(\"div\", {id: \"b\", onbeforeupdate: onbeforeupdate})\n\n\t\trender(root, vnode)\n\t\trender(root, updated)\n\n\t\tfunction onbeforeupdate() {\n\t\t\tcount++\n\t\t\treturn true\n\t\t}\n\n\t\to(count).equals(1)\n\t})\n\n\to(\"doesn't fire on recycled nodes\", function() {\n\t\tvar onbeforeupdate = o.spy()\n\t\tvar vnodes = [m(\"div\", {key: 1})]\n\t\tvar temp = []\n\t\tvar updated = [m(\"div\", {key: 1, onbeforeupdate: onbeforeupdate})]\n\n\t\trender(root, vnodes)\n\t\trender(root, temp)\n\t\trender(root, updated)\n\n\t\to(vnodes[0].dom).notEquals(updated[0].dom) // this used to be a recycling pool test\n\t\to(updated[0].dom.nodeName).equals(\"DIV\")\n\t\to(onbeforeupdate.callCount).equals(0)\n\t})\n\n\tcomponents.forEach(function(cmp){\n\t\to.spec(cmp.kind, function(){\n\t\t\tvar createComponent = cmp.create\n\n\t\t\to(\"prevents update in component\", function() {\n\t\t\t\tvar component = createComponent({\n\t\t\t\t\tonbeforeupdate: function() {return false},\n\t\t\t\t\tview: function(vnode) {\n\t\t\t\t\t\treturn m(\"div\", vnode.children)\n\t\t\t\t\t},\n\t\t\t\t})\n\t\t\t\tvar vnode = m(component, \"a\")\n\t\t\t\tvar updated = m(component, \"b\")\n\n\t\t\t\trender(root, vnode)\n\t\t\t\trender(root, updated)\n\n\t\t\t\to(root.firstChild.firstChild.nodeValue).equals(\"a\")\n\t\t\t})\n\n\t\t\to(\"prevents update if returning false in component and false in vnode\", function() {\n\t\t\t\tvar component = createComponent({\n\t\t\t\t\tonbeforeupdate: function() {return false},\n\t\t\t\t\tview: function(vnode) {\n\t\t\t\t\t\treturn m(\"div\", {id: vnode.attrs.id})\n\t\t\t\t\t},\n\t\t\t\t})\n\t\t\t\tvar vnode = m(component, {id: \"a\", onbeforeupdate: function() {return false}})\n\t\t\t\tvar updated = m(component, {id: \"b\", onbeforeupdate: function() {return false}})\n\n\t\t\t\trender(root, vnode)\n\t\t\t\trender(root, updated)\n\n\t\t\t\to(root.firstChild.attributes[\"id\"].value).equals(\"a\")\n\t\t\t})\n\n\t\t\to(\"does not prevent update if returning true in component and true in vnode\", function() {\n\t\t\t\tvar component = createComponent({\n\t\t\t\t\tonbeforeupdate: function() {return true},\n\t\t\t\t\tview: function(vnode) {\n\t\t\t\t\t\treturn m(\"div\", {id: vnode.attrs.id})\n\t\t\t\t\t},\n\t\t\t\t})\n\t\t\t\tvar vnode = m(component, {id: \"a\", onbeforeupdate: function() {return true}})\n\t\t\t\tvar updated = m(component, {id: \"b\", onbeforeupdate: function() {return true}})\n\n\t\t\t\trender(root, vnode)\n\t\t\t\trender(root, updated)\n\n\t\t\t\to(root.firstChild.attributes[\"id\"].value).equals(\"b\")\n\t\t\t})\n\n\t\t\to(\"prevents update if returning false in component but true in vnode\", function() {\n\t\t\t\tvar component = createComponent({\n\t\t\t\t\tonbeforeupdate: function() {return false},\n\t\t\t\t\tview: function(vnode) {\n\t\t\t\t\t\treturn m(\"div\", {id: vnode.attrs.id})\n\t\t\t\t\t},\n\t\t\t\t})\n\t\t\t\tvar vnode = m(component, {id: \"a\", onbeforeupdate: function() {return true}})\n\t\t\t\tvar updated = m(component, {id: \"b\", onbeforeupdate: function() {return true}})\n\n\t\t\t\trender(root, vnode)\n\t\t\t\trender(root, updated)\n\n\t\t\t\to(root.firstChild.attributes[\"id\"].value).equals(\"a\")\n\t\t\t})\n\n\t\t\to(\"prevents update if returning true in component but false in vnode\", function() {\n\t\t\t\tvar component = createComponent({\n\t\t\t\t\tonbeforeupdate: function() {return true},\n\t\t\t\t\tview: function(vnode) {\n\t\t\t\t\t\treturn m(\"div\", {id: vnode.attrs.id})\n\t\t\t\t\t},\n\t\t\t\t})\n\t\t\t\tvar vnode = m(component, {id: \"a\", onbeforeupdate: function() {return false}})\n\t\t\t\tvar updated = m(component, {id: \"b\", onbeforeupdate: function() {return false}})\n\n\t\t\t\trender(root, vnode)\n\t\t\t\trender(root, updated)\n\n\t\t\t\to(root.firstChild.attributes[\"id\"].value).equals(\"a\")\n\t\t\t})\n\n\t\t\to(\"does not prevent update if returning true from component\", function() {\n\t\t\t\tvar component = createComponent({\n\t\t\t\t\tonbeforeupdate: function() {return true},\n\t\t\t\t\tview: function(vnode) {\n\t\t\t\t\t\treturn m(\"div\", vnode.attrs)\n\t\t\t\t\t},\n\t\t\t\t})\n\t\t\t\tvar vnode = m(component, {id: \"a\"})\n\t\t\t\tvar updated = m(component, {id: \"b\"})\n\n\t\t\t\trender(root, vnode)\n\t\t\t\trender(root, updated)\n\n\t\t\t\to(root.firstChild.attributes[\"id\"].value).equals(\"b\")\n\t\t\t})\n\n\t\t\to(\"accepts arguments for comparison in component\", function() {\n\t\t\t\tvar component = createComponent({\n\t\t\t\t\tonbeforeupdate: onbeforeupdate,\n\t\t\t\t\tview: function(vnode) {\n\t\t\t\t\t\treturn m(\"div\", vnode.attrs)\n\t\t\t\t\t},\n\t\t\t\t})\n\t\t\t\tvar count = 0\n\t\t\t\tvar vnode = m(component, {id: \"a\"})\n\t\t\t\tvar updated = m(component, {id: \"b\"})\n\n\t\t\t\trender(root, vnode)\n\t\t\t\trender(root, updated)\n\n\t\t\t\tfunction onbeforeupdate(vnode, old) {\n\t\t\t\t\tcount++\n\n\t\t\t\t\to(old.attrs.id).equals(\"a\")\n\t\t\t\t\to(vnode.attrs.id).equals(\"b\")\n\n\t\t\t\t\treturn old.attrs.id !== vnode.attrs.id\n\t\t\t\t}\n\n\t\t\t\to(count).equals(1)\n\t\t\t\to(root.firstChild.attributes[\"id\"].value).equals(\"b\")\n\t\t\t})\n\n\t\t\to(\"is not called on component creation\", function() {\n\t\t\t\tcreateComponent({\n\t\t\t\t\tonbeforeupdate: onbeforeupdate,\n\t\t\t\t\tview: function(vnode) {\n\t\t\t\t\t\treturn m(\"div\", vnode.attrs)\n\t\t\t\t\t},\n\t\t\t\t})\n\n\t\t\t\tvar count = 0\n\t\t\t\tvar vnode = m(\"div\", {id: \"a\"})\n\n\t\t\t\trender(root, vnode)\n\n\t\t\t\tfunction onbeforeupdate() {\n\t\t\t\t\tcount++\n\t\t\t\t\treturn true\n\t\t\t\t}\n\n\t\t\t\to(count).equals(0)\n\t\t\t})\n\n\t\t\to(\"is called only once on component update\", function() {\n\t\t\t\tvar component = createComponent({\n\t\t\t\t\tonbeforeupdate: onbeforeupdate,\n\t\t\t\t\tview: function(vnode) {\n\t\t\t\t\t\treturn m(\"div\", vnode.attrs)\n\t\t\t\t\t},\n\t\t\t\t})\n\n\t\t\t\tvar count = 0\n\t\t\t\tvar vnode = m(component, {id: \"a\"})\n\t\t\t\tvar updated = m(component, {id: \"b\"})\n\n\t\t\t\trender(root, vnode)\n\t\t\t\trender(root, updated)\n\n\t\t\t\tfunction onbeforeupdate() {\n\t\t\t\t\tcount++\n\t\t\t\t\treturn true\n\t\t\t\t}\n\n\t\t\t\to(count).equals(1)\n\t\t\t})\n\t\t})\n\t})\n\n\t// https://github.com/MithrilJS/mithril.js/issues/2067\n\to.spec(\"after prevented update\", function() {\n\t\to(\"old attributes are retained\", function() {\n\t\t\trender(root, [\n\t\t\t\tm(\"div\", {\"id\": \"foo\", onbeforeupdate: function() { return true }})\n\t\t\t])\n\t\t\trender(root, [\n\t\t\t\tm(\"div\", {\"id\": \"bar\", onbeforeupdate: function() { return false }})\n\t\t\t])\n\t\t\trender(root, [\n\t\t\t\tm(\"div\", {\"id\": \"bar\", onbeforeupdate: function() { return true }})\n\t\t\t])\n\t\t\to(root.firstChild.attributes[\"id\"].value).equals(\"bar\")\n\t\t})\n\t\to(\"old children is retained\", function() {\n\t\t\trender(root,\n\t\t\t\tm(\"div\", {onbeforeupdate: function() { return true }},\n\t\t\t\t\tm(\"div\")\n\t\t\t\t)\n\t\t\t)\n\t\t\trender(root,\n\t\t\t\tm(\"div\", {onbeforeupdate: function() { return false }},\n\t\t\t\t\tm(\"div\", m(\"div\"))\n\t\t\t\t)\n\t\t\t)\n\t\t\trender(root,\n\t\t\t\tm(\"div\", {onbeforeupdate: function() { return true }},\n\t\t\t\t\tm(\"div\", m(\"div\"))\n\t\t\t\t)\n\t\t\t)\n\t\t\to(root.firstChild.firstChild.childNodes.length).equals(1)\n\t\t})\n\t\to(\"old text is retained\", function() {\n\t\t\trender(root,\n\t\t\t\tm(\"div\", {onbeforeupdate: function() { return true }},\n\t\t\t\t\tm(\"div\")\n\t\t\t\t)\n\t\t\t)\n\t\t\trender(root,\n\t\t\t\tm(\"div\", {onbeforeupdate: function() { return false }},\n\t\t\t\t\tm(\"div\", \"foo\")\n\t\t\t\t)\n\t\t\t)\n\t\t\trender(root,\n\t\t\t\tm(\"div\", {onbeforeupdate: function() { return true }},\n\t\t\t\t\tm(\"div\", \"foo\")\n\t\t\t\t)\n\t\t\t)\n\t\t\to(root.firstChild.firstChild.firstChild.nodeValue).equals(\"foo\")\n\t\t})\n\t\to(\"updating component children doesn't error\", function() {\n\t\t\tvar Child = {\n\t\t\t\tview(v) {\n\t\t\t\t\treturn m(\"div\",\n\t\t\t\t\t\tv.attrs.foo ? m(\"div\") : null\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\trender(root,\n\t\t\t\tm(\"div\", {onbeforeupdate: function() { return true }},\n\t\t\t\t\tm(Child, {foo: false})\n\t\t\t\t)\n\t\t\t)\n\t\t\trender(root,\n\t\t\t\tm(\"div\", {onbeforeupdate: function() { return false }},\n\t\t\t\t\tm(Child, {foo: false})\n\t\t\t\t)\n\t\t\t)\n\t\t\trender(root,\n\t\t\t\tm(\"div\", {onbeforeupdate: function() { return true }},\n\t\t\t\t\tm(Child, {foo: true})\n\t\t\t\t)\n\t\t\t)\n\t\t\to(root.firstChild.firstChild.childNodes.length).equals(1)\n\t\t})\n\t\to(\"adding dom children doesn't error\", function() {\n\t\t\trender(root,\n\t\t\t\tm(\"div\", {onbeforeupdate: function() { return true }},\n\t\t\t\t\tm(\"div\")\n\t\t\t\t)\n\t\t\t)\n\t\t\trender(root,\n\t\t\t\tm(\"div\", {onbeforeupdate: function() { return false }},\n\t\t\t\t\tm(\"div\")\n\t\t\t\t)\n\t\t\t)\n\t\t\trender(root,\n\t\t\t\tm(\"div\", {onbeforeupdate: function() { return true }},\n\t\t\t\t\tm(\"div\", m(\"div\"))\n\t\t\t\t)\n\t\t\t)\n\t\t\to(root.firstChild.firstChild.childNodes.length).equals(1)\n\t\t})\n\t})\n})\n"
  },
  {
    "path": "render/tests/test-oncreate.js",
    "content": "\"use strict\"\n\nvar o = require(\"ospec\")\nvar domMock = require(\"../../test-utils/domMock\")\nvar vdom = require(\"../../render/render\")\nvar m = require(\"../../render/hyperscript\")\nvar fragment = require(\"../../render/fragment\")\n\no.spec(\"oncreate\", function() {\n\tvar $window, root, render\n\to.beforeEach(function() {\n\t\t$window = domMock()\n\t\troot = $window.document.createElement(\"div\")\n\t\trender = vdom($window)\n\t})\n\n\to(\"calls oncreate when creating element\", function() {\n\t\tvar callback = o.spy()\n\t\tvar vnode = m(\"div\", {oncreate: callback})\n\n\t\trender(root, vnode)\n\n\t\to(callback.callCount).equals(1)\n\t\to(callback.this).equals(vnode.state)\n\t\to(callback.args[0]).equals(vnode)\n\t})\n\to(\"calls oncreate when creating fragment\", function() {\n\t\tvar callback = o.spy()\n\t\tvar vnode = fragment({oncreate: callback})\n\n\t\trender(root, vnode)\n\n\t\to(callback.callCount).equals(1)\n\t\to(callback.this).equals(vnode.state)\n\t\to(callback.args[0]).equals(vnode)\n\t})\n\to(\"calls oncreate when replacing keyed\", function() {\n\t\tvar createDiv = o.spy()\n\t\tvar createA = o.spy()\n\t\tvar vnode = m(\"div\", {key: 1, oncreate: createDiv})\n\t\tvar updated = m(\"a\", {key: 1, oncreate: createA})\n\n\t\trender(root, vnode)\n\t\trender(root, updated)\n\n\t\to(createDiv.callCount).equals(1)\n\t\to(createDiv.this).equals(vnode.state)\n\t\to(createDiv.args[0]).equals(vnode)\n\t\to(createA.callCount).equals(1)\n\t\to(createA.this).equals(updated.state)\n\t\to(createA.args[0]).equals(updated)\n\t})\n\to(\"does not call oncreate when noop\", function() {\n\t\tvar create = o.spy()\n\t\tvar update = o.spy()\n\t\tvar vnode = m(\"div\", {oncreate: create})\n\t\tvar updated = m(\"div\", {oncreate: update})\n\n\t\trender(root, vnode)\n\t\trender(root, updated)\n\n\t\to(create.callCount).equals(1)\n\t\to(create.this).equals(vnode.state)\n\t\to(create.args[0]).equals(vnode)\n\t\to(update.callCount).equals(0)\n\t})\n\to(\"does not call oncreate when updating attr\", function() {\n\t\tvar create = o.spy()\n\t\tvar update = o.spy()\n\t\tvar vnode = m(\"div\", {oncreate: create})\n\t\tvar updated = m(\"div\", {oncreate: update, id: \"a\"})\n\n\t\trender(root, vnode)\n\t\trender(root, updated)\n\n\t\to(create.callCount).equals(1)\n\t\to(create.this).equals(vnode.state)\n\t\to(create.args[0]).equals(vnode)\n\t\to(update.callCount).equals(0)\n\t})\n\to(\"does not call oncreate when updating children\", function() {\n\t\tvar create = o.spy()\n\t\tvar update = o.spy()\n\t\tvar vnode = m(\"div\", {oncreate: create}, m(\"a\"))\n\t\tvar updated = m(\"div\", {oncreate: update}, m(\"b\"))\n\n\t\trender(root, vnode)\n\t\trender(root, updated)\n\n\t\to(create.callCount).equals(1)\n\t\to(create.this).equals(vnode.state)\n\t\to(create.args[0]).equals(vnode)\n\t\to(update.callCount).equals(0)\n\t})\n\to(\"does not call oncreate when updating keyed\", function() {\n\t\tvar create = o.spy()\n\t\tvar update = o.spy()\n\t\tvar vnode = m(\"div\", {key: 1, oncreate: create})\n\t\tvar otherVnode = m(\"a\", {key: 2})\n\t\tvar updated = m(\"div\", {key: 1, oncreate: update})\n\t\tvar otherUpdated = m(\"a\", {key: 2})\n\n\t\trender(root, [vnode, otherVnode])\n\t\trender(root, [otherUpdated, updated])\n\n\t\to(create.callCount).equals(1)\n\t\to(create.this).equals(vnode.state)\n\t\to(create.args[0]).equals(vnode)\n\t\to(update.callCount).equals(0)\n\t})\n\to(\"does not call oncreate when removing\", function() {\n\t\tvar create = o.spy()\n\t\tvar vnode = m(\"div\", {oncreate: create})\n\n\t\trender(root, vnode)\n\t\trender(root, [])\n\n\t\to(create.callCount).equals(1)\n\t\to(create.this).equals(vnode.state)\n\t\to(create.args[0]).equals(vnode)\n\t})\n\to(\"does not recycle when there's an oncreate\", function() {\n\t\tvar create = o.spy()\n\t\tvar update = o.spy()\n\t\tvar vnode = m(\"div\", {key: 1, oncreate: create})\n\t\tvar updated = m(\"div\", {key: 1, oncreate: update})\n\n\t\trender(root, vnode)\n\t\trender(root, [])\n\t\trender(root, updated)\n\n\t\to(vnode.dom).notEquals(updated.dom)\n\t\to(create.callCount).equals(1)\n\t\to(create.this).equals(vnode.state)\n\t\to(create.args[0]).equals(vnode)\n\t\to(update.callCount).equals(1)\n\t\to(update.this).equals(updated.state)\n\t\to(update.args[0]).equals(updated)\n\t})\n\to(\"calls oncreate at the same step as onupdate\", function() {\n\t\tvar create = o.spy()\n\t\tvar update = o.spy()\n\t\tvar callback = o.spy()\n\t\tvar vnode = m(\"div\", {onupdate: create})\n\t\tvar updated = m(\"div\", {onupdate: update}, m(\"a\", {oncreate: callback}))\n\n\t\trender(root, vnode)\n\t\trender(root, updated)\n\n\t\to(create.callCount).equals(0)\n\t\to(update.callCount).equals(1)\n\t\to(update.this).equals(vnode.state)\n\t\to(update.args[0]).equals(updated)\n\t\to(callback.callCount).equals(1)\n\t\to(callback.this).equals(updated.children[0].state)\n\t\to(callback.args[0]).equals(updated.children[0])\n\t})\n\to(\"calls oncreate on unkeyed that falls into reverse list diff code path\", function() {\n\t\tvar create = o.spy()\n\t\trender(root, m(\"p\", m(\"div\")))\n\t\trender(root, m(\"div\", {oncreate: create}, m(\"div\")))\n\n\t\to(create.callCount).equals(1)\n\t})\n\to(\"calls oncreate on unkeyed that falls into forward list diff code path\", function() {\n\t\tvar create = o.spy()\n\t\trender(root, [m(\"div\"), m(\"p\")])\n\t\trender(root, [m(\"div\"), m(\"div\", {oncreate: create})])\n\n\t\to(create.callCount).equals(1)\n\t})\n\to(\"calls oncreate after full DOM creation\", function() {\n\t\tvar created = false\n\t\tvar vnode = m(\"div\",\n\t\t\tm(\"a\", {oncreate: create},\n\t\t\t\tm(\"b\")\n\t\t\t)\n\t\t)\n\n\t\trender(root, vnode)\n\n\t\tfunction create(vnode) {\n\t\t\tcreated = true\n\n\t\t\to(vnode.dom.parentNode).notEquals(null)\n\t\t\to(vnode.dom.childNodes.length).equals(1)\n\t\t}\n\t\to(created).equals(true)\n\t})\n\to(\"does not set oncreate as an event handler\", function() {\n\t\tvar create = o.spy()\n\t\tvar vnode = m(\"div\", {oncreate: create})\n\n\t\trender(root, vnode)\n\n\t\to(vnode.dom.oncreate).equals(undefined)\n\t\to(vnode.dom.attributes[\"oncreate\"]).equals(undefined)\n\t})\n\to(\"calls oncreate on recycle\", function() {\n\t\tvar create = o.spy()\n\t\tvar vnodes = m(\"div\", {key: 1, oncreate: create})\n\t\tvar temp = []\n\t\tvar updated = m(\"div\", {key: 1, oncreate: create})\n\n\t\trender(root, vnodes)\n\t\trender(root, temp)\n\t\trender(root, updated)\n\n\t\to(create.callCount).equals(2)\n\t})\n})\n"
  },
  {
    "path": "render/tests/test-oninit.js",
    "content": "\"use strict\"\n\nvar o = require(\"ospec\")\nvar domMock = require(\"../../test-utils/domMock\")\nvar vdom = require(\"../../render/render\")\nvar m = require(\"../../render/hyperscript\")\nvar fragment = require(\"../../render/fragment\")\n\no.spec(\"oninit\", function() {\n\tvar $window, root, render\n\to.beforeEach(function() {\n\t\t$window = domMock()\n\t\troot = $window.document.createElement(\"div\")\n\t\trender = vdom($window)\n\t})\n\n\to(\"calls oninit when creating element\", function() {\n\t\tvar callback = o.spy()\n\t\tvar vnode = m(\"div\", {oninit: callback})\n\n\t\trender(root, vnode)\n\n\t\to(callback.callCount).equals(1)\n\t\to(callback.this).equals(vnode.state)\n\t\to(callback.args[0]).equals(vnode)\n\t})\n\to(\"calls oninit when creating fragment\", function() {\n\t\tvar callback = o.spy()\n\t\tvar vnode = fragment({oninit: callback})\n\n\t\trender(root, vnode)\n\n\t\to(callback.callCount).equals(1)\n\t\to(callback.this).equals(vnode.state)\n\t\to(callback.args[0]).equals(vnode)\n\t})\n\to(\"calls oninit when replacing keyed\", function() {\n\t\tvar createDiv = o.spy()\n\t\tvar createA = o.spy()\n\t\tvar vnode = m(\"div\", {key: 1, oninit: createDiv})\n\t\tvar updated = m(\"a\", {key: 1, oninit: createA})\n\n\t\trender(root, vnode)\n\t\trender(root, updated)\n\n\t\to(createDiv.callCount).equals(1)\n\t\to(createDiv.this).equals(vnode.state)\n\t\to(createDiv.args[0]).equals(vnode)\n\t\to(createA.callCount).equals(1)\n\t\to(createA.this).equals(updated.state)\n\t\to(createA.args[0]).equals(updated)\n\t})\n\to(\"does not call oninit when noop\", function() {\n\t\tvar create = o.spy()\n\t\tvar update = o.spy()\n\t\tvar vnode = m(\"div\", {oninit: create})\n\t\tvar updated = m(\"div\", {oninit: update})\n\n\t\trender(root, vnode)\n\t\trender(root, updated)\n\n\t\to(create.callCount).equals(1)\n\t\to(create.this).equals(vnode.state)\n\t\to(create.args[0]).equals(vnode)\n\t\to(update.callCount).equals(0)\n\t})\n\to(\"does not call oninit when updating attr\", function() {\n\t\tvar create = o.spy()\n\t\tvar update = o.spy()\n\t\tvar vnode = m(\"div\", {oninit: create})\n\t\tvar updated = m(\"div\", {oninit: update, id: \"a\"})\n\n\t\trender(root, vnode)\n\t\trender(root, updated)\n\n\t\to(create.callCount).equals(1)\n\t\to(create.this).equals(vnode.state)\n\t\to(create.args[0]).equals(vnode)\n\t\to(update.callCount).equals(0)\n\t})\n\to(\"does not call oninit when updating children\", function() {\n\t\tvar create = o.spy()\n\t\tvar update = o.spy()\n\t\tvar vnode = m(\"div\", {oninit: create}, m(\"a\"))\n\t\tvar updated = m(\"div\", {oninit: update}, m(\"b\"))\n\n\t\trender(root, vnode)\n\t\trender(root, updated)\n\n\t\to(create.callCount).equals(1)\n\t\to(create.this).equals(vnode.state)\n\t\to(create.args[0]).equals(vnode)\n\t\to(update.callCount).equals(0)\n\t})\n\to(\"does not call oninit when updating keyed\", function() {\n\t\tvar create = o.spy()\n\t\tvar update = o.spy()\n\t\tvar vnode = m(\"div\", {key: 1, oninit: create})\n\t\tvar otherVnode = m(\"a\", {key: 2})\n\t\tvar updated = m(\"div\", {key: 1, oninit: update})\n\t\tvar otherUpdated = m(\"a\", {key: 2})\n\n\t\trender(root, [vnode, otherVnode])\n\t\trender(root, [otherUpdated, updated])\n\n\t\to(create.callCount).equals(1)\n\t\to(create.this).equals(vnode.state)\n\t\to(create.args[0]).equals(vnode)\n\t\to(update.callCount).equals(0)\n\t})\n\to(\"does not call oninit when removing\", function() {\n\t\tvar create = o.spy()\n\t\tvar vnode = m(\"div\", {oninit: create})\n\n\t\trender(root, vnode)\n\t\trender(root, [])\n\n\t\to(create.callCount).equals(1)\n\t\to(create.this).equals(vnode.state)\n\t\to(create.args[0]).equals(vnode)\n\t})\n\to(\"calls oninit when recycling\", function() {\n\t\tvar create = o.spy()\n\t\tvar update = o.spy()\n\t\tvar vnode = m(\"div\", {key: 1, oninit: create})\n\t\tvar updated = m(\"div\", {key: 1, oninit: update})\n\n\t\trender(root, vnode)\n\t\trender(root, [])\n\t\trender(root, updated)\n\n\t\to(create.callCount).equals(1)\n\t\to(create.this).equals(vnode.state)\n\t\to(create.args[0]).equals(vnode)\n\t\to(update.callCount).equals(1)\n\t\to(update.this).equals(updated.state)\n\t\to(update.args[0]).equals(updated)\n\t})\n\to(\"calls oninit at the same step as onupdate\", function() {\n\t\tvar create = o.spy()\n\t\tvar update = o.spy()\n\t\tvar callback = o.spy()\n\t\tvar vnode = m(\"div\", {onupdate: create})\n\t\tvar updated = m(\"div\", {onupdate: update}, m(\"a\", {oninit: callback}))\n\n\t\trender(root, vnode)\n\t\trender(root, updated)\n\n\t\to(create.callCount).equals(0)\n\t\to(update.callCount).equals(1)\n\t\to(update.this).equals(vnode.state)\n\t\to(update.args[0]).equals(updated)\n\t\to(callback.callCount).equals(1)\n\t\to(callback.this).equals(updated.children[0].state)\n\t\to(callback.args[0]).equals(updated.children[0])\n\t})\n\to(\"calls oninit before full DOM creation\", function() {\n\t\tvar called = false\n\t\tvar vnode = m(\"div\",\n\t\t\tm(\"a\", {oninit: create},\n\t\t\t\tm(\"b\")\n\t\t\t)\n\t\t)\n\n\t\trender(root, vnode)\n\n\t\tfunction create(vnode) {\n\t\t\tcalled = true\n\n\t\t\to(vnode.dom).equals(undefined)\n\t\t\to(root.childNodes.length).equals(1)\n\t\t}\n\t\to(called).equals(true)\n\t})\n\to(\"does not set oninit as an event handler\", function() {\n\t\tvar create = o.spy()\n\t\tvar vnode = m(\"div\", {oninit: create})\n\n\t\trender(root, vnode)\n\n\t\to(vnode.dom.oninit).equals(undefined)\n\t\to(vnode.dom.attributes[\"oninit\"]).equals(undefined)\n\t})\n\n\to(\"No spurious oninit calls in mapped keyed diff when the pool is involved (#1992)\", function () {\n\t\tvar oninit1 = o.spy()\n\t\tvar oninit2 = o.spy()\n\t\tvar oninit3 = o.spy()\n\n\t\trender(root, [\n\t\t\tm(\"p\", {key: 1, oninit: oninit1}),\n\t\t\tm(\"p\", {key: 2, oninit: oninit2}),\n\t\t\tm(\"p\", {key: 3, oninit: oninit3}),\n\t\t])\n\t\trender(root, [\n\t\t\tm(\"p\", {key: 1, oninit: oninit1}),\n\t\t\tm(\"p\", {key: 3, oninit: oninit3}),\n\t\t])\n\t\trender(root, [\n\t\t\tm(\"p\", {key: 3, oninit: oninit3}),\n\t\t])\n\n\t\to(oninit1.callCount).equals(1)\n\t\to(oninit2.callCount).equals(1)\n\t\to(oninit3.callCount).equals(1)\n\t})\n})\n"
  },
  {
    "path": "render/tests/test-onremove.js",
    "content": "\"use strict\"\n\nvar o = require(\"ospec\")\nvar components = require(\"../../test-utils/components\")\nvar domMock = require(\"../../test-utils/domMock\")\nvar vdom = require(\"../../render/render\")\nvar m = require(\"../../render/hyperscript\")\nvar fragment = require(\"../../render/fragment\")\nvar callAsync = require(\"../../test-utils/callAsync\")\n\no.spec(\"onremove\", function() {\n\tvar $window, root, render\n\to.beforeEach(function() {\n\t\t$window = domMock()\n\t\troot = $window.document.createElement(\"div\")\n\t\trender = vdom($window)\n\t})\n\n\to(\"does not call onremove when creating\", function() {\n\t\tvar create = o.spy()\n\t\tvar update = o.spy()\n\t\tvar vnode = m(\"div\", {onremove: create})\n\t\tvar updated = m(\"div\", {onremove: update})\n\n\t\trender(root, vnode)\n\t\trender(root, updated)\n\n\t\to(create.callCount).equals(0)\n\t})\n\to(\"does not call onremove when updating\", function() {\n\t\tvar create = o.spy()\n\t\tvar update = o.spy()\n\t\tvar vnode = m(\"div\", {onremove: create})\n\t\tvar updated = m(\"div\", {onremove: update})\n\n\t\trender(root, vnode)\n\t\trender(root, updated)\n\n\t\to(create.callCount).equals(0)\n\t\to(update.callCount).equals(0)\n\t})\n\to(\"calls onremove when removing element\", function() {\n\t\tvar remove = o.spy()\n\t\tvar vnode = m(\"div\", {onremove: remove})\n\n\t\trender(root, vnode)\n\t\trender(root, [])\n\n\t\to(remove.callCount).equals(1)\n\t\to(remove.this).equals(vnode.state)\n\t\to(remove.args[0]).equals(vnode)\n\t})\n\to(\"calls onremove when removing fragment\", function() {\n\t\tvar remove = o.spy()\n\t\tvar vnode = fragment({onremove: remove})\n\n\t\trender(root, vnode)\n\t\trender(root, [])\n\n\t\to(remove.callCount).equals(1)\n\t\to(remove.this).equals(vnode.state)\n\t\to(remove.args[0]).equals(vnode)\n\t})\n\to(\"does not set onremove as an event handler\", function() {\n\t\tvar remove = o.spy()\n\t\tvar vnode = m(\"div\", {onremove: remove})\n\n\t\trender(root, vnode)\n\n\t\to(vnode.dom.onremove).equals(undefined)\n\t\to(vnode.dom.attributes[\"onremove\"]).equals(undefined)\n\t\to(vnode.events).equals(undefined)\n\t})\n\to(\"calls onremove on keyed nodes\", function() {\n\t\tvar remove = o.spy()\n\t\tvar vnodes = [m(\"div\", {key: 1})]\n\t\tvar temp = [m(\"div\", {key: 2, onremove: remove})]\n\t\tvar updated = [m(\"div\", {key: 1})]\n\n\t\trender(root, vnodes)\n\t\trender(root, temp)\n\t\trender(root, updated)\n\n\t\to(vnodes[0].dom).notEquals(updated[0].dom) // this used to be a recycling pool test\n\t\to(remove.callCount).equals(1)\n\t})\n\to(\"does not recycle when there's an onremove\", function() {\n\t\tvar remove = o.spy()\n\t\tvar vnode = m(\"div\", {key: 1, onremove: remove})\n\t\tvar updated = m(\"div\", {key: 1, onremove: remove})\n\n\t\trender(root, vnode)\n\t\trender(root, [])\n\t\trender(root, updated)\n\n\t\to(vnode.dom).notEquals(updated.dom)\n\t})\n\tcomponents.forEach(function(cmp){\n\t\to.spec(cmp.kind, function(){\n\t\t\tvar createComponent = cmp.create\n\n\t\t\to(\"calls onremove on nested component\", function() {\n\t\t\t\tvar spy = o.spy()\n\t\t\t\tvar comp = createComponent({\n\t\t\t\t\tview: function() {return m(outer)}\n\t\t\t\t})\n\t\t\t\tvar outer = createComponent({\n\t\t\t\t\tview: function() {return m(inner)}\n\t\t\t\t})\n\t\t\t\tvar inner = createComponent({\n\t\t\t\t\tonremove: spy,\n\t\t\t\t\tview: function() {return m(\"div\")}\n\t\t\t\t})\n\t\t\t\trender(root, m(comp))\n\t\t\t\trender(root, null)\n\n\t\t\t\to(spy.callCount).equals(1)\n\t\t\t})\n\t\t\to(\"calls onremove on nested component child\", function() {\n\t\t\t\tvar spy = o.spy()\n\t\t\t\tvar comp = createComponent({\n\t\t\t\t\tview: function() {return m(outer)}\n\t\t\t\t})\n\t\t\t\tvar outer = createComponent({\n\t\t\t\t\tview: function() {return m(inner, m(\"a\", {onremove: spy}))}\n\t\t\t\t})\n\t\t\t\tvar inner = createComponent({\n\t\t\t\t\tview: function(vnode) {return m(\"div\", vnode.children)}\n\t\t\t\t})\n\t\t\t\trender(root, m(comp))\n\t\t\t\trender(root, null)\n\n\t\t\t\to(spy.callCount).equals(1)\n\t\t\t})\n\t\t\to(\"doesn't call onremove on children when the corresponding view returns null (after removing the parent)\", function() {\n\t\t\t\tvar threw = false\n\t\t\t\tvar spy = o.spy()\n\t\t\t\tvar parent = createComponent({\n\t\t\t\t\tview: function() {}\n\t\t\t\t})\n\t\t\t\tvar child = createComponent({\n\t\t\t\t\tview: function() {},\n\t\t\t\t\tonremove: spy\n\t\t\t\t})\n\t\t\t\trender(root, m(parent, m(child)))\n\t\t\t\ttry {\n\t\t\t\t\trender(root, null)\n\t\t\t\t} catch (e) {\n\t\t\t\t\tthrew = e\n\t\t\t\t}\n\n\t\t\t\to(spy.callCount).equals(0)\n\t\t\t\to(threw).equals(false)\n\t\t\t})\n\t\t\to(\"doesn't call onremove on children when the corresponding view returns null (after removing the children)\", function() {\n\t\t\t\tvar threw = false\n\t\t\t\tvar spy = o.spy()\n\t\t\t\tvar parent = createComponent({\n\t\t\t\t\tview: function() {}\n\t\t\t\t})\n\t\t\t\tvar child = createComponent({\n\t\t\t\t\tview: function() {},\n\t\t\t\t\tonremove: spy\n\t\t\t\t})\n\t\t\t\trender(root, m(parent, m(child)))\n\t\t\t\ttry {\n\t\t\t\t\trender(root, m(parent))\n\t\t\t\t} catch (e) {\n\t\t\t\t\tthrew = true\n\t\t\t\t}\n\n\t\t\t\to(spy.callCount).equals(0)\n\t\t\t\to(threw).equals(false)\n\t\t\t})\n\t\t\to(\"onremove doesn't fire on nodes that go from pool to pool (#1990)\", function() {\n\t\t\t\tvar onremove = o.spy();\n\n\t\t\t\trender(root, [m(\"div\", m(\"div\")), m(\"div\", m(\"div\", {onremove: onremove}))]);\n\t\t\t\trender(root, [m(\"div\", m(\"div\"))]);\n\t\t\t\trender(root, []);\n\n\t\t\t\to(onremove.callCount).equals(1)\n\t\t\t})\n\t\t\to(\"doesn't fire when removing the children of a node that's brought back from the pool (#1991 part 2)\", function() {\n\t\t\t\tvar onremove = o.spy()\n\t\t\t\tvar vnode = m(\"div\", {key: 1}, m(\"div\", {onremove: onremove}))\n\t\t\t\tvar temp = m(\"div\", {key: 2})\n\t\t\t\tvar updated = m(\"div\", {key: 1}, m(\"p\"))\n\n\t\t\t\trender(root, vnode)\n\t\t\t\trender(root, temp)\n\t\t\t\trender(root, updated)\n\n\t\t\t\to(vnode.dom).notEquals(updated.dom) // this used to be a recycling pool test\n\t\t\t\to(onremove.callCount).equals(1)\n\t\t\t})\n\t\t\t// Warning: this test is complicated because it's replicating a race condition.\n\t\t\to(\"removes correct nodes in fragment when child delays removal, parent removes, then child resolves\", function (done) {\n\t\t\t\t// Custom assertion - we need to test the entire tree for consistency.\n\n\t\t\t\tconst template = (tpl) => (root) => {\n\t\t\t\t\tvar expected = []\n\n\t\t\t\t\tfor (var i = 0; i < tpl.length; i++) {\n\t\t\t\t\t\tvar name = tpl[i][0]\n\t\t\t\t\t\tvar text = tpl[i][1]\n\t\t\t\t\t\texpected.push({\n\t\t\t\t\t\t\tname: name,\n\t\t\t\t\t\t\tfirstType: name === \"#text\" ? null : \"#text\",\n\t\t\t\t\t\t\ttext: text,\n\t\t\t\t\t\t})\n\t\t\t\t\t}\n\n\t\t\t\t\tvar actual = []\n\t\t\t\t\tvar list = root.firstChild.childNodes\n\t\t\t\t\tfor (var i = 0; i < list.length; i++) {\n\t\t\t\t\t\tvar current = list[i]\n\t\t\t\t\t\tvar textNode = current.childNodes.length === 1\n\t\t\t\t\t\t\t? current.firstChild\n\t\t\t\t\t\t\t: current\n\t\t\t\t\t\tactual.push({\n\t\t\t\t\t\t\tname: current.nodeName,\n\t\t\t\t\t\t\tfirstType: textNode === current ? null : textNode.nodeName,\n\t\t\t\t\t\t\ttext: textNode.nodeValue,\n\t\t\t\t\t\t})\n\t\t\t\t\t}\n\t\t\t\t\tactual = JSON.stringify(actual, null, \"  \")\n\t\t\t\t\texpected = JSON.stringify(expected, null, \"  \")\n\t\t\t\t\treturn {\n\t\t\t\t\t\tpass: actual === expected,\n\t\t\t\t\t\tmessage:\n`${expected}\n  expected, got\n${actual}`\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tvar thenCB1\n\t\t\t\tvar thenCB2\n\t\t\t\tvar C = createComponent({\n\t\t\t\t\tview({children}){return children},\n\t\t\t\t\tonbeforeremove(){\n\t\t\t\t\t\treturn {then(resolve){thenCB1=resolve}}\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t\tfunction update(id, showParent, showChild) {\n\t\t\t\t\tconst removeParent = o.spy()\n\t\t\t\t\tconst removeSyncChild = o.spy()\n\t\t\t\t\tconst removeAsyncChild = o.spy()\n\n\t\t\t\t\trender(root,\n\t\t\t\t\t\tm(\"div\",\n\t\t\t\t\t\t\tshowParent && fragment(\n\t\t\t\t\t\t\t\t{onremove: removeParent},\n\t\t\t\t\t\t\t\tm(\"a\", {onremove: removeSyncChild}, \"sync child\"),\n\t\t\t\t\t\t\t\tshowChild && m(C, {\n\t\t\t\t\t\t\t\t\tonbeforeremove: function () {\n\t\t\t\t\t\t\t\t\t\treturn {then(resolve){thenCB2=resolve}}\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\tonremove: removeAsyncChild\n\t\t\t\t\t\t\t\t}, m(\"div\", id))\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t)\n\t\t\t\t\t)\n\t\t\t\t\treturn {removeAsyncChild,removeParent, removeSyncChild}\n\t\t\t\t}\n\n\t\t\t\tconst hooks1 = update(\"1\", true, true)\n\t\t\t\to(root).satisfies(template([\n\t\t\t\t\t[\"A\", \"sync child\"],\n\t\t\t\t\t[\"DIV\", \"1\"],\n\t\t\t\t]))\n\t\t\t\to(thenCB1).equals(undefined)\n\t\t\t\to(thenCB2).equals(undefined)\n\n\t\t\t\tconst hooks2 = update(\"2\", true, false)\n\n\t\t\t\to(root).satisfies(template([\n\t\t\t\t\t[\"A\", \"sync child\"],\n\t\t\t\t\t[\"DIV\", \"1\"],\n\t\t\t\t]))\n\t\t\t\to(thenCB1).equals(undefined)\n\t\t\t\to(thenCB2).equals(undefined)\n\n\t\t\t\t// Promises (micro-tasks) are processed before the callAsync callback.\n\t\t\t\tcallAsync(() => {\n\t\t\t\t\to(typeof thenCB1).equals(\"function\")\n\t\t\t\t\to(typeof thenCB2).equals(\"function\")\n\n\t\t\t\t\tvar original1 = thenCB1\n\t\t\t\t\tvar original2 = thenCB2\n\n\t\t\t\t\tconst hooks3 = update(\"3\", true, true)\n\n\t\t\t\t\to(root).satisfies(template([\n\t\t\t\t\t\t[\"A\", \"sync child\"],\n\t\t\t\t\t\t[\"DIV\", \"1\"],\n\t\t\t\t\t\t[\"DIV\", \"3\"],\n\t\t\t\t\t]))\n\n\t\t\t\t\to(hooks3.removeParent.callCount).equals(0)\n\t\t\t\t\to(hooks3.removeSyncChild.callCount).equals(0)\n\t\t\t\t\to(hooks3.removeAsyncChild.callCount).equals(0)\n\t\t\t\t\to(thenCB1).equals(original1)\n\t\t\t\t\to(thenCB2).equals(original2)\n\n\t\t\t\t\tconst hooks4 = update(\"4\", false, true)\n\n\t\t\t\t\to(root).satisfies(template([\n\t\t\t\t\t\t[\"DIV\", \"1\"],\n\t\t\t\t\t]))\n\n\t\t\t\t\to(hooks3.removeParent.callCount).equals(1)\n\t\t\t\t\to(hooks3.removeSyncChild.callCount).equals(1)\n\t\t\t\t\to(hooks3.removeAsyncChild.callCount).equals(1)\n\t\t\t\t\to(hooks3.removeParent.args[0].tag).equals(\"[\")\n\t\t\t\t\to(thenCB1).equals(original1)\n\t\t\t\t\to(thenCB2).equals(original2)\n\n\t\t\t\t\tconst hooks5 = update(\"5\", true, true)\n\n\n\t\t\t\t\to(root).satisfies(template([\n\t\t\t\t\t\t[\"DIV\", \"1\"],\n\t\t\t\t\t\t[\"A\", \"sync child\"],\n\t\t\t\t\t\t[\"DIV\", \"5\"],\n\t\t\t\t\t]))\n\t\t\t\t\to(thenCB1).equals(original1)\n\t\t\t\t\to(thenCB2).equals(original2)\n\n\t\t\t\t\to(hooks1.removeAsyncChild.callCount).equals(0)\n\n\t\t\t\t\tthenCB1()\n\n\t\t\t\t\to(hooks1.removeAsyncChild.callCount).equals(0)\n\t\t\t\t\tcallAsync(() => {\n\t\t\t\t\t\to(hooks1.removeAsyncChild.callCount).equals(0)\n\n\t\t\t\t\t\tthenCB2()\n\n\t\t\t\t\t\to(hooks1.removeAsyncChild.callCount).equals(0)\n\t\t\t\t\t\tcallAsync(() => {\n\t\t\t\t\t\t\to(hooks1.removeAsyncChild.callCount).equals(1)\n\n\t\t\t\t\t\t\to(root).satisfies(template([\n\t\t\t\t\t\t\t\t[\"A\", \"sync child\"],\n\t\t\t\t\t\t\t\t[\"DIV\", \"5\"],\n\t\t\t\t\t\t\t]))\n\t\t\t\t\t\t\to(thenCB1).equals(original1)\n\t\t\t\t\t\t\to(thenCB2).equals(original2)\n\n\t\t\t\t\t\t\tconst hooks6 = update(\"6\", true, true)\n\n\t\t\t\t\t\t\to(root).satisfies(template([\n\t\t\t\t\t\t\t\t[\"A\", \"sync child\"],\n\t\t\t\t\t\t\t\t[\"DIV\", \"6\"],\n\t\t\t\t\t\t\t]))\n\t\t\t\t\t\t\to(thenCB1).equals(original1)\n\t\t\t\t\t\t\to(thenCB2).equals(original2)\n\n\t\t\t\t\t\t\t// final tally\n\t\t\t\t\t\t\to(hooks1.removeParent.callCount).equals(0)\n\t\t\t\t\t\t\to(hooks1.removeSyncChild.callCount).equals(0)\n\t\t\t\t\t\t\to(hooks1.removeAsyncChild.callCount).equals(1)\n\n\t\t\t\t\t\t\to(hooks2.removeParent.callCount).equals(0)\n\t\t\t\t\t\t\to(hooks2.removeSyncChild.callCount).equals(0)\n\t\t\t\t\t\t\to(hooks2.removeAsyncChild.callCount).equals(0)\n\n\t\t\t\t\t\t\to(hooks3.removeParent.callCount).equals(1)\n\t\t\t\t\t\t\to(hooks3.removeSyncChild.callCount).equals(1)\n\t\t\t\t\t\t\to(hooks3.removeAsyncChild.callCount).equals(1)\n\n\t\t\t\t\t\t\to(hooks4.removeParent.callCount).equals(0)\n\t\t\t\t\t\t\to(hooks4.removeSyncChild.callCount).equals(0)\n\t\t\t\t\t\t\to(hooks4.removeAsyncChild.callCount).equals(0)\n\n\t\t\t\t\t\t\to(hooks5.removeParent.callCount).equals(0)\n\t\t\t\t\t\t\to(hooks5.removeSyncChild.callCount).equals(0)\n\t\t\t\t\t\t\to(hooks5.removeAsyncChild.callCount).equals(0)\n\n\t\t\t\t\t\t\to(hooks6.removeParent.callCount).equals(0)\n\t\t\t\t\t\t\to(hooks6.removeSyncChild.callCount).equals(0)\n\t\t\t\t\t\t\to(hooks6.removeAsyncChild.callCount).equals(0)\n\n\t\t\t\t\t\t\tdone()\n\t\t\t\t\t\t})\n\t\t\t\t\t})\n\t\t\t\t})\n\t\t\t})\n\t\t})\n\t})\n})\n"
  },
  {
    "path": "render/tests/test-onupdate.js",
    "content": "\"use strict\"\n\nvar o = require(\"ospec\")\nvar domMock = require(\"../../test-utils/domMock\")\nvar vdom = require(\"../../render/render\")\nvar m = require(\"../../render/hyperscript\")\nvar fragment = require(\"../../render/fragment\")\n\no.spec(\"onupdate\", function() {\n\tvar $window, root, render\n\to.beforeEach(function() {\n\t\t$window = domMock()\n\t\troot = $window.document.createElement(\"div\")\n\t\trender = vdom($window)\n\t})\n\n\to(\"does not call onupdate when creating element\", function() {\n\t\tvar create = o.spy()\n\t\tvar update = o.spy()\n\t\tvar vnode = m(\"div\", {onupdate: create})\n\t\tvar updated = m(\"div\", {onupdate: update})\n\n\t\trender(root, vnode)\n\t\trender(root, updated)\n\n\t\to(create.callCount).equals(0)\n\t\to(update.callCount).equals(1)\n\t\to(update.this).equals(vnode.state)\n\t\to(update.args[0]).equals(updated)\n\t})\n\to(\"does not call onupdate when removing element\", function() {\n\t\tvar create = o.spy()\n\t\tvar vnode = m(\"div\", {onupdate: create})\n\n\t\trender(root, vnode)\n\t\trender(root, [])\n\n\t\to(create.callCount).equals(0)\n\t})\n\to(\"does not call onupdate when replacing keyed element\", function() {\n\t\tvar create = o.spy()\n\t\tvar update = o.spy()\n\t\tvar vnode = m(\"div\", {key: 1, onupdate: create})\n\t\tvar updated = m(\"a\", {key: 1, onupdate: update})\n\t\trender(root, vnode)\n\t\trender(root, updated)\n\n\t\to(create.callCount).equals(0)\n\t\to(update.callCount).equals(0)\n\t})\n\to(\"does not recycle when there's an onupdate\", function() {\n\t\tvar update = o.spy()\n\t\tvar vnode = m(\"div\", {key: 1, onupdate: update})\n\t\tvar updated = m(\"div\", {key: 1, onupdate: update})\n\n\t\trender(root, vnode)\n\t\trender(root, [])\n\t\trender(root, updated)\n\n\t\to(vnode.dom).notEquals(updated.dom)\n\t})\n\to(\"does not call old onupdate when removing the onupdate property in new vnode\", function() {\n\t\tvar create = o.spy()\n\t\tvar vnode = m(\"a\", {onupdate: create})\n\t\tvar updated = m(\"a\")\n\n\t\trender(root, vnode)\n\t\trender(root, updated)\n\n\t\to(create.callCount).equals(0)\n\t})\n\to(\"calls onupdate when noop\", function() {\n\t\tvar create = o.spy()\n\t\tvar update = o.spy()\n\t\tvar vnode = m(\"div\", {onupdate: create})\n\t\tvar updated = m(\"div\", {onupdate: update})\n\n\t\trender(root, vnode)\n\t\trender(root, updated)\n\n\t\to(create.callCount).equals(0)\n\t\to(update.callCount).equals(1)\n\t\to(update.this).equals(vnode.state)\n\t\to(update.args[0]).equals(updated)\n\t})\n\to(\"calls onupdate when updating attr\", function() {\n\t\tvar create = o.spy()\n\t\tvar update = o.spy()\n\t\tvar vnode = m(\"div\", {onupdate: create})\n\t\tvar updated = m(\"div\", {onupdate: update, id: \"a\"})\n\n\t\trender(root, vnode)\n\t\trender(root, updated)\n\n\t\to(create.callCount).equals(0)\n\t\to(update.callCount).equals(1)\n\t\to(update.this).equals(vnode.state)\n\t\to(update.args[0]).equals(updated)\n\t})\n\to(\"calls onupdate when updating children\", function() {\n\t\tvar create = o.spy()\n\t\tvar update = o.spy()\n\t\tvar vnode = m(\"div\", {onupdate: create}, m(\"a\"))\n\t\tvar updated = m(\"div\", {onupdate: update}, m(\"b\"))\n\n\t\trender(root, vnode)\n\t\trender(root, updated)\n\n\t\to(create.callCount).equals(0)\n\t\to(update.callCount).equals(1)\n\t\to(update.this).equals(vnode.state)\n\t\to(update.args[0]).equals(updated)\n\t})\n\to(\"calls onupdate when updating fragment\", function() {\n\t\tvar create = o.spy()\n\t\tvar update = o.spy()\n\t\tvar vnode = fragment({onupdate: create})\n\t\tvar updated = fragment({onupdate: update})\n\n\t\trender(root, vnode)\n\t\trender(root, updated)\n\n\t\to(create.callCount).equals(0)\n\t\to(update.callCount).equals(1)\n\t\to(update.this).equals(vnode.state)\n\t\to(update.args[0]).equals(updated)\n\t})\n\to(\"calls onupdate after full DOM update\", function() {\n\t\tvar called = false\n\t\tvar vnode = m(\"div\", {id: \"1\"},\n\t\t\tm(\"a\", {id: \"2\"},\n\t\t\t\tm(\"b\", {id: \"3\"})\n\t\t\t)\n\t\t)\n\t\tvar updated = m(\"div\", {id: \"11\"},\n\t\t\tm(\"a\", {id: \"22\", onupdate: update},\n\t\t\t\tm(\"b\", {id: \"33\"})\n\t\t\t)\n\t\t)\n\n\t\trender(root, vnode)\n\t\trender(root, updated)\n\n\t\tfunction update(vnode) {\n\t\t\tcalled = true\n\n\t\t\to(vnode.dom.parentNode.attributes[\"id\"].value).equals(\"11\")\n\t\t\to(vnode.dom.attributes[\"id\"].value).equals(\"22\")\n\t\t\to(vnode.dom.childNodes[0].attributes[\"id\"].value).equals(\"33\")\n\t\t}\n\t\to(called).equals(true)\n\t})\n\to(\"does not set onupdate as an event handler\", function() {\n\t\tvar update = o.spy()\n\t\tvar vnode = m(\"div\", {onupdate: update})\n\n\t\trender(root, vnode)\n\n\t\to(vnode.dom.onupdate).equals(undefined)\n\t\to(vnode.dom.attributes[\"onupdate\"]).equals(undefined)\n\t})\n})\n"
  },
  {
    "path": "render/tests/test-render-hyperscript-integration.js",
    "content": "\"use strict\"\n\nvar o = require(\"ospec\")\nvar m = require(\"../../render/hyperscript\")\nvar domMock = require(\"../../test-utils/domMock\")\nvar vdom = require(\"../../render/render\")\n\no.spec(\"render/hyperscript integration\", function() {\n\tvar $window, root, render\n\to.beforeEach(function() {\n\t\t$window = domMock()\n\t\troot = $window.document.createElement(\"div\")\n\t\trender = vdom($window)\n\t})\n\to.spec(\"setting class\", function() {\n\t\to(\"selector only\", function() {\n\t\t\trender(root, m(\".foo\"))\n\n\t\t\to(root.firstChild.className).equals(\"foo\")\n\t\t})\n\t\to(\"class only\", function() {\n\t\t\trender(root, m(\"div\", {class: \"foo\"}))\n\n\t\t\to(root.firstChild.className).equals(\"foo\")\n\t\t})\n\t\to(\"className only\", function() {\n\t\t\trender(root, m(\"div\", {className: \"foo\"}))\n\n\t\t\to(root.firstChild.className).equals(\"foo\")\n\t\t})\n\t\to(\"selector and class\", function() {\n\t\t\trender(root, m(\".bar\", {class: \"foo\"}))\n\n\t\t\to(root.firstChild.className.split(\" \").sort()).deepEquals([\"bar\", \"foo\"])\n\t\t})\n\t\to(\"selector and className\", function() {\n\t\t\trender(root, m(\".bar\", {className: \"foo\"}))\n\n\t\t\to(root.firstChild.className.split(\" \").sort()).deepEquals([\"bar\", \"foo\"])\n\t\t})\n\t\to(\"selector and a null class\", function() {\n\t\t\trender(root, m(\".foo\", {class: null}))\n\n\t\t\to(root.firstChild.className).equals(\"foo\")\n\t\t})\n\t\to(\"selector and a null className\", function() {\n\t\t\trender(root, m(\".foo\", {className: null}))\n\n\t\t\to(root.firstChild.className).equals(\"foo\")\n\t\t})\n\t\to(\"selector and an undefined class\", function() {\n\t\t\trender(root, m(\".foo\", {class: undefined}))\n\n\t\t\to(root.firstChild.className).equals(\"foo\")\n\t\t})\n\t\to(\"selector and an undefined className\", function() {\n\t\t\trender(root, m(\".foo\", {className: undefined}))\n\n\t\t\to(root.firstChild.className).equals(\"foo\")\n\t\t})\n\t})\n\to.spec(\"updating class\", function() {\n\t\to.spec(\"from selector only\", function() {\n\t\t\to(\"to selector only\", function() {\n\t\t\t\trender(root, m(\".foo1\"))\n\t\t\t\trender(root, m(\".foo2\"))\n\n\t\t\t\to(root.firstChild.className).equals(\"foo2\")\n\t\t\t})\n\t\t\to(\"to class only\", function() {\n\t\t\t\trender(root, m(\".foo1\"))\n\t\t\t\trender(root, m(\"div\", {class: \"foo2\"}))\n\n\t\t\t\to(root.firstChild.className).equals(\"foo2\")\n\t\t\t})\n\t\t\to(\"to className only\", function() {\n\t\t\t\trender(root, m(\".foo1\"))\n\t\t\t\trender(root, m(\"div\", {className: \"foo2\"}))\n\n\t\t\t\to(root.firstChild.className).equals(\"foo2\")\n\t\t\t})\n\t\t\to(\"to selector and class\", function() {\n\t\t\t\trender(root, m(\".foo1\"))\n\t\t\t\trender(root, m(\".bar2\", {class: \"foo2\"}))\n\n\t\t\t\to(root.firstChild.className.split(\" \").sort()).deepEquals([\"bar2\", \"foo2\"])\n\t\t\t})\n\t\t\to(\"to selector and className\", function() {\n\t\t\t\trender(root, m(\".foo1\"))\n\t\t\t\trender(root, m(\".bar2\", {className: \"foo2\"}))\n\n\t\t\t\to(root.firstChild.className.split(\" \").sort()).deepEquals([\"bar2\", \"foo2\"])\n\t\t\t})\n\t\t\to(\"to selector and a null class\", function() {\n\t\t\t\trender(root, m(\".foo1\"))\n\t\t\t\trender(root, m(\".foo2\", {class: null}))\n\n\t\t\t\to(root.firstChild.className).equals(\"foo2\")\n\t\t\t})\n\t\t\to(\"to selector and a null className\", function() {\n\t\t\t\trender(root, m(\".foo1\"))\n\t\t\t\trender(root, m(\".foo2\", {className: null}))\n\n\t\t\t\to(root.firstChild.className).equals(\"foo2\")\n\t\t\t})\n\t\t\to(\"to selector and an undefined class\", function() {\n\t\t\t\trender(root, m(\".foo1\"))\n\t\t\t\trender(root, m(\".foo2\", {class: undefined}))\n\n\t\t\t\to(root.firstChild.className).equals(\"foo2\")\n\t\t\t})\n\t\t\to(\"to selector and an undefined className\", function() {\n\t\t\t\trender(root, m(\".foo1\"))\n\t\t\t\trender(root, m(\".foo2\", {className: undefined}))\n\n\t\t\t\to(root.firstChild.className).equals(\"foo2\")\n\t\t\t})\n\t\t})\n\t\to.spec(\"from class only\", function() {\n\t\t\to(\"to selector only\", function() {\n\t\t\t\trender(root, m(\"div\", {class: \"foo2\"}))\n\t\t\t\trender(root, m(\".foo2\"))\n\n\t\t\t\to(root.firstChild.className).equals(\"foo2\")\n\t\t\t})\n\t\t\to(\"to class only\", function() {\n\t\t\t\trender(root, m(\"div\", {class: \"foo2\"}))\n\t\t\t\trender(root, m(\"div\", {class: \"foo2\"}))\n\n\t\t\t\to(root.firstChild.className).equals(\"foo2\")\n\t\t\t})\n\t\t\to(\"to className only\", function() {\n\t\t\t\trender(root, m(\"div\", {class: \"foo2\"}))\n\t\t\t\trender(root, m(\"div\", {className: \"foo2\"}))\n\n\t\t\t\to(root.firstChild.className).equals(\"foo2\")\n\t\t\t})\n\t\t\to(\"to selector and class\", function() {\n\t\t\t\trender(root, m(\"div\", {class: \"foo2\"}))\n\t\t\t\trender(root, m(\".bar2\", {class: \"foo2\"}))\n\n\t\t\t\to(root.firstChild.className.split(\" \").sort()).deepEquals([\"bar2\", \"foo2\"])\n\t\t\t})\n\t\t\to(\"to selector and className\", function() {\n\t\t\t\trender(root, m(\".bar2\", {className: \"foo2\"}))\n\n\t\t\t\to(root.firstChild.className.split(\" \").sort()).deepEquals([\"bar2\", \"foo2\"])\n\t\t\t})\n\t\t\to(\"to selector and a null class\", function() {\n\t\t\t\trender(root, m(\"div\", {class: \"foo2\"}))\n\t\t\t\trender(root, m(\".foo2\", {class: null}))\n\n\t\t\t\to(root.firstChild.className).equals(\"foo2\")\n\t\t\t})\n\t\t\to(\"to selector and a null className\", function() {\n\t\t\t\trender(root, m(\"div\", {class: \"foo2\"}))\n\t\t\t\trender(root, m(\".foo2\", {className: null}))\n\n\t\t\t\to(root.firstChild.className).equals(\"foo2\")\n\t\t\t})\n\t\t\to(\"to selector and an undefined class\", function() {\n\t\t\t\trender(root, m(\"div\", {class: \"foo2\"}))\n\t\t\t\trender(root, m(\".foo2\", {class: undefined}))\n\n\t\t\t\to(root.firstChild.className).equals(\"foo2\")\n\t\t\t})\n\t\t\to(\"to selector and an undefined className\", function() {\n\t\t\t\trender(root, m(\"div\", {class: \"foo2\"}))\n\t\t\t\trender(root, m(\".foo2\", {className: undefined}))\n\n\t\t\t\to(root.firstChild.className).equals(\"foo2\")\n\t\t\t})\n\t\t})\n\t\to.spec(\"from \", function() {\n\t\t\to(\"to selector only\", function() {\n\t\t\t\trender(root, m(\".foo2\"))\n\n\t\t\t\to(root.firstChild.className).equals(\"foo2\")\n\t\t\t})\n\t\t\to(\"to class only\", function() {\n\t\t\t\trender(root, m(\"div\", {class: \"foo2\"}))\n\n\t\t\t\to(root.firstChild.className).equals(\"foo2\")\n\t\t\t})\n\t\t\to(\"to className only\", function() {\n\t\t\t\trender(root, m(\"div\", {className: \"foo2\"}))\n\n\t\t\t\to(root.firstChild.className).equals(\"foo2\")\n\t\t\t})\n\t\t\to(\"to selector and class\", function() {\n\t\t\t\trender(root, m(\".bar2\", {class: \"foo2\"}))\n\n\t\t\t\to(root.firstChild.className.split(\" \").sort()).deepEquals([\"bar2\", \"foo2\"])\n\t\t\t})\n\t\t\to(\"to selector and className\", function() {\n\t\t\t\trender(root, m(\".bar2\", {className: \"foo2\"}))\n\n\t\t\t\to(root.firstChild.className.split(\" \").sort()).deepEquals([\"bar2\", \"foo2\"])\n\t\t\t})\n\t\t\to(\"to selector and a null class\", function() {\n\t\t\t\trender(root, m(\".foo2\", {class: null}))\n\n\t\t\t\to(root.firstChild.className).equals(\"foo2\")\n\t\t\t})\n\t\t\to(\"to selector and a null className\", function() {\n\t\t\t\trender(root, m(\".foo2\", {className: null}))\n\n\t\t\t\to(root.firstChild.className).equals(\"foo2\")\n\t\t\t})\n\t\t\to(\"to selector and an undefined class\", function() {\n\t\t\t\trender(root, m(\".foo2\", {class: undefined}))\n\n\t\t\t\to(root.firstChild.className).equals(\"foo2\")\n\t\t\t})\n\t\t\to(\"to selector and an undefined className\", function() {\n\t\t\t\trender(root, m(\".foo2\", {className: undefined}))\n\n\t\t\t\to(root.firstChild.className).equals(\"foo2\")\n\t\t\t})\n\t\t})\n\t\to.spec(\"from className only\", function() {\n\t\t\to(\"to selector only\", function() {\n\t\t\t\trender(root, m(\"div\", {className: \"foo1\"}))\n\t\t\t\trender(root, m(\".foo2\"))\n\n\t\t\t\to(root.firstChild.className).equals(\"foo2\")\n\t\t\t})\n\t\t\to(\"to class only\", function() {\n\t\t\t\trender(root, m(\"div\", {className: \"foo1\"}))\n\t\t\t\trender(root, m(\"div\", {class: \"foo2\"}))\n\n\t\t\t\to(root.firstChild.className).equals(\"foo2\")\n\t\t\t})\n\t\t\to(\"to className only\", function() {\n\t\t\t\trender(root, m(\"div\", {className: \"foo1\"}))\n\t\t\t\trender(root, m(\"div\", {className: \"foo2\"}))\n\n\t\t\t\to(root.firstChild.className).equals(\"foo2\")\n\t\t\t})\n\t\t\to(\"to selector and class\", function() {\n\t\t\t\trender(root, m(\"div\", {className: \"foo1\"}))\n\t\t\t\trender(root, m(\".bar2\", {class: \"foo2\"}))\n\n\t\t\t\to(root.firstChild.className.split(\" \").sort()).deepEquals([\"bar2\", \"foo2\"])\n\t\t\t})\n\t\t\to(\"to selector and className\", function() {\n\t\t\t\trender(root, m(\"div\", {className: \"foo1\"}))\n\t\t\t\trender(root, m(\".bar2\", {className: \"foo2\"}))\n\n\t\t\t\to(root.firstChild.className.split(\" \").sort()).deepEquals([\"bar2\", \"foo2\"])\n\t\t\t})\n\t\t\to(\"to selector and a null class\", function() {\n\t\t\t\trender(root, m(\"div\", {className: \"foo1\"}))\n\t\t\t\trender(root, m(\".foo2\", {class: null}))\n\n\t\t\t\to(root.firstChild.className).equals(\"foo2\")\n\t\t\t})\n\t\t\to(\"to selector and a null className\", function() {\n\t\t\t\trender(root, m(\"div\", {className: \"foo1\"}))\n\t\t\t\trender(root, m(\".foo2\", {className: null}))\n\n\t\t\t\to(root.firstChild.className).equals(\"foo2\")\n\t\t\t})\n\t\t\to(\"to selector and an undefined class\", function() {\n\t\t\t\trender(root, m(\"div\", {className: \"foo1\"}))\n\t\t\t\trender(root, m(\".foo2\", {class: undefined}))\n\n\t\t\t\to(root.firstChild.className).equals(\"foo2\")\n\t\t\t})\n\t\t\to(\"to selector and an undefined className\", function() {\n\t\t\t\trender(root, m(\"div\", {className: \"foo1\"}))\n\t\t\t\trender(root, m(\".foo2\", {className: undefined}))\n\n\t\t\t\to(root.firstChild.className).equals(\"foo2\")\n\t\t\t})\n\t\t})\n\t\to.spec(\"from selector and class\", function() {\n\t\t\to(\"to selector only\", function() {\n\t\t\t\trender(root, m(\".bar1\", {class: \"foo1\"}))\n\t\t\t\trender(root, m(\".foo2\"))\n\n\t\t\t\to(root.firstChild.className).equals(\"foo2\")\n\t\t\t})\n\t\t\to(\"to class only\", function() {\n\t\t\t\trender(root, m(\".bar1\", {class: \"foo1\"}))\n\t\t\t\trender(root, m(\"div\", {class: \"foo2\"}))\n\n\t\t\t\to(root.firstChild.className).equals(\"foo2\")\n\t\t\t})\n\t\t\to(\"to className only\", function() {\n\t\t\t\trender(root, m(\".bar1\", {class: \"foo1\"}))\n\t\t\t\trender(root, m(\"div\", {className: \"foo2\"}))\n\n\t\t\t\to(root.firstChild.className).equals(\"foo2\")\n\t\t\t})\n\t\t\to(\"to selector and class\", function() {\n\t\t\t\trender(root, m(\".bar1\", {class: \"foo1\"}))\n\t\t\t\trender(root, m(\".bar2\", {class: \"foo2\"}))\n\n\t\t\t\to(root.firstChild.className.split(\" \").sort()).deepEquals([\"bar2\", \"foo2\"])\n\t\t\t})\n\t\t\to(\"to selector and className\", function() {\n\t\t\t\trender(root, m(\".bar1\", {class: \"foo1\"}))\n\t\t\t\trender(root, m(\".bar2\", {className: \"foo2\"}))\n\n\t\t\t\to(root.firstChild.className.split(\" \").sort()).deepEquals([\"bar2\", \"foo2\"])\n\t\t\t})\n\t\t\to(\"to selector and a null class\", function() {\n\t\t\t\trender(root, m(\".bar1\", {class: \"foo1\"}))\n\t\t\t\trender(root, m(\".foo2\", {class: null}))\n\n\t\t\t\to(root.firstChild.className).equals(\"foo2\")\n\t\t\t})\n\t\t\to(\"to selector and a null className\", function() {\n\t\t\t\trender(root, m(\".bar1\", {class: \"foo1\"}))\n\t\t\t\trender(root, m(\".foo2\", {className: null}))\n\n\t\t\t\to(root.firstChild.className).equals(\"foo2\")\n\t\t\t})\n\t\t\to(\"to selector and an undefined class\", function() {\n\t\t\t\trender(root, m(\".bar1\", {class: \"foo1\"}))\n\t\t\t\trender(root, m(\".foo2\", {class: undefined}))\n\n\t\t\t\to(root.firstChild.className).equals(\"foo2\")\n\t\t\t})\n\t\t\to(\"to selector and an undefined className\", function() {\n\t\t\t\trender(root, m(\".bar1\", {class: \"foo1\"}))\n\t\t\t\trender(root, m(\".foo2\", {className: undefined}))\n\n\t\t\t\to(root.firstChild.className).equals(\"foo2\")\n\t\t\t})\n\t\t})\n\t\to.spec(\"from selector and className\", function() {\n\t\t\to(\"to selector only\", function() {\n\t\t\t\trender(root, m(\".bar1\", {className: \"foo1\"}))\n\t\t\t\trender(root, m(\".foo2\"))\n\n\t\t\t\to(root.firstChild.className).equals(\"foo2\")\n\t\t\t})\n\t\t\to(\"to class only\", function() {\n\t\t\t\trender(root, m(\".bar1\", {className: \"foo1\"}))\n\t\t\t\trender(root, m(\"div\", {class: \"foo2\"}))\n\n\t\t\t\to(root.firstChild.className).equals(\"foo2\")\n\t\t\t})\n\t\t\to(\"to className only\", function() {\n\t\t\t\trender(root, m(\".bar1\", {className: \"foo1\"}))\n\t\t\t\trender(root, m(\"div\", {className: \"foo2\"}))\n\n\t\t\t\to(root.firstChild.className).equals(\"foo2\")\n\t\t\t})\n\t\t\to(\"to selector and class\", function() {\n\t\t\t\trender(root, m(\".bar1\", {className: \"foo1\"}))\n\t\t\t\trender(root, m(\".bar2\", {class: \"foo2\"}))\n\n\t\t\t\to(root.firstChild.className.split(\" \").sort()).deepEquals([\"bar2\", \"foo2\"])\n\t\t\t})\n\t\t\to(\"to selector and className\", function() {\n\t\t\t\trender(root, m(\".bar1\", {className: \"foo1\"}))\n\t\t\t\trender(root, m(\".bar2\", {className: \"foo2\"}))\n\n\t\t\t\to(root.firstChild.className.split(\" \").sort()).deepEquals([\"bar2\", \"foo2\"])\n\t\t\t})\n\t\t\to(\"to selector and a null class\", function() {\n\t\t\t\trender(root, m(\".bar1\", {className: \"foo1\"}))\n\t\t\t\trender(root, m(\".foo2\", {class: null}))\n\n\t\t\t\to(root.firstChild.className).equals(\"foo2\")\n\t\t\t})\n\t\t\to(\"to selector and a null className\", function() {\n\t\t\t\trender(root, m(\".bar1\", {className: \"foo1\"}))\n\t\t\t\trender(root, m(\".foo2\", {className: null}))\n\n\t\t\t\to(root.firstChild.className).equals(\"foo2\")\n\t\t\t})\n\t\t\to(\"to selector and an undefined class\", function() {\n\t\t\t\trender(root, m(\".bar1\", {className: \"foo1\"}))\n\t\t\t\trender(root, m(\".foo2\", {class: undefined}))\n\n\t\t\t\to(root.firstChild.className).equals(\"foo2\")\n\t\t\t})\n\t\t\to(\"to selector and an undefined className\", function() {\n\t\t\t\trender(root, m(\".bar1\", {className: \"foo1\"}))\n\t\t\t\trender(root, m(\".foo2\", {className: undefined}))\n\n\t\t\t\to(root.firstChild.className).equals(\"foo2\")\n\t\t\t})\n\t\t})\n\t\to.spec(\"from  and a null class\", function() {\n\t\t\to(\"to selector only\", function() {\n\t\t\t\trender(root, m(\".foo1\", {class: null}))\n\t\t\t\trender(root, m(\".foo2\"))\n\n\t\t\t\to(root.firstChild.className).equals(\"foo2\")\n\t\t\t})\n\t\t\to(\"to class only\", function() {\n\t\t\t\trender(root, m(\".foo1\", {class: null}))\n\t\t\t\trender(root, m(\"div\", {class: \"foo2\"}))\n\n\t\t\t\to(root.firstChild.className).equals(\"foo2\")\n\t\t\t})\n\t\t\to(\"to className only\", function() {\n\t\t\t\trender(root, m(\".foo1\", {class: null}))\n\t\t\t\trender(root, m(\"div\", {className: \"foo2\"}))\n\n\t\t\t\to(root.firstChild.className).equals(\"foo2\")\n\t\t\t})\n\t\t\to(\"to selector and class\", function() {\n\t\t\t\trender(root, m(\".foo1\", {class: null}))\n\t\t\t\trender(root, m(\".bar2\", {class: \"foo2\"}))\n\n\t\t\t\to(root.firstChild.className.split(\" \").sort()).deepEquals([\"bar2\", \"foo2\"])\n\t\t\t})\n\t\t\to(\"to selector and className\", function() {\n\t\t\t\trender(root, m(\".foo1\", {class: null}))\n\t\t\t\trender(root, m(\".bar2\", {className: \"foo2\"}))\n\n\t\t\t\to(root.firstChild.className.split(\" \").sort()).deepEquals([\"bar2\", \"foo2\"])\n\t\t\t})\n\t\t\to(\"to selector and a null class\", function() {\n\t\t\t\trender(root, m(\".foo1\", {class: null}))\n\t\t\t\trender(root, m(\".foo2\", {class: null}))\n\n\t\t\t\to(root.firstChild.className).equals(\"foo2\")\n\t\t\t})\n\t\t\to(\"to selector and a null className\", function() {\n\t\t\t\trender(root, m(\".foo1\", {class: null}))\n\t\t\t\trender(root, m(\".foo2\", {className: null}))\n\n\t\t\t\to(root.firstChild.className).equals(\"foo2\")\n\t\t\t})\n\t\t\to(\"to selector and an undefined class\", function() {\n\t\t\t\trender(root, m(\".foo1\", {class: null}))\n\t\t\t\trender(root, m(\".foo2\", {class: undefined}))\n\n\t\t\t\to(root.firstChild.className).equals(\"foo2\")\n\t\t\t})\n\t\t\to(\"to selector and an undefined className\", function() {\n\t\t\t\trender(root, m(\".foo1\", {class: null}))\n\t\t\t\trender(root, m(\".foo2\", {className: undefined}))\n\n\t\t\t\to(root.firstChild.className).equals(\"foo2\")\n\t\t\t})\n\t\t})\n\t\to.spec(\"from selector and a null className\", function() {\n\t\t\to(\"to selector only\", function() {\n\t\t\t\trender(root, m(\".foo1\", {className: null}))\n\t\t\t\trender(root, m(\".foo2\"))\n\n\t\t\t\to(root.firstChild.className).equals(\"foo2\")\n\t\t\t})\n\t\t\to(\"to class only\", function() {\n\t\t\t\trender(root, m(\".foo1\", {className: null}))\n\t\t\t\trender(root, m(\"div\", {class: \"foo2\"}))\n\n\t\t\t\to(root.firstChild.className).equals(\"foo2\")\n\t\t\t})\n\t\t\to(\"to className only\", function() {\n\t\t\t\trender(root, m(\".foo1\", {className: null}))\n\t\t\t\trender(root, m(\"div\", {className: \"foo2\"}))\n\n\t\t\t\to(root.firstChild.className).equals(\"foo2\")\n\t\t\t})\n\t\t\to(\"to selector and class\", function() {\n\t\t\t\trender(root, m(\".foo1\", {className: null}))\n\t\t\t\trender(root, m(\".bar2\", {class: \"foo2\"}))\n\n\t\t\t\to(root.firstChild.className.split(\" \").sort()).deepEquals([\"bar2\", \"foo2\"])\n\t\t\t})\n\t\t\to(\"to selector and className\", function() {\n\t\t\t\trender(root, m(\".foo1\", {className: null}))\n\t\t\t\trender(root, m(\".bar2\", {className: \"foo2\"}))\n\n\t\t\t\to(root.firstChild.className.split(\" \").sort()).deepEquals([\"bar2\", \"foo2\"])\n\t\t\t})\n\t\t\to(\"to selector and a null class\", function() {\n\t\t\t\trender(root, m(\".foo1\", {className: null}))\n\t\t\t\trender(root, m(\".foo2\", {class: null}))\n\n\t\t\t\to(root.firstChild.className).equals(\"foo2\")\n\t\t\t})\n\t\t\to(\"to selector and a null className\", function() {\n\t\t\t\trender(root, m(\".foo1\", {className: null}))\n\t\t\t\trender(root, m(\".foo2\", {className: null}))\n\n\t\t\t\to(root.firstChild.className).equals(\"foo2\")\n\t\t\t})\n\t\t\to(\"to selector and an undefined class\", function() {\n\t\t\t\trender(root, m(\".foo1\", {className: null}))\n\t\t\t\trender(root, m(\".foo2\", {class: undefined}))\n\n\t\t\t\to(root.firstChild.className).equals(\"foo2\")\n\t\t\t})\n\t\t\to(\"to selector and an undefined className\", function() {\n\t\t\t\trender(root, m(\".foo1\", {className: null}))\n\t\t\t\trender(root, m(\".foo2\", {className: undefined}))\n\n\t\t\t\to(root.firstChild.className).equals(\"foo2\")\n\t\t\t})\n\t\t})\n\t\to.spec(\"from selector and an undefined class\", function() {\n\t\t\to(\"to selector only\", function() {\n\t\t\t\trender(root, m(\".foo1\", {class: undefined}))\n\t\t\t\trender(root, m(\".foo2\"))\n\n\t\t\t\to(root.firstChild.className).equals(\"foo2\")\n\t\t\t})\n\t\t\to(\"to class only\", function() {\n\t\t\t\trender(root, m(\".foo1\", {class: undefined}))\n\t\t\t\trender(root, m(\"div\", {class: \"foo2\"}))\n\n\t\t\t\to(root.firstChild.className).equals(\"foo2\")\n\t\t\t})\n\t\t\to(\"to className only\", function() {\n\t\t\t\trender(root, m(\".foo1\", {class: undefined}))\n\t\t\t\trender(root, m(\"div\", {className: \"foo2\"}))\n\n\t\t\t\to(root.firstChild.className).equals(\"foo2\")\n\t\t\t})\n\t\t\to(\"to selector and class\", function() {\n\t\t\t\trender(root, m(\".foo1\", {class: undefined}))\n\t\t\t\trender(root, m(\".bar2\", {class: \"foo2\"}))\n\n\t\t\t\to(root.firstChild.className.split(\" \").sort()).deepEquals([\"bar2\", \"foo2\"])\n\t\t\t})\n\t\t\to(\"to selector and className\", function() {\n\t\t\t\trender(root, m(\".foo1\", {class: undefined}))\n\t\t\t\trender(root, m(\".bar2\", {className: \"foo2\"}))\n\n\t\t\t\to(root.firstChild.className.split(\" \").sort()).deepEquals([\"bar2\", \"foo2\"])\n\t\t\t})\n\t\t\to(\"to selector and a null class\", function() {\n\t\t\t\trender(root, m(\".foo1\", {class: undefined}))\n\t\t\t\trender(root, m(\".foo2\", {class: null}))\n\n\t\t\t\to(root.firstChild.className).equals(\"foo2\")\n\t\t\t})\n\t\t\to(\"to selector and a null className\", function() {\n\t\t\t\trender(root, m(\".foo1\", {class: undefined}))\n\t\t\t\trender(root, m(\".foo2\", {className: null}))\n\n\t\t\t\to(root.firstChild.className).equals(\"foo2\")\n\t\t\t})\n\t\t\to(\"to selector and an undefined class\", function() {\n\t\t\t\trender(root, m(\".foo1\", {class: undefined}))\n\t\t\t\trender(root, m(\".foo2\", {class: undefined}))\n\n\t\t\t\to(root.firstChild.className).equals(\"foo2\")\n\t\t\t})\n\t\t\to(\"to selector and an undefined className\", function() {\n\t\t\t\trender(root, m(\".foo1\", {class: undefined}))\n\t\t\t\trender(root, m(\".foo2\", {className: undefined}))\n\n\t\t\t\to(root.firstChild.className).equals(\"foo2\")\n\t\t\t})\n\t\t})\n\t\to.spec(\"from selector and an undefined className\", function() {\n\t\t\to(\"to selector only\", function() {\n\t\t\t\trender(root, m(\".foo1\", {className: undefined}))\n\t\t\t\trender(root, m(\".foo2\"))\n\n\t\t\t\to(root.firstChild.className).equals(\"foo2\")\n\t\t\t})\n\t\t\to(\"to class only\", function() {\n\t\t\t\trender(root, m(\".foo1\", {className: undefined}))\n\t\t\t\trender(root, m(\"div\", {class: \"foo2\"}))\n\n\t\t\t\to(root.firstChild.className).equals(\"foo2\")\n\t\t\t})\n\t\t\to(\"to className only\", function() {\n\t\t\t\trender(root, m(\".foo1\", {className: undefined}))\n\t\t\t\trender(root, m(\"div\", {className: \"foo2\"}))\n\n\t\t\t\to(root.firstChild.className).equals(\"foo2\")\n\t\t\t})\n\t\t\to(\"to selector and class\", function() {\n\t\t\t\trender(root, m(\".foo1\", {className: undefined}))\n\t\t\t\trender(root, m(\".bar2\", {class: \"foo2\"}))\n\n\t\t\t\to(root.firstChild.className.split(\" \").sort()).deepEquals([\"bar2\", \"foo2\"])\n\t\t\t})\n\t\t\to(\"to selector and className\", function() {\n\t\t\t\trender(root, m(\".foo1\", {className: undefined}))\n\t\t\t\trender(root, m(\".bar2\", {className: \"foo2\"}))\n\n\t\t\t\to(root.firstChild.className.split(\" \").sort()).deepEquals([\"bar2\", \"foo2\"])\n\t\t\t})\n\t\t\to(\"to selector and a null class\", function() {\n\t\t\t\trender(root, m(\".foo1\", {className: undefined}))\n\t\t\t\trender(root, m(\".foo2\", {class: null}))\n\n\t\t\t\to(root.firstChild.className).equals(\"foo2\")\n\t\t\t})\n\t\t\to(\"to selector and a null className\", function() {\n\t\t\t\trender(root, m(\".foo1\", {className: undefined}))\n\t\t\t\trender(root, m(\".foo2\", {className: null}))\n\n\t\t\t\to(root.firstChild.className).equals(\"foo2\")\n\t\t\t})\n\t\t\to(\"to selector and an undefined class\", function() {\n\t\t\t\trender(root, m(\".foo1\", {className: undefined}))\n\t\t\t\trender(root, m(\".foo2\", {class: undefined}))\n\n\t\t\t\to(root.firstChild.className).equals(\"foo2\")\n\t\t\t})\n\t\t\to(\"to selector and an undefined className\", function() {\n\t\t\t\trender(root, m(\".foo1\", {className: undefined}))\n\t\t\t\trender(root, m(\".foo2\", {className: undefined}))\n\n\t\t\t\to(root.firstChild.className).equals(\"foo2\")\n\t\t\t})\n\t\t})\n\t})\n})\n"
  },
  {
    "path": "render/tests/test-render.js",
    "content": "\"use strict\"\n\nvar o = require(\"ospec\")\nvar domMock = require(\"../../test-utils/domMock\")\nvar vdom = require(\"../../render/render\")\nvar m = require(\"../../render/hyperscript\")\n\no.spec(\"render\", function() {\n\tvar $window, root, render\n\to.beforeEach(function() {\n\t\t$window = domMock()\n\t\troot = $window.document.createElement(\"div\")\n\t\trender = vdom($window)\n\t})\n\n\to(\"initializes without DOM\", function() {\n\t\tvdom()\n\t})\n\n\to(\"renders plain text\", function() {\n\t\trender(root, \"a\")\n\t\to(root.childNodes.length).equals(1)\n\t\to(root.childNodes[0].nodeValue).equals(\"a\")\n\t})\n\n\to(\"updates plain text\", function() {\n\t\trender(root, \"a\")\n\t\trender(root, \"b\")\n\t\to(root.childNodes.length).equals(1)\n\t\to(root.childNodes[0].nodeValue).equals(\"b\")\n\t})\n\n\to(\"renders a number\", function() {\n\t\trender(root, 1)\n\t\to(root.childNodes.length).equals(1)\n\t\to(root.childNodes[0].nodeValue).equals(\"1\")\n\t})\n\n\to(\"updates a number\", function() {\n\t\trender(root, 1)\n\t\trender(root, 2)\n\t\to(root.childNodes.length).equals(1)\n\t\to(root.childNodes[0].nodeValue).equals(\"2\")\n\t})\n\n\to(\"overwrites existing content\", function() {\n\t\tvar vnodes = []\n\n\t\troot.appendChild($window.document.createElement(\"div\"));\n\n\t\trender(root, vnodes)\n\n\t\to(root.childNodes.length).equals(0)\n\t})\n\n\to(\"throws on invalid root node\", function() {\n\t\tvar threw = false\n\t\ttry {\n\t\t\trender(null, [])\n\t\t} catch (e) {\n\t\t\tthrew = true\n\t\t}\n\t\to(threw).equals(true)\n\t})\n\n\to(\"does not enter infinite loop when oninit triggers render and view throws with an object literal component\", function(done) {\n\t\tvar A = {\n\t\t\toninit: init,\n\t\t\tview: function() {throw new Error(\"error\")}\n\t\t}\n\t\tfunction run() {\n\t\t\trender(root, m(A))\n\t\t}\n\t\tfunction init() {\n\t\t\tsetTimeout(function() {\n\t\t\t\tvar threwInner = false\n\t\t\t\ttry {run()} catch (e) {threwInner = true}\n\n\t\t\t\to(threwInner).equals(false)\n\t\t\t\tdone()\n\t\t\t}, 0)\n\t\t}\n\n\t\tvar threwOuter = false\n\t\ttry {run()} catch (e) {threwOuter = true}\n\n\t\to(threwOuter).equals(true)\n\t})\n\to(\"does not try to re-initialize a constructibe component whose view has thrown\", function() {\n\t\tvar oninit = o.spy()\n\t\tvar onbeforeupdate = o.spy()\n\t\tfunction A(){}\n\t\tA.prototype.view = function() {throw new Error(\"error\")}\n\t\tA.prototype.oninit = oninit\n\t\tA.prototype.onbeforeupdate = onbeforeupdate\n\t\tvar throwCount = 0\n\n\t\ttry {render(root, m(A))} catch (e) {throwCount++}\n\n\t\to(throwCount).equals(1)\n\t\to(oninit.callCount).equals(1)\n\t\to(onbeforeupdate.callCount).equals(0)\n\n\t\ttry {render(root, m(A))} catch (e) {throwCount++}\n\n\t\to(throwCount).equals(1)\n\t\to(oninit.callCount).equals(1)\n\t\to(onbeforeupdate.callCount).equals(0)\n\t})\n\to(\"does not try to re-initialize a constructible component whose oninit has thrown\", function() {\n\t\tvar oninit = o.spy(function(){throw new Error(\"error\")})\n\t\tvar onbeforeupdate = o.spy()\n\t\tfunction A(){}\n\t\tA.prototype.view = function(){}\n\t\tA.prototype.oninit = oninit\n\t\tA.prototype.onbeforeupdate = onbeforeupdate\n\t\tvar throwCount = 0\n\n\t\ttry {render(root, m(A))} catch (e) {throwCount++}\n\n\t\to(throwCount).equals(1)\n\t\to(oninit.callCount).equals(1)\n\t\to(onbeforeupdate.callCount).equals(0)\n\n\t\ttry {render(root, m(A))} catch (e) {throwCount++}\n\n\t\to(throwCount).equals(1)\n\t\to(oninit.callCount).equals(1)\n\t\to(onbeforeupdate.callCount).equals(0)\n\t})\n\to(\"does not try to re-initialize a constructible component whose constructor has thrown\", function() {\n\t\tvar oninit = o.spy()\n\t\tvar onbeforeupdate = o.spy()\n\t\tfunction A(){throw new Error(\"error\")}\n\t\tA.prototype.view = function() {}\n\t\tA.prototype.oninit = oninit\n\t\tA.prototype.onbeforeupdate = onbeforeupdate\n\t\tvar throwCount = 0\n\n\t\ttry {render(root, m(A))} catch (e) {throwCount++}\n\n\t\to(throwCount).equals(1)\n\t\to(oninit.callCount).equals(0)\n\t\to(onbeforeupdate.callCount).equals(0)\n\n\t\ttry {render(root, m(A))} catch (e) {throwCount++}\n\n\t\to(throwCount).equals(1)\n\t\to(oninit.callCount).equals(0)\n\t\to(onbeforeupdate.callCount).equals(0)\n\t})\n\to(\"does not try to re-initialize a closure component whose view has thrown\", function() {\n\t\tvar oninit = o.spy()\n\t\tvar onbeforeupdate = o.spy()\n\t\tfunction A() {\n\t\t\treturn {\n\t\t\t\tview: function() {throw new Error(\"error\")},\n\t\t\t\toninit: oninit,\n\t\t\t\tonbeforeupdate: onbeforeupdate\n\t\t\t}\n\t\t}\n\t\tvar throwCount = 0\n\t\ttry {render(root, m(A))} catch (e) {throwCount++}\n\n\t\to(throwCount).equals(1)\n\t\to(oninit.callCount).equals(1)\n\t\to(onbeforeupdate.callCount).equals(0)\n\n\t\ttry {render(root, m(A))} catch (e) {throwCount++}\n\n\t\to(throwCount).equals(1)\n\t\to(oninit.callCount).equals(1)\n\t\to(onbeforeupdate.callCount).equals(0)\n\t})\n\to(\"does not try to re-initialize a closure component whose oninit has thrown\", function() {\n\t\tvar oninit = o.spy(function() {throw new Error(\"error\")})\n\t\tvar onbeforeupdate = o.spy()\n\t\tfunction A() {\n\t\t\treturn {\n\t\t\t\tview: function() {},\n\t\t\t\toninit: oninit,\n\t\t\t\tonbeforeupdate: onbeforeupdate\n\t\t\t}\n\t\t}\n\t\tvar throwCount = 0\n\t\ttry {render(root, m(A))} catch (e) {throwCount++}\n\n\t\to(throwCount).equals(1)\n\t\to(oninit.callCount).equals(1)\n\t\to(onbeforeupdate.callCount).equals(0)\n\n\t\ttry {render(root, m(A))} catch (e) {throwCount++}\n\n\t\to(throwCount).equals(1)\n\t\to(oninit.callCount).equals(1)\n\t\to(onbeforeupdate.callCount).equals(0)\n\t})\n\to(\"does not try to re-initialize a closure component whose closure has thrown\", function() {\n\t\tfunction A() {\n\t\t\tthrow new Error(\"error\")\n\t\t}\n\t\tvar throwCount = 0\n\t\ttry {render(root, m(A))} catch (e) {throwCount++}\n\n\t\to(throwCount).equals(1)\n\n\t\ttry {render(root, m(A))} catch (e) {throwCount++}\n\n\t\to(throwCount).equals(1)\n\t})\n\to(\"lifecycle methods work in keyed children of recycled keyed\", function() {\n\t\tvar createA = o.spy()\n\t\tvar updateA = o.spy()\n\t\tvar removeA = o.spy()\n\t\tvar createB = o.spy()\n\t\tvar updateB = o.spy()\n\t\tvar removeB = o.spy()\n\t\tvar a = function() {\n\t\t\treturn m(\"div\", {key: 1},\n\t\t\t\tm(\"div\", {key: 11, oncreate: createA, onupdate: updateA, onremove: removeA}),\n\t\t\t\tm(\"div\", {key: 12})\n\t\t\t)\n\t\t}\n\t\tvar b = function() {\n\t\t\treturn m(\"div\", {key: 2},\n\t\t\t\tm(\"div\", {key: 21, oncreate: createB, onupdate: updateB, onremove: removeB}),\n\t\t\t\tm(\"div\", {key: 22})\n\t\t\t)\n\t\t}\n\t\trender(root, a())\n\t\trender(root, b())\n\t\trender(root, a())\n\n\t\to(createA.callCount).equals(2)\n\t\to(updateA.callCount).equals(0)\n\t\to(removeA.callCount).equals(1)\n\t\to(createB.callCount).equals(1)\n\t\to(updateB.callCount).equals(0)\n\t\to(removeB.callCount).equals(1)\n\t})\n\to(\"lifecycle methods work in unkeyed children of recycled keyed\", function() {\n\t\tvar createA = o.spy()\n\t\tvar updateA = o.spy()\n\t\tvar removeA = o.spy()\n\t\tvar createB = o.spy()\n\t\tvar updateB = o.spy()\n\t\tvar removeB = o.spy()\n\t\tvar a = function() {\n\t\t\treturn m(\"div\", {key: 1},\n\t\t\t\tm(\"div\", {oncreate: createA, onupdate: updateA, onremove: removeA})\n\t\t\t)\n\t\t}\n\t\tvar b = function() {\n\t\t\treturn m(\"div\", {key: 2},\n\t\t\t\tm(\"div\", {oncreate: createB, onupdate: updateB, onremove: removeB})\n\t\t\t)\n\t\t}\n\t\trender(root, a())\n\t\trender(root, b())\n\t\trender(root, a())\n\n\t\to(createA.callCount).equals(2)\n\t\to(updateA.callCount).equals(0)\n\t\to(removeA.callCount).equals(1)\n\t\to(createB.callCount).equals(1)\n\t\to(updateB.callCount).equals(0)\n\t\to(removeB.callCount).equals(1)\n\t})\n\to(\"update lifecycle methods work on children of recycled keyed\", function() {\n\t\tvar createA = o.spy()\n\t\tvar updateA = o.spy()\n\t\tvar removeA = o.spy()\n\t\tvar createB = o.spy()\n\t\tvar updateB = o.spy()\n\t\tvar removeB = o.spy()\n\n\t\tvar a = function() {\n\t\t\treturn m(\"div\", {key: 1},\n\t\t\t\tm(\"div\", {oncreate: createA, onupdate: updateA, onremove: removeA})\n\t\t\t)\n\t\t}\n\t\tvar b = function() {\n\t\t\treturn m(\"div\", {key: 2},\n\t\t\t\tm(\"div\", {oncreate: createB, onupdate: updateB, onremove: removeB})\n\t\t\t)\n\t\t}\n\t\trender(root, a())\n\t\trender(root, a())\n\t\to(createA.callCount).equals(1)\n\t\to(updateA.callCount).equals(1)\n\t\to(removeA.callCount).equals(0)\n\n\t\trender(root, b())\n\t\to(createA.callCount).equals(1)\n\t\to(updateA.callCount).equals(1)\n\t\to(removeA.callCount).equals(1)\n\n\t\trender(root, a())\n\t\trender(root, a())\n\n\t\to(createA.callCount).equals(2)\n\t\to(updateA.callCount).equals(2)\n\t\to(removeA.callCount).equals(1)\n\t})\n\to(\"svg namespace is preserved in keyed diff (#1820)\", function(){\n\t\t// note that this only exerciese one branch of the keyed diff algo\n\t\tvar svg = m(\"svg\",\n\t\t\tm(\"g\", {key: 0}),\n\t\t\tm(\"g\", {key: 1})\n\t\t)\n\t\trender(root, svg)\n\n\t\to(svg.dom.namespaceURI).equals(\"http://www.w3.org/2000/svg\")\n\t\to(svg.dom.childNodes[0].namespaceURI).equals(\"http://www.w3.org/2000/svg\")\n\t\to(svg.dom.childNodes[1].namespaceURI).equals(\"http://www.w3.org/2000/svg\")\n\n\t\tsvg = m(\"svg\",\n\t\t\tm(\"g\", {key: 1, x: 1}),\n\t\t\tm(\"g\", {key: 2, x: 2})\n\t\t)\n\t\trender(root, svg)\n\n\t\to(svg.dom.namespaceURI).equals(\"http://www.w3.org/2000/svg\")\n\t\to(svg.dom.childNodes[0].namespaceURI).equals(\"http://www.w3.org/2000/svg\")\n\t\to(svg.dom.childNodes[1].namespaceURI).equals(\"http://www.w3.org/2000/svg\")\n\t})\n\to(\"the namespace of the root is passed to children\", function() {\n\t\trender(root, m(\"svg\"))\n\t\to(root.childNodes[0].namespaceURI).equals(\"http://www.w3.org/2000/svg\")\n\t\trender(root.childNodes[0], m(\"g\"))\n\t\to(root.childNodes[0].childNodes[0].namespaceURI).equals(\"http://www.w3.org/2000/svg\")\n\t})\n\to(\"does not allow reentrant invocations\", function() {\n\t\tvar thrown = []\n\t\tfunction A() {\n\t\t\tvar updated = false\n\t\t\ttry {render(root, m(A))} catch (e) {thrown.push(\"construct\")}\n\t\t\treturn {\n\t\t\t\toninit: function() {\n\t\t\t\t\ttry {render(root, m(A))} catch (e) {thrown.push(\"oninit\")}\n\t\t\t\t},\n\t\t\t\toncreate: function() {\n\t\t\t\t\ttry {render(root, m(A))} catch (e) {thrown.push(\"oncreate\")}\n\t\t\t\t},\n\t\t\t\tonbeforeupdate: function() {\n\t\t\t\t\ttry {render(root, m(A))} catch (e) {thrown.push(\"onbeforeupdate\")}\n\t\t\t\t},\n\t\t\t\tonupdate: function() {\n\t\t\t\t\tif (updated) return\n\t\t\t\t\tupdated = true\n\t\t\t\t\ttry {render(root, m(A))} catch (e) {thrown.push(\"onupdate\")}\n\t\t\t\t},\n\t\t\t\tonbeforeremove: function() {\n\t\t\t\t\ttry {render(root, m(A))} catch (e) {thrown.push(\"onbeforeremove\")}\n\t\t\t\t},\n\t\t\t\tonremove: function() {\n\t\t\t\t\ttry {render(root, m(A))} catch (e) {thrown.push(\"onremove\")}\n\t\t\t\t},\n\t\t\t\tview: function() {\n\t\t\t\t\ttry {render(root, m(A))} catch (e) {thrown.push(\"view\")}\n\t\t\t\t},\n\t\t\t}\n\t\t}\n\t\trender(root, m(A))\n\t\to(thrown).deepEquals([\n\t\t\t\"construct\",\n\t\t\t\"oninit\",\n\t\t\t\"view\",\n\t\t\t\"oncreate\",\n\t\t])\n\t\trender(root, m(A))\n\t\to(thrown).deepEquals([\n\t\t\t\"construct\",\n\t\t\t\"oninit\",\n\t\t\t\"view\",\n\t\t\t\"oncreate\",\n\t\t\t\"onbeforeupdate\",\n\t\t\t\"view\",\n\t\t\t\"onupdate\",\n\t\t])\n\t\trender(root, [])\n\t\to(thrown).deepEquals([\n\t\t\t\"construct\",\n\t\t\t\"oninit\",\n\t\t\t\"view\",\n\t\t\t\"oncreate\",\n\t\t\t\"onbeforeupdate\",\n\t\t\t\"view\",\n\t\t\t\"onupdate\",\n\t\t\t\"onbeforeremove\",\n\t\t\t\"onremove\",\n\t\t])\n\t})\n})\n"
  },
  {
    "path": "render/tests/test-textContent.js",
    "content": "\"use strict\"\n\nvar o = require(\"ospec\")\nvar domMock = require(\"../../test-utils/domMock\")\nvar vdom = require(\"../../render/render\")\nvar m = require(\"../../render/hyperscript\")\n\no.spec(\"textContent\", function() {\n\tvar $window, root, render\n\to.beforeEach(function() {\n\t\t$window = domMock()\n\t\troot = $window.document.createElement(\"div\")\n\t\trender = vdom($window)\n\t})\n\n\to(\"ignores null\", function() {\n\t\tvar vnode = m(\"a\", null)\n\n\t\trender(root, vnode)\n\n\t\to(root.childNodes.length).equals(1)\n\t\to(vnode.dom.childNodes.length).equals(0)\n\t\to(vnode.dom).equals(root.childNodes[0])\n\t})\n\to(\"ignores undefined\", function() {\n\t\tvar vnode = m(\"a\", undefined)\n\n\t\trender(root, vnode)\n\n\t\to(root.childNodes.length).equals(1)\n\t\to(vnode.dom.childNodes.length).equals(0)\n\t\to(vnode.dom).equals(root.childNodes[0])\n\t})\n\to(\"creates string\", function() {\n\t\tvar vnode = m(\"a\", \"a\")\n\n\t\trender(root, vnode)\n\n\t\to(root.childNodes.length).equals(1)\n\t\to(vnode.dom.childNodes.length).equals(1)\n\t\to(vnode.dom.childNodes[0].nodeValue).equals(\"a\")\n\t\to(vnode.dom).equals(root.childNodes[0])\n\t})\n\to(\"creates falsy string\", function() {\n\t\tvar vnode = m(\"a\", \"\")\n\n\t\trender(root, vnode)\n\n\t\to(root.childNodes.length).equals(1)\n\t\to(vnode.dom.childNodes.length).equals(1)\n\t\to(vnode.dom.childNodes[0].nodeValue).equals(\"\")\n\t\to(vnode.dom).equals(root.childNodes[0])\n\t})\n\to(\"creates number\", function() {\n\t\tvar vnode = m(\"a\", 1)\n\n\t\trender(root, vnode)\n\n\t\to(root.childNodes.length).equals(1)\n\t\to(vnode.dom.childNodes.length).equals(1)\n\t\to(vnode.dom.childNodes[0].nodeValue).equals(\"1\")\n\t\to(vnode.dom).equals(root.childNodes[0])\n\t})\n\to(\"creates falsy number\", function() {\n\t\tvar vnode = m(\"a\", 0)\n\n\t\trender(root, vnode)\n\n\t\to(root.childNodes.length).equals(1)\n\t\to(vnode.dom.childNodes.length).equals(1)\n\t\to(vnode.dom.childNodes[0].nodeValue).equals(\"0\")\n\t\to(vnode.dom).equals(root.childNodes[0])\n\t})\n\to(\"creates boolean\", function() {\n\t\tvar vnode = m(\"a\", true)\n\n\t\trender(root, vnode)\n\n\t\to(root.childNodes.length).equals(1)\n\t\to(vnode.dom.childNodes.length).equals(0)\n\t\to(vnode.dom).equals(root.childNodes[0])\n\t})\n\to(\"creates falsy boolean\", function() {\n\t\tvar vnode = m(\"a\", false)\n\n\t\trender(root, vnode)\n\n\t\to(root.childNodes.length).equals(1)\n\t\to(vnode.dom.childNodes.length).equals(0)\n\t\to(vnode.dom).equals(root.childNodes[0])\n\t})\n\to(\"updates to string\", function() {\n\t\tvar vnode = m(\"a\", \"a\")\n\t\tvar updated = m(\"a\", \"b\")\n\n\t\trender(root, vnode)\n\t\trender(root, updated)\n\n\t\to(root.childNodes.length).equals(1)\n\t\to(vnode.dom.childNodes.length).equals(1)\n\t\to(vnode.dom.childNodes[0].nodeValue).equals(\"b\")\n\t\to(updated.dom).equals(root.childNodes[0])\n\t})\n\to(\"updates to falsy string\", function() {\n\t\tvar vnode = m(\"a\", \"a\")\n\t\tvar updated = m(\"a\", \"\")\n\n\t\trender(root, vnode)\n\t\trender(root, updated)\n\n\t\to(root.childNodes.length).equals(1)\n\t\to(vnode.dom.childNodes.length).equals(1)\n\t\to(vnode.dom.childNodes[0].nodeValue).equals(\"\")\n\t\to(updated.dom).equals(root.childNodes[0])\n\t})\n\to(\"updates to number\", function() {\n\t\tvar vnode = m(\"a\", \"a\")\n\t\tvar updated = m(\"a\", 1)\n\n\t\trender(root, vnode)\n\t\trender(root, updated)\n\n\t\to(root.childNodes.length).equals(1)\n\t\to(vnode.dom.childNodes.length).equals(1)\n\t\to(vnode.dom.childNodes[0].nodeValue).equals(\"1\")\n\t\to(updated.dom).equals(root.childNodes[0])\n\t})\n\to(\"updates to falsy number\", function() {\n\t\tvar vnode = m(\"a\", \"a\")\n\t\tvar updated = m(\"a\", 0)\n\n\t\trender(root, vnode)\n\t\trender(root, updated)\n\n\t\to(root.childNodes.length).equals(1)\n\t\to(vnode.dom.childNodes.length).equals(1)\n\t\to(vnode.dom.childNodes[0].nodeValue).equals(\"0\")\n\t\to(updated.dom).equals(root.childNodes[0])\n\t})\n\to(\"updates true to nothing\", function() {\n\t\tvar vnode = m(\"a\", \"a\")\n\t\tvar updated = m(\"a\", true)\n\n\t\trender(root, vnode)\n\t\trender(root, updated)\n\n\t\to(root.childNodes.length).equals(1)\n\t\to(vnode.dom.childNodes.length).equals(0)\n\t\to(updated.dom).equals(root.childNodes[0])\n\t})\n\to(\"updates false to nothing\", function() {\n\t\tvar vnode = m(\"a\", \"a\")\n\t\tvar updated = m(\"a\", false)\n\n\t\trender(root, vnode)\n\t\trender(root, updated)\n\n\t\to(root.childNodes.length).equals(1)\n\t\to(vnode.dom.childNodes.length).equals(0)\n\t\to(updated.dom).equals(root.childNodes[0])\n\t})\n\to(\"updates with typecasting\", function() {\n\t\tvar vnode = m(\"a\", \"1\")\n\t\tvar updated = m(\"a\", 1)\n\n\t\trender(root, vnode)\n\t\trender(root, updated)\n\n\t\to(root.childNodes.length).equals(1)\n\t\to(vnode.dom.childNodes.length).equals(1)\n\t\to(vnode.dom.childNodes[0].nodeValue).equals(\"1\")\n\t\to(updated.dom).equals(root.childNodes[0])\n\t})\n\to(\"updates from without text to with text\", function() {\n\t\tvar vnode = m(\"a\")\n\t\tvar updated = m(\"a\", \"b\")\n\n\t\trender(root, vnode)\n\t\trender(root, updated)\n\n\t\to(root.childNodes.length).equals(1)\n\t\to(vnode.dom.childNodes.length).equals(1)\n\t\to(vnode.dom.childNodes[0].nodeValue).equals(\"b\")\n\t\to(updated.dom).equals(root.childNodes[0])\n\t})\n\to(\"updates from with text to without text\", function() {\n\t\tvar vnode = m(\"a\", \"a\")\n\t\tvar updated = m(\"a\")\n\n\t\trender(root, vnode)\n\t\trender(root, updated)\n\n\t\to(root.childNodes.length).equals(1)\n\t\to(vnode.dom.childNodes.length).equals(0)\n\t\to(updated.dom).equals(root.childNodes[0])\n\t})\n})\n"
  },
  {
    "path": "render/tests/test-trust.js",
    "content": "\"use strict\"\n\nvar o = require(\"ospec\")\nvar trust = require(\"../../render/trust\")\n\no.spec(\"trust\", function() {\n\to(\"works with html\", function() {\n\t\tvar vnode = trust(\"<a></a>\")\n\n\t\to(vnode.tag).equals(\"<\")\n\t\to(vnode.children).equals(\"<a></a>\")\n\t})\n\to(\"works with text\", function() {\n\t\tvar vnode = trust(\"abc\")\n\n\t\to(vnode.tag).equals(\"<\")\n\t\to(vnode.children).equals(\"abc\")\n\t})\n\to(\"casts null to empty string\", function() {\n\t\tvar vnode = trust(null)\n\n\t\to(vnode.tag).equals(\"<\")\n\t\to(vnode.children).equals(\"\")\n\t})\n\to(\"casts undefined to empty string\", function() {\n\t\tvar vnode = trust(undefined)\n\n\t\to(vnode.tag).equals(\"<\")\n\t\to(vnode.children).equals(\"\")\n\t})\n})\n"
  },
  {
    "path": "render/tests/test-updateElement.js",
    "content": "\"use strict\"\n\nvar o = require(\"ospec\")\nvar domMock = require(\"../../test-utils/domMock\")\nvar vdom = require(\"../../render/render\")\nvar m = require(\"../../render/hyperscript\")\n\no.spec(\"updateElement\", function() {\n\tvar $window, root, render\n\to.beforeEach(function() {\n\t\t$window = domMock()\n\t\troot = $window.document.createElement(\"div\")\n\t\trender = vdom($window)\n\t})\n\n\to(\"updates attr\", function() {\n\t\tvar vnode = m(\"a\", {id: \"b\"})\n\t\tvar updated = m(\"a\", {id: \"c\"})\n\n\t\trender(root, vnode)\n\t\trender(root, updated)\n\n\t\to(updated.dom).equals(vnode.dom)\n\t\to(updated.dom).equals(root.firstChild)\n\t\to(updated.dom.attributes[\"id\"].value).equals(\"c\")\n\t})\n\to(\"adds attr\", function() {\n\t\tvar vnode = m(\"a\", {id: \"b\"})\n\t\tvar updated = m(\"a\", {id: \"c\", title: \"d\"})\n\n\t\trender(root, vnode)\n\t\trender(root, updated)\n\n\t\to(updated.dom).equals(vnode.dom)\n\t\to(updated.dom).equals(root.firstChild)\n\t\to(updated.dom.attributes[\"title\"].value).equals(\"d\")\n\t})\n\to(\"adds attr from empty attrs\", function() {\n\t\tvar vnode = m(\"a\")\n\t\tvar updated = m(\"a\", {title: \"d\"})\n\n\t\trender(root, vnode)\n\t\trender(root, updated)\n\n\t\to(updated.dom).equals(vnode.dom)\n\t\to(updated.dom).equals(root.firstChild)\n\t\to(updated.dom.attributes[\"title\"].value).equals(\"d\")\n\t})\n\to(\"removes attr\", function() {\n\t\tvar vnode = m(\"a\", {id: \"b\", title: \"d\"})\n\t\tvar updated = m(\"a\", {id: \"c\"})\n\n\t\trender(root, vnode)\n\t\trender(root, updated)\n\n\t\to(updated.dom).equals(vnode.dom)\n\t\to(updated.dom).equals(root.firstChild)\n\t\to(\"title\" in updated.dom.attributes).equals(false)\n\t})\n\to(\"removes class\", function() {\n\t\tvar vnode = m(\"a\", {id: \"b\", className: \"d\"})\n\t\tvar updated = m(\"a\", {id: \"c\"})\n\n\t\trender(root, vnode)\n\t\trender(root, updated)\n\n\t\to(updated.dom).equals(vnode.dom)\n\t\to(updated.dom).equals(root.firstChild)\n\t\to(\"class\" in updated.dom.attributes).equals(false)\n\t})\n\to(\"creates style object\", function() {\n\t\tvar vnode = m(\"a\")\n\t\tvar updated = m(\"a\", {style: {backgroundColor: \"green\"}})\n\n\t\trender(root, vnode)\n\t\trender(root, updated)\n\n\t\to(updated.dom.style.backgroundColor).equals(\"green\")\n\t})\n\to(\"creates style string\", function() {\n\t\tvar vnode = m(\"a\")\n\t\tvar updated = m(\"a\", {style: \"background-color:green\"})\n\n\t\trender(root, vnode)\n\t\trender(root, updated)\n\n\t\to(updated.dom.style.backgroundColor).equals(\"green\")\n\t})\n\to(\"updates style from object to object\", function() {\n\t\tvar vnode = m(\"a\", {style: {backgroundColor: \"red\"}})\n\t\tvar updated = m(\"a\", {style: {backgroundColor: \"green\"}})\n\n\t\trender(root, vnode)\n\t\trender(root, updated)\n\n\t\to(updated.dom.style.backgroundColor).equals(\"green\")\n\t})\n\to(\"updates style from object to string\", function() {\n\t\tvar vnode = m(\"a\", {style: {backgroundColor: \"red\"}})\n\t\tvar updated = m(\"a\", {style: \"background-color:green;\"})\n\n\t\trender(root, vnode)\n\t\trender(root, updated)\n\n\t\to(updated.dom.style.backgroundColor).equals(\"green\")\n\t})\n\to(\"handles noop style change when style is string\", function() {\n\t\tvar vnode = m(\"a\", {style: \"background-color:green;\"})\n\t\tvar updated = m(\"a\", {style: \"background-color:green;\"})\n\n\t\trender(root, vnode)\n\t\trender(root, updated)\n\n\t\to(updated.dom.style.backgroundColor).equals(\"green\")\n\t})\n\to(\"handles noop style change when style is object\", function() {\n\t\tvar vnode = m(\"a\", {style: {backgroundColor: \"red\"}})\n\t\tvar updated = m(\"a\", {style: {backgroundColor: \"red\"}})\n\n\t\trender(root, vnode)\n\t\trender(root, updated)\n\n\t\to(updated.dom.style.backgroundColor).equals(\"red\")\n\t})\n\to(\"updates style from string to object\", function() {\n\t\tvar vnode = m(\"a\", {style: \"background-color:red;\"})\n\t\tvar updated = m(\"a\", {style: {backgroundColor: \"green\"}})\n\n\t\trender(root, vnode)\n\t\trender(root, updated)\n\n\t\to(updated.dom.style.backgroundColor).equals(\"green\")\n\t})\n\to(\"updates style from string to string\", function() {\n\t\tvar vnode = m(\"a\", {style: \"background-color:red;\"})\n\t\tvar updated = m(\"a\", {style: \"background-color:green;\"})\n\n\t\trender(root, vnode)\n\t\trender(root, updated)\n\n\t\to(updated.dom.style.backgroundColor).equals(\"green\")\n\t})\n\to(\"removes style from object to object\", function() {\n\t\tvar vnode = m(\"a\", {style: {backgroundColor: \"red\", border: \"1px solid red\"}})\n\t\tvar updated = m(\"a\", {style: {backgroundColor: \"red\"}})\n\n\t\trender(root, vnode)\n\t\trender(root, updated)\n\n\t\to(updated.dom.style.backgroundColor).equals(\"red\")\n\t\to(updated.dom.style.border).equals(\"\")\n\t})\n\to(\"removes style from string to object\", function() {\n\t\tvar vnode = m(\"a\", {style: \"background-color:red;border:1px solid red\"})\n\t\tvar updated = m(\"a\", {style: {backgroundColor: \"red\"}})\n\n\t\trender(root, vnode)\n\t\trender(root, updated)\n\n\t\to(updated.dom.style.backgroundColor).equals(\"red\")\n\t\to(updated.dom.style.border).notEquals(\"1px solid red\")\n\t})\n\to(\"removes style from object to string\", function() {\n\t\tvar vnode = m(\"a\", {style: {backgroundColor: \"red\", border: \"1px solid red\"}})\n\t\tvar updated = m(\"a\", {style: \"background-color:red\"})\n\n\t\trender(root, vnode)\n\t\trender(root, updated)\n\n\t\to(updated.dom.style.backgroundColor).equals(\"red\")\n\t\to(updated.dom.style.border).equals(\"\")\n\t})\n\to(\"removes style from string to string\", function() {\n\t\tvar vnode = m(\"a\", {style: \"background-color:red;border:1px solid red\"})\n\t\tvar updated = m(\"a\", {style: \"background-color:red\"})\n\n\t\trender(root, vnode)\n\t\trender(root, updated)\n\n\t\to(updated.dom.style.backgroundColor).equals(\"red\")\n\t\to(updated.dom.style.border).equals(\"\")\n\t})\n\to(\"does not re-render element styles for equivalent style objects\", function() {\n\t\tvar style = {color: \"gold\"}\n\t\tvar vnode = m(\"a\", {style: style})\n\n\t\trender(root, vnode)\n\n\t\troot.firstChild.style.color = \"red\"\n\t\tstyle = {color: \"gold\"}\n\t\tvar updated = m(\"a\", {style: style})\n\t\trender(root, updated)\n\n\t\to(updated.dom.style.color).equals(\"red\")\n\t})\n\to(\"setting style to `null` removes all styles\", function() {\n\t\tvar vnode = m(\"p\", {style: \"background-color: red\"})\n\t\tvar updated = m(\"p\", {style: null})\n\n\t\trender(root, vnode)\n\n\t\to(\"style\" in vnode.dom.attributes).equals(true)\n\t\to(vnode.dom.attributes.style.value).equals(\"background-color: red;\")\n\n\t\trender(root, updated)\n\n\t\t//browsers disagree here\n\t\ttry {\n\t\t\to(updated.dom.attributes.style.value).equals(\"\")\n\n\t\t} catch (e) {\n\t\t\to(\"style\" in updated.dom.attributes).equals(false)\n\n\t\t}\n\t})\n\to(\"setting style to `undefined` removes all styles\", function() {\n\t\tvar vnode = m(\"p\", {style: \"background-color: red\"})\n\t\tvar updated = m(\"p\", {style: undefined})\n\n\t\trender(root, vnode)\n\n\t\to(\"style\" in vnode.dom.attributes).equals(true)\n\t\to(vnode.dom.attributes.style.value).equals(\"background-color: red;\")\n\n\t\trender(root, updated)\n\n\t\t//browsers disagree here\n\t\ttry {\n\n\t\t\to(updated.dom.attributes.style.value).equals(\"\")\n\n\t\t} catch (e) {\n\n\t\t\to(\"style\" in updated.dom.attributes).equals(false)\n\n\t\t}\n\t})\n\to(\"not setting style removes all styles\", function() {\n\t\tvar vnode = m(\"p\", {style: \"background-color: red\"})\n\t\tvar updated = m(\"p\")\n\n\t\trender(root, vnode)\n\n\t\to(\"style\" in vnode.dom.attributes).equals(true)\n\t\to(vnode.dom.attributes.style.value).equals(\"background-color: red;\")\n\n\t\trender(root, updated)\n\n\t\t//browsers disagree here\n\t\ttry {\n\n\t\t\to(updated.dom.attributes.style.value).equals(\"\")\n\n\t\t} catch (e) {\n\n\t\t\to(\"style\" in updated.dom.attributes).equals(false)\n\n\t\t}\n\t})\n\to(\"use style property setter only when cameCase keys\", function() {\n\t\tvar spySetProperty = o.spy()\n\t\tvar spyRemoveProperty = o.spy()\n\t\tvar spyDashed1 = o.spy()\n\t\tvar spyDashed2 = o.spy()\n\t\tvar spyDashed3 = o.spy()\n\t\tvar spyCamelCase1 = o.spy()\n\t\tvar spyCamelCase2 = o.spy()\n\n\t\trender(root, m(\"a\"))\n\t\tvar el = root.firstChild\n\n\t\tel.style.setProperty = spySetProperty\n\t\tel.style.removeProperty = spyRemoveProperty\n\t\tObject.defineProperties(el.style, {\n\t\t\t/* eslint-disable accessor-pairs */\n\t\t\t\"background-color\": {set: spyDashed1},\n\t\t\t\"-webkit-border-radius\": {set: spyDashed2},\n\t\t\t\"--foo\": {set: spyDashed3},\n\t\t\tbackgroundColor: {set: spyCamelCase1},\n\t\t\tcolor: {set: spyCamelCase2}\n\t\t\t/* eslint-enable accessor-pairs */\n\t\t})\n\n\t\t// sets dashed properties\n\t\trender(root, m(\"a\", {\n\t\t\tstyle: {\n\t\t\t\t\"background-color\": \"red\",\n\t\t\t\t\"-webkit-border-radius\": \"10px\",\n\t\t\t\t\"--foo\": \"bar\"\n\t\t\t}\n\t\t}))\n\t\to(spySetProperty.callCount).equals(3)\n\t\to(spySetProperty.calls[0].args).deepEquals([\"background-color\", \"red\"])\n\t\to(spySetProperty.calls[1].args).deepEquals([\"-webkit-border-radius\", \"10px\"])\n\t\to(spySetProperty.calls[2].args).deepEquals([\"--foo\", \"bar\"])\n\n\t\t// sets camelCase properties and removes dashed properties\n\t\trender(root, m(\"a\", {\n\t\t\tstyle: {\n\t\t\t\tbackgroundColor: \"green\",\n\t\t\t\tcolor: \"red\",\n\t\t\t}\n\t\t}))\n\t\to(spyCamelCase1.callCount).equals(1)\n\t\to(spyCamelCase2.callCount).equals(1)\n\t\to(spyCamelCase1.calls[0].args).deepEquals([\"green\"])\n\t\to(spyCamelCase2.calls[0].args).deepEquals([\"red\"])\n\t\to(spyRemoveProperty.callCount).equals(3)\n\t\to(spyRemoveProperty.calls[0].args).deepEquals([\"background-color\"])\n\t\to(spyRemoveProperty.calls[1].args).deepEquals([\"-webkit-border-radius\"])\n\t\to(spyRemoveProperty.calls[2].args).deepEquals([\"--foo\"])\n\n\t\t// updates \"color\" and removes \"backgroundColor\"\n\t\trender(root, m(\"a\", {style: {color: \"blue\"}}))\n\t\to(spyCamelCase1.callCount).equals(2) // set and remove\n\t\to(spyCamelCase2.callCount).equals(2) // set and update\n\t\to(spyCamelCase1.calls[1].args).deepEquals([\"\"])\n\t\to(spyCamelCase2.calls[1].args).deepEquals([\"blue\"])\n\n\t\t// unchanged by camelCase properties\n\t\to(spySetProperty.callCount).equals(3)\n\t\to(spyRemoveProperty.callCount).equals(3)\n\n\t\t// never calls dashed property setter\n\t\to(spyDashed1.callCount).equals(0)\n\t\to(spyDashed2.callCount).equals(0)\n\t\to(spyDashed3.callCount).equals(0)\n\t})\n\to(\"replaces el\", function() {\n\t\tvar vnode = m(\"a\")\n\t\tvar updated = m(\"b\")\n\n\t\trender(root, vnode)\n\t\trender(root, updated)\n\n\t\to(updated.dom).equals(root.firstChild)\n\t\to(updated.dom.nodeName).equals(\"B\")\n\t})\n\to(\"updates svg class\", function() {\n\t\tvar vnode = m(\"svg\", {className: \"a\"})\n\t\tvar updated = m(\"svg\", {className: \"b\"})\n\n\t\trender(root, vnode)\n\t\trender(root, updated)\n\n\t\to(updated.dom.attributes[\"class\"].value).equals(\"b\")\n\t})\n\to(\"updates svg child\", function() {\n\t\tvar vnode = m(\"svg\", m(\"circle\"))\n\t\tvar updated = m(\"svg\", m(\"line\"))\n\n\t\trender(root, vnode)\n\t\trender(root, updated)\n\n\t\to(updated.dom.firstChild.namespaceURI).equals(\"http://www.w3.org/2000/svg\")\n\t})\n\to(\"doesn't restore since we're not recycling\", function() {\n\t\tvar vnode = m(\"div\", {key: 1})\n\t\tvar updated = m(\"div\", {key: 2})\n\n\t\trender(root, vnode)\n\t\tvar a = vnode.dom\n\n\t\trender(root, updated)\n\n\t\trender(root, vnode)\n\t\tvar c = vnode.dom\n\n\t\to(root.childNodes.length).equals(1)\n\t\to(a).notEquals(c) // this used to be a recycling pool test\n\t})\n\to(\"doesn't restore since we're not recycling (via map)\", function() {\n\t\tvar a = m(\"div\", {key: 1})\n\t\tvar b = m(\"div\", {key: 2})\n\t\tvar c = m(\"div\", {key: 3})\n\t\tvar d = m(\"div\", {key: 4})\n\t\tvar e = m(\"div\", {key: 5})\n\t\tvar f = m(\"div\", {key: 6})\n\n\t\trender(root, [a, b, c])\n\t\tvar x = root.childNodes[1]\n\n\t\trender(root, d)\n\n\t\trender(root, [e, b, f])\n\t\tvar y = root.childNodes[1]\n\n\t\to(root.childNodes.length).equals(3)\n\t\to(x).notEquals(y) // this used to be a recycling pool test\n\t})\n\to.spec(\"element node with `is` attribute\", function() {\n\t\to(\"recreate element node with `is` attribute (set `is`)\", function() {\n\t\t\tvar vnode = m(\"a\")\n\t\t\tvar updated = m(\"a\", {is: \"bar\"})\n\n\t\t\trender(root, vnode)\n\t\t\trender(root, updated)\n\t\t\t\n\t\t\to(vnode.dom).notEquals(root.firstChild)\n\t\t\to(updated.dom).equals(root.firstChild)\n\t\t\to(updated.dom.nodeName).equals(\"A\")\n\t\t\to(updated.dom.getAttribute(\"is\")).equals(\"bar\")\n\t\t})\n\t\to(\"recreate element node without `is` attribute (remove `is`)\", function() {\n\t\t\tvar vnode = m(\"a\", {is: \"foo\"})\n\t\t\tvar updated = m(\"a\")\n\n\t\t\trender(root, vnode)\n\t\t\trender(root, updated)\n\t\t\t\n\t\t\to(vnode.dom).notEquals(root.firstChild)\n\t\t\to(updated.dom).equals(root.firstChild)\n\t\t\to(updated.dom.nodeName).equals(\"A\")\n\t\t\to(updated.dom.getAttribute(\"is\")).equals(null)\n\t\t})\n\t\to(\"recreate element node with `is` attribute (same tag, different `is`)\", function() {\n\t\t\tvar vnode = m(\"a\", {is: \"foo\"})\n\t\t\tvar updated = m(\"a\", {is: \"bar\"})\n\n\t\t\trender(root, vnode)\n\t\t\trender(root, updated)\n\t\t\t\n\t\t\to(vnode.dom).notEquals(root.firstChild)\n\t\t\to(updated.dom).equals(root.firstChild)\n\t\t\to(updated.dom.nodeName).equals(\"A\")\n\t\t\to(updated.dom.getAttribute(\"is\")).equals(\"bar\")\n\t\t})\n\t\to(\"recreate element node with `is` attribute (different tag, same `is`)\", function() {\n\t\t\tvar vnode = m(\"a\", {is: \"foo\"})\n\t\t\tvar updated = m(\"b\", {is: \"foo\"})\n\n\t\t\trender(root, vnode)\n\t\t\trender(root, updated)\n\t\t\t\n\t\t\to(vnode.dom).notEquals(root.firstChild)\n\t\t\to(updated.dom).equals(root.firstChild)\n\t\t\to(updated.dom.nodeName).equals(\"B\")\n\t\t\to(updated.dom.getAttribute(\"is\")).equals(\"foo\")\n\t\t})\n\t\to(\"recreate element node with `is` attribute (different tag, different `is`)\", function() {\n\t\t\tvar vnode = m(\"a\", {is: \"foo\"})\n\t\t\tvar updated = m(\"b\", {is: \"bar\"})\n\n\t\t\trender(root, vnode)\n\t\t\trender(root, updated)\n\t\t\t\n\t\t\to(vnode.dom).notEquals(root.firstChild)\n\t\t\to(updated.dom).equals(root.firstChild)\n\t\t\to(updated.dom.nodeName).equals(\"B\")\n\t\t\to(updated.dom.getAttribute(\"is\")).equals(\"bar\")\n\t\t})\n\t\to(\"keep element node with `is` attribute (same tag, same `is`)\", function() {\n\t\t\tvar vnode = m(\"a\", {is: \"foo\"})\n\t\t\tvar updated = m(\"a\", {is: \"foo\"}, \"x\")\n\n\t\t\trender(root, vnode)\n\t\t\trender(root, updated)\n\t\t\t\n\t\t\to(vnode.dom).equals(root.firstChild)\n\t\t\to(updated.dom).equals(root.firstChild)\n\t\t\to(updated.dom.nodeName).equals(\"A\")\n\t\t\to(updated.dom.getAttribute(\"is\")).equals(\"foo\")\n\t\t\to(updated.dom.firstChild.nodeValue).equals(\"x\")\n\t\t})\n\t\to(\"recreate element node with `is` attribute (set `is`, CSS selector)\", function() {\n\t\t\tvar vnode = m(\"a\")\n\t\t\tvar updated = m(\"a[is=bar]\")\n\n\t\t\trender(root, vnode)\n\t\t\trender(root, updated)\n\t\t\t\n\t\t\to(vnode.dom).notEquals(root.firstChild)\n\t\t\to(updated.dom).equals(root.firstChild)\n\t\t\to(updated.dom.nodeName).equals(\"A\")\n\t\t\to(updated.dom.getAttribute(\"is\")).equals(\"bar\")\n\t\t})\n\t\to(\"recreate element node without `is` attribute (remove `is`, CSS selector)\", function() {\n\t\t\tvar vnode = m(\"a[is=foo]\")\n\t\t\tvar updated = m(\"a\")\n\n\t\t\trender(root, vnode)\n\t\t\trender(root, updated)\n\t\t\t\n\t\t\to(vnode.dom).notEquals(root.firstChild)\n\t\t\to(updated.dom).equals(root.firstChild)\n\t\t\to(updated.dom.nodeName).equals(\"A\")\n\t\t\to(updated.dom.getAttribute(\"is\")).equals(null)\n\t\t})\n\t\to(\"recreate element node with `is` attribute (same tag, different `is`, CSS selector)\", function() {\n\t\t\tvar vnode = m(\"a[is=foo]\")\n\t\t\tvar updated = m(\"a[is=bar]\")\n\n\t\t\trender(root, vnode)\n\t\t\trender(root, updated)\n\t\t\t\n\t\t\to(vnode.dom).notEquals(root.firstChild)\n\t\t\to(updated.dom).equals(root.firstChild)\n\t\t\to(updated.dom.nodeName).equals(\"A\")\n\t\t\to(updated.dom.getAttribute(\"is\")).equals(\"bar\")\n\t\t})\n\t\to(\"recreate element node with `is` attribute (different tag, same `is`, CSS selector)\", function() {\n\t\t\tvar vnode = m(\"a[is=foo]\")\n\t\t\tvar updated = m(\"b[is=foo]\")\n\n\t\t\trender(root, vnode)\n\t\t\trender(root, updated)\n\t\t\t\n\t\t\to(vnode.dom).notEquals(root.firstChild)\n\t\t\to(updated.dom).equals(root.firstChild)\n\t\t\to(updated.dom.nodeName).equals(\"B\")\n\t\t\to(updated.dom.getAttribute(\"is\")).equals(\"foo\")\n\t\t})\n\t\to(\"recreate element node with `is` attribute (different tag, different `is`, CSS selector)\", function() {\n\t\t\tvar vnode = m(\"a[is=foo]\")\n\t\t\tvar updated = m(\"b[is=bar]\")\n\n\t\t\trender(root, vnode)\n\t\t\trender(root, updated)\n\t\t\t\n\t\t\to(vnode.dom).notEquals(root.firstChild)\n\t\t\to(updated.dom).equals(root.firstChild)\n\t\t\to(updated.dom.nodeName).equals(\"B\")\n\t\t\to(updated.dom.getAttribute(\"is\")).equals(\"bar\")\n\t\t})\n\t\to(\"keep element node with `is` attribute (same tag, same `is`, CSS selector)\", function() {\n\t\t\tvar vnode = m(\"a[is=foo]\")\n\t\t\tvar updated = m(\"a[is=foo]\", \"x\")\n\n\t\t\trender(root, vnode)\n\t\t\trender(root, updated)\n\t\t\t\n\t\t\to(vnode.dom).equals(root.firstChild)\n\t\t\to(updated.dom).equals(root.firstChild)\n\t\t\to(updated.dom.nodeName).equals(\"A\")\n\t\t\to(updated.dom.getAttribute(\"is\")).equals(\"foo\")\n\t\t\to(updated.dom.firstChild.nodeValue).equals(\"x\")\n\t\t})\n\t\to(\"keep element node with `is` attribute (same tag, same `is`, from attrs to CSS selector)\", function() {\n\t\t\tvar vnode = m(\"a\", {is: \"foo\"})\n\t\t\tvar updated = m(\"a[is=foo]\", \"x\")\n\n\t\t\trender(root, vnode)\n\t\t\trender(root, updated)\n\t\t\t\n\t\t\to(vnode.dom).equals(root.firstChild)\n\t\t\to(updated.dom).equals(root.firstChild)\n\t\t\to(updated.dom.nodeName).equals(\"A\")\n\t\t\to(updated.dom.getAttribute(\"is\")).equals(\"foo\")\n\t\t\to(updated.dom.firstChild.nodeValue).equals(\"x\")\n\t\t})\n\t\to(\"keep element node with `is` attribute (same tag, same `is`, from CSS selector to attrs)\", function() {\n\t\t\tvar vnode = m(\"a[is=foo]\")\n\t\t\tvar updated = m(\"a\", {is: \"foo\"}, \"x\")\n\n\t\t\trender(root, vnode)\n\t\t\trender(root, updated)\n\t\t\t\n\t\t\to(vnode.dom).equals(root.firstChild)\n\t\t\to(updated.dom).equals(root.firstChild)\n\t\t\to(updated.dom.nodeName).equals(\"A\")\n\t\t\to(updated.dom.getAttribute(\"is\")).equals(\"foo\")\n\t\t\to(updated.dom.firstChild.nodeValue).equals(\"x\")\n\t\t})\n\t})\n})\n"
  },
  {
    "path": "render/tests/test-updateFragment.js",
    "content": "\"use strict\"\n\nvar o = require(\"ospec\")\nvar domMock = require(\"../../test-utils/domMock\")\nvar vdom = require(\"../../render/render\")\nvar m = require(\"../../render/hyperscript\")\nvar fragment = require(\"../../render/fragment\")\n\no.spec(\"updateFragment\", function() {\n\tvar $window, root, render\n\to.beforeEach(function() {\n\t\t$window = domMock()\n\t\troot = $window.document.createElement(\"div\")\n\t\trender = vdom($window)\n\t})\n\n\to(\"updates fragment\", function() {\n\t\tvar vnode = fragment(m(\"a\"))\n\t\tvar updated = fragment(m(\"b\"))\n\n\t\trender(root, vnode)\n\t\trender(root, updated)\n\n\t\to(updated.dom).equals(root.firstChild)\n\t\to(updated.dom.nodeName).equals(\"B\")\n\t\to(updated.domSize).equals(1)\n\t})\n\to(\"adds els\", function() {\n\t\tvar vnode = fragment()\n\t\tvar updated = fragment(m(\"a\"), m(\"b\"))\n\n\t\trender(root, vnode)\n\t\trender(root, updated)\n\n\t\to(updated.dom).equals(root.firstChild)\n\t\to(updated.domSize).equals(2)\n\t\to(root.childNodes.length).equals(2)\n\t\to(root.childNodes[0].nodeName).equals(\"A\")\n\t\to(root.childNodes[1].nodeName).equals(\"B\")\n\t})\n\to(\"removes els\", function() {\n\t\tvar vnode = fragment(m(\"a\"), m(\"b\"))\n\t\tvar updated = fragment()\n\n\t\trender(root, vnode)\n\t\trender(root, updated)\n\n\t\to(updated.dom).equals(null)\n\t\to(updated.domSize).equals(0)\n\t\to(root.childNodes.length).equals(0)\n\t})\n\to(\"updates from childless fragment\", function() {\n\t\tvar vnode = fragment()\n\t\tvar updated = fragment(m(\"a\"))\n\n\t\trender(root, vnode)\n\t\trender(root, updated)\n\n\t\to(updated.dom).equals(root.firstChild)\n\t\to(updated.dom.nodeName).equals(\"A\")\n\t\to(updated.domSize).equals(1)\n\t})\n\to(\"updates to childless fragment\", function() {\n\t\tvar vnode = fragment(m(\"a\"))\n\t\tvar updated = fragment()\n\n\t\trender(root, vnode)\n\t\trender(root, updated)\n\n\t\to(updated.dom).equals(null)\n\t\to(updated.domSize).equals(0)\n\t\to(root.childNodes.length).equals(0)\n\t})\n})\n"
  },
  {
    "path": "render/tests/test-updateHTML.js",
    "content": "\"use strict\"\n\nvar o = require(\"ospec\")\nvar domMock = require(\"../../test-utils/domMock\")\nvar vdom = require(\"../../render/render\")\nvar m = require(\"../../render/hyperscript\")\nvar trust = require(\"../../render/trust\")\n\no.spec(\"updateHTML\", function() {\n\tvar $window, root, render\n\to.beforeEach(function() {\n\t\t$window = domMock()\n\t\troot = $window.document.createElement(\"div\")\n\t\trender = vdom($window)\n\t})\n\n\to(\"updates html\", function() {\n\t\tvar vnode = trust(\"a\")\n\t\tvar updated = trust(\"b\")\n\n\t\trender(root, vnode)\n\t\trender(root, updated)\n\n\t\to(updated.dom).equals(root.firstChild)\n\t\to(updated.domSize).equals(1)\n\t\to(updated.dom.nodeValue).equals(\"b\")\n\t})\n\to(\"adds html\", function() {\n\t\tvar vnode = trust(\"\")\n\t\tvar updated = trust(\"<a></a><b></b>\")\n\n\t\trender(root, vnode)\n\t\trender(root, updated)\n\n\t\to(updated.domSize).equals(2)\n\t\to(updated.dom).equals(root.firstChild)\n\t\to(root.childNodes.length).equals(2)\n\t\to(root.childNodes[0].nodeName).equals(\"A\")\n\t\to(root.childNodes[1].nodeName).equals(\"B\")\n\t})\n\to(\"removes html\", function() {\n\t\tvar vnode = trust(\"<a></a><b></b>\")\n\t\tvar updated = trust(\"\")\n\n\t\trender(root, vnode)\n\t\trender(root, updated)\n\n\t\to(updated.dom).equals(null)\n\t\to(updated.domSize).equals(0)\n\t\to(root.childNodes.length).equals(0)\n\t})\n\tfunction childKeysOf(elem, key) {\n\t\tvar keys = key.split(\".\")\n\t\tvar result = []\n\t\tfor (var i = 0; i < elem.childNodes.length; i++) {\n\t\t\tvar child = elem.childNodes[i]\n\t\t\tfor (var j = 0; j < keys.length; j++) child = child[keys[j]]\n\t\t\tresult.push(child)\n\t\t}\n\t\treturn result\n\t}\n\to(\"updates the dom correctly with a contenteditable parent\", function() {\n\t\tvar div = m(\"div\", {contenteditable: true}, trust(\"<a></a>\"))\n\n\t\trender(root, div)\n\t\to(childKeysOf(div.dom, \"nodeName\")).deepEquals([\"A\"])\n\t})\n\to(\"updates dom with multiple text children\", function() {\n\t\tvar vnode = [\"a\", trust(\"<a></a>\"), trust(\"<b></b>\")]\n\t\tvar replacement = [\"a\", trust(\"<c></c>\"), trust(\"<d></d>\")]\n\n\t\trender(root, vnode)\n\t\trender(root, replacement)\n\n\t\to(childKeysOf(root, \"nodeName\")).deepEquals([\"#text\", \"C\", \"D\"])\n\t})\n\to(\"updates dom with multiple text children in other parents\", function() {\n\t\tvar vnode = [\n\t\t\tm(\"div\", \"a\", trust(\"<a></a>\")),\n\t\t\tm(\"div\", \"b\", trust(\"<b></b>\")),\n\t\t]\n\t\tvar replacement = [\n\t\t\tm(\"div\", \"c\", trust(\"<c></c>\")),\n\t\t\tm(\"div\", \"d\", trust(\"<d></d>\")),\n\t\t]\n\n\t\trender(root, vnode)\n\t\trender(root, replacement)\n\n\t\to(childKeysOf(root, \"nodeName\")).deepEquals([\"DIV\", \"DIV\"])\n\t\to(childKeysOf(root.childNodes[0], \"nodeName\")).deepEquals([\"#text\", \"C\"])\n\t\to(root.childNodes[0].firstChild.nodeValue).equals(\"c\")\n\t\to(childKeysOf(root.childNodes[1], \"nodeName\")).deepEquals([\"#text\", \"D\"])\n\t\to(root.childNodes[1].firstChild.nodeValue).equals(\"d\")\n\t})\n\to(\"correctly diffs if followed by another trusted vnode\", function() {\n\t\trender(root, [\n\t\t\ttrust(\"<span>A</span>\"),\n\t\t\ttrust(\"<span>A</span>\"),\n\t\t])\n\t\to(childKeysOf(root, \"nodeName\")).deepEquals([\"SPAN\", \"SPAN\"])\n\t\to(childKeysOf(root, \"firstChild.nodeValue\")).deepEquals([\"A\", \"A\"])\n\t\trender(root, [\n\t\t\ttrust(\"<span>B</span>\"),\n\t\t\ttrust(\"<span>A</span>\"),\n\t\t])\n\t\to(childKeysOf(root, \"nodeName\")).deepEquals([\"SPAN\", \"SPAN\"])\n\t\to(childKeysOf(root, \"firstChild.nodeValue\")).deepEquals([\"B\", \"A\"])\n\t\trender(root, [\n\t\t\ttrust(\"<span>B</span>\"),\n\t\t\ttrust(\"<span>B</span>\"),\n\t\t])\n\t\to(childKeysOf(root, \"nodeName\")).deepEquals([\"SPAN\", \"SPAN\"])\n\t\to(childKeysOf(root, \"firstChild.nodeValue\")).deepEquals([\"B\", \"B\"])\n\t})\n})\n"
  },
  {
    "path": "render/tests/test-updateNodes.js",
    "content": "\"use strict\"\n\nvar o = require(\"ospec\")\nvar components = require(\"../../test-utils/components\")\nvar domMock = require(\"../../test-utils/domMock\")\nvar vdom = require(\"../../render/render\")\nvar m = require(\"../../render/hyperscript\")\nvar fragment = require(\"../../render/fragment\")\nvar trust = require(\"../../render/trust\")\n\nfunction vnodify(str) {\n\treturn str.split(\",\").map(function(k) {return m(k, {key: k})})\n}\n\no.spec(\"updateNodes\", function() {\n\tvar $window, root, render\n\to.beforeEach(function() {\n\t\t$window = domMock()\n\t\troot = $window.document.createElement(\"div\")\n\t\trender = vdom($window)\n\t})\n\n\to(\"handles el noop\", function() {\n\t\tvar vnodes = [m(\"a\", {key: 1}), m(\"b\", {key: 2})]\n\t\tvar updated = [m(\"a\", {key: 1}), m(\"b\", {key: 2})]\n\n\t\trender(root, vnodes)\n\t\trender(root, updated)\n\n\t\to(root.childNodes.length).equals(2)\n\t\to(updated[0].dom.nodeName).equals(\"A\")\n\t\to(updated[0].dom).equals(root.childNodes[0])\n\t\to(updated[1].dom.nodeName).equals(\"B\")\n\t\to(updated[1].dom).equals(root.childNodes[1])\n\t})\n\to(\"handles el noop without key\", function() {\n\t\tvar vnodes = [m(\"a\"), m(\"b\")]\n\t\tvar updated = [m(\"a\"), m(\"b\")]\n\n\t\trender(root, vnodes)\n\t\trender(root, updated)\n\n\t\to(root.childNodes.length).equals(2)\n\t\to(updated[0].dom.nodeName).equals(\"A\")\n\t\to(updated[0].dom).equals(root.childNodes[0])\n\t\to(updated[1].dom.nodeName).equals(\"B\")\n\t\to(updated[1].dom).equals(root.childNodes[1])\n\t})\n\to(\"handles text noop\", function() {\n\t\tvar vnodes = \"a\"\n\t\tvar updated = \"a\"\n\n\t\trender(root, vnodes)\n\t\trender(root, updated)\n\n\t\to(root.childNodes.length).equals(1)\n\t\to(root.firstChild.nodeValue).equals(\"a\")\n\t})\n\to(\"handles text noop w/ type casting\", function() {\n\t\tvar vnodes = 1\n\t\tvar updated = \"1\"\n\n\t\trender(root, vnodes)\n\t\trender(root, updated)\n\n\t\to(root.childNodes.length).equals(1)\n\t\to(root.firstChild.nodeValue).equals(\"1\")\n\t})\n\to(\"handles falsy text noop w/ type casting\", function() {\n\t\tvar vnodes = 0\n\t\tvar updated = \"0\"\n\n\t\trender(root, vnodes)\n\t\trender(root, updated)\n\n\t\to(root.childNodes.length).equals(1)\n\t\to(root.childNodes[0].nodeValue).equals(\"0\")\n\t})\n\to(\"handles html noop\", function() {\n\t\tvar vnodes = trust(\"a\")\n\t\tvar updated = trust(\"a\")\n\n\t\trender(root, vnodes)\n\t\trender(root, updated)\n\n\t\to(root.childNodes.length).equals(1)\n\t\to(root.childNodes[0].nodeValue).equals(\"a\")\n\t\to(updated.dom).equals(root.childNodes[0])\n\t})\n\to(\"handles fragment noop\", function() {\n\t\tvar vnodes = fragment(m(\"a\"))\n\t\tvar updated = fragment(m(\"a\"))\n\n\t\trender(root, vnodes)\n\t\trender(root, updated)\n\n\t\to(root.childNodes.length).equals(1)\n\t\to(updated.dom.nodeName).equals(\"A\")\n\t\to(updated.dom).equals(root.childNodes[0])\n\t\to(updated.domSize).equals(1)\n\t})\n\to(\"handles fragment noop w/ text child\", function() {\n\t\tvar vnodes = fragment(\"a\")\n\t\tvar updated = fragment(\"a\")\n\n\t\trender(root, vnodes)\n\t\trender(root, updated)\n\n\t\to(root.childNodes.length).equals(1)\n\t\to(updated.dom.nodeValue).equals(\"a\")\n\t\to(updated.dom).equals(root.childNodes[0])\n\t\to(updated.domSize).equals(1)\n\t})\n\to(\"handles undefined to null noop\", function() {\n\t\tvar vnodes = [null, m(\"div\")]\n\t\tvar updated = [undefined, m(\"div\")]\n\n\t\trender(root, vnodes)\n\t\trender(root, updated)\n\n\t\to(root.childNodes.length).equals(1)\n\t})\n\to(\"reverses els w/ even count\", function() {\n\t\tvar vnodes = [m(\"a\", {key: 1}), m(\"b\", {key: 2}), m(\"i\", {key: 3}), m(\"s\", {key: 4})]\n\t\tvar updated = [m(\"s\", {key: 4}), m(\"i\", {key: 3}), m(\"b\", {key: 2}), m(\"a\", {key: 1})]\n\n\t\trender(root, vnodes)\n\t\trender(root, updated)\n\n\t\to(root.childNodes.length).equals(4)\n\t\to(updated[0].dom.nodeName).equals(\"S\")\n\t\to(updated[0].dom).equals(root.childNodes[0])\n\t\to(updated[1].dom.nodeName).equals(\"I\")\n\t\to(updated[1].dom).equals(root.childNodes[1])\n\t\to(updated[2].dom.nodeName).equals(\"B\")\n\t\to(updated[2].dom).equals(root.childNodes[2])\n\t\to(updated[3].dom.nodeName).equals(\"A\")\n\t\to(updated[3].dom).equals(root.childNodes[3])\n\t})\n\to(\"reverses els w/ odd count\", function() {\n\t\tvar vnodes = [m(\"a\", {key: 1}), m(\"b\", {key: 2}), m(\"i\", {key: 3})]\n\t\tvar updated = [m(\"i\", {key: 3}), m(\"b\", {key: 2}), m(\"a\", {key: 1})]\n\t\tvar expectedTags = updated.map(function(vn) {return vn.tag})\n\t\trender(root, vnodes)\n\t\trender(root, updated)\n\n\t\tvar tagNames = [].map.call(root.childNodes, function(n) {return n.nodeName.toLowerCase()})\n\n\t\to(root.childNodes.length).equals(3)\n\t\to(updated[0].dom.nodeName).equals(\"I\")\n\t\to(updated[1].dom.nodeName).equals(\"B\")\n\t\to(updated[2].dom.nodeName).equals(\"A\")\n\t\to(tagNames).deepEquals(expectedTags)\n\t})\n\to(\"creates el at start\", function() {\n\t\tvar vnodes = [m(\"a\", {key: 1})]\n\t\tvar updated = [m(\"b\", {key: 2}), m(\"a\", {key: 1})]\n\n\t\trender(root, vnodes)\n\t\trender(root, updated)\n\n\t\to(root.childNodes.length).equals(2)\n\t\to(updated[0].dom.nodeName).equals(\"B\")\n\t\to(updated[0].dom).equals(root.childNodes[0])\n\t\to(updated[1].dom.nodeName).equals(\"A\")\n\t\to(updated[1].dom).equals(root.childNodes[1])\n\t})\n\to(\"creates el at end\", function() {\n\t\tvar vnodes = [m(\"a\", {key: 1})]\n\t\tvar updated = [m(\"a\", {key: 1}), m(\"b\", {key: 2})]\n\n\t\trender(root, vnodes)\n\t\trender(root, updated)\n\n\t\to(root.childNodes.length).equals(2)\n\t\to(updated[0].dom.nodeName).equals(\"A\")\n\t\to(updated[0].dom).equals(root.childNodes[0])\n\t\to(updated[1].dom.nodeName).equals(\"B\")\n\t\to(updated[1].dom).equals(root.childNodes[1])\n\t})\n\to(\"creates el in middle\", function() {\n\t\tvar vnodes = [m(\"a\", {key: 1}), m(\"b\", {key: 2})]\n\t\tvar updated = [m(\"a\", {key: 1}), m(\"i\", {key: 3}), m(\"b\", {key: 2})]\n\n\t\trender(root, vnodes)\n\t\trender(root, updated)\n\n\t\to(updated[0].dom.nodeName).equals(\"A\")\n\t\to(updated[0].dom).equals(root.childNodes[0])\n\t\to(updated[1].dom.nodeName).equals(\"I\")\n\t\to(updated[1].dom).equals(root.childNodes[1])\n\t\to(updated[2].dom.nodeName).equals(\"B\")\n\t\to(updated[2].dom).equals(root.childNodes[2])\n\t})\n\to(\"creates el while reversing\", function() {\n\t\tvar vnodes = [m(\"a\", {key: 1}), m(\"b\", {key: 2})]\n\t\tvar updated = [m(\"b\", {key: 2}), m(\"i\", {key: 3}), m(\"a\", {key: 1})]\n\n\t\trender(root, vnodes)\n\t\trender(root, updated)\n\n\t\to(root.childNodes.length).equals(3)\n\t\to(updated[0].dom.nodeName).equals(\"B\")\n\t\to(updated[0].dom).equals(root.childNodes[0])\n\t\to(updated[1].dom.nodeName).equals(\"I\")\n\t\to(updated[1].dom).equals(root.childNodes[1])\n\t\to(updated[2].dom.nodeName).equals(\"A\")\n\t\to(updated[2].dom).equals(root.childNodes[2])\n\t})\n\to(\"deletes el at start\", function() {\n\t\tvar vnodes = [m(\"b\", {key: 2}), m(\"a\", {key: 1})]\n\t\tvar updated = [m(\"a\", {key: 1})]\n\n\t\trender(root, vnodes)\n\t\trender(root, updated)\n\n\t\to(root.childNodes.length).equals(1)\n\t\to(updated[0].dom.nodeName).equals(\"A\")\n\t\to(updated[0].dom).equals(root.childNodes[0])\n\t})\n\to(\"deletes el at end\", function() {\n\t\tvar vnodes = [m(\"a\", {key: 1}), m(\"b\", {key: 2})]\n\t\tvar updated = [m(\"a\", {key: 1})]\n\n\t\trender(root, vnodes)\n\t\trender(root, updated)\n\n\t\to(root.childNodes.length).equals(1)\n\t\to(updated[0].dom.nodeName).equals(\"A\")\n\t\to(updated[0].dom).equals(root.childNodes[0])\n\t})\n\to(\"deletes el at middle\", function() {\n\t\tvar vnodes = [m(\"a\", {key: 1}), m(\"i\", {key: 3}), m(\"b\", {key: 2})]\n\t\tvar updated = [m(\"a\", {key: 1}), m(\"b\", {key: 2})]\n\n\t\trender(root, vnodes)\n\t\trender(root, updated)\n\n\t\to(root.childNodes.length).equals(2)\n\t\to(updated[0].dom.nodeName).equals(\"A\")\n\t\to(updated[0].dom).equals(root.childNodes[0])\n\t\to(updated[1].dom.nodeName).equals(\"B\")\n\t\to(updated[1].dom).equals(root.childNodes[1])\n\t})\n\to(\"deletes el while reversing\", function() {\n\t\tvar vnodes = [m(\"a\", {key: 1}), m(\"i\", {key: 3}), m(\"b\", {key: 2})]\n\t\tvar updated = [m(\"b\", {key: 2}), m(\"a\", {key: 1})]\n\n\t\trender(root, vnodes)\n\t\trender(root, updated)\n\n\t\to(root.childNodes.length).equals(2)\n\t\to(updated[0].dom.nodeName).equals(\"B\")\n\t\to(updated[0].dom).equals(root.childNodes[0])\n\t\to(updated[1].dom.nodeName).equals(\"A\")\n\t\to(updated[1].dom).equals(root.childNodes[1])\n\t})\n\to(\"creates, deletes, reverses els at same time\", function() {\n\t\tvar vnodes = [m(\"a\", {key: 1}), m(\"i\", {key: 3}), m(\"b\", {key: 2})]\n\t\tvar updated = [m(\"b\", {key: 2}), m(\"a\", {key: 1}), m(\"s\", {key: 4})]\n\n\t\trender(root, vnodes)\n\t\trender(root, updated)\n\n\t\to(root.childNodes.length).equals(3)\n\t\to(updated[0].dom.nodeName).equals(\"B\")\n\t\to(updated[0].dom).equals(root.childNodes[0])\n\t\to(updated[1].dom.nodeName).equals(\"A\")\n\t\to(updated[1].dom).equals(root.childNodes[1])\n\t\to(updated[2].dom.nodeName).equals(\"S\")\n\t\to(updated[2].dom).equals(root.childNodes[2])\n\t})\n\to(\"creates, deletes, reverses els at same time with '__proto__' key\", function() {\n\t\tvar vnodes = [m(\"a\", {key: \"__proto__\"}), m(\"i\", {key: 3}), m(\"b\", {key: 2})]\n\t\tvar updated = [m(\"b\", {key: 2}), m(\"a\", {key: \"__proto__\"}), m(\"s\", {key: 4})]\n\n\t\trender(root, vnodes)\n\t\trender(root, updated)\n\n\t\to(root.childNodes.length).equals(3)\n\t\to(updated[0].dom.nodeName).equals(\"B\")\n\t\to(updated[0].dom).equals(root.childNodes[0])\n\t\to(updated[1].dom.nodeName).equals(\"A\")\n\t\to(updated[1].dom).equals(root.childNodes[1])\n\t\to(updated[2].dom.nodeName).equals(\"S\")\n\t\to(updated[2].dom).equals(root.childNodes[2])\n\t})\n\to(\"adds to empty fragment followed by el\", function() {\n\t\tvar vnodes = [fragment({key: 1}), m(\"b\", {key: 2})]\n\t\tvar updated = [fragment({key: 1}, m(\"a\")), m(\"b\", {key: 2})]\n\n\t\trender(root, vnodes)\n\t\trender(root, updated)\n\n\t\to(root.childNodes.length).equals(2)\n\t\to(updated[0].children[0].dom.nodeName).equals(\"A\")\n\t\to(updated[0].children[0].dom).equals(root.childNodes[0])\n\t\to(updated[0].domSize).equals(1)\n\t\to(updated[1].dom.nodeName).equals(\"B\")\n\t\to(updated[1].dom).equals(root.childNodes[1])\n\t})\n\to(\"reverses followed by el\", function() {\n\t\tvar vnodes = [fragment({key: 1}, m(\"a\", {key: 2}), m(\"b\", {key: 3})), m(\"i\", {key: 4})]\n\t\tvar updated = [fragment({key: 1}, m(\"b\", {key: 3}), m(\"a\", {key: 2})), m(\"i\", {key: 4})]\n\n\t\trender(root, vnodes)\n\t\trender(root, updated)\n\n\t\to(root.childNodes.length).equals(3)\n\t\to(updated[0].children[0].dom.nodeName).equals(\"B\")\n\t\to(updated[0].children[0].dom).equals(root.childNodes[0])\n\t\to(updated[0].children[1].dom.nodeName).equals(\"A\")\n\t\to(updated[0].children[1].dom).equals(root.childNodes[1])\n\t\to(updated[0].domSize).equals(2)\n\t\to(updated[1].dom.nodeName).equals(\"I\")\n\t\to(updated[1].dom).equals(root.childNodes[2])\n\t})\n\to(\"updates empty fragment to html without key\", function() {\n\t\tvar vnodes = fragment()\n\t\tvar updated = trust(\"<a></a><b></b>\")\n\n\t\trender(root, vnodes)\n\t\trender(root, updated)\n\n\t\to(root.childNodes.length).equals(2)\n\t\to(updated.dom.nodeName).equals(\"A\")\n\t\to(updated.dom).equals(root.childNodes[0])\n\t\to(updated.domSize).equals(2)\n\t\to(updated.dom.nextSibling.nodeName).equals(\"B\")\n\t\to(updated.dom.nextSibling).equals(root.childNodes[1])\n\t})\n\to(\"updates empty html to fragment without key\", function() {\n\t\tvar vnodes = trust()\n\t\tvar updated = fragment(m(\"a\"), m(\"b\"))\n\n\t\trender(root, vnodes)\n\t\trender(root, updated)\n\n\t\to(root.childNodes.length).equals(2)\n\t\to(updated.dom.nodeName).equals(\"A\")\n\t\to(updated.dom).equals(root.childNodes[0])\n\t\to(updated.domSize).equals(2)\n\t\to(updated.dom.nextSibling.nodeName).equals(\"B\")\n\t\to(updated.dom.nextSibling).equals(root.childNodes[1])\n\t})\n\to(\"updates fragment to html without key\", function() {\n\t\tvar vnodes = fragment(m(\"a\"), m(\"b\"))\n\t\tvar updated = trust(\"<i></i><s></s>\")\n\n\t\trender(root, vnodes)\n\t\trender(root, updated)\n\n\t\to(root.childNodes.length).equals(2)\n\t\to(updated.dom.nodeName).equals(\"I\")\n\t\to(updated.dom).equals(root.childNodes[0])\n\t\to(updated.domSize).equals(2)\n\t\to(updated.dom.nextSibling.nodeName).equals(\"S\")\n\t\to(updated.dom.nextSibling).equals(root.childNodes[1])\n\t})\n\to(\"updates html to fragment without key\", function() {\n\t\tvar vnodes = trust(\"<a></a><b></b>\")\n\t\tvar updated = fragment(m(\"i\"), m(\"s\"))\n\n\t\trender(root, vnodes)\n\t\trender(root, updated)\n\n\t\to(root.childNodes.length).equals(2)\n\t\to(updated.dom.nodeName).equals(\"I\")\n\t\to(updated.dom).equals(root.childNodes[0])\n\t\to(updated.domSize).equals(2)\n\t\to(updated.dom.nextSibling.nodeName).equals(\"S\")\n\t\to(updated.dom.nextSibling).equals(root.childNodes[1])\n\t})\n\to(\"populates fragment followed by el keyed\", function() {\n\t\tvar vnodes = [fragment({key: 1}), m(\"i\", {key: 2})]\n\t\tvar updated = [fragment({key: 1}, m(\"a\"), m(\"b\")), m(\"i\", {key: 2})]\n\n\t\trender(root, vnodes)\n\t\trender(root, updated)\n\n\t\to(root.childNodes.length).equals(3)\n\t\to(updated[0].dom.nodeName).equals(\"A\")\n\t\to(updated[0].dom).equals(root.childNodes[0])\n\t\to(updated[0].domSize).equals(2)\n\t\to(updated[0].dom.nextSibling.nodeName).equals(\"B\")\n\t\to(updated[0].dom.nextSibling).equals(root.childNodes[1])\n\t\to(updated[1].dom.nodeName).equals(\"I\")\n\t\to(updated[1].dom).equals(root.childNodes[2])\n\t})\n\to(\"throws if fragment followed by null then el on first render keyed\", function() {\n\t\tvar vnodes = [fragment({key: 1}), null, m(\"i\", {key: 2})]\n\n\t\to(function () { render(root, vnodes) }).throws(TypeError)\n\t})\n\to(\"throws if fragment followed by null then el on next render keyed\", function() {\n\t\tvar vnodes = [fragment({key: 1}), m(\"i\", {key: 2})]\n\t\tvar updated = [fragment({key: 1}, m(\"a\"), m(\"b\")), null, m(\"i\", {key: 2})]\n\n\t\trender(root, vnodes)\n\t\to(function () { render(root, updated) }).throws(TypeError)\n\t})\n\to(\"populates childless fragment replaced followed by el keyed\", function() {\n\t\tvar vnodes = [fragment({key: 1}), m(\"i\", {key: 2})]\n\t\tvar updated = [fragment({key: 1}, m(\"a\"), m(\"b\")), m(\"i\", {key: 2})]\n\n\t\trender(root, vnodes)\n\t\trender(root, updated)\n\n\t\to(root.childNodes.length).equals(3)\n\t\to(updated[0].dom.nodeName).equals(\"A\")\n\t\to(updated[0].dom).equals(root.childNodes[0])\n\t\to(updated[0].domSize).equals(2)\n\t\to(updated[0].dom.nextSibling.nodeName).equals(\"B\")\n\t\to(updated[0].dom.nextSibling).equals(root.childNodes[1])\n\t\to(updated[1].dom.nodeName).equals(\"I\")\n\t\to(updated[1].dom).equals(root.childNodes[2])\n\t})\n\to(\"throws if childless fragment replaced followed by null then el keyed\", function() {\n\t\tvar vnodes = [fragment({key: 1}), m(\"i\", {key: 2})]\n\t\tvar updated = [fragment({key: 1}, m(\"a\"), m(\"b\")), null, m(\"i\", {key: 2})]\n\n\t\trender(root, vnodes)\n\t\to(function () { render(root, updated) }).throws(TypeError)\n\t})\n\to(\"moves from end to start\", function() {\n\t\tvar vnodes = [m(\"a\", {key: 1}), m(\"b\", {key: 2}), m(\"i\", {key: 3}), m(\"s\", {key: 4})]\n\t\tvar updated = [m(\"s\", {key: 4}), m(\"a\", {key: 1}), m(\"b\", {key: 2}), m(\"i\", {key: 3})]\n\n\t\trender(root, vnodes)\n\t\trender(root, updated)\n\n\t\to(root.childNodes.length).equals(4)\n\t\to(updated[0].dom.nodeName).equals(\"S\")\n\t\to(updated[0].dom).equals(root.childNodes[0])\n\t\to(updated[1].dom.nodeName).equals(\"A\")\n\t\to(updated[1].dom).equals(root.childNodes[1])\n\t\to(updated[2].dom.nodeName).equals(\"B\")\n\t\to(updated[2].dom).equals(root.childNodes[2])\n\t\to(updated[3].dom.nodeName).equals(\"I\")\n\t\to(updated[3].dom).equals(root.childNodes[3])\n\t})\n\to(\"moves from start to end\", function() {\n\t\tvar vnodes = [m(\"a\", {key: 1}), m(\"b\", {key: 2}), m(\"i\", {key: 3}), m(\"s\", {key: 4})]\n\t\tvar updated = [m(\"b\", {key: 2}), m(\"i\", {key: 3}), m(\"s\", {key: 4}), m(\"a\", {key: 1})]\n\n\t\trender(root, vnodes)\n\t\trender(root, updated)\n\n\t\to(root.childNodes.length).equals(4)\n\t\to(updated[0].dom.nodeName).equals(\"B\")\n\t\to(updated[0].dom).equals(root.childNodes[0])\n\t\to(updated[1].dom.nodeName).equals(\"I\")\n\t\to(updated[1].dom).equals(root.childNodes[1])\n\t\to(updated[2].dom.nodeName).equals(\"S\")\n\t\to(updated[2].dom).equals(root.childNodes[2])\n\t\to(updated[3].dom.nodeName).equals(\"A\")\n\t\to(updated[3].dom).equals(root.childNodes[3])\n\t})\n\to(\"removes then recreate\", function() {\n\t\tvar vnodes = [m(\"a\", {key: 1}), m(\"b\", {key: 2}), m(\"i\", {key: 3}), m(\"s\", {key: 4})]\n\t\tvar temp = []\n\t\tvar updated = [m(\"a\", {key: 1}), m(\"b\", {key: 2}), m(\"i\", {key: 3}), m(\"s\", {key: 4})]\n\n\t\trender(root, vnodes)\n\t\trender(root, temp)\n\t\trender(root, updated)\n\n\t\to(root.childNodes.length).equals(4)\n\t\to(updated[0].dom.nodeName).equals(\"A\")\n\t\to(updated[0].dom).equals(root.childNodes[0])\n\t\to(updated[1].dom.nodeName).equals(\"B\")\n\t\to(updated[1].dom).equals(root.childNodes[1])\n\t\to(updated[2].dom.nodeName).equals(\"I\")\n\t\to(updated[2].dom).equals(root.childNodes[2])\n\t\to(updated[3].dom.nodeName).equals(\"S\")\n\t\to(updated[3].dom).equals(root.childNodes[3])\n\t})\n\to(\"removes then recreate reversed\", function() {\n\t\tvar vnodes = [m(\"a\", {key: 1}), m(\"b\", {key: 2}), m(\"i\", {key: 3}), m(\"s\", {key: 4})]\n\t\tvar temp = []\n\t\tvar updated = [m(\"s\", {key: 4}), m(\"i\", {key: 3}), m(\"b\", {key: 2}), m(\"a\", {key: 1})]\n\n\t\trender(root, vnodes)\n\t\trender(root, temp)\n\t\trender(root, updated)\n\n\t\to(root.childNodes.length).equals(4)\n\t\to(updated[0].dom.nodeName).equals(\"S\")\n\t\to(updated[0].dom).equals(root.childNodes[0])\n\t\to(updated[1].dom.nodeName).equals(\"I\")\n\t\to(updated[1].dom).equals(root.childNodes[1])\n\t\to(updated[2].dom.nodeName).equals(\"B\")\n\t\to(updated[2].dom).equals(root.childNodes[2])\n\t\to(updated[3].dom.nodeName).equals(\"A\")\n\t\to(updated[3].dom).equals(root.childNodes[3])\n\t})\n\to(\"removes then recreate smaller\", function() {\n\t\tvar vnodes = [m(\"a\", {key: 1}), m(\"b\", {key: 2})]\n\t\tvar temp = []\n\t\tvar updated = [m(\"a\", {key: 1})]\n\n\t\trender(root, vnodes)\n\t\trender(root, temp)\n\t\trender(root, updated)\n\n\t\to(root.childNodes.length).equals(1)\n\t\to(updated[0].dom.nodeName).equals(\"A\")\n\t\to(updated[0].dom).equals(root.childNodes[0])\n\t})\n\to(\"removes then recreate bigger\", function() {\n\t\tvar vnodes = [m(\"a\", {key: 1}), m(\"b\", {key: 2})]\n\t\tvar temp = []\n\t\tvar updated = [m(\"a\", {key: 1}), m(\"b\", {key: 2}), m(\"i\", {key: 3})]\n\n\t\trender(root, vnodes)\n\t\trender(root, temp)\n\t\trender(root, updated)\n\n\t\to(root.childNodes.length).equals(3)\n\t\to(updated[0].dom.nodeName).equals(\"A\")\n\t\to(updated[0].dom).equals(root.childNodes[0])\n\t\to(updated[1].dom.nodeName).equals(\"B\")\n\t\to(updated[1].dom).equals(root.childNodes[1])\n\t\to(updated[2].dom.nodeName).equals(\"I\")\n\t\to(updated[2].dom).equals(root.childNodes[2])\n\t})\n\to(\"removes then create different\", function() {\n\t\tvar vnodes = [m(\"a\", {key: 1}), m(\"b\", {key: 2})]\n\t\tvar temp = []\n\t\tvar updated = [m(\"i\", {key: 3}), m(\"s\", {key: 4})]\n\n\t\trender(root, vnodes)\n\t\trender(root, temp)\n\t\trender(root, updated)\n\n\t\to(root.childNodes.length).equals(2)\n\t\to(updated[0].dom.nodeName).equals(\"I\")\n\t\to(updated[0].dom).equals(root.childNodes[0])\n\t\to(updated[1].dom.nodeName).equals(\"S\")\n\t\to(updated[1].dom).equals(root.childNodes[1])\n\t})\n\to(\"removes then create different smaller\", function() {\n\t\tvar vnodes = [m(\"a\", {key: 1}), m(\"b\", {key: 2})]\n\t\tvar temp = []\n\t\tvar updated = [m(\"i\", {key: 3})]\n\n\t\trender(root, vnodes)\n\t\trender(root, temp)\n\t\trender(root, updated)\n\n\t\to(root.childNodes.length).equals(1)\n\t\to(updated[0].dom.nodeName).equals(\"I\")\n\t\to(updated[0].dom).equals(root.childNodes[0])\n\t})\n\to(\"cached keyed nodes move when the list is reversed\", function(){\n\t\tvar a = m(\"a\", {key: \"a\"})\n\t\tvar b = m(\"b\", {key: \"b\"})\n\t\tvar c = m(\"c\", {key: \"c\"})\n\t\tvar d = m(\"d\", {key: \"d\"})\n\n\t\trender(root, [a, b, c, d])\n\t\trender(root, [d, c, b, a])\n\n\t\to(root.childNodes.length).equals(4)\n\t\to(root.childNodes[0].nodeName).equals(\"D\")\n\t\to(root.childNodes[1].nodeName).equals(\"C\")\n\t\to(root.childNodes[2].nodeName).equals(\"B\")\n\t\to(root.childNodes[3].nodeName).equals(\"A\")\n\t})\n\to(\"cached keyed nodes move when diffed via the map\", function() {\n\t\tvar onupdate = o.spy()\n\t\tvar a = m(\"a\", {key: \"a\", onupdate: onupdate})\n\t\tvar b = m(\"b\", {key: \"b\", onupdate: onupdate})\n\t\tvar c = m(\"c\", {key: \"c\", onupdate: onupdate})\n\t\tvar d = m(\"d\", {key: \"d\", onupdate: onupdate})\n\n\t\trender(root, [a, b, c, d])\n\t\trender(root, [b, d, a, c])\n\n\t\to(root.childNodes.length).equals(4)\n\t\to(root.childNodes[0].nodeName).equals(\"B\")\n\t\to(root.childNodes[1].nodeName).equals(\"D\")\n\t\to(root.childNodes[2].nodeName).equals(\"A\")\n\t\to(root.childNodes[3].nodeName).equals(\"C\")\n\t\to(onupdate.callCount).equals(0)\n\t})\n\to(\"removes then create different bigger\", function() {\n\t\tvar vnodes = [m(\"a\", {key: 1}), m(\"b\", {key: 2})]\n\t\tvar temp = []\n\t\tvar updated = [m(\"i\", {key: 3}), m(\"s\", {key: 4}), m(\"div\", {key: 5})]\n\n\t\trender(root, vnodes)\n\t\trender(root, temp)\n\t\trender(root, updated)\n\n\t\to(root.childNodes.length).equals(3)\n\t\to(updated[0].dom.nodeName).equals(\"I\")\n\t\to(updated[0].dom).equals(root.childNodes[0])\n\t\to(updated[1].dom.nodeName).equals(\"S\")\n\t\to(updated[1].dom).equals(root.childNodes[1])\n\t\to(updated[2].dom.nodeName).equals(\"DIV\")\n\t\to(updated[2].dom).equals(root.childNodes[2])\n\t})\n\to(\"removes then create mixed\", function() {\n\t\tvar vnodes = [m(\"a\", {key: 1}), m(\"b\", {key: 2})]\n\t\tvar temp = []\n\t\tvar updated = [m(\"a\", {key: 1}), m(\"s\", {key: 4})]\n\n\t\trender(root, vnodes)\n\t\trender(root, temp)\n\t\trender(root, updated)\n\n\t\to(root.childNodes.length).equals(2)\n\t\to(updated[0].dom.nodeName).equals(\"A\")\n\t\to(updated[0].dom).equals(root.childNodes[0])\n\t\to(updated[1].dom.nodeName).equals(\"S\")\n\t\to(updated[1].dom).equals(root.childNodes[1])\n\t})\n\to(\"removes then create mixed reversed\", function() {\n\t\tvar vnodes = [m(\"a\", {key: 1}), m(\"b\", {key: 2})]\n\t\tvar temp = []\n\t\tvar updated = [m(\"s\", {key: 4}), m(\"a\", {key: 1})]\n\n\t\trender(root, vnodes)\n\t\trender(root, temp)\n\t\trender(root, updated)\n\n\t\to(root.childNodes.length).equals(2)\n\t\to(updated[0].dom.nodeName).equals(\"S\")\n\t\to(updated[0].dom).equals(root.childNodes[0])\n\t\to(updated[1].dom.nodeName).equals(\"A\")\n\t\to(updated[1].dom).equals(root.childNodes[1])\n\t})\n\to(\"removes then create mixed smaller\", function() {\n\t\tvar vnodes = [m(\"a\", {key: 1}), m(\"b\", {key: 2}), m(\"i\", {key: 3})]\n\t\tvar temp = []\n\t\tvar updated = [m(\"a\", {key: 1}), m(\"s\", {key: 4})]\n\n\t\trender(root, vnodes)\n\t\trender(root, temp)\n\t\trender(root, updated)\n\n\t\to(root.childNodes.length).equals(2)\n\t\to(updated[0].dom.nodeName).equals(\"A\")\n\t\to(updated[0].dom).equals(root.childNodes[0])\n\t\to(updated[1].dom.nodeName).equals(\"S\")\n\t\to(updated[1].dom).equals(root.childNodes[1])\n\t})\n\to(\"removes then create mixed smaller reversed\", function() {\n\t\tvar vnodes = [m(\"a\", {key: 1}), m(\"b\", {key: 2}), m(\"i\", {key: 3})]\n\t\tvar temp = []\n\t\tvar updated = [m(\"s\", {key: 4}), m(\"a\", {key: 1})]\n\n\t\trender(root, vnodes)\n\t\trender(root, temp)\n\t\trender(root, updated)\n\n\t\to(root.childNodes.length).equals(2)\n\t\to(updated[0].dom.nodeName).equals(\"S\")\n\t\to(updated[0].dom).equals(root.childNodes[0])\n\t\to(updated[1].dom.nodeName).equals(\"A\")\n\t\to(updated[1].dom).equals(root.childNodes[1])\n\t})\n\to(\"removes then create mixed bigger\", function() {\n\t\tvar vnodes = [m(\"a\", {key: 1}), m(\"b\", {key: 2})]\n\t\tvar temp = []\n\t\tvar updated = [m(\"a\", {key: 1}), m(\"i\", {key: 3}), m(\"s\", {key: 4})]\n\n\t\trender(root, vnodes)\n\t\trender(root, temp)\n\t\trender(root, updated)\n\n\t\to(root.childNodes.length).equals(3)\n\t\to(updated[0].dom.nodeName).equals(\"A\")\n\t\to(updated[0].dom).equals(root.childNodes[0])\n\t\to(updated[1].dom.nodeName).equals(\"I\")\n\t\to(updated[1].dom).equals(root.childNodes[1])\n\t\to(updated[2].dom.nodeName).equals(\"S\")\n\t\to(updated[2].dom).equals(root.childNodes[2])\n\t})\n\to(\"removes then create mixed bigger reversed\", function() {\n\t\tvar vnodes = [m(\"a\", {key: 1}), m(\"b\", {key: 2})]\n\t\tvar temp = []\n\t\tvar updated = [m(\"s\", {key: 4}), m(\"i\", {key: 3}), m(\"a\", {key: 1})]\n\n\t\trender(root, vnodes)\n\t\trender(root, temp)\n\t\trender(root, updated)\n\n\t\to(root.childNodes.length).equals(3)\n\t\to(updated[0].dom.nodeName).equals(\"S\")\n\t\to(updated[0].dom).equals(root.childNodes[0])\n\t\to(updated[1].dom.nodeName).equals(\"I\")\n\t\to(updated[1].dom).equals(root.childNodes[1])\n\t\to(updated[2].dom.nodeName).equals(\"A\")\n\t\to(updated[2].dom).equals(root.childNodes[2])\n\t})\n\to(\"change type, position and length\", function() {\n\t\tvar vnodes = m(\"div\",\n\t\t\tundefined,\n\t\t\tm(\"#\", \"a\")\n\t\t)\n\t\tvar updated = m(\"div\",\n\t\t\tfragment(m(\"#\", \"b\")),\n\t\t\tundefined,\n\t\t\tundefined\n\t\t)\n\n\t\trender(root, vnodes)\n\t\trender(root, updated)\n\n\t\to(root.firstChild.childNodes.length).equals(1)\n\t})\n\to(\"removes then recreates then reverses children\", function() {\n\t\tvar vnodes = [m(\"a\", {key: 1}, m(\"i\", {key: 3}), m(\"s\", {key: 4})), m(\"b\", {key: 2})]\n\t\tvar temp1 = []\n\t\tvar temp2 = [m(\"a\", {key: 1}, m(\"i\", {key: 3}), m(\"s\", {key: 4})), m(\"b\", {key: 2})]\n\t\tvar updated = [m(\"a\", {key: 1}, m(\"s\", {key: 4}), m(\"i\", {key: 3})), m(\"b\", {key: 2})]\n\n\t\trender(root, vnodes)\n\t\trender(root, temp1)\n\t\trender(root, temp2)\n\t\trender(root, updated)\n\n\t\to(root.childNodes.length).equals(2)\n\t\to(updated[0].dom.nodeName).equals(\"A\")\n\t\to(updated[0].dom).equals(root.childNodes[0])\n\t\to(updated[1].dom.nodeName).equals(\"B\")\n\t\to(updated[1].dom).equals(root.childNodes[1])\n\t\to(updated[0].dom.childNodes.length).equals(2)\n\t\to(updated[0].dom.childNodes[0].nodeName).equals(\"S\")\n\t\to(updated[0].dom.childNodes[1].nodeName).equals(\"I\")\n\t})\n\to(\"removes then recreates nested\", function() {\n\t\tvar vnodes = [m(\"a\", {key: 1}, m(\"a\", {key: 3}, m(\"a\", {key: 5})), m(\"a\", {key: 4}, m(\"a\", {key: 5}))), m(\"a\", {key: 2})]\n\t\tvar temp = []\n\t\tvar updated = [m(\"a\", {key: 1}, m(\"a\", {key: 3}, m(\"a\", {key: 5})), m(\"a\", {key: 4}, m(\"a\", {key: 5}))), m(\"a\", {key: 2})]\n\n\t\trender(root, vnodes)\n\t\trender(root, temp)\n\t\trender(root, updated)\n\n\t\to(root.childNodes.length).equals(2)\n\t\to(root.childNodes[0].childNodes.length).equals(2)\n\t\to(root.childNodes[0].childNodes[0].childNodes.length).equals(1)\n\t\to(root.childNodes[0].childNodes[1].childNodes.length).equals(1)\n\t\to(root.childNodes[1].childNodes.length).equals(0)\n\t})\n\to(\"doesn't recycle\", function() {\n\t\tvar vnodes = [m(\"div\", {key: 1})]\n\t\tvar temp = []\n\t\tvar updated = [m(\"div\", {key: 1})]\n\n\t\trender(root, vnodes)\n\t\trender(root, temp)\n\t\trender(root, updated)\n\n\t\to(vnodes[0].dom).notEquals(updated[0].dom) // this used to be a recycling pool test\n\t\to(updated[0].dom.nodeName).equals(\"DIV\")\n\t})\n\to(\"doesn't recycle when not keyed\", function() {\n\t\tvar vnodes = [m(\"div\")]\n\t\tvar temp = []\n\t\tvar updated = [m(\"div\")]\n\n\t\trender(root, vnodes)\n\t\trender(root, temp)\n\t\trender(root, updated)\n\n\t\to(root.childNodes.length).equals(1)\n\t\to(vnodes[0].dom).notEquals(updated[0].dom) // this used to be a recycling pool test\n\t\to(updated[0].dom.nodeName).equals(\"DIV\")\n\t})\n\to(\"doesn't recycle deep\", function() {\n\t\tvar vnodes = [m(\"div\", m(\"a\", {key: 1}))]\n\t\tvar temp = [m(\"div\")]\n\t\tvar updated = [m(\"div\", m(\"a\", {key: 1}))]\n\n\t\trender(root, vnodes)\n\n\t\tvar oldChild = vnodes[0].dom.firstChild\n\n\t\trender(root, temp)\n\t\trender(root, updated)\n\n\t\to(oldChild).notEquals(updated[0].dom.firstChild) // this used to be a recycling pool test\n\t\to(updated[0].dom.firstChild.nodeName).equals(\"A\")\n\t})\n\to(\"mixed unkeyed tags are not broken by recycle\", function() {\n\t\tvar vnodes = [m(\"a\"), m(\"b\")]\n\t\tvar temp = [m(\"b\")]\n\t\tvar updated = [m(\"a\"), m(\"b\")]\n\n\t\trender(root, vnodes)\n\t\trender(root, temp)\n\t\trender(root, updated)\n\n\t\to(root.childNodes.length).equals(2)\n\t\to(root.childNodes[0].nodeName).equals(\"A\")\n\t\to(root.childNodes[1].nodeName).equals(\"B\")\n\t})\n\to(\"mixed unkeyed vnode types are not broken by recycle\", function() {\n\t\tvar vnodes = [fragment(m(\"a\")), m(\"b\")]\n\t\tvar temp = [m(\"b\")]\n\t\tvar updated = [fragment(m(\"a\")), m(\"b\")]\n\n\t\trender(root, vnodes)\n\t\trender(root, temp)\n\t\trender(root, updated)\n\n\t\to(root.childNodes.length).equals(2)\n\t\to(root.childNodes[0].nodeName).equals(\"A\")\n\t\to(root.childNodes[1].nodeName).equals(\"B\")\n\t})\n\to(\"onremove doesn't fire from nodes in the pool (#1990)\", function () {\n\t\tvar onremove = o.spy()\n\t\trender(root, [\n\t\t\tm(\"div\", m(\"div\", {onremove: onremove})),\n\t\t\tm(\"div\", m(\"div\", {onremove: onremove}))\n\t\t])\n\t\trender(root, [\n\t\t\tm(\"div\", m(\"div\", {onremove: onremove}))\n\t\t])\n\t\trender(root,[])\n\n\t\to(onremove.callCount).equals(2)\n\t})\n\to(\"cached, non-keyed nodes skip diff\", function () {\n\t\tvar onupdate = o.spy();\n\t\tvar cached = m(\"a\", {onupdate: onupdate})\n\n\t\trender(root, cached)\n\t\trender(root, cached)\n\n\t\to(onupdate.callCount).equals(0)\n\t})\n\to(\"cached, keyed nodes skip diff\", function () {\n\t\tvar onupdate = o.spy()\n\t\tvar cached = m(\"a\", {key: \"a\", onupdate: onupdate})\n\n\t\trender(root, cached)\n\t\trender(root, cached)\n\n\t\to(onupdate.callCount).equals(0)\n\t})\n\to(\"keyed cached elements are re-initialized when brought back from the pool (#2003)\", function () {\n\t\tvar onupdate = o.spy()\n\t\tvar oncreate = o.spy()\n\t\tvar cached = m(\"B\", {key: 1},\n\t\t\tm(\"A\", {oncreate: oncreate, onupdate: onupdate}, \"A\")\n\t\t)\n\t\trender(root, m(\"div\", cached))\n\t\trender(root, [])\n\t\trender(root, m(\"div\", cached))\n\n\t\to(oncreate.callCount).equals(2)\n\t\to(onupdate.callCount).equals(0)\n\t})\n\n\to(\"unkeyed cached elements are re-initialized when brought back from the pool (#2003)\", function () {\n\t\tvar onupdate = o.spy()\n\t\tvar oncreate = o.spy()\n\t\tvar cached = m(\"B\",\n\t\t\tm(\"A\", {oncreate: oncreate, onupdate: onupdate}, \"A\")\n\t\t)\n\t\trender(root, m(\"div\", cached))\n\t\trender(root, [])\n\t\trender(root, m(\"div\", cached))\n\n\t\to(oncreate.callCount).equals(2)\n\t\to(onupdate.callCount).equals(0)\n\t})\n\n\to(\"keyed cached elements are re-initialized when brought back from nested pools (#2003)\", function () {\n\t\tvar onupdate = o.spy()\n\t\tvar oncreate = o.spy()\n\t\tvar cached = m(\"B\", {key: 1},\n\t\t\tm(\"A\", {oncreate: oncreate, onupdate: onupdate}, \"A\")\n\t\t)\n\t\trender(root, m(\"div\", cached))\n\t\trender(root, m(\"div\"))\n\t\trender(root, [])\n\t\trender(root, m(\"div\", cached))\n\n\t\to(oncreate.callCount).equals(2)\n\t\to(onupdate.callCount).equals(0)\n\t})\n\n\to(\"unkeyed cached elements are re-initialized when brought back from nested pools (#2003)\", function () {\n\t\tvar onupdate = o.spy()\n\t\tvar oncreate = o.spy()\n\t\tvar cached = m(\"B\",\n\t\t\tm(\"A\", {oncreate: oncreate, onupdate: onupdate}, \"A\")\n\t\t)\n\t\trender(root, m(\"div\", cached))\n\t\trender(root, m(\"div\"))\n\t\trender(root, [])\n\t\trender(root, m(\"div\", cached))\n\n\t\to(oncreate.callCount).equals(2)\n\t\to(onupdate.callCount).equals(0)\n\t})\n\n\to(\"null stays in place\", function() {\n\t\tvar create = o.spy()\n\t\tvar update = o.spy()\n\t\tvar remove = o.spy()\n\t\tvar vnodes = [m(\"div\"), m(\"a\", {oncreate: create, onupdate: update, onremove: remove})]\n\t\tvar temp = [null, m(\"a\", {oncreate: create, onupdate: update, onremove: remove})]\n\t\tvar updated = [m(\"div\"), m(\"a\", {oncreate: create, onupdate: update, onremove: remove})]\n\n\t\trender(root, vnodes)\n\t\tvar before = vnodes[1].dom\n\t\trender(root, temp)\n\t\trender(root, updated)\n\t\tvar after = updated[1].dom\n\n\t\to(before).equals(after)\n\t\to(create.callCount).equals(1)\n\t\to(update.callCount).equals(2)\n\t\to(remove.callCount).equals(0)\n\t})\n\to(\"null stays in place if not first\", function() {\n\t\tvar create = o.spy()\n\t\tvar update = o.spy()\n\t\tvar remove = o.spy()\n\t\tvar vnodes = [m(\"b\"), m(\"div\"), m(\"a\", {oncreate: create, onupdate: update, onremove: remove})]\n\t\tvar temp = [m(\"b\"), null, m(\"a\", {oncreate: create, onupdate: update, onremove: remove})]\n\t\tvar updated = [m(\"b\"), m(\"div\"), m(\"a\", {oncreate: create, onupdate: update, onremove: remove})]\n\n\t\trender(root, vnodes)\n\t\tvar before = vnodes[2].dom\n\t\trender(root, temp)\n\t\trender(root, updated)\n\t\tvar after = updated[2].dom\n\n\t\to(before).equals(after)\n\t\to(create.callCount).equals(1)\n\t\to(update.callCount).equals(2)\n\t\to(remove.callCount).equals(0)\n\t})\n\to(\"node is recreated if key changes to undefined\", function () {\n\t\tvar vnode = m(\"b\", {key: 1})\n\t\tvar updated = m(\"b\")\n\n\t\trender(root, vnode)\n\t\trender(root, updated)\n\n\t\to(vnode.dom).notEquals(updated.dom)\n\t})\n\to(\"don't add back elements from fragments that are restored from the pool #1991\", function() {\n\t\trender(root, [\n\t\t\tfragment(),\n\t\t\tfragment()\n\t\t])\n\t\trender(root, [\n\t\t\tfragment(),\n\t\t\tfragment(\n\t\t\t\tm(\"div\")\n\t\t\t)\n\t\t])\n\t\trender(root, [\n\t\t\tfragment(null)\n\t\t])\n\t\trender(root, [\n\t\t\tfragment(),\n\t\t\tfragment()\n\t\t])\n\n\t\to(root.childNodes.length).equals(0)\n\t})\n\to(\"don't add back elements from fragments that are being removed #1991\", function() {\n\t\trender(root, [\n\t\t\tfragment(),\n\t\t\tm(\"p\"),\n\t\t])\n\t\trender(root, [\n\t\t\tfragment(\n\t\t\t\tm(\"div\", 5)\n\t\t\t)\n\t\t])\n\t\trender(root, [\n\t\t\tfragment(),\n\t\t\tfragment()\n\t\t])\n\n\t\to(root.childNodes.length).equals(0)\n\t})\n\to(\"handles null values in unkeyed lists of different length (#2003)\", function() {\n\t\tvar oncreate = o.spy()\n\t\tvar onremove = o.spy()\n\t\tvar onupdate = o.spy()\n\n\t\trender(root, [m(\"div\", {oncreate: oncreate, onremove: onremove, onupdate: onupdate}), null])\n\t\trender(root, [null, m(\"div\", {oncreate: oncreate, onremove: onremove, onupdate: onupdate}), null])\n\n\t\to(oncreate.callCount).equals(2)\n\t\to(onremove.callCount).equals(1)\n\t\to(onupdate.callCount).equals(0)\n\t})\n\to(\"supports changing the element of a keyed element in a list when traversed bottom-up\", function() {\n\t\ttry {\n\t\t\trender(root, [m(\"a\", {key: 2})])\n\t\t\trender(root, [m(\"b\", {key: 1}), m(\"b\", {key: 2})])\n\n\t\t\to(root.childNodes.length).equals(2)\n\t\t\to(root.childNodes[0].nodeName).equals(\"B\")\n\t\t\to(root.childNodes[1].nodeName).equals(\"B\")\n\t\t} catch (e) {\n\t\t\to(e).equals(null)\n\t\t}\n\t})\n\to(\"supports changing the element of a keyed element in a list when looking up nodes using the map\", function() {\n\t\ttry {\n\t\t\trender(root, [m(\"x\", {key: 1}), m(\"y\", {key: 2}), m(\"z\", {key: 3})])\n\t\t\trender(root, [m(\"b\", {key: 2}), m(\"c\", {key: 1}), m(\"d\", {key: 4}), m(\"e\", {key: 3})])\n\n\t\t\to(root.childNodes.length).equals(4)\n\t\t\to(root.childNodes[0].nodeName).equals(\"B\")\n\t\t\to(root.childNodes[1].nodeName).equals(\"C\")\n\t\t\to(root.childNodes[2].nodeName).equals(\"D\")\n\t\t\to(root.childNodes[3].nodeName).equals(\"E\")\n\t\t} catch (e) {\n\t\t\to(e).equals(null)\n\t\t}\n\t})\n\to(\"don't fetch the nextSibling from the pool\", function() {\n\t\trender(root, [fragment(m(\"div\", {key: 1}), m(\"div\", {key: 2})), m(\"p\")])\n\t\trender(root, [fragment(), m(\"p\")])\n\t\trender(root, [fragment(m(\"div\", {key: 2}), m(\"div\", {key: 1})), m(\"p\")])\n\n\t\to([].map.call(root.childNodes, function(el) {return el.nodeName})).deepEquals([\"DIV\", \"DIV\", \"P\"])\n\t})\n\to(\"minimizes DOM operations when scrambling a keyed lists\", function() {\n\t\tvar vnodes = vnodify(\"a,b,c,d\")\n\t\tvar updated = vnodify(\"b,a,d,c\")\n\t\tvar expectedTagNames = updated.map(function(vn) {return vn.tag})\n\n\t\trender(root, vnodes)\n\n\t\troot.appendChild = o.spy(root.appendChild)\n\t\troot.insertBefore = o.spy(root.insertBefore)\n\n\t\trender(root, updated)\n\n\t\tvar tagNames = [].map.call(root.childNodes, function(n) {return n.nodeName.toLowerCase()})\n\n\t\to(root.appendChild.callCount + root.insertBefore.callCount).equals(2)\n\t\to(tagNames).deepEquals(expectedTagNames)\n\t})\n\to(\"minimizes DOM operations when reversing a keyed lists with an odd number of items\", function() {\n\t\tvar vnodes = vnodify(\"a,b,c,d\")\n\t\tvar updated = vnodify(\"d,c,b,a\")\n\t\tvar expectedTagNames = updated.map(function(vn) {return vn.tag})\n\n\t\trender(root, vnodes)\n\n\t\troot.appendChild = o.spy(root.appendChild)\n\t\troot.insertBefore = o.spy(root.insertBefore)\n\n\t\trender(root, updated)\n\n\t\tvar tagNames = [].map.call(root.childNodes, function(n) {return n.nodeName.toLowerCase()})\n\n\t\to(root.appendChild.callCount + root.insertBefore.callCount).equals(3)\n\t\to(tagNames).deepEquals(expectedTagNames)\n\t})\n\to(\"minimizes DOM operations when reversing a keyed lists with an even number of items\", function() {\n\t\tvar vnodes = vnodify(\"a,b,c\")\n\t\tvar updated = vnodify(\"c,b,a\")\n\t\tvar vnodes = [m(\"a\", {key: \"a\"}), m(\"b\", {key: \"b\"}), m(\"c\", {key: \"c\"})]\n\t\tvar updated = [m(\"c\", {key: \"c\"}), m(\"b\", {key: \"b\"}), m(\"a\", {key: \"a\"})]\n\t\tvar expectedTagNames = updated.map(function(vn) {return vn.tag})\n\n\t\trender(root, vnodes)\n\n\t\troot.appendChild = o.spy(root.appendChild)\n\t\troot.insertBefore = o.spy(root.insertBefore)\n\n\t\trender(root, updated)\n\n\t\tvar tagNames = [].map.call(root.childNodes, function(n) {return n.nodeName.toLowerCase()})\n\n\t\to(root.appendChild.callCount + root.insertBefore.callCount).equals(2)\n\t\to(tagNames).deepEquals(expectedTagNames)\n\t})\n\to(\"minimizes DOM operations when scrambling a keyed lists with prefixes and suffixes\", function() {\n\t\tvar vnodes = vnodify(\"i,a,b,c,d,j\")\n\t\tvar updated = vnodify(\"i,b,a,d,c,j\")\n\t\tvar expectedTagNames = updated.map(function(vn) {return vn.tag})\n\n\t\trender(root, vnodes)\n\n\t\troot.appendChild = o.spy(root.appendChild)\n\t\troot.insertBefore = o.spy(root.insertBefore)\n\n\t\trender(root, updated)\n\n\t\tvar tagNames = [].map.call(root.childNodes, function(n) {return n.nodeName.toLowerCase()})\n\n\t\to(root.appendChild.callCount + root.insertBefore.callCount).equals(2)\n\t\to(tagNames).deepEquals(expectedTagNames)\n\t})\n\to(\"minimizes DOM operations when reversing a keyed lists with an odd number of items with prefixes and suffixes\", function() {\n\t\tvar vnodes = vnodify(\"i,a,b,c,d,j\")\n\t\tvar updated = vnodify(\"i,d,c,b,a,j\")\n\t\tvar expectedTagNames = updated.map(function(vn) {return vn.tag})\n\n\t\trender(root, vnodes)\n\n\t\troot.appendChild = o.spy(root.appendChild)\n\t\troot.insertBefore = o.spy(root.insertBefore)\n\n\t\trender(root, updated)\n\n\t\tvar tagNames = [].map.call(root.childNodes, function(n) {return n.nodeName.toLowerCase()})\n\n\t\to(root.appendChild.callCount + root.insertBefore.callCount).equals(3)\n\t\to(tagNames).deepEquals(expectedTagNames)\n\t})\n\to(\"minimizes DOM operations when reversing a keyed lists with an even number of items with prefixes and suffixes\", function() {\n\t\tvar vnodes = vnodify(\"i,a,b,c,j\")\n\t\tvar updated = vnodify(\"i,c,b,a,j\")\n\t\tvar expectedTagNames = updated.map(function(vn) {return vn.tag})\n\n\t\trender(root, vnodes)\n\n\t\troot.appendChild = o.spy(root.appendChild)\n\t\troot.insertBefore = o.spy(root.insertBefore)\n\n\t\trender(root, updated)\n\n\t\tvar tagNames = [].map.call(root.childNodes, function(n) {return n.nodeName.toLowerCase()})\n\n\t\to(root.appendChild.callCount + root.insertBefore.callCount).equals(2)\n\t\to(tagNames).deepEquals(expectedTagNames)\n\t})\n\to(\"scrambling sample 1\", function() {\n\t\tvar vnodes = vnodify(\"k0,k1,k2,k3,k4,k5,k6,k7,k8,k9\")\n\t\tvar updated = vnodify(\"k4,k1,k2,k9,k0,k3,k6,k5,k8,k7\")\n\t\tvar expectedTagNames = updated.map(function(vn) {return vn.tag})\n\n\t\trender(root, vnodes)\n\n\t\troot.appendChild = o.spy(root.appendChild)\n\t\troot.insertBefore = o.spy(root.insertBefore)\n\n\t\trender(root, updated)\n\n\t\tvar tagNames = [].map.call(root.childNodes, function(n) {return n.nodeName.toLowerCase()})\n\n\t\to(root.appendChild.callCount + root.insertBefore.callCount).equals(5)\n\t\to(tagNames).deepEquals(expectedTagNames)\n\t})\n\to(\"scrambling sample 2\", function() {\n\t\tvar vnodes = vnodify(\"k0,k1,k2,k3,k4,k5,k6,k7,k8,k9\")\n\t\tvar updated = vnodify(\"b,d,k1,k0,k2,k3,k4,a,c,k5,k6,k7,k8,k9\")\n\t\tvar expectedTagNames = updated.map(function(vn) {return vn.tag})\n\n\t\trender(root, vnodes)\n\n\t\troot.appendChild = o.spy(root.appendChild)\n\t\troot.insertBefore = o.spy(root.insertBefore)\n\n\t\trender(root, updated)\n\n\t\tvar tagNames = [].map.call(root.childNodes, function(n) {return n.nodeName.toLowerCase()})\n\n\t\to(root.appendChild.callCount + root.insertBefore.callCount).equals(5)\n\t\to(tagNames).deepEquals(expectedTagNames)\n\t})\n\to(\"update keyed element vnodes with another tag (#3059)\", function() {\n\t\tvar e = function(k) {return m(k, {key: k})} // element vnode\n\t\tvar p = function(k) {return m(k + \"p\", {key: k})} // element vnode (another tag)\n\n\t\tconst o1 = [e(\"k1\"),e(\"k2\"),e(\"k3\"),e(\"k4\")]\n\t\tconst v1 = [p(\"k2\"),e(\"k4\"),e(\"k3\")]\n\n\t\t// create\n\t\trender(root, v1)\n\t\to(Array.from(root.childNodes).map(function(n) {return n.nodeName})).deepEquals([\"K2P\", \"K4\", \"K3\"])\n\n\t\t// update\n\t\trender(root, [])\n\t\trender(root, o1)\n\t\trender(root, v1)\n\t\to(Array.from(root.childNodes).map(function(n) {return n.nodeName})).deepEquals([\"K2P\", \"K4\", \"K3\"])\n\t})\n\to(\"update keyed element vnodes with dom and keyed fragment vnodes without dom (1) (#3059)\", function() {\n\t\to(function() {\n\t\t\tvar e = function(k) {return m(k, {key: k})} // element vnode (with dom)\n\t\t\tvar f = function(k) {return m(\"[\", {key: k})} // fragment vnode (without dom)\n\n\t\t\tvar o1 = [f(\"k1\"),e(\"k2\")]\n\t\t\tvar v1 = [e(\"k1\"),e(\"a\"),f(\"k2\")]\n\n\t\t\trender(root, o1)\n\t\t\trender(root, v1)\n\n\t\t\to(Array.from(root.childNodes).map(function(n) {return n.nodeName})).deepEquals([\"K1\", \"A\"])\n\t\t}).notThrows(Error)\n\t})\n\to(\"update keyed element vnodes with dom and keyed fragment vnodes without dom (2) (#3059)\", function() {\n\t\to(function() {\n\t\t\tvar e = function(k) {return m(k, {key: k})} // element vnode (with dom)\n\t\t\tvar f = function(k) {return m(\"[\", {key: k})} // fragment vnode (without dom)\n\n\t\t\tvar o1 = [f(\"k1\"),f(\"k2\"),e(\"k3\")]\n\t\t\tvar v1 = [e(\"k1\"),f(\"k3\"),e(\"k2\")]\n\n\t\t\trender(root, o1)\n\t\t\trender(root, v1)\n\n\t\t\to(Array.from(root.childNodes).map(function(n) {return n.nodeName})).deepEquals([\"K1\", \"K2\"])\n\t\t}).notThrows(Error)\n\t})\n\n\tcomponents.forEach(function(cmp){\n\t\to.spec(cmp.kind, function(){\n\t\t\tvar createComponent = cmp.create\n\n\t\t\to(\"fragment child toggles from null when followed by null component then tag\", function() {\n\t\t\t\tvar component = createComponent({view: function() {return null}})\n\t\t\t\tvar vnodes = [fragment(m(\"a\"), m(component), m(\"b\"))]\n\t\t\t\tvar temp = [fragment(null, m(component), m(\"b\"))]\n\t\t\t\tvar updated = [fragment(m(\"a\"), m(component), m(\"b\"))]\n\n\t\t\t\trender(root, vnodes)\n\t\t\t\trender(root, temp)\n\t\t\t\trender(root, updated)\n\n\t\t\t\to(root.childNodes.length).equals(2)\n\t\t\t\to(root.childNodes[0].nodeName).equals(\"A\")\n\t\t\t\to(root.childNodes[1].nodeName).equals(\"B\")\n\t\t\t})\n\t\t\to(\"fragment child toggles from null in component when followed by null component then tag\", function() {\n\t\t\t\tvar flag = true\n\t\t\t\tvar a = createComponent({view: function() {return flag ? m(\"a\") : null}})\n\t\t\t\tvar b = createComponent({view: function() {return null}})\n\t\t\t\tvar vnodes = [fragment(m(a), m(b), m(\"s\"))]\n\t\t\t\tvar temp = [fragment(m(a), m(b), m(\"s\"))]\n\t\t\t\tvar updated = [fragment(m(a), m(b), m(\"s\"))]\n\n\t\t\t\trender(root, vnodes)\n\t\t\t\tflag = false\n\t\t\t\trender(root, temp)\n\t\t\t\tflag = true\n\t\t\t\trender(root, updated)\n\n\t\t\t\to(root.childNodes.length).equals(2)\n\t\t\t\to(root.childNodes[0].nodeName).equals(\"A\")\n\t\t\t\to(root.childNodes[1].nodeName).equals(\"S\")\n\t\t\t})\n\t\t\to(\"removing a component that returns a fragment doesn't throw (regression test for incidental bug introduced while debugging some Flems)\", function() {\n\t\t\t\tvar component = createComponent({\n\t\t\t\t\tview: function() {return fragment(m(\"a\"), m(\"b\"))}\n\t\t\t\t})\n\t\t\t\ttry {\n\t\t\t\t\trender(root, [m(component)])\n\t\t\t\t\trender(root, [])\n\n\t\t\t\t\to(root.childNodes.length).equals(0)\n\t\t\t\t} catch (e) {\n\t\t\t\t\to(e).equals(null)\n\t\t\t\t}\n\t\t\t})\n\t\t})\n\t})\n})\n"
  },
  {
    "path": "render/tests/test-updateNodesFuzzer.js",
    "content": "\"use strict\"\n\nvar o = require(\"ospec\")\nvar domMock = require(\"../../test-utils/domMock\")\nvar vdom = require(\"../../render/render\")\nvar m = require(\"../../render/hyperscript\")\n\n// pilfered and adapted from https://github.com/domvm/domvm/blob/7aaec609e4c625b9acf9a22d035d6252a5ca654f/test/src/flat-list-keyed-fuzz.js\no.spec(\"updateNodes keyed list Fuzzer\", function() {\n\tvar i = 0, $window, root, render\n\to.beforeEach(function() {\n\t\t$window = domMock()\n\t\troot = $window.document.createElement(\"div\")\n\t\trender = vdom($window)\n\t})\n\n\n\tvoid [\n\t\t{delMax: 0, movMax: 50, insMax: 9},\n\t\t{delMax: 3, movMax: 5, insMax: 5},\n\t\t{delMax: 7, movMax: 15, insMax: 0},\n\t\t{delMax: 5, movMax: 100, insMax: 3},\n\t\t{delMax: 5, movMax: 0, insMax: 3},\n\t].forEach(function(c) {\n\t\tvar tests = 250\n\n\t\twhile (tests--) {\n\t\t\tconst test = fuzzTest(c.delMax, c.movMax, c.insMax)\n\t\t\tconst id = i++\n\t\t\to(id + \": \" + test.list.join() + \" -> \" + test.updated.join(), function() {\n\t\t\t\trender(root, test.list.map(function(x){return m(x, {key: x})}))\n\t\t\t\taddSpies(root)\n\t\t\t\trender(root, test.updated.map(function(x){return m(x, {key: x})}))\n\n\t\t\t\t// FIXME: This does not take into account the \"swaps and list reversals\" heuristic in updateNodes().\n\t\t\t\t// Here, we’re checking whether the number of node moves matches the theoretical value derived from the LIS.\n\t\t\t\t// However, in updateNodes(), when patterns such as swaps or reversed lists are detected,\n\t\t\t\t// nodes are moved before the LIS-based reordering is applied.\n\t\t\t\t// Once these heuristic moves occur, the actual number of moves no longer matches the LIS-based theoretical value.\n\t\t\t\t// if (root.appendChild.callCount + root.insertBefore.callCount !== test.expected.creations + test.expected.moves) console.log(test, {aC: root.appendChild.callCount, iB: root.insertBefore.callCount}, [].map.call(root.childNodes, function(n){return n.nodeName.toLowerCase()}))\n\t\t\t\t//\n\t\t\t\t// o(root.appendChild.callCount + root.insertBefore.callCount).equals(test.expected.creations + test.expected.moves)(\"moves\")\n\t\t\t\to(root.removeChild.callCount).equals(test.expected.deletions)(\"deletions\")\n\t\t\t\to([].map.call(root.childNodes, function(n){return n.nodeName.toLowerCase()})).deepEquals(test.updated)\n\t\t\t})\n\t\t\to(id + \": including tag changes\", function() {\n\t\t\t\t// change some tags before and after the update\n\t\t\t\tvar list = test.list.map(function(x){return m(x + (Math.random() > 0.5 ? \"_\" : \"\"), {key: x})})\n\t\t\t\tvar updated = test.updated.map(function(x){return m(x, {key: x})})\n\t\t\t\tvar str = list.map(function(v) {return v.tag}).join() + \" -> \" + updated.map(function(v) {return v.tag}).join()\n\n\t\t\t\trender(root, list)\n\t\t\t\trender(root, updated)\n\n\t\t\t\to([].map.call(root.childNodes, function(n){return n.nodeName.toLowerCase()})).deepEquals(test.updated)(str)\n\t\t\t})\n\t\t\to(id + \": including empty fragments (without dom)\", function() {\n\t\t\t\t// change some vnodes to empty fragments without DOM before and after the update\n\t\t\t\tvar list = test.list.map(function(x){return m(Math.random() > 0.5 ? x : \"[\", {key: x})})\n\t\t\t\tvar updated = test.updated.map(function(x){return m(Math.random() > 0.5 ? x : \"[\", {key: x})})\n\t\t\t\tvar expected = updated.map(function(v){return v.tag}).filter(function(x){return x !== \"[\"})\n\t\t\t\tvar str = list.map(function(v) {return v.tag + \".\" + v.key}).join() + \" -> \" + updated.map(function(v) {return v.tag + \".\" + v.key}).join()\n\n\t\t\t\trender(root, list)\n\t\t\t\trender(root, updated)\n\n\t\t\t\to([].map.call(root.childNodes, function(n){return n.nodeName.toLowerCase()})).deepEquals(expected)(str)\n\t\t\t})\n\t\t}\n\t})\n})\n\n// https://en.wikipedia.org/wiki/Longest_increasing_subsequence\n// impl borrowed from https://github.com/ivijs/ivi\nfunction longestIncreasingSubsequence(a) {\n\tvar p = a.slice()\n\tvar result = []\n\tresult.push(0)\n\tvar u\n\tvar v\n\n\tfor (var i = 0, il = a.length; i < il; ++i) {\n\t\tvar j = result[result.length - 1]\n\t\tif (a[j] < a[i]) {\n\t\t\tp[i] = j\n\t\t\tresult.push(i)\n\t\t\tcontinue\n\t\t}\n\n\t\tu = 0\n\t\tv = result.length - 1\n\n\t\twhile (u < v) {\n\t\t\tvar c = ((u + v) / 2) | 0 // eslint-disable-line no-bitwise\n\t\t\tif (a[result[c]] < a[i]) {\n\t\t\t\tu = c + 1\n\t\t\t} else {\n\t\t\t\tv = c\n\t\t\t}\n\t\t}\n\n\t\tif (a[i] < a[result[u]]) {\n\t\t\tif (u > 0) {\n\t\t\t\tp[i] = result[u - 1]\n\t\t\t}\n\t\t\tresult[u] = i\n\t\t}\n\t}\n\n\tu = result.length\n\tv = result[u - 1]\n\n\twhile (u-- > 0) {\n\t\tresult[u] = v\n\t\tv = p[v]\n\t}\n\n\treturn result\n}\n\nfunction rand(min, max) {\n\treturn Math.floor(Math.random() * (max - min + 1)) + min\n}\n\nfunction ins(arr, qty) {\n\tvar p = [\"a\",\"b\",\"c\",\"d\",\"e\",\"f\",\"g\",\"h\",\"i\"]\n\n\twhile (qty-- > 0)\n\t\tarr.splice(rand(0, arr.length - 1), 0, p.shift())\n}\n\nfunction del(arr, qty) {\n\twhile (qty-- > 0)\n\t\tarr.splice(rand(0, arr.length - 1), 1)\n}\n\nfunction mov(arr, qty) {\n\twhile (qty-- > 0) {\n\t\tvar from = rand(0, arr.length - 1)\n\t\tvar to = rand(0, arr.length - 1)\n\n\t\tarr.splice(to, 0, arr.splice(from, 1)[0])\n\t}\n}\n\nfunction fuzzTest(delMax, movMax, insMax) {\n\tvar list = [\"k0\",\"k1\",\"k2\",\"k3\",\"k4\",\"k5\",\"k6\",\"k7\",\"k8\",\"k9\"]\n\tvar copy = list.slice()\n\n\tvar delCount = rand(0, delMax),\n\t\tmovCount = rand(0, movMax),\n\t\tinsCount = rand(0, insMax)\n\n\tdel(copy, delCount)\n\tmov(copy, movCount)\n\n\tvar expected = {\n\t\tcreations: insCount,\n\t\tdeletions: delCount,\n\t\tmoves: 0\n\t}\n\n\tif (movCount > 0) {\n\t\tvar newPos = copy.map(function(v) {\n\t\t\treturn list.indexOf(v)\n\t\t}).filter(function(i) {\n\t\t\treturn i != -1\n\t\t})\n\t\tvar lis = longestIncreasingSubsequence(newPos)\n\t\texpected.moves = copy.length - lis.length\n\t}\n\n\tins(copy, insCount)\n\n\treturn {\n\t\texpected: expected,\n\t\tlist: list,\n\t\tupdated: copy\n\t}\n}\n\nfunction addSpies(node) {\n\tnode.appendChild = o.spy(node.appendChild)\n\tnode.insertBefore = o.spy(node.insertBefore)\n\tnode.removeChild = o.spy(node.removeChild)\n}\n"
  },
  {
    "path": "render/tests/test-updateText.js",
    "content": "\"use strict\"\n\nvar o = require(\"ospec\")\nvar domMock = require(\"../../test-utils/domMock\")\nvar vdom = require(\"../../render/render\")\n\no.spec(\"updateText\", function() {\n\tvar $window, root, render\n\to.beforeEach(function() {\n\t\t$window = domMock()\n\t\troot = $window.document.createElement(\"div\")\n\t\trender = vdom($window)\n\t})\n\n\to(\"updates to string\", function() {\n\t\tvar vnode = \"a\"\n\t\tvar updated = \"b\"\n\n\t\trender(root, vnode)\n\t\trender(root, updated)\n\n\t\to(root.firstChild.nodeValue).equals(\"b\")\n\t})\n\to(\"updates to falsy string\", function() {\n\t\tvar vnode = \"a\"\n\t\tvar updated = \"\"\n\n\t\trender(root, vnode)\n\t\trender(root, updated)\n\n\t\to(root.firstChild.nodeValue).equals(\"\")\n\t})\n\to(\"updates from falsy string\", function() {\n\t\tvar vnode = \"\"\n\t\tvar updated = \"b\"\n\n\t\trender(root, vnode)\n\t\trender(root, updated)\n\n\t\to(root.firstChild.nodeValue).equals(\"b\")\n\t})\n\to(\"updates to number\", function() {\n\t\tvar vnode = \"a\"\n\t\tvar updated = 1\n\n\t\trender(root, vnode)\n\t\trender(root, updated)\n\n\t\to(root.firstChild.nodeValue).equals(\"1\")\n\t})\n\to(\"updates to falsy number\", function() {\n\t\tvar vnode = \"a\"\n\t\tvar updated = 0\n\n\t\trender(root, vnode)\n\t\trender(root, updated)\n\n\t\to(root.firstChild.nodeValue).equals(\"0\")\n\t})\n\to(\"updates from falsy number\", function() {\n\t\tvar vnode = 0\n\t\tvar updated = \"b\"\n\n\t\trender(root, vnode)\n\t\trender(root, updated)\n\n\t\to(root.firstChild.nodeValue).equals(\"b\")\n\t})\n\to(\"updates to boolean\", function() {\n\t\tvar vnode = \"a\"\n\t\tvar updated = true\n\n\t\trender(root, vnode)\n\t\trender(root, updated)\n\n\t\to(root.childNodes.length).equals(0)\n\t})\n\to(\"updates to falsy boolean\", function() {\n\t\tvar vnode = \"a\"\n\t\tvar updated = false\n\n\t\trender(root, vnode)\n\t\trender(root, updated)\n\n\t\to(root.childNodes.length).equals(0)\n\t})\n\to(\"updates from falsy boolean\", function() {\n\t\tvar vnode = false\n\t\tvar updated = \"b\"\n\n\t\trender(root, vnode)\n\t\trender(root, updated)\n\n\t\to(root.firstChild.nodeValue).equals(\"b\")\n\t})\n})\n"
  },
  {
    "path": "render/trust.js",
    "content": "\"use strict\"\n\nvar Vnode = require(\"../render/vnode\")\n\nmodule.exports = function(html) {\n\tif (html == null) html = \"\"\n\treturn Vnode(\"<\", undefined, undefined, html, undefined, undefined)\n}\n"
  },
  {
    "path": "render/vnode.js",
    "content": "\"use strict\"\n\nfunction Vnode(tag, key, attrs, children, text, dom) {\n\treturn {tag: tag, key: key, attrs: attrs, children: children, text: text, dom: dom, is: undefined, domSize: undefined, state: undefined, events: undefined, instance: undefined}\n}\nVnode.normalize = function(node) {\n\tif (Array.isArray(node)) return Vnode(\"[\", undefined, undefined, Vnode.normalizeChildren(node), undefined, undefined)\n\tif (node == null || typeof node === \"boolean\") return null\n\tif (typeof node === \"object\") return node\n\treturn Vnode(\"#\", undefined, undefined, String(node), undefined, undefined)\n}\nVnode.normalizeChildren = function(input) {\n\t// Preallocate the array length (initially holey) and fill every index immediately in order.\n\t// Benchmarking shows better performance on V8.\n\tvar children = new Array(input.length)\n\t// Count the number of keyed normalized vnodes for consistency check.\n\t// Note: this is a perf-sensitive check.\n\t// Fun fact: merging the loop like this is somehow faster than splitting\n\t// the check within updateNodes(), noticeably so.\n\tvar numKeyed = 0\n\tfor (var i = 0; i < input.length; i++) {\n\t\tchildren[i] = Vnode.normalize(input[i])\n\t\tif (children[i] !== null && children[i].key != null) numKeyed++\n\t}\n\tif (numKeyed !== 0 && numKeyed !== input.length) {\n\t\tthrow new TypeError(children.includes(null)\n\t\t\t? \"In fragments, vnodes must either all have keys or none have keys. You may wish to consider using an explicit keyed empty fragment, m.fragment({key: ...}), instead of a hole.\"\n\t\t\t: \"In fragments, vnodes must either all have keys or none have keys.\"\n\t\t)\n\t}\n\treturn children\n}\n\nmodule.exports = Vnode\n"
  },
  {
    "path": "render.js",
    "content": "\"use strict\"\n\nmodule.exports = require(\"./render/render\")()\n"
  },
  {
    "path": "request/request.js",
    "content": "\"use strict\"\n\nvar buildPathname = require(\"../pathname/build\")\nvar hasOwn = require(\"../util/hasOwn\")\n\nmodule.exports = function($window, oncompletion) {\n\tfunction PromiseProxy(executor) {\n\t\treturn new Promise(executor)\n\t}\n\n\tfunction makeRequest(url, args) {\n\t\treturn new Promise(function(resolve, reject) {\n\t\t\turl = buildPathname(url, args.params)\n\t\t\tvar method = args.method != null ? args.method.toUpperCase() : \"GET\"\n\t\t\tvar body = args.body\n\t\t\tvar assumeJSON = (args.serialize == null || args.serialize === JSON.serialize) && !(body instanceof $window.FormData || body instanceof $window.URLSearchParams)\n\t\t\tvar responseType = args.responseType || (typeof args.extract === \"function\" ? \"\" : \"json\")\n\n\t\t\tvar xhr = new $window.XMLHttpRequest(), aborted = false, isTimeout = false\n\t\t\tvar original = xhr, replacedAbort\n\t\t\tvar abort = xhr.abort\n\n\t\t\txhr.abort = function() {\n\t\t\t\taborted = true\n\t\t\t\tabort.call(this)\n\t\t\t}\n\n\t\t\txhr.open(method, url, args.async !== false, typeof args.user === \"string\" ? args.user : undefined, typeof args.password === \"string\" ? args.password : undefined)\n\n\t\t\tif (assumeJSON && body != null && !hasHeader(args, \"content-type\")) {\n\t\t\t\txhr.setRequestHeader(\"Content-Type\", \"application/json; charset=utf-8\")\n\t\t\t}\n\t\t\tif (typeof args.deserialize !== \"function\" && !hasHeader(args, \"accept\")) {\n\t\t\t\txhr.setRequestHeader(\"Accept\", \"application/json, text/*\")\n\t\t\t}\n\t\t\tif (args.withCredentials) xhr.withCredentials = args.withCredentials\n\t\t\tif (args.timeout) xhr.timeout = args.timeout\n\t\t\txhr.responseType = responseType\n\n\t\t\tfor (var key in args.headers) {\n\t\t\t\tif (hasOwn.call(args.headers, key)) {\n\t\t\t\t\txhr.setRequestHeader(key, args.headers[key])\n\t\t\t\t}\n\t\t\t}\n\n\t\t\txhr.onreadystatechange = function(ev) {\n\t\t\t\t// Don't throw errors on xhr.abort().\n\t\t\t\tif (aborted) return\n\n\t\t\t\tif (ev.target.readyState === 4) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tvar success = (ev.target.status >= 200 && ev.target.status < 300) || ev.target.status === 304 || (/^file:\\/\\//i).test(url)\n\t\t\t\t\t\t// When the response type isn't \"\" or \"text\",\n\t\t\t\t\t\t// `xhr.responseText` is the wrong thing to use.\n\t\t\t\t\t\t// Browsers do the right thing and throw here, and we\n\t\t\t\t\t\t// should honor that and do the right thing by\n\t\t\t\t\t\t// preferring `xhr.response` where possible/practical.\n\t\t\t\t\t\tvar response = ev.target.response, message\n\n\t\t\t\t\t\tif (responseType === \"json\") {\n\t\t\t\t\t\t\t// For IE and Edge, which don't implement\n\t\t\t\t\t\t\t// `responseType: \"json\"`.\n\t\t\t\t\t\t\tif (!ev.target.responseType && typeof args.extract !== \"function\") {\n\t\t\t\t\t\t\t\t// Handle no-content which will not parse.\n\t\t\t\t\t\t\t\ttry { response = JSON.parse(ev.target.responseText) }\n\t\t\t\t\t\t\t\tcatch (e) { response = null }\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else if (!responseType || responseType === \"text\") {\n\t\t\t\t\t\t\t// Only use this default if it's text. If a parsed\n\t\t\t\t\t\t\t// document is needed on old IE and friends (all\n\t\t\t\t\t\t\t// unsupported), the user should use a custom\n\t\t\t\t\t\t\t// `config` instead. They're already using this at\n\t\t\t\t\t\t\t// their own risk.\n\t\t\t\t\t\t\tif (response == null) response = ev.target.responseText\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (typeof args.extract === \"function\") {\n\t\t\t\t\t\t\tresponse = args.extract(ev.target, args)\n\t\t\t\t\t\t\tsuccess = true\n\t\t\t\t\t\t} else if (typeof args.deserialize === \"function\") {\n\t\t\t\t\t\t\tresponse = args.deserialize(response)\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (success) {\n\t\t\t\t\t\t\tif (typeof args.type === \"function\") {\n\t\t\t\t\t\t\t\tif (Array.isArray(response)) {\n\t\t\t\t\t\t\t\t\tfor (var i = 0; i < response.length; i++) {\n\t\t\t\t\t\t\t\t\t\tresponse[i] = new args.type(response[i])\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\telse response = new args.type(response)\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tresolve(response)\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse {\n\t\t\t\t\t\t\tvar completeErrorResponse = function() {\n\t\t\t\t\t\t\t\ttry { message = ev.target.responseText }\n\t\t\t\t\t\t\t\tcatch (e) { message = response }\n\t\t\t\t\t\t\t\tvar error = new Error(message)\n\t\t\t\t\t\t\t\terror.code = ev.target.status\n\t\t\t\t\t\t\t\terror.response = response\n\t\t\t\t\t\t\t\treject(error)\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tif (xhr.status === 0) {\n\t\t\t\t\t\t\t\t// Use setTimeout to push this code block onto the event queue\n\t\t\t\t\t\t\t\t// This allows `xhr.ontimeout` to run in the case that there is a timeout\n\t\t\t\t\t\t\t\t// Without this setTimeout, `xhr.ontimeout` doesn't have a chance to reject\n\t\t\t\t\t\t\t\t// as `xhr.onreadystatechange` will run before it\n\t\t\t\t\t\t\t\tsetTimeout(function() {\n\t\t\t\t\t\t\t\t\tif (isTimeout) return\n\t\t\t\t\t\t\t\t\tcompleteErrorResponse()\n\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t} else completeErrorResponse()\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tcatch (e) {\n\t\t\t\t\t\treject(e)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\txhr.ontimeout = function (ev) {\n\t\t\t\tisTimeout = true\n\t\t\t\tvar error = new Error(\"Request timed out\")\n\t\t\t\terror.code = ev.target.status\n\t\t\t\treject(error)\n\t\t\t}\n\n\t\t\tif (typeof args.config === \"function\") {\n\t\t\t\txhr = args.config(xhr, args, url) || xhr\n\n\t\t\t\t// Propagate the `abort` to any replacement XHR as well.\n\t\t\t\tif (xhr !== original) {\n\t\t\t\t\treplacedAbort = xhr.abort\n\t\t\t\t\txhr.abort = function() {\n\t\t\t\t\t\taborted = true\n\t\t\t\t\t\treplacedAbort.call(this)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (body == null) xhr.send()\n\t\t\telse if (typeof args.serialize === \"function\") xhr.send(args.serialize(body))\n\t\t\telse if (body instanceof $window.FormData || body instanceof $window.URLSearchParams) xhr.send(body)\n\t\t\telse xhr.send(JSON.stringify(body))\n\t\t})\n\t}\n\n\t// In case the global Promise is some userland library's where they rely on\n\t// `foo instanceof this.constructor`, `this.constructor.resolve(value)`, or\n\t// similar. Let's *not* break them.\n\tPromiseProxy.prototype = Promise.prototype\n\tPromiseProxy.__proto__ = Promise // eslint-disable-line no-proto\n\n\tfunction hasHeader(args, name) {\n\t\tfor (var key in args.headers) {\n\t\t\tif (hasOwn.call(args.headers, key) && key.toLowerCase() === name) return true\n\t\t}\n\t\treturn false\n\t}\n\n\treturn {\n\t\trequest: function(url, args) {\n\t\t\tif (typeof url !== \"string\") { args = url; url = url.url }\n\t\t\telse if (args == null) args = {}\n\t\t\tvar promise = makeRequest(url, args)\n\t\t\tif (args.background === true) return promise\n\t\t\tvar count = 0\n\t\t\tfunction complete() {\n\t\t\t\tif (--count === 0 && typeof oncompletion === \"function\") oncompletion()\n\t\t\t}\n\n\t\t\treturn wrap(promise)\n\n\t\t\tfunction wrap(promise) {\n\t\t\t\tvar then = promise.then\n\t\t\t\t// Set the constructor, so engines know to not await or resolve\n\t\t\t\t// this as a native promise. At the time of writing, this is\n\t\t\t\t// only necessary for V8, but their behavior is the correct\n\t\t\t\t// behavior per spec. See this spec issue for more details:\n\t\t\t\t// https://github.com/tc39/ecma262/issues/1577. Also, see the\n\t\t\t\t// corresponding comment in `request/tests/test-request.js` for\n\t\t\t\t// a bit more background on the issue at hand.\n\t\t\t\tpromise.constructor = PromiseProxy\n\t\t\t\tpromise.then = function() {\n\t\t\t\t\tcount++\n\t\t\t\t\tvar next = then.apply(promise, arguments)\n\t\t\t\t\tnext.then(complete, function(e) {\n\t\t\t\t\t\tcomplete()\n\t\t\t\t\t\tif (count === 0) throw e\n\t\t\t\t\t})\n\t\t\t\t\treturn wrap(next)\n\t\t\t\t}\n\t\t\t\treturn promise\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "request/tests/test-request.js",
    "content": "\"use strict\"\n\nvar o = require(\"ospec\")\nvar callAsync = require(\"../../test-utils/callAsync\")\nvar xhrMock = require(\"../../test-utils/xhrMock\")\nvar Request = require(\"../../request/request\")\n\no.spec(\"request\", function() {\n\tvar mock, request, complete\n\to.beforeEach(function() {\n\t\tmock = xhrMock()\n\t\tcomplete = o.spy()\n\t\trequest = Request(mock, complete).request\n\t})\n\n\to.spec(\"success\", function() {\n\t\to(\"works via GET\", function(done) {\n\t\t\tmock.$defineRoutes({\n\t\t\t\t\"GET /item\": function() {\n\t\t\t\t\treturn {status: 200, responseText: JSON.stringify({a: 1})}\n\t\t\t\t}\n\t\t\t})\n\t\t\trequest({method: \"GET\", url: \"/item\"}).then(function(data) {\n\t\t\t\to(data).deepEquals({a: 1})\n\t\t\t}).then(function() {\n\t\t\t\tdone()\n\t\t\t})\n\t\t})\n\t\to(\"implicit GET method\", function(done){\n\t\t\tmock.$defineRoutes({\n\t\t\t\t\"GET /item\": function() {\n\t\t\t\t\treturn {status: 200, responseText: JSON.stringify({a: 1})}\n\t\t\t\t}\n\t\t\t})\n\t\t\trequest({url: \"/item\"}).then(function(data) {\n\t\t\t\to(data).deepEquals({a: 1})\n\t\t\t}).then(function() {\n\t\t\t\tdone()\n\t\t\t})\n\t\t})\n\t\to(\"first argument can be a string aliasing url property\", function(done){\n\t\t\tmock.$defineRoutes({\n\t\t\t\t\"GET /item\": function() {\n\t\t\t\t\treturn {status: 200, responseText: JSON.stringify({a: 1})}\n\t\t\t\t}\n\t\t\t})\n\t\t\trequest(\"/item\").then(function(data) {\n\t\t\t\to(data).deepEquals({a: 1})\n\t\t\t}).then(function() {\n\t\t\t\tdone()\n\t\t\t})\n\t\t})\n\t\to(\"works via POST\", function(done) {\n\t\t\tmock.$defineRoutes({\n\t\t\t\t\"POST /item\": function() {\n\t\t\t\t\treturn {status: 200, responseText: JSON.stringify({a: 1})}\n\t\t\t\t}\n\t\t\t})\n\t\t\trequest({method: \"POST\", url: \"/item\"}).then(function(data) {\n\t\t\t\to(data).deepEquals({a: 1})\n\t\t\t}).then(done)\n\t\t})\n\t\to(\"first argument can act as URI with second argument providing options\", function(done) {\n\t\t\tmock.$defineRoutes({\n\t\t\t\t\"POST /item\": function() {\n\t\t\t\t\treturn {status: 200, responseText: JSON.stringify({a: 1})}\n\t\t\t\t}\n\t\t\t})\n\t\t\trequest(\"/item\", {method: \"POST\"}).then(function(data) {\n\t\t\t\to(data).deepEquals({a: 1})\n\t\t\t}).then(done)\n\t\t})\n\t\to(\"first argument keeps protocol\", function(done) {\n\t\t\tmock.$defineRoutes({\n\t\t\t\t\"POST /item\": function(request) {\n\t\t\t\t\to(request.rawUrl).equals(\"https://example.com/item\")\n\t\t\t\t\treturn {status: 200, responseText: JSON.stringify({a: 1})}\n\t\t\t\t}\n\t\t\t})\n\t\t\trequest(\"https://example.com/item\", {method: \"POST\"}).then(function(data) {\n\t\t\t\to(data).deepEquals({a: 1})\n\t\t\t}).then(done)\n\t\t})\n\t\to(\"works w/ parameterized data via GET\", function(done) {\n\t\t\tmock.$defineRoutes({\n\t\t\t\t\"GET /item\": function(request) {\n\t\t\t\t\treturn {status: 200, responseText: JSON.stringify({a: request.query})}\n\t\t\t\t}\n\t\t\t})\n\t\t\trequest({method: \"GET\", url: \"/item\", params: {x: \"y\"}}).then(function(data) {\n\t\t\t\to(data).deepEquals({a: \"?x=y\"})\n\t\t\t}).then(done)\n\t\t})\n\t\to(\"works w/ parameterized data via POST\", function(done) {\n\t\t\tmock.$defineRoutes({\n\t\t\t\t\"POST /item\": function(request) {\n\t\t\t\t\treturn {status: 200, responseText: JSON.stringify({a: JSON.parse(request.body)})}\n\t\t\t\t}\n\t\t\t})\n\t\t\trequest({method: \"POST\", url: \"/item\", body: {x: \"y\"}}).then(function(data) {\n\t\t\t\to(data).deepEquals({a: {x: \"y\"}})\n\t\t\t}).then(done)\n\t\t})\n\t\to(\"works w/ parameterized data containing colon via GET\", function(done) {\n\t\t\tmock.$defineRoutes({\n\t\t\t\t\"GET /item\": function(request) {\n\t\t\t\t\treturn {status: 200, responseText: JSON.stringify({a: request.query})}\n\t\t\t\t}\n\t\t\t})\n\t\t\trequest({method: \"GET\", url: \"/item\", params: {x: \":y\"}}).then(function(data) {\n\t\t\t\to(data).deepEquals({a: \"?x=%3Ay\"})\n\t\t\t}).then(done)\n\t\t})\n\t\to(\"works w/ parameterized data containing colon via POST\", function(done) {\n\t\t\tmock.$defineRoutes({\n\t\t\t\t\"POST /item\": function(request) {\n\t\t\t\t\treturn {status: 200, responseText: JSON.stringify({a: JSON.parse(request.body)})}\n\t\t\t\t}\n\t\t\t})\n\t\t\trequest({method: \"POST\", url: \"/item\", body: {x: \":y\"}}).then(function(data) {\n\t\t\t\to(data).deepEquals({a: {x: \":y\"}})\n\t\t\t}).then(done)\n\t\t})\n\t\to(\"works w/ parameterized url via GET\", function(done) {\n\t\t\tmock.$defineRoutes({\n\t\t\t\t\"GET /item/y\": function(request) {\n\t\t\t\t\treturn {status: 200, responseText: JSON.stringify({a: request.url, b: request.query, c: request.body})}\n\t\t\t\t}\n\t\t\t})\n\t\t\trequest({method: \"GET\", url: \"/item/:x\", params: {x: \"y\"}}).then(function(data) {\n\t\t\t\to(data).deepEquals({a: \"/item/y\", b: {}, c: null})\n\t\t\t}).then(done)\n\t\t})\n\t\to(\"works w/ parameterized url via POST\", function(done) {\n\t\t\tmock.$defineRoutes({\n\t\t\t\t\"POST /item/y\": function(request) {\n\t\t\t\t\treturn {status: 200, responseText: JSON.stringify({a: request.url, b: request.query, c: request.body})}\n\t\t\t\t}\n\t\t\t})\n\t\t\trequest({method: \"POST\", url: \"/item/:x\", params: {x: \"y\"}}).then(function(data) {\n\t\t\t\to(data).deepEquals({a: \"/item/y\", b: {}, c: null})\n\t\t\t}).then(done)\n\t\t})\n\t\to(\"works w/ parameterized url + body via GET\", function(done) {\n\t\t\tmock.$defineRoutes({\n\t\t\t\t\"GET /item/y\": function(request) {\n\t\t\t\t\treturn {status: 200, responseText: JSON.stringify({a: request.url, b: request.query, c: JSON.parse(request.body)})}\n\t\t\t\t}\n\t\t\t})\n\t\t\trequest({method: \"GET\", url: \"/item/:x\", params: {x: \"y\"}, body: {a: \"b\"}}).then(function(data) {\n\t\t\t\to(data).deepEquals({a: \"/item/y\", b: {}, c: {a: \"b\"}})\n\t\t\t}).then(done)\n\t\t})\n\t\to(\"works w/ parameterized url + body via POST\", function(done) {\n\t\t\tmock.$defineRoutes({\n\t\t\t\t\"POST /item/y\": function(request) {\n\t\t\t\t\treturn {status: 200, responseText: JSON.stringify({a: request.url, b: request.query, c: JSON.parse(request.body)})}\n\t\t\t\t}\n\t\t\t})\n\t\t\trequest({method: \"POST\", url: \"/item/:x\", params: {x: \"y\"}, body: {a: \"b\"}}).then(function(data) {\n\t\t\t\to(data).deepEquals({a: \"/item/y\", b: {}, c: {a: \"b\"}})\n\t\t\t}).then(done)\n\t\t})\n\t\to(\"works w/ parameterized url + query via GET\", function(done) {\n\t\t\tmock.$defineRoutes({\n\t\t\t\t\"GET /item/y\": function(request) {\n\t\t\t\t\treturn {status: 200, responseText: JSON.stringify({a: request.url, b: request.query, c: request.body})}\n\t\t\t\t}\n\t\t\t})\n\t\t\trequest({method: \"GET\", url: \"/item/:x\", params: {x: \"y\", q: \"term\"}}).then(function(data) {\n\t\t\t\to(data).deepEquals({a: \"/item/y\", b: \"?q=term\", c: null})\n\t\t\t}).then(done)\n\t\t})\n\t\to(\"works w/ parameterized url + query via POST\", function(done) {\n\t\t\tmock.$defineRoutes({\n\t\t\t\t\"POST /item/y\": function(request) {\n\t\t\t\t\treturn {status: 200, responseText: JSON.stringify({a: request.url, b: request.query, c: request.body})}\n\t\t\t\t}\n\t\t\t})\n\t\t\trequest({method: \"POST\", url: \"/item/:x\", params: {x: \"y\", q: \"term\"}}).then(function(data) {\n\t\t\t\to(data).deepEquals({a: \"/item/y\", b: \"?q=term\", c: null})\n\t\t\t}).then(done)\n\t\t})\n\t\to(\"works w/ parameterized url + query + body via GET\", function(done) {\n\t\t\tmock.$defineRoutes({\n\t\t\t\t\"GET /item/y\": function(request) {\n\t\t\t\t\treturn {status: 200, responseText: JSON.stringify({a: request.url, b: request.query, c: JSON.parse(request.body)})}\n\t\t\t\t}\n\t\t\t})\n\t\t\trequest({method: \"GET\", url: \"/item/:x\", params: {x: \"y\", q: \"term\"}, body: {a: \"b\"}}).then(function(data) {\n\t\t\t\to(data).deepEquals({a: \"/item/y\", b: \"?q=term\", c: {a: \"b\"}})\n\t\t\t}).then(done)\n\t\t})\n\t\to(\"works w/ parameterized url + query + body via POST\", function(done) {\n\t\t\tmock.$defineRoutes({\n\t\t\t\t\"POST /item/y\": function(request) {\n\t\t\t\t\treturn {status: 200, responseText: JSON.stringify({a: request.url, b: request.query, c: JSON.parse(request.body)})}\n\t\t\t\t}\n\t\t\t})\n\t\t\trequest({method: \"POST\", url: \"/item/:x\", params: {x: \"y\", q: \"term\"}, body: {a: \"b\"}}).then(function(data) {\n\t\t\t\to(data).deepEquals({a: \"/item/y\", b: \"?q=term\", c: {a: \"b\"}})\n\t\t\t}).then(done)\n\t\t})\n\t\to(\"works w/ array\", function(done) {\n\t\t\tmock.$defineRoutes({\n\t\t\t\t\"POST /items\": function(request) {\n\t\t\t\t\treturn {status: 200, responseText: JSON.stringify({a: request.url, b: JSON.parse(request.body)})}\n\t\t\t\t}\n\t\t\t})\n\t\t\trequest({method: \"POST\", url: \"/items\", body: [{x: \"y\"}]}).then(function(data) {\n\t\t\t\to(data).deepEquals({a: \"/items\", b: [{x: \"y\"}]})\n\t\t\t}).then(done)\n\t\t})\n\t\to(\"works w/ URLSearchParams body\", function(done) {\n\t\t\tmock.$defineRoutes({\n\t\t\t\t\"POST /item\": function(request) {\n\t\t\t\t\treturn {status: 200, responseText: JSON.stringify({a: request.url, b: request.body.toString()})}\n\t\t\t\t}\n\t\t\t})\n\t\t\trequest({method: \"POST\", url: \"/item\", body: new URLSearchParams({x: \"y\", z: \"w\"})}).then(function(data) {\n\t\t\t\to(data).deepEquals({a: \"/item\", b: \"x=y&z=w\"})\n\t\t\t}).then(done)\n\t\t});\n\t\to(\"ignores unresolved parameter via GET\", function(done) {\n\t\t\tmock.$defineRoutes({\n\t\t\t\t\"GET /item/:x\": function(request) {\n\t\t\t\t\treturn {status: 200, responseText: JSON.stringify({a: request.url})}\n\t\t\t\t}\n\t\t\t})\n\t\t\trequest({method: \"GET\", url: \"/item/:x\"}).then(function(data) {\n\t\t\t\to(data).deepEquals({a: \"/item/:x\"})\n\t\t\t}).then(done)\n\t\t})\n\t\to(\"ignores unresolved parameter via POST\", function(done) {\n\t\t\tmock.$defineRoutes({\n\t\t\t\t\"GET /item/:x\": function(request) {\n\t\t\t\t\treturn {status: 200, responseText: JSON.stringify({a: request.url})}\n\t\t\t\t}\n\t\t\t})\n\t\t\trequest({method: \"GET\", url: \"/item/:x\"}).then(function(data) {\n\t\t\t\to(data).deepEquals({a: \"/item/:x\"})\n\t\t\t}).then(done)\n\t\t})\n\t\to(\"type parameter works for Array responses\", function(done) {\n\t\t\tvar Entity = function(args) {\n\t\t\t\treturn {_id: args.id}\n\t\t\t}\n\n\t\t\tmock.$defineRoutes({\n\t\t\t\t\"GET /item\": function() {\n\t\t\t\t\treturn {status: 200, responseText: JSON.stringify([{id: 1}, {id: 2}, {id: 3}])}\n\t\t\t\t}\n\t\t\t})\n\t\t\trequest({method: \"GET\", url: \"/item\", type: Entity}).then(function(data) {\n\t\t\t\to(data).deepEquals([{_id: 1}, {_id: 2}, {_id: 3}])\n\t\t\t}).then(done)\n\t\t})\n\t\to(\"type parameter works for Object responses\", function(done) {\n\t\t\tvar Entity = function(args) {\n\t\t\t\treturn {_id: args.id}\n\t\t\t}\n\n\t\t\tmock.$defineRoutes({\n\t\t\t\t\"GET /item\": function() {\n\t\t\t\t\treturn {status: 200, responseText: JSON.stringify({id: 1})}\n\t\t\t\t}\n\t\t\t})\n\t\t\trequest({method: \"GET\", url: \"/item\", type: Entity}).then(function(data) {\n\t\t\t\to(data).deepEquals({_id: 1})\n\t\t\t}).then(done)\n\t\t})\n\t\to(\"serialize parameter works in GET\", function(done) {\n\t\t\tvar serialize = function(data) {\n\t\t\t\treturn \"id=\" + data.id\n\t\t\t}\n\n\t\t\tmock.$defineRoutes({\n\t\t\t\t\"GET /item\": function(request) {\n\t\t\t\t\treturn {status: 200, responseText: JSON.stringify({body: request.query})}\n\t\t\t\t}\n\t\t\t})\n\t\t\trequest({method: \"GET\", url: \"/item\", serialize: serialize, params: {id: 1}}).then(function(data) {\n\t\t\t\to(data.body).equals(\"?id=1\")\n\t\t\t}).then(done)\n\t\t})\n\t\to(\"serialize parameter works in POST\", function(done) {\n\t\t\tvar serialize = function(data) {\n\t\t\t\treturn \"id=\" + data.id\n\t\t\t}\n\n\t\t\tmock.$defineRoutes({\n\t\t\t\t\"POST /item\": function(request) {\n\t\t\t\t\treturn {status: 200, responseText: JSON.stringify({body: request.body})}\n\t\t\t\t}\n\t\t\t})\n\t\t\trequest({method: \"POST\", url: \"/item\", serialize: serialize, body: {id: 1}}).then(function(data) {\n\t\t\t\to(data.body).equals(\"id=1\")\n\t\t\t}).then(done)\n\t\t})\n\t\to(\"deserialize parameter works in GET\", function(done) {\n\t\t\tvar deserialize = function(data) {\n\t\t\t\treturn data\n\t\t\t}\n\n\t\t\tmock.$defineRoutes({\n\t\t\t\t\"GET /item\": function() {\n\t\t\t\t\treturn {status: 200, responseText: JSON.stringify({test: 123})}\n\t\t\t\t}\n\t\t\t})\n\t\t\trequest({method: \"GET\", url: \"/item\", deserialize: deserialize}).then(function(data) {\n\t\t\t\to(data).deepEquals({test: 123})\n\t\t\t}).then(done)\n\t\t})\n\t\to(\"deserialize parameter works in POST\", function(done) {\n\t\t\tvar deserialize = function(data) {\n\t\t\t\treturn data\n\t\t\t}\n\n\t\t\tmock.$defineRoutes({\n\t\t\t\t\"POST /item\": function() {\n\t\t\t\t\treturn {status: 200, responseText: JSON.stringify({test: 123})}\n\t\t\t\t}\n\t\t\t})\n\t\t\trequest({method: \"POST\", url: \"/item\", deserialize: deserialize}).then(function(data) {\n\t\t\t\to(data).deepEquals({test: 123})\n\t\t\t}).then(done)\n\t\t})\n\t\to(\"extract parameter works in GET\", function(done) {\n\t\t\tvar extract = function() {\n\t\t\t\treturn {test: 123}\n\t\t\t}\n\n\t\t\tmock.$defineRoutes({\n\t\t\t\t\"GET /item\": function() {\n\t\t\t\t\treturn {status: 200, responseText: \"\"}\n\t\t\t\t}\n\t\t\t})\n\t\t\trequest({method: \"GET\", url: \"/item\", extract: extract}).then(function(data) {\n\t\t\t\to(data).deepEquals({test: 123})\n\t\t\t}).then(done)\n\t\t})\n\t\to(\"extract parameter works in POST\", function(done) {\n\t\t\tvar extract = function() {\n\t\t\t\treturn {test: 123}\n\t\t\t}\n\n\t\t\tmock.$defineRoutes({\n\t\t\t\t\"POST /item\": function() {\n\t\t\t\t\treturn {status: 200, responseText: \"\"}\n\t\t\t\t}\n\t\t\t})\n\t\t\trequest({method: \"POST\", url: \"/item\", extract: extract}).then(function(data) {\n\t\t\t\to(data).deepEquals({test: 123})\n\t\t\t}).then(done)\n\t\t})\n\t\to(\"ignores deserialize if extract is defined\", function(done) {\n\t\t\tvar extract = function(data) {\n\t\t\t\treturn data.status\n\t\t\t}\n\t\t\tvar deserialize = o.spy()\n\n\t\t\tmock.$defineRoutes({\n\t\t\t\t\"GET /item\": function() {\n\t\t\t\t\treturn {status: 200, responseText: \"\"}\n\t\t\t\t}\n\t\t\t})\n\t\t\trequest({method: \"GET\", url: \"/item\", extract: extract, deserialize: deserialize}).then(function(data) {\n\t\t\t\to(data).equals(200)\n\t\t\t}).then(function() {\n\t\t\t\to(deserialize.callCount).equals(0)\n\t\t\t}).then(done)\n\t\t})\n\t\to(\"config parameter works\", function(done) {\n\t\t\tmock.$defineRoutes({\n\t\t\t\t\"POST /item\": function() {\n\t\t\t\t\treturn {status: 200, responseText: \"\"}\n\t\t\t\t}\n\t\t\t})\n\t\t\trequest({method: \"POST\", url: \"/item\", config: config}).then(done)\n\n\t\t\tfunction config(xhr) {\n\t\t\t\to(typeof xhr.setRequestHeader).equals(\"function\")\n\t\t\t\to(typeof xhr.open).equals(\"function\")\n\t\t\t\to(typeof xhr.send).equals(\"function\")\n\t\t\t}\n\t\t})\n\t\to(\"requests don't block each other\", function(done) {\n\t\t\tmock.$defineRoutes({\n\t\t\t\t\"GET /item\": function() {\n\t\t\t\t\treturn {status: 200, responseText: \"[]\"}\n\t\t\t\t}\n\t\t\t})\n\t\t\trequest(\"/item\").then(function() {\n\t\t\t\treturn request(\"/item\")\n\t\t\t})\n\t\t\trequest(\"/item\").then(function() {\n\t\t\t\treturn request(\"/item\")\n\t\t\t})\n\t\t\tsetTimeout(function() {\n\t\t\t\to(complete.callCount).equals(4)\n\t\t\t\tdone()\n\t\t\t}, 20)\n\t\t})\n\t\to(\"requests trigger finally once with a chained then\", function(done) {\n\t\t\tmock.$defineRoutes({\n\t\t\t\t\"GET /item\": function() {\n\t\t\t\t\treturn {status: 200, responseText: \"[]\"}\n\t\t\t\t}\n\t\t\t})\n\t\t\tvar promise = request(\"/item\")\n\t\t\tpromise.then(function() {}).then(function() {})\n\t\t\tpromise.then(function() {}).then(function() {})\n\t\t\tsetTimeout(function() {\n\t\t\t\to(complete.callCount).equals(1)\n\t\t\t\tdone()\n\t\t\t}, 20)\n\t\t})\n\t\to(\"requests does not trigger finally when background: true\", function(done) {\n\t\t\tmock.$defineRoutes({\n\t\t\t\t\"GET /item\": function() {\n\t\t\t\t\treturn {status: 200, responseText: \"[]\"}\n\t\t\t\t}\n\t\t\t})\n\t\t\trequest(\"/item\", {background: true}).then(function() {})\n\n\t\t\tsetTimeout(function() {\n\t\t\t\to(complete.callCount).equals(0)\n\t\t\t\tdone()\n\t\t\t}, 20)\n\t\t})\n\t\to(\"headers are set when header arg passed\", function(done) {\n\t\t\tmock.$defineRoutes({\n\t\t\t\t\"POST /item\": function() {\n\t\t\t\t\treturn {status: 200, responseText: \"\"}\n\t\t\t\t}\n\t\t\t})\n\t\t\trequest({method: \"POST\", url: \"/item\", config: config, headers: {\"Custom-Header\": \"Value\"}}).then(done)\n\n\t\t\tfunction config(xhr) {\n\t\t\t\to(xhr.getRequestHeader(\"Custom-Header\")).equals(\"Value\")\n\t\t\t}\n\t\t})\n\t\to(\"headers are with higher precedence than default headers\", function(done) {\n\t\t\tmock.$defineRoutes({\n\t\t\t\t\"POST /item\": function() {\n\t\t\t\t\treturn {status: 200, responseText: \"\"}\n\t\t\t\t}\n\t\t\t})\n\t\t\trequest({method: \"POST\", url: \"/item\", config: config, headers: {\"Content-Type\": \"Value\"}}).then(done)\n\n\t\t\tfunction config(xhr) {\n\t\t\t\to(xhr.getRequestHeader(\"Content-Type\")).equals(\"Value\")\n\t\t\t}\n\t\t})\n\t\to(\"doesn't fail on abort\", function(done) {\n\t\t\tmock.$defineRoutes({\n\t\t\t\t\"GET /item\": function() {\n\t\t\t\t\treturn {status: 200, responseText: JSON.stringify({a: 1})}\n\t\t\t\t}\n\t\t\t})\n\n\t\t\tvar failed = false\n\t\t\tvar resolved = false\n\t\t\tfunction handleAbort(xhr) {\n\t\t\t\tvar onreadystatechange = xhr.onreadystatechange\n\t\t\t\txhr.onreadystatechange = function() {\n\t\t\t\t\tonreadystatechange.call(xhr, {target: xhr})\n\t\t\t\t\tsetTimeout(function() { // allow promises to (not) resolve first\n\t\t\t\t\t\to(failed).equals(false)\n\t\t\t\t\t\to(resolved).equals(false)\n\t\t\t\t\t\tdone()\n\t\t\t\t\t}, 0)\n\t\t\t\t}\n\t\t\t\txhr.abort()\n\t\t\t}\n\t\t\trequest({method: \"GET\", url: \"/item\", config: handleAbort}).catch(function() {\n\t\t\t\tfailed = true\n\t\t\t})\n\t\t\t\t.then(function() {\n\t\t\t\t\tresolved = true\n\t\t\t\t})\n\t\t})\n\t\to(\"doesn't fail on replaced abort\", function(done) {\n\t\t\tmock.$defineRoutes({\n\t\t\t\t\"GET /item\": function() {\n\t\t\t\t\treturn {status: 200, responseText: JSON.stringify({a: 1})}\n\t\t\t\t}\n\t\t\t})\n\n\t\t\tvar failed = false\n\t\t\tvar resolved = false\n\t\t\tvar abortSpy = o.spy()\n\t\t\tvar replacement\n\t\t\tfunction handleAbort(xhr) {\n\t\t\t\tvar onreadystatechange = xhr.onreadystatechange\n\t\t\t\txhr.onreadystatechange = function() {\n\t\t\t\t\tonreadystatechange.call(xhr, {target: xhr})\n\t\t\t\t\tsetTimeout(function() { // allow promises to (not) resolve first\n\t\t\t\t\t\to(failed).equals(false)\n\t\t\t\t\t\to(resolved).equals(false)\n\t\t\t\t\t\tdone()\n\t\t\t\t\t}, 0)\n\t\t\t\t}\n\t\t\t\treturn replacement = {\n\t\t\t\t\tsend: xhr.send.bind(xhr),\n\t\t\t\t\tabort: abortSpy,\n\t\t\t\t}\n\t\t\t}\n\t\t\trequest({method: \"GET\", url: \"/item\", config: handleAbort}).then(function() {\n\t\t\t\tresolved = true\n\t\t\t}, function() {\n\t\t\t\tfailed = true\n\t\t\t})\n\t\t\treplacement.abort()\n\t\t\to(abortSpy.callCount).equals(1)\n\t\t})\n\t\to(\"doesn't fail on file:// status 0\", function(done) {\n\t\t\tmock.$defineRoutes({\n\t\t\t\t\"GET /item\": function() {\n\t\t\t\t\treturn {status: 0, responseText: JSON.stringify({a: 1})}\n\t\t\t\t}\n\t\t\t})\n\t\t\tvar failed = false\n\t\t\trequest({method: \"GET\", url: \"file:///item\"}).catch(function() {\n\t\t\t\tfailed = true\n\t\t\t}).then(function(data) {\n\t\t\t\to(failed).equals(false)\n\t\t\t\to(data).deepEquals({a: 1})\n\t\t\t}).then(function() {\n\t\t\t\tdone()\n\t\t\t})\n\t\t})\n\t\to(\"set timeout to xhr instance\", function() {\n\t\t\tmock.$defineRoutes({\n\t\t\t\t\"GET /item\": function() {\n\t\t\t\t\treturn {status: 200, responseText: \"\"}\n\t\t\t\t}\n\t\t\t})\n\t\t\treturn request({\n\t\t\t\tmethod: \"GET\", url: \"/item\",\n\t\t\t\ttimeout: 42,\n\t\t\t\tconfig: function(xhr) {\n\t\t\t\t\to(xhr.timeout).equals(42)\n\t\t\t\t}\n\t\t\t})\n\t\t})\n\t\to(\"set responseType to request instance\", function() {\n\t\t\tmock.$defineRoutes({\n\t\t\t\t\"GET /item\": function() {\n\t\t\t\t\treturn {status: 200, responseText: \"\"}\n\t\t\t\t}\n\t\t\t})\n\t\t\treturn request({\n\t\t\t\tmethod: \"GET\", url: \"/item\",\n\t\t\t\tresponseType: \"blob\",\n\t\t\t\tconfig: function(xhr) {\n\t\t\t\t\to(xhr.responseType).equals(\"blob\")\n\t\t\t\t}\n\t\t\t})\n\t\t})\n\t\to(\"params unmodified after interpolate\", function() {\n\t\t\tmock.$defineRoutes({\n\t\t\t\t\"PUT /items/1\": function() {\n\t\t\t\t\treturn {status: 200, responseText: \"[]\"}\n\t\t\t\t}\n\t\t\t})\n\t\t\tvar params = {x: 1, y: 2}\n\t\t\tvar p = request({method: \"PUT\", url: \"/items/:x\", params: params})\n\n\t\t\to(params).deepEquals({x: 1, y: 2})\n\n\t\t\treturn p\n\t\t})\n\t\to(\"can return replacement from config\", function() {\n\t\t\tmock.$defineRoutes({\n\t\t\t\t\"GET /a\": function() {\n\t\t\t\t\treturn {status: 200, responseText: \"[]\"}\n\t\t\t\t}\n\t\t\t})\n\t\t\tvar result\n\t\t\treturn request({\n\t\t\t\turl: \"/a\",\n\t\t\t\tconfig: function(xhr) {\n\t\t\t\t\treturn result = {\n\t\t\t\t\t\tsend: o.spy(xhr.send.bind(xhr)),\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t})\n\t\t\t\t.then(function () {\n\t\t\t\t\to(result.send.callCount).equals(1)\n\t\t\t\t})\n\t\t})\n\t\to(\"can abort from replacement\", function() {\n\t\t\tmock.$defineRoutes({\n\t\t\t\t\"GET /a\": function() {\n\t\t\t\t\treturn {status: 200, responseText: \"[]\"}\n\t\t\t\t}\n\t\t\t})\n\t\t\tvar result\n\n\t\t\trequest({\n\t\t\t\turl: \"/a\",\n\t\t\t\tconfig: function(xhr) {\n\t\t\t\t\treturn result = {\n\t\t\t\t\t\tsend: o.spy(xhr.send.bind(xhr)),\n\t\t\t\t\t\tabort: o.spy(),\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t})\n\n\t\t\tresult.abort()\n\t\t})\n\t})\n\to.spec(\"failure\", function() {\n\t\to(\"rejects on server error\", function(done) {\n\t\t\tmock.$defineRoutes({\n\t\t\t\t\"GET /item\": function() {\n\t\t\t\t\treturn {status: 500, responseText: JSON.stringify({error: \"error\"})}\n\t\t\t\t}\n\t\t\t})\n\t\t\trequest({method: \"GET\", url: \"/item\"}).catch(function(e) {\n\t\t\t\to(e instanceof Error).equals(true)\n\t\t\t\to(e.message).equals(\"[object Object]\")\n\t\t\t\to(e.response).deepEquals({error: \"error\"})\n\t\t\t\to(e.code).equals(500)\n\t\t\t}).then(done)\n\t\t})\n\t\to(\"adds response to Error\", function(done) {\n\t\t\tmock.$defineRoutes({\n\t\t\t\t\"GET /item\": function() {\n\t\t\t\t\treturn {status: 500, responseText: JSON.stringify({message: \"error\", stack: \"error on line 1\"})}\n\t\t\t\t}\n\t\t\t})\n\t\t\trequest({method: \"GET\", url: \"/item\"}).catch(function(e) {\n\t\t\t\to(e instanceof Error).equals(true)\n\t\t\t\to(e.response.message).equals(\"error\")\n\t\t\t\to(e.response.stack).equals(\"error on line 1\")\n\t\t\t}).then(done)\n\t\t})\n\t\to(\"rejects on non-JSON server error\", function(done) {\n\t\t\tmock.$defineRoutes({\n\t\t\t\t\"GET /item\": function() {\n\t\t\t\t\treturn {status: 500, responseText: \"error\"}\n\t\t\t\t}\n\t\t\t})\n\t\t\trequest({method: \"GET\", url: \"/item\"}).catch(function(e) {\n\t\t\t\to(e.message).equals(\"null\")\n\t\t\t\to(e.response).equals(null)\n\t\t\t}).then(done)\n\t\t})\n\t\to(\"triggers all branched catches upon rejection\", function(done) {\n\t\t\tmock.$defineRoutes({\n\t\t\t\t\"GET /item\": function() {\n\t\t\t\t\treturn {status: 500, responseText: \"error\"}\n\t\t\t\t}\n\t\t\t})\n\t\t\tvar promise = request({method: \"GET\", url: \"/item\"})\n\t\t\tvar then = o.spy()\n\t\t\tvar catch1 = o.spy()\n\t\t\tvar catch2 = o.spy()\n\t\t\tvar catch3 = o.spy()\n\n\t\t\tpromise.catch(catch1)\n\t\t\tpromise.then(then, catch2)\n\t\t\tpromise.then(then).catch(catch3)\n\n\t\t\tcallAsync(function() {\n\t\t\t\tcallAsync(function() {\n\t\t\t\t\tcallAsync(function() {\n\t\t\t\t\t\to(catch1.callCount).equals(1)\n\t\t\t\t\t\to(then.callCount).equals(0)\n\t\t\t\t\t\to(catch2.callCount).equals(1)\n\t\t\t\t\t\to(catch3.callCount).equals(1)\n\t\t\t\t\t\tdone()\n\t\t\t\t\t})\n\t\t\t\t})\n\t\t\t})\n\t\t})\n\t\to(\"rejects on cors-like error\", function(done) {\n\t\t\tmock.$defineRoutes({\n\t\t\t\t\"GET /item\": function() {\n\t\t\t\t\treturn {status: 0}\n\t\t\t\t}\n\t\t\t})\n\t\t\trequest({method: \"GET\", url: \"/item\"}).catch(function(e) {\n\t\t\t\to(e instanceof Error).equals(true)\n\t\t\t}).then(done)\n\t\t})\n\t\to(\"rejects on request timeout\", function(done) {\n\t\t\tvar timeout = 50\n\t\t\tvar timeToGetItem = timeout + 1\n\n\t\t\tmock.$defineRoutes({\n\t\t\t\t\"GET /item\": function() {\n\t\t\t\t\treturn new Promise(function(resolve) {\n\t\t\t\t\t\tsetTimeout(function() {\n\t\t\t\t\t\t\tresolve({status: 200})\n\t\t\t\t\t\t}, timeToGetItem)\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t})\n\n\t\t\trequest({\n\t\t\t\tmethod: \"GET\", url: \"/item\",\n\t\t\t\ttimeout: timeout\n\t\t\t}).catch(function(e) {\n\t\t\t\to(e instanceof Error).equals(true)\n\t\t\t\to(e.message).equals(\"Request timed out\")\n\t\t\t\to(e.code).equals(0)\n\t\t\t}).then(function() {\n\t\t\t\tdone()\n\t\t\t})\n\t\t})\n\t\to(\"does not reject when time to request resource does not exceed timeout\", function(done) {\n\t\t\tvar timeout = 50\n\t\t\tvar timeToGetItem = timeout - 1\n\t\t\tvar isRequestRejected = false\n\n\t\t\tmock.$defineRoutes({\n\t\t\t\t\"GET /item\": function() {\n\t\t\t\t\treturn new Promise(function(resolve) {\n\t\t\t\t\t\tsetTimeout(function() {\n\t\t\t\t\t\t\tresolve({status: 200})\n\t\t\t\t\t\t}, timeToGetItem)\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t})\n\n\t\t\trequest({\n\t\t\t\tmethod: \"GET\", url: \"/item\",\n\t\t\t\ttimeout: timeout\n\t\t\t}).catch(function(e) {\n\t\t\t\tisRequestRejected = true\n\t\t\t\to(e.message).notEquals(\"Request timed out\")\n\t\t\t}).then(function() {\n\t\t\t\to(isRequestRejected).equals(false)\n\t\t\t\tdone()\n\t\t\t})\n\t\t})\n\t\to(\"does not reject on status error code when extract provided\", function(done) {\n\t\t\tmock.$defineRoutes({\n\t\t\t\t\"GET /item\": function() {\n\t\t\t\t\treturn {status: 500, responseText: JSON.stringify({message: \"error\"})}\n\t\t\t\t}\n\t\t\t})\n\t\t\trequest({\n\t\t\t\tmethod: \"GET\", url: \"/item\",\n\t\t\t\textract: function(xhr) {return JSON.parse(xhr.responseText)}\n\t\t\t}).then(function(data) {\n\t\t\t\to(data.message).equals(\"error\")\n\t\t\t\tdone()\n\t\t\t})\n\t\t})\n\t\to(\"rejects on error in extract\", function(done) {\n\t\t\tmock.$defineRoutes({\n\t\t\t\t\"GET /item\": function() {\n\t\t\t\t\treturn {status: 200, responseText: JSON.stringify({a: 1})}\n\t\t\t\t}\n\t\t\t})\n\t\t\trequest({\n\t\t\t\tmethod: \"GET\", url: \"/item\",\n\t\t\t\textract: function() {throw new Error(\"error\")}\n\t\t\t}).catch(function(e) {\n\t\t\t\to(e instanceof Error).equals(true)\n\t\t\t\to(e.message).equals(\"error\")\n\t\t\t}).then(function() {\n\t\t\t\tdone()\n\t\t\t})\n\t\t})\n\t})\n\to.spec(\"json header\", function() {\n\t\tfunction checkUnset(method) {\n\t\t\to(\"doesn't set header on \" + method + \" without body\", function(done) {\n\t\t\t\tvar routes = {}\n\t\t\t\troutes[method + \" /item\"] = function() {\n\t\t\t\t\treturn {status: 200, responseText: JSON.stringify({a: 1})}\n\t\t\t\t}\n\t\t\t\tmock.$defineRoutes(routes)\n\t\t\t\trequest({\n\t\t\t\t\tmethod: method, url: \"/item\",\n\t\t\t\t\tconfig: function(xhr) {\n\t\t\t\t\t\tvar header = xhr.getRequestHeader(\"Content-Type\")\n\t\t\t\t\t\to(header).equals(undefined)\n\t\t\t\t\t\theader = xhr.getRequestHeader(\"Accept\")\n\t\t\t\t\t\to(header).equals(\"application/json, text/*\")\n\t\t\t\t\t}\n\t\t\t\t}).then(function(result) {\n\t\t\t\t\to(result).deepEquals({a: 1})\n\t\t\t\t\tdone()\n\t\t\t\t}).catch(function(e) {\n\t\t\t\t\tdone(e)\n\t\t\t\t})\n\t\t\t})\n\t\t}\n\n\t\tfunction checkSet(method, body) {\n\t\t\to(\"sets header on \" + method + \" with body\", function(done) {\n\t\t\t\tvar routes = {}\n\t\t\t\troutes[method + \" /item\"] = function(response) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\tstatus: 200,\n\t\t\t\t\t\tresponseText: JSON.stringify({body: JSON.parse(response.body)}),\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tmock.$defineRoutes(routes)\n\t\t\t\trequest({\n\t\t\t\t\tmethod: method, url: \"/item\", body: body,\n\t\t\t\t\tconfig: function(xhr) {\n\t\t\t\t\t\tvar header = xhr.getRequestHeader(\"Content-Type\")\n\t\t\t\t\t\to(header).equals(\"application/json; charset=utf-8\")\n\t\t\t\t\t\theader = xhr.getRequestHeader(\"Accept\")\n\t\t\t\t\t\to(header).equals(\"application/json, text/*\")\n\t\t\t\t\t}\n\t\t\t\t}).then(function(result) {\n\t\t\t\t\to(result).deepEquals({body: body})\n\t\t\t\t\tdone()\n\t\t\t\t}).catch(function(e) {\n\t\t\t\t\tdone(e)\n\t\t\t\t})\n\t\t\t})\n\t\t}\n\n\t\tcheckUnset(\"GET\")\n\t\tcheckUnset(\"HEAD\")\n\t\tcheckUnset(\"OPTIONS\")\n\t\tcheckUnset(\"POST\")\n\t\tcheckUnset(\"PUT\")\n\t\tcheckUnset(\"DELETE\")\n\t\tcheckUnset(\"PATCH\")\n\n\t\tcheckSet(\"GET\", {foo: \"bar\"})\n\t\tcheckSet(\"HEAD\", {foo: \"bar\"})\n\t\tcheckSet(\"OPTIONS\", {foo: \"bar\"})\n\t\tcheckSet(\"POST\", {foo: \"bar\"})\n\t\tcheckSet(\"PUT\", {foo: \"bar\"})\n\t\tcheckSet(\"DELETE\", {foo: \"bar\"})\n\t\tcheckSet(\"PATCH\", {foo: \"bar\"})\n\t})\n\n\t// See: https://github.com/MithrilJS/mithril.js/issues/2426\n\t//\n\t// TL;DR: lots of subtlety. Make sure you read the ES spec closely before\n\t// updating this code or the corresponding finalizer code in\n\t// `request/request` responsible for scheduling autoredraws, or you might\n\t// inadvertently break things.\n\t//\n\t// The precise behavior here is that it schedules a redraw immediately after\n\t// the second tick *after* the promise resolves, but `await` in engines that\n\t// have implemented the change in https://github.com/tc39/ecma262/pull/1250\n\t// will only take one tick to get the value. Engines that haven't\n\t// implemented that spec change would wait until the tick after the redraw\n\t// was scheduled before it can see the new value. But this only applies when\n\t// the engine needs to coerce the value, and this is where things get a bit\n\t// hairy. As per spec, V8 checks the `.constructor` property of promises and\n\t// if that `=== Promise`, it does *not* coerce it using `.then`, but instead\n\t// just resolves it directly. This, of course, can screw with our autoredraw\n\t// behavior, and we have to work around that. At the time of writing, no\n\t// other browser checks for this additional constraint, and just blindly\n\t// invokes `.then` instead, and so we end up working as anticipated. But for\n\t// obvious reasons, it's a bad idea to rely on a spec violation for things\n\t// to work unless the spec itself is clearly broken (in this case, it's\n\t// not). And so we need to test for this very unusual edge case.\n\t//\n\t// The direct `eval` is just so I can convert early errors to runtime\n\t// errors without having to explicitly wire up all the bindings set up in\n\t// `o.beforeEach`. I evaluate it immediately inside a `try`/`catch` instead\n\t// of inside the test code so any relevant syntax error can be detected\n\t// ahead of time and the test skipped entirely. It might trigger mental\n\t// alarms because `eval` is normally asking for problems, but this is a\n\t// rare case where it's genuinely safe and rational.\n\ttry {\n\t\t// eslint-disable-next-line no-eval\n\t\tvar runAsyncTest = eval(\n\t\t\t\"async () => {\\n\" +\n\t\t\t\"    var p = request('/item')\\n\" +\n\t\t\t\"    o(complete.callCount).equals(0)\\n\" +\n\t\t\t// Note: this step does *not* invoke `.then` on the promise returned\n\t\t\t// from `p.then(resolve, reject)`.\n\t\t\t\"    await p\\n\" +\n\t\t\t// The spec prior to https://github.com/tc39/ecma262/pull/1250 used\n\t\t\t// to take 3 ticks instead of 1, so `complete` would have been\n\t\t\t// called already and we would've been done. After it, it now takes\n\t\t\t// 1 tick and so `complete` wouldn't have yet been called - it takes\n\t\t\t// 2 ticks to get called. And so we have to wait for one more ticks\n\t\t\t// for `complete` to get called.\n\t\t\t\"    await null\\n\" +\n\t\t\t\"    o(complete.callCount).equals(1)\\n\" +\n\t\t\t\"}\"\n\t\t)\n\n\t\to(\"invokes the redraw in native async/await\", function () {\n\t\t\t// Use the native promise for correct semantics. This test will fail\n\t\t\t// if you use the polyfill, as it's based on `setImmediate` (falling\n\t\t\t// back to `setTimeout`), and promise microtasks are run at higher\n\t\t\t// priority than either of those.\n\t\t\trequest = Request(mock, complete).request\n\t\t\tmock.$defineRoutes({\n\t\t\t\t\"GET /item\": function() {\n\t\t\t\t\treturn {status: 200, responseText: \"[]\"}\n\t\t\t\t}\n\t\t\t})\n\t\t\treturn runAsyncTest()\n\t\t})\n\t} catch (e) {\n\t\t// ignore - this is just for browsers that natively support\n\t\t// `async`/`await`, like most modern browsers.\n\t\t// it's just a syntax error anyways.\n\t}\n})\n"
  },
  {
    "path": "request.js",
    "content": "\"use strict\"\n\nvar mountRedraw = require(\"./mount-redraw\")\n\nmodule.exports = require(\"./request/request\")(typeof window !== \"undefined\" ? window : null, mountRedraw.redraw)\n"
  },
  {
    "path": "route.js",
    "content": "\"use strict\"\n\nvar mountRedraw = require(\"./mount-redraw\")\n\nmodule.exports = require(\"./api/router\")(typeof window !== \"undefined\" ? window : null, mountRedraw)\n"
  },
  {
    "path": "scripts/.eslintrc.js",
    "content": "\"use strict\"\n\nmodule.exports = {\n\t\"extends\": \"../.eslintrc.js\",\n\t\"env\": {\n\t\t\"browser\": null,\n\t\t\"node\": true,\n\t\t\"es2022\": true,\n\t},\n\t\"parserOptions\": {\n\t\t\"ecmaVersion\": 2022,\n\t},\n\t\"rules\": {\n\t\t\"no-process-env\": \"off\",\n\t},\n};\n"
  },
  {
    "path": "scripts/_bundler-impl.js",
    "content": "\"use strict\"\n\nconst fs = require(\"fs\")\nconst path = require(\"path\")\nconst execFileSync = require(\"child_process\").execFileSync\nconst util = require(\"util\")\n\nconst readFile = util.promisify(fs.readFile)\nconst access = util.promisify(fs.access)\n\nfunction isFile(filepath) {\n\treturn access(filepath).then(() => true, () => false)\n}\nfunction escapeRegExp(string) {\n\treturn string.replace(/[|\\\\{}()[\\]^$+*?.-]/g, \"\\\\$&\")\n}\nfunction escapeReplace(string) {\n\treturn string.replace(/\\$/g, \"\\\\$&\")\n}\n\nasync function resolve(filepath, filename) {\n\tif (filename[0] !== \".\") {\n\t\t// resolve as npm dependency\n\t\tconst packagePath = `./node_modules/${filename}/package.json`\n\t\tlet json, meta\n\n\t\ttry {\n\t\t\tjson = await readFile(packagePath, \"utf8\")\n\t\t} catch (e) {\n\t\t\tmeta = {}\n\t\t}\n\n\t\tif (json) {\n\t\t\ttry {\n\t\t\t\tmeta = JSON.parse(json)\n\t\t\t}\n\t\t\tcatch (e) {\n\t\t\t\tthrow new Error(`invalid JSON for ${packagePath}: ${json}`)\n\t\t\t}\n\t\t}\n\n\t\tconst main = `./node_modules/${filename}/${meta.main || `${filename}.js`}`\n\t\treturn path.resolve(await isFile(main) ? main : `./node_modules/${filename}/index.js`)\n\t}\n\telse {\n\t\t// resolve as local dependency\n\t\treturn path.resolve(path.dirname(filepath), filename + \".js\")\n\t}\n}\n\nfunction matchAll(str, regexp) {\n\tregexp.lastIndex = 0\n\tconst result = []\n\tlet exec\n\twhile ((exec = regexp.exec(str)) != null) result.push(exec)\n\treturn result\n}\n\nlet error\nmodule.exports = async (input) => {\n\tconst modules = new Map()\n\tconst bindings = new Map()\n\tconst declaration = /^\\s*(?:var|let|const|function)[\\t ]+([\\w_$]+)/gm\n\tconst include = /(?:((?:var|let|const|,|)[\\t ]*)([\\w_$\\.\\[\\]\"'`]+)(\\s*=\\s*))?require\\(([^\\)]+)\\)(\\s*[`\\.\\(\\[])?/gm\n\tlet uuid = 0\n\tasync function process(filepath, data) {\n\t\tfor (const [, binding] of matchAll(data, declaration)) {\n\t\t\tif (!bindings.has(binding)) bindings.set(binding, 0)\n\t\t}\n\n\t\tconst tasks = []\n\n\t\tfor (const [, def = \"\", variable = \"\", eq = \"\", dep, rest = \"\"] of matchAll(data, include)) {\n\t\t\ttasks.push({filename: JSON.parse(dep), def, variable, eq, rest})\n\t\t}\n\n\t\tconst imports = await Promise.all(\n\t\t\ttasks.map((t) => resolve(filepath, t.filename))\n\t\t)\n\n\t\tconst results = []\n\t\tfor (const [i, task] of tasks.entries()) {\n\t\t\tconst dependency = imports[i]\n\t\t\tlet pre = \"\", def = task.def\n\t\t\tif (def[0] === \",\") def = \"\\nvar \", pre = \"\\n\"\n\t\t\tconst localUUID = uuid // global uuid can update from nested `process` call, ensure same id is used on declaration and consumption\n\t\t\tconst existingModule = modules.get(dependency)\n\t\t\tmodules.set(dependency, task.rest ? `_${localUUID}` : task.variable)\n\t\t\tconst code = await process(\n\t\t\t\tdependency,\n\t\t\t\tpre + (\n\t\t\t\t\texistingModule == null\n\t\t\t\t\t\t? await exportCode(task.filename, dependency, def, task.variable, task.eq, task.rest, localUUID)\n\t\t\t\t\t\t: def + task.variable + task.eq + existingModule\n\t\t\t\t)\n\t\t\t)\n\t\t\tuuid++\n\t\t\tresults.push(code + task.rest)\n\t\t}\n\n\t\tlet i = 0\n\t\treturn data.replace(include, () => results[i++])\n\t}\n\n\tasync function exportCode(filename, filepath, def, variable, eq, rest, uuid) {\n\t\tlet code = await readFile(filepath, \"utf-8\")\n\t\t// if there's a syntax error, report w/ proper stack trace\n\t\ttry {\n\t\t\tnew Function(code)\n\t\t}\n\t\tcatch (e) {\n\t\t\ttry {\n\t\t\t\texecFileSync(\"node\", [\"--check\", filepath], {\n\t\t\t\t\tstdio: \"pipe\",\n\t\t\t\t})\n\t\t\t}\n\t\t\tcatch (e) {\n\t\t\t\tif (e.message !== error) {\n\t\t\t\t\terror = e.message\n\t\t\t\t\tconsole.log(`\\x1b[31m${e.message}\\x1b[0m`)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// disambiguate collisions\n\t\tconst targetPromises = []\n\t\tcode.replace(include, (match, def, variable, eq, dep) => {\n\t\t\ttargetPromises.push(resolve(filepath, JSON.parse(dep)))\n\t\t})\n\n\t\tconst ignoredTargets = await Promise.all(targetPromises)\n\t\tconst ignored = new Set()\n\n\t\tfor (const target of ignoredTargets) {\n\t\t\tconst binding = modules.get(target)\n\t\t\tif (binding != null) ignored.add(binding)\n\t\t}\n\n\t\tif (new RegExp(`module\\\\.exports\\\\s*=\\\\s*${variable}\\s*$`, \"m\").test(code)) ignored.add(variable)\n\t\tfor (const [binding, count] of bindings) {\n\t\t\tif (!ignored.has(binding)) {\n\t\t\t\tconst before = code\n\t\t\t\tcode = code.replace(\n\t\t\t\t\tnew RegExp(`(\\\\b)${escapeRegExp(binding)}\\\\b`, \"g\"),\n\t\t\t\t\tescapeReplace(binding) + count\n\t\t\t\t)\n\t\t\t\tif (before !== code) bindings.set(binding, count + 1)\n\t\t\t}\n\t\t}\n\n\t\t// fix strings that got mangled by collision disambiguation\n\t\tconst string = /([\"'])((?:\\\\\\1|.)*?)(\\1)/g\n\t\tconst candidates = Array.from(bindings, ([binding, count]) => escapeRegExp(binding) + (count - 1)).join(\"|\")\n\t\tconst variables = new RegExp(candidates, \"g\")\n\t\tcode = code.replace(string, (match, open, data, close) => {\n\t\t\tconst fixed = data.replace(variables, (match) => match.replace(/\\d+$/, \"\"))\n\t\t\treturn open + fixed + close\n\t\t})\n\n\t\t// fix regexp literals\n\t\t// Note: This regexp, while it doesn't technically capture all cases a regexp could appear, should hopefully work for now.\n\t\tconst regexpLiteral = /([=({[](?:[\\s\\u2028\\u2029]|\\/\\/.*?[\\r\\n\\u2028\\u2029]|\\/\\*[\\s\\S]*?\\*\\/)*)(\\/(?:[^\\\\\\/[\\r\\n\\u2028\\u2029]|\\\\[^\\r\\n\\u2028\\u2029]|\\[(?:[^\\]\\\\\\r\\n\\u2028\\u2029]|\\\\[^\\r\\n\\u2028\\u2029])*\\])+\\/[$\\p{ID_Continue}]*)/ug\n\t\tcode = code.replace(regexpLiteral, (match, pre, literal) => {\n\t\t\tconst fixed = literal.replace(variables, (match) => match.replace(/\\d+$/, \"\"))\n\t\t\treturn pre + fixed\n\t\t})\n\n\t\t//fix props\n\t\tconst props = new RegExp(`(\\\\.\\\\.)?((?:[^:]\\\\/\\\\/.*)?\\\\.\\\\s*)(${candidates})|([\\\\{,]\\\\s*)(${candidates})(\\\\s*:)`, \"gm\")\n\t\tcode = code.replace(props, (match, dotdot, dot, a, pre, b, post) => {\n\t\t\t// Don't do anything because dot was matched in a comment\n\t\t\tif (dot && dot.indexOf(\"//\") === 1) return match\n\t\t\t// Don't do anything because dot is a part of spread syntax or destructuring\n\t\t\tif (dotdot) return match\n\t\t\tif (dot) return dot + a.replace(/\\d+$/, \"\")\n\t\t\treturn pre + b.replace(/\\d+$/, \"\") + post\n\t\t})\n\n\t\t// fix comment‑only lines\n\t\tconst commentOnlyLines = /^(?:[ \\t]*\\/\\/[^\\r\\n]*|[ \\t]*\\/\\*[\\s\\S]*?\\*\\/[ \\t]*)\\r?$/gm\n\t\tcode = code.replace(commentOnlyLines, (comment) =>\n\t\t\tcomment.replace(variables, (match) => match.replace(/\\d+$/, \"\"))\n\t\t)\n\n\t\treturn code\n\t\t\t.replace(/(\"|')use strict\\1;?/gm, \"\") // remove extraneous \"use strict\"\n\t\t\t.replace(/module\\.exports\\s*=\\s*/gm, escapeReplace(rest ? `var _${uuid}` + eq : def + (rest ? \"_\" : \"\") + variable + eq)) // export\n\t\t\t+ (rest ? `\\n${def}${variable}${eq}_${uuid}` : \"\") // if `rest` is truthy, it means the expression is fluent or higher-order (e.g. require(path).foo or require(path)(foo)\n\t}\n\n\tconst code = \";(function() {\\n\" +\n\t\t(await process(path.resolve(input), await readFile(input, \"utf-8\")))\n\t\t\t.replace(/^\\s*((?:var|let|const|)[\\t ]*)([\\w_$\\.]+)(\\s*=\\s*)(\\2)(?=[\\s]+(\\w)|;|$)/gm, \"\") // remove assignments to self\n\t\t\t.replace(/;+(\\r|\\n|$)/g, \";$1\") // remove redundant semicolons\n\t\t\t.replace(/(\\r|\\n)+/g, \"\\n\").replace(/(\\r|\\n)$/, \"\") + // remove multiline breaks\n\t\t\"\\n}());\"\n\n\t//try {new Function(code); console.log(`build completed at ${new Date()}`)} catch (e) {}\n\terror = null\n\treturn code\n}\n"
  },
  {
    "path": "scripts/bundler-readme.md",
    "content": "# bundler.js\n\nSimplistic CommonJS module bundler\n\nVersion: 0.1\nLicense: MIT\n\n## About\n\nThis bundler attempts to aggressively bundle CommonJS modules by assuming the dependency tree is static, similar to what Rollup does for ES6 modules.\n\nMost browsers don't support ES6 `import/export` syntax, but we can achieve modularity by using CommonJS module syntax and transpiling it.\n\nWebpack is conservative and treats CommonJS modules as non-statically-analyzable since `require` and `module.exports` are legally allowed everywhere. Therefore, it must generate extra code to resolve dependencies at runtime (i.e. `__webpack_require()`). Rollup only works with ES6 modules. ES6 modules can be bundled more efficiently because they are statically analyzable, but some use cases are difficult to handle due to ES6's support for cyclic dependencies and hosting rules. This bundler assumes code is written in CommonJS style but follows a strict set of rules that emulate statically analyzable code and favors the usage of the factory pattern instead of relying on obscure corners of the JavaScript language (hoisting rules and binding semantics).\n\n### Caveats\n\n- Only supports modules that have the `require` and `module.exports` statement declared at the top-level scope before all other code, i.e. it does not support CommonJS modules that rely on dynamic importing/exporting. This means modules should only export a pure function or export a factory function if there are multiple statements and/or internal module state. The factory function pattern allows easier dependency injection in stateful modules, thus making modules testable.\n- Changes the semantics of value/binding exporting between unbundled and bundled code, and therefore relying on those semantics is discouraged.\n- Top level strictness is infectious (i.e. if entry file is in `\"use strict\"` mode, all modules inherit strict mode, and conversely, if the entry file is not in strict mode, all modules are pulled out of strict mode)\n- Currently only supports assignments to `module.exports` (i.e. `module.exports.foo = bar` will not work)\n- It is tiny and dependency-free because it uses regular expressions, and it only supports the narrow range of import/export declaration patterns outlined above.\n"
  },
  {
    "path": "scripts/bundler.js",
    "content": "\"use strict\"\n\nconst fs = require(\"fs\")\nconst zlib = require(\"zlib\")\nconst chokidar = require(\"chokidar\")\nconst Terser = require(\"terser\")\nconst util = require(\"util\")\n\nconst readFile = util.promisify(fs.readFile)\nconst writeFile = util.promisify(fs.writeFile)\nconst gzip = util.promisify(zlib.gzip)\n\nconst bundle = require(\"./_bundler-impl\")\n\nconst aliases = {o: \"output\", m: \"minify\", w: \"watch\", s: \"save\"}\nconst params = Object.create(null)\nlet command\nfor (let arg of process.argv.slice(2)) {\n\tif (arg[0] === '\"') arg = JSON.parse(arg)\n\tif (arg[0] === \"-\") {\n\t\tif (command != null) add(true)\n\t\tcommand = arg.replace(/\\-+/g, \"\")\n\t}\n\telse if (command != null) add(arg)\n\telse params.input = arg\n}\nif (command != null) add(true)\n\nfunction add(value) {\n\tparams[aliases[command] || command] = value\n\tcommand = null\n}\n\nfunction format(n) {\n\treturn n.toString().replace(/(\\d)(?=(\\d\\d\\d)+(?!\\d))/g, \"$1,\")\n}\n\nasync function build() {\n\tconst original = await bundle(params.input)\n\tif (!params.minify) {\n\t\tawait writeFile(params.output, original, \"utf-8\")\n\t\treturn\n\t}\n\tconsole.log(\"minifying...\")\n\t// Terser's \"conditionals\" and \"reduce_funcs\" options seem to degrade performance. So, disable them.\n\tconst minified = await Terser.minify(original, {compress: {conditionals: false, reduce_funcs: false}, mangle: true})\n\tif (minified.error) throw new Error(minified.error)\n\tawait writeFile(params.output, minified.code, \"utf-8\")\n\tconst originalSize = Buffer.byteLength(original, \"utf-8\")\n\tconst compressedSize = Buffer.byteLength(minified.code, \"utf-8\")\n\tconst originalGzipSize = (await gzip(original)).byteLength\n\tconst compressedGzipSize = (await gzip(minified.code)).byteLength\n\n\tconsole.log(\"Original size: \" + format(originalGzipSize) + \" bytes gzipped (\" + format(originalSize) + \" bytes uncompressed)\")\n\tconsole.log(\"Compiled size: \" + format(compressedGzipSize) + \" bytes gzipped (\" + format(compressedSize) + \" bytes uncompressed)\")\n\n\tif (params.save) {\n\t\tconst readme = await readFile(\"./README.md\", \"utf8\")\n\t\tconst kb = compressedGzipSize / 1000\n\n\t\tawait writeFile(\"./README.md\",\n\t\t\treadme.replace(\n\t\t\t\t/(<!-- size -->)(.+?)(<!-- \\/size -->)/,\n\t\t\t\t\"$1\" + (kb % 1 ? kb.toFixed(2) : kb) + \" KB$3\"\n\t\t\t)\n\t\t)\n\t}\n}\n\nbuild()\nif (params.watch) chokidar.watch(\".\", {ignored: params.output}).on(\"all\", build)\n"
  },
  {
    "path": "scripts/minify-stream.js",
    "content": "#!/usr/bin/env node\n/* eslint-disable no-process-exit */\n\"use strict\"\n\nprocess.on(\"unhandledRejection\", (e) => {\n\tprocess.exitCode = 1\n\n\tif (!e.stdout || !e.stderr) throw e\n\n\tconsole.error(e.stack)\n\n\tif (e.stdout?.length) {\n\t\tconsole.error(e.stdout.toString(\"utf-8\"))\n\t}\n\n\tif (e.stderr?.length) {\n\t\tconsole.error(e.stderr.toString(\"utf-8\"))\n\t}\n\n\t// eslint-disable-next-line no-process-exit\n\tprocess.exit()\n})\n\nconst {promises: fs} = require(\"fs\")\nconst path = require(\"path\")\nconst zlib = require(\"zlib\")\nconst Terser = require(\"terser\")\n\nfunction format(n) {\n\treturn n.toString().replace(/(\\d)(?=(\\d\\d\\d)+(?!\\d))/g, \"$1,\")\n}\n\nmodule.exports = minify\nasync function minify() {\n\tconst input = path.resolve(__dirname, \"../stream/stream.js\")\n\tconst output = path.resolve(__dirname, \"../stream/stream.min.js\")\n\tconst original = await fs.readFile(input, \"utf-8\")\n\tconst minified = await Terser.minify(original)\n\tif (minified.error) throw new Error(minified.error)\n\tawait fs.writeFile(output, minified.code, \"utf-8\")\n\tconst originalSize = Buffer.byteLength(original, \"utf-8\")\n\tconst compressedSize = Buffer.byteLength(minified.code, \"utf-8\")\n\tconst originalGzipSize = zlib.gzipSync(original).byteLength\n\tconst compressedGzipSize = zlib.gzipSync(minified.code).byteLength\n\n\tconsole.log(\"Original size: \" + format(originalGzipSize) + \" bytes gzipped (\" + format(originalSize) + \" bytes uncompressed)\")\n\tconsole.log(\"Compiled size: \" + format(compressedGzipSize) + \" bytes gzipped (\" + format(compressedSize) + \" bytes uncompressed)\")\n}\n\nminify()\n"
  },
  {
    "path": "scripts/set-versioned-branch.sh",
    "content": "#!/usr/bin/env bash\nset -euo pipefail\n\nbase=\"$1\"\n\nif [[ -z \"$base\" ]]; then\n    echo '::error::Base branch is missing. Invoke as `bash scripts/set-versioned-branch.sh BASE' >&2\n    exit 1\nfi\n\nmajor=$(node -pe 'require(\"./package.json\").version.replace(/\\..*$/,\"\")')\n# Can't do a force push due to branch protection rules.\ngit checkout \"${base}\"\ngit checkout -B \"${base}-v${major}\"\ngit push --force origin \"${base}-v${major}\"\n"
  },
  {
    "path": "scripts/tests/test-bundler.js",
    "content": "\"use strict\"\n\nconst fs = require(\"fs\")\nconst util = require(\"util\")\nconst path = require(\"path\")\nconst access = util.promisify(fs.access)\nconst writeFile = util.promisify(fs.writeFile)\nconst unlink = util.promisify(fs.unlink)\n\nconst o = require(\"ospec\")\nconst bundle = require(\"../_bundler-impl\")\n\no.spec(\"bundler\", async () => {\n\tlet filesCreated\n\tconst root = path.resolve(__dirname, \"../..\")\n\tconst p = (file) => path.join(root, file)\n\n\tasync function write(filepath, data) {\n\t\ttry {\n\t\t\tawait access(p(filepath))\n\t\t} catch (e) {\n\t\t\treturn writeFile(p(filepath), data, \"utf8\")\n\t\t}\n\t\tthrow new Error(`Don't call \\`write('${filepath}')\\`. Cannot overwrite file.`)\n\t}\n\n\tfunction setup(files) {\n\t\tfilesCreated = Object.keys(files)\n\t\treturn Promise.all(filesCreated.map((f) => write(f, files[f])))\n\t}\n\n\to.afterEach(() => Promise.all(\n\t\tfilesCreated.map((filepath) => unlink(p(filepath)))\n\t))\n\n\to(\"relative imports works\", async () => {\n\t\tawait setup({\n\t\t\t\"a.js\": 'var b = require(\"./b\")',\n\t\t\t\"b.js\": \"module.exports = 1\",\n\t\t})\n\n\t\to(await bundle(p(\"a.js\"))).equals(\";(function() {\\nvar b = 1\\n}());\")\n\t})\n\to(\"relative imports works with semicolons\", async () => {\n\t\tawait setup({\n\t\t\t\"a.js\": 'var b = require(\"./b\");',\n\t\t\t\"b.js\": \"module.exports = 1;\",\n\t\t})\n\n\t\to(await bundle(p(\"a.js\"))).equals(\";(function() {\\nvar b = 1;\\n}());\")\n\t})\n\to(\"relative imports works with let\", async () => {\n\t\tawait setup({\n\t\t\t\"a.js\": 'let b = require(\"./b\")',\n\t\t\t\"b.js\": \"module.exports = 1\",\n\t\t})\n\n\t\to(await bundle(p(\"a.js\"))).equals(\";(function() {\\nlet b = 1\\n}());\")\n\t})\n\to(\"relative imports works with const\", async () => {\n\t\tawait setup({\n\t\t\t\"a.js\": 'const b = require(\"./b\")',\n\t\t\t\"b.js\": \"module.exports = 1\",\n\t\t})\n\n\t\to(await bundle(p(\"a.js\"))).equals(\";(function() {\\nconst b = 1\\n}());\")\n\t})\n\to(\"relative imports works with assignment\", async () => {\n\t\tawait setup({\n\t\t\t\"a.js\": 'var a = {}\\na.b = require(\"./b\")',\n\t\t\t\"b.js\": \"module.exports = 1\",\n\t\t})\n\n\t\to(await bundle(p(\"a.js\"))).equals(\";(function() {\\nvar a = {}\\na.b = 1\\n}());\")\n\t})\n\to(\"relative imports works with reassignment\", async () => {\n\t\tawait setup({\n\t\t\t\"a.js\": 'var b = {}\\nb = require(\"./b\")',\n\t\t\t\"b.js\": \"module.exports = 1\",\n\t\t})\n\n\t\to(await bundle(p(\"a.js\"))).equals(\";(function() {\\nvar b = {}\\nb = 1\\n}());\")\n\t})\n\to(\"relative imports removes extra use strict\", async () => {\n\t\tawait setup({\n\t\t\t\"a.js\": '\"use strict\"\\nvar b = require(\"./b\")',\n\t\t\t\"b.js\": '\"use strict\"\\nmodule.exports = 1',\n\t\t})\n\n\t\to(await bundle(p(\"a.js\"))).equals(';(function() {\\n\"use strict\"\\nvar b = 1\\n}());')\n\t})\n\to(\"relative imports removes extra use strict using single quotes\", async () => {\n\t\tawait setup({\n\t\t\t\"a.js\": \"'use strict'\\nvar b = require(\\\"./b\\\")\",\n\t\t\t\"b.js\": \"'use strict'\\nmodule.exports = 1\",\n\t\t})\n\n\t\to(await bundle(p(\"a.js\"))).equals(\";(function() {\\n'use strict'\\nvar b = 1\\n}());\")\n\t})\n\to(\"relative imports removes extra use strict using mixed quotes\", async () => {\n\t\tawait setup({\n\t\t\t\"a.js\": '\"use strict\"\\nvar b = require(\"./b\")',\n\t\t\t\"b.js\": \"'use strict'\\nmodule.exports = 1\",\n\t\t})\n\n\t\to(await bundle(p(\"a.js\"))).equals(';(function() {\\n\"use strict\"\\nvar b = 1\\n}());')\n\t})\n\to(\"works w/ window\", async () => {\n\t\tawait setup({\n\t\t\t\"a.js\": 'window.a = 1\\nvar b = require(\"./b\")',\n\t\t\t\"b.js\": \"module.exports = function() {return a}\",\n\t\t})\n\n\t\to(await bundle(p(\"a.js\"))).equals(\";(function() {\\nwindow.a = 1\\nvar b = function() {return a}\\n}());\")\n\t})\n\to(\"works without assignment\", async () => {\n\t\tawait setup({\n\t\t\t\"a.js\": 'require(\"./b\")',\n\t\t\t\"b.js\": \"1 + 1\",\n\t\t})\n\n\t\to(await bundle(p(\"a.js\"))).equals(\";(function() {\\n1 + 1\\n}());\")\n\t})\n\to(\"works if used fluently\", async () => {\n\t\tawait setup({\n\t\t\t\"a.js\": 'var b = require(\"./b\").toString()',\n\t\t\t\"b.js\": \"module.exports = []\",\n\t\t})\n\n\t\to(await bundle(p(\"a.js\"))).equals(\";(function() {\\nvar _0 = []\\nvar b = _0.toString()\\n}());\")\n\t})\n\to(\"works if used fluently w/ multiline\", async () => {\n\t\tawait setup({\n\t\t\t\"a.js\": 'var b = require(\"./b\")\\n\\t.toString()',\n\t\t\t\"b.js\": \"module.exports = []\",\n\t\t})\n\n\t\to(await bundle(p(\"a.js\"))).equals(\";(function() {\\nvar _0 = []\\nvar b = _0\\n\\t.toString()\\n}());\")\n\t})\n\to(\"works if used w/ curry\", async () => {\n\t\tawait setup({\n\t\t\t\"a.js\": 'var b = require(\"./b\")()',\n\t\t\t\"b.js\": \"module.exports = function() {}\",\n\t\t})\n\n\t\to(await bundle(p(\"a.js\"))).equals(\";(function() {\\nvar _0 = function() {}\\nvar b = _0()\\n}());\")\n\t})\n\to(\"works if used w/ curry w/ multiline\", async () => {\n\t\tawait setup({\n\t\t\t\"a.js\": 'var b = require(\"./b\")\\n()',\n\t\t\t\"b.js\": \"module.exports = function() {}\",\n\t\t})\n\n\t\to(await bundle(p(\"a.js\"))).equals(\";(function() {\\nvar _0 = function() {}\\nvar b = _0\\n()\\n}());\")\n\t})\n\to(\"works if used fluently in one place and not in another\", async () => {\n\t\tawait setup({\n\t\t\t\"a.js\": 'var b = require(\"./b\").toString()\\nvar c = require(\"./c\")',\n\t\t\t\"b.js\": \"module.exports = []\",\n\t\t\t\"c.js\": 'var b = require(\"./b\")\\nmodule.exports = function() {return b}',\n\t\t})\n\n\t\to(await bundle(p(\"a.js\"))).equals(\";(function() {\\nvar _0 = []\\nvar b = _0.toString()\\nvar b0 = _0\\nvar c = function() {return b0}\\n}());\")\n\t})\n\to(\"works if used in sequence\", async () => {\n\t\tawait setup({\n\t\t\t\"a.js\": 'var b = require(\"./b\"), c = require(\"./c\")',\n\t\t\t\"b.js\": \"module.exports = 1\",\n\t\t\t\"c.js\": \"var x\\nmodule.exports = 2\",\n\t\t})\n\n\t\to(await bundle(p(\"a.js\"))).equals(\";(function() {\\nvar b = 1\\nvar x\\nvar c = 2\\n}());\")\n\t})\n\to(\"works if assigned to property\", async () => {\n\t\tawait setup({\n\t\t\t\"a.js\": 'var x = {}\\nx.b = require(\"./b\")\\nx.c = require(\"./c\")',\n\t\t\t\"b.js\": \"var bb = 1\\nmodule.exports = bb\",\n\t\t\t\"c.js\": \"var cc = 2\\nmodule.exports = cc\",\n\t\t})\n\n\t\to(await bundle(p(\"a.js\"))).equals(\";(function() {\\nvar x = {}\\nvar bb = 1\\nx.b = bb\\nvar cc = 2\\nx.c = cc\\n}());\")\n\t})\n\to(\"works if assigned to property using bracket notation\", async () => {\n\t\tawait setup({\n\t\t\t\"a.js\": 'var x = {}\\nx[\"b\"] = require(\"./b\")\\nx[\"c\"] = require(\"./c\")',\n\t\t\t\"b.js\": \"var bb = 1\\nmodule.exports = bb\",\n\t\t\t\"c.js\": \"var cc = 2\\nmodule.exports = cc\",\n\t\t})\n\n\t\to(await bundle(p(\"a.js\"))).equals(';(function() {\\nvar x = {}\\nvar bb = 1\\nx[\"b\"] = bb\\nvar cc = 2\\nx[\"c\"] = cc\\n}());')\n\t})\n\to(\"works if collision\", async () => {\n\t\tawait setup({\n\t\t\t\"a.js\": 'var b = require(\"./b\")',\n\t\t\t\"b.js\": \"var b = 1\\nmodule.exports = 2\",\n\t\t})\n\n\t\to(await bundle(p(\"a.js\"))).equals(\";(function() {\\nvar b0 = 1\\nvar b = 2\\n}());\")\n\t})\n\to(\"works if multiple aliases\", async () => {\n\t\tawait setup({\n\t\t\t\"a.js\": 'var b = require(\"./b\")\\n',\n\t\t\t\"b.js\": 'var b = require(\"./c\")\\nb.x = 1\\nmodule.exports = b',\n\t\t\t\"c.js\": \"var b = {}\\nmodule.exports = b\",\n\t\t})\n\n\t\to(await bundle(p(\"a.js\"))).equals(\";(function() {\\nvar b = {}\\nb.x = 1\\n}());\")\n\t})\n\to(\"works if multiple collision\", async () => {\n\t\tawait setup({\n\t\t\t\"a.js\": 'var b = require(\"./b\")\\nvar c = require(\"./c\")\\nvar d = require(\"./d\")',\n\t\t\t\"b.js\": \"var a = 1\\nmodule.exports = a\",\n\t\t\t\"c.js\": \"var a = 2\\nmodule.exports = a\",\n\t\t\t\"d.js\": \"var a = 3\\nmodule.exports = a\",\n\t\t})\n\n\t\to(await bundle(p(\"a.js\"))).equals(\";(function() {\\nvar a = 1\\nvar b = a\\nvar a0 = 2\\nvar c = a0\\nvar a1 = 3\\nvar d = a1\\n}());\")\n\t})\n\to(\"works if included multiple times\", async () => {\n\t\tawait setup({\n\t\t\t\"a.js\": \"module.exports = 123\",\n\t\t\t\"b.js\": 'var a = require(\"./a\").toString()\\nmodule.exports = a',\n\t\t\t\"c.js\": 'var a = require(\"./a\").toString()\\nvar b = require(\"./b\")',\n\t\t})\n\n\t\to(await bundle(p(\"c.js\"))).equals(\";(function() {\\nvar _0 = 123\\nvar a = _0.toString()\\nvar a0 = _0.toString()\\nvar b = a0\\n}());\")\n\t})\n\to(\"works if included multiple times reverse\", async () => {\n\t\tawait setup({\n\t\t\t\"a.js\": \"module.exports = 123\",\n\t\t\t\"b.js\": 'var a = require(\"./a\").toString()\\nmodule.exports = a',\n\t\t\t\"c.js\": 'var b = require(\"./b\")\\nvar a = require(\"./a\").toString()',\n\t\t})\n\n\t\to(await bundle(p(\"c.js\"))).equals(\";(function() {\\nvar _0 = 123\\nvar a0 = _0.toString()\\nvar b = a0\\nvar a = _0.toString()\\n}());\")\n\t})\n\to(\"reuses binding if possible\", async () => {\n\t\tawait setup({\n\t\t\t\"a.js\": 'var b = require(\"./b\")\\nvar c = require(\"./c\")',\n\t\t\t\"b.js\": 'var d = require(\"./d\")\\nmodule.exports = function() {return d + 1}',\n\t\t\t\"c.js\": 'var d = require(\"./d\")\\nmodule.exports = function() {return d + 2}',\n\t\t\t\"d.js\": \"module.exports = 1\",\n\t\t})\n\n\t\to(await bundle(p(\"a.js\"))).equals(\";(function() {\\nvar d = 1\\nvar b = function() {return d + 1}\\nvar c = function() {return d + 2}\\n}());\")\n\t})\n\to(\"disambiguates conflicts if imported collides with itself\", async () => {\n\t\tawait setup({\n\t\t\t\"a.js\": 'var b = require(\"./b\")',\n\t\t\t\"b.js\": \"var b = 1\\nmodule.exports = function() {return b}\",\n\t\t})\n\n\t\to(await bundle(p(\"a.js\"))).equals(\";(function() {\\nvar b0 = 1\\nvar b = function() {return b0}\\n}());\")\n\t})\n\to(\"disambiguates conflicts if imported collides with something else\", async () => {\n\t\tawait setup({\n\t\t\t\"a.js\": 'var a = 1\\nvar b = require(\"./b\")',\n\t\t\t\"b.js\": \"var a = 2\\nmodule.exports = function() {return a}\",\n\t\t})\n\n\t\to(await bundle(p(\"a.js\"))).equals(\";(function() {\\nvar a = 1\\nvar a0 = 2\\nvar b = function() {return a0}\\n}());\")\n\t})\n\to(\"disambiguates conflicts if imported collides with function declaration\", async () => {\n\t\tawait setup({\n\t\t\t\"a.js\": 'function a() {}\\nvar b = require(\"./b\")',\n\t\t\t\"b.js\": \"var a = 2\\nmodule.exports = function() {return a}\",\n\t\t})\n\n\t\to(await bundle(p(\"a.js\"))).equals(\";(function() {\\nfunction a() {}\\nvar a0 = 2\\nvar b = function() {return a0}\\n}());\")\n\t})\n\to(\"disambiguates conflicts if imported collides with another module's private\", async () => {\n\t\tawait setup({\n\t\t\t\"a.js\": 'var b = require(\"./b\")\\nvar c = require(\"./c\")',\n\t\t\t\"b.js\": \"var a = 1\\nmodule.exports = function() {return a}\",\n\t\t\t\"c.js\": \"var a = 2\\nmodule.exports = function() {return a}\",\n\t\t})\n\n\t\to(await bundle(p(\"a.js\"))).equals(\";(function() {\\nvar a = 1\\nvar b = function() {return a}\\nvar a0 = 2\\nvar c = function() {return a0}\\n}());\")\n\t})\n\to(\"does not mess up strings\", async () => {\n\t\tawait setup({\n\t\t\t\"a.js\": 'var b = require(\"./b\")',\n\t\t\t\"b.js\": 'var b = \"b b b \\\\\" b\"\\nmodule.exports = function() {return b}',\n\t\t})\n\n\t\to(await bundle(p(\"a.js\"))).equals(';(function() {\\nvar b0 = \"b b b \\\\\\\" b\"\\nvar b = function() {return b0}\\n}());')\n\t})\n\to(\"does not mess up regexp literals\", async () => {\n\t\tawait setup({\n\t\t\t\"a.js\": 'var b = require(\"./b\")\\nvar c = require(\"./c\")',\n\t\t\t\"b.js\": \"var b = /b/\\nvar g = 0\\nmodule.exports = function() {return b}\",\n\t\t\t\"c.js\": \"var b =\\n\\t/ b \\\\/ \\\\/ [a-b]/g\\nvar d = b/b\\nmodule.exports = function() {return b}\",\n\t\t})\n\n\t\to(await bundle(p(\"a.js\"))).equals(\";(function() {\\nvar b0 = /b/\\nvar g = 0\\nvar b = function() {return b0}\\nvar b1 =\\n\\t/ b \\\\/ \\\\/ [a-b]/g\\nvar d = b1/b1\\nvar c = function() {return b1}\\n}());\")\n\t})\n\to(\"does not mess up properties\", async () => {\n\t\tawait setup({\n\t\t\t\"a.js\": 'var b = require(\"./b\")',\n\t\t\t\"b.js\": \"var b = {b: 1}\\nmodule.exports = function() {return b.b}\",\n\t\t})\n\n\t\to(await bundle(p(\"a.js\"))).equals(\";(function() {\\nvar b0 = {b: 1}\\nvar b = function() {return b0.b}\\n}());\")\n\t})\n\to.spec(\"fix comments\", () => {\n\t\to(\"fix /* */ comments\", async () => {\n\t\t\tawait setup({\n\t\t\t\t\"a.js\": 'var b = require(\"./b\")\\nvar c = require(\"./c\")',\n\t\t\t\t\"b.js\": \"var a = 1\\nmodule.exports = a\",\n\t\t\t\t\"c.js\": \"var a = 2\\n/* a */\\nmodule.exports = a\",\n\t\t\t})\n\n\t\t\to(await bundle(p(\"a.js\"))).equals(\";(function() {\\nvar a = 1\\nvar b = a\\nvar a0 = 2\\n/* a */\\nvar c = a0\\n}());\")\n\t\t})\n\t\to(\"fix // comments\", async () => {\n\t\t\tawait setup({\n\t\t\t\t\"a.js\": 'var b = require(\"./b\")\\nvar c = require(\"./c\")',\n\t\t\t\t\"b.js\": \"var a = 1\\nmodule.exports = a\",\n\t\t\t\t\"c.js\": \"var a = 2\\n// a\\nmodule.exports = a\",\n\t\t\t})\n\n\t\t\to(await bundle(p(\"a.js\"))).equals(\";(function() {\\nvar a = 1\\nvar b = a\\nvar a0 = 2\\n// a\\nvar c = a0\\n}());\")\n\t\t})\n\t\to(\"fix multi-line /* */ comments\", async () => {\n\t\t\tawait setup({\n\t\t\t\t\"a.js\": 'var b = require(\"./b\")\\nvar c = require(\"./c\")',\n\t\t\t\t\"b.js\": \"var a = 1\\nmodule.exports = a\",\n\t\t\t\t\"c.js\": \"var a = 2\\n/* \\na */\\nmodule.exports = a\",\n\t\t\t})\n\n\t\t\to(await bundle(p(\"a.js\"))).equals(\";(function() {\\nvar a = 1\\nvar b = a\\nvar a0 = 2\\n/* \\na */\\nvar c = a0\\n}());\")\n\t\t})\n\t\to(\"does not fix trailing /* */ comments\", async () => {\n\t\t\tawait setup({\n\t\t\t\t\"a.js\": 'var b = require(\"./b\")\\nvar c = require(\"./c\")',\n\t\t\t\t\"b.js\": \"var a = 1\\nmodule.exports = a\",\n\t\t\t\t\"c.js\": \"var a = 2/* a */\\nmodule.exports = a\",\n\t\t\t})\n\n\t\t\to(await bundle(p(\"a.js\"))).equals(\";(function() {\\nvar a = 1\\nvar b = a\\nvar a0 = 2/* a0 */\\nvar c = a0\\n}());\")\n\t\t})\n\t\to(\"does not fix trailing // comments\", async () => {\n\t\t\tawait setup({\n\t\t\t\t\"a.js\": 'var b = require(\"./b\")\\nvar c = require(\"./c\")',\n\t\t\t\t\"b.js\": \"var a = 1\\nmodule.exports = a\",\n\t\t\t\t\"c.js\": \"var a = 2// a\\nmodule.exports = a\",\n\t\t\t})\n\n\t\t\to(await bundle(p(\"a.js\"))).equals(\";(function() {\\nvar a = 1\\nvar b = a\\nvar a0 = 2// a0\\nvar c = a0\\n}());\")\n\t\t})\n\t\to(\"does not fix trailing multi-line /* */ comments\", async () => {\n\t\t\tawait setup({\n\t\t\t\t\"a.js\": 'var b = require(\"./b\")\\nvar c = require(\"./c\")',\n\t\t\t\t\"b.js\": \"var a = 1\\nmodule.exports = a\",\n\t\t\t\t\"c.js\": \"var a = 2/* \\na */\\nmodule.exports = a\",\n\t\t\t})\n\n\t\t\to(await bundle(p(\"a.js\"))).equals(\";(function() {\\nvar a = 1\\nvar b = a\\nvar a0 = 2/* \\na0 */\\nvar c = a0\\n}());\")\n\t\t})\n\t})\n\to(\"prevents double suffixes (mountRedraw00)\", async () => {\n\t\tawait setup({\n\t\t\t// /index.js (request(b), mount-redraw(z), route(c))\n\t\t\t\"a.js\": 'var b = require(\"./b\")\\nvar z = require(\"./z\")\\nvar c = require(\"./c\")',\n\t\t\t// /request.js\n\t\t\t\"b.js\": 'var z = require(\"./z\")\\nmodule.exports = require(\"./p\")(z)',\n\t\t\t// /route.js\n\t\t\t\"c.js\": 'var z = require(\"./z\")\\nmodule.exports = require(\"./q\")(z)',\n\t\t\t// /request/request.js\n\t\t\t\"p.js\": \"module.exports = function(z){}\",\n\t\t\t// /api/router.js\n\t\t\t\"q.js\": \"module.exports = function(z){}\",\n\t\t\t// /mount-redraw.js\n\t\t\t\"z.js\": \"module.exports = {}\",\n\t\t})\n\n\t\t// check that the argument z2 is not z00\n\t\to(await bundle(p(\"a.js\"))).equals(\";(function() {\\nvar z0 = {}\\nvar _1 = function(z1){}\\nvar b = _1(z0)\\nvar z = z0\\nvar _5 = function(z2){}\\nvar c = _5(z)\\n}());\")\n\t})\n\to.spec(\"spread syntax and destructuring (...)\", () => {\n\t\to(\"rest parameter\", async () => {\n\t\t\tawait setup({\n\t\t\t\t\"a.js\": 'var b = require(\"./b\")\\nvar c = require(\"./c\")',\n\t\t\t\t\"b.js\": \"var a = 1\\nmodule.exports = a\",\n\t\t\t\t\"c.js\": \"function f(d, ...a){}\\nmodule.exports = f\",\n\t\t\t})\n\n\t\t\to(await bundle(p(\"a.js\"))).equals(\";(function() {\\nvar a = 1\\nvar b = a\\nfunction f(d, ...a0){}\\nvar c = f\\n}());\")\n\t\t})\n\t\to(\"function call\", async () => {\n\t\t\tawait setup({\n\t\t\t\t\"a.js\": 'var b = require(\"./b\")\\nvar c = require(\"./c\")',\n\t\t\t\t\"b.js\": \"var a = 1\\nmodule.exports = a\",\n\t\t\t\t\"c.js\": \"var a = [1, 2, 3]\\nvar d = f(...a)\\nmodule.exports = d\",\n\t\t\t})\n\n\t\t\to(await bundle(p(\"a.js\"))).equals(\";(function() {\\nvar a = 1\\nvar b = a\\nvar a0 = [1, 2, 3]\\nvar d = f(...a0)\\nvar c = d\\n}());\")\n\t\t})\n\t\to(\"new\", async () => {\n\t\t\tawait setup({\n\t\t\t\t\"a.js\": 'var b = require(\"./b\")\\nvar c = require(\"./c\")',\n\t\t\t\t\"b.js\": \"var a = 1\\nmodule.exports = a\",\n\t\t\t\t\"c.js\": \"var a = [1, 2, 3]\\nvar d = new f(...a)\\nmodule.exports = d\",\n\t\t\t})\n\n\t\t\to(await bundle(p(\"a.js\"))).equals(\";(function() {\\nvar a = 1\\nvar b = a\\nvar a0 = [1, 2, 3]\\nvar d = new f(...a0)\\nvar c = d\\n}());\")\n\t\t})\n\t\to(\"array spread\", async () => {\n\t\t\tawait setup({\n\t\t\t\t\"a.js\": 'var b = require(\"./b\")\\nvar c = require(\"./c\")',\n\t\t\t\t\"b.js\": \"var a = 1\\nmodule.exports = a\",\n\t\t\t\t\"c.js\": \"var a = [1, 2, 3]\\nvar arr = [...a]\\nmodule.exports = arr\",\n\t\t\t})\n\n\t\t\to(await bundle(p(\"a.js\"))).equals(\";(function() {\\nvar a = 1\\nvar b = a\\nvar a0 = [1, 2, 3]\\nvar arr = [...a0]\\nvar c = arr\\n}());\")\n\t\t})\n\t\to(\"array spread (merge)\", async () => {\n\t\t\tawait setup({\n\t\t\t\t\"a.js\": 'var b = require(\"./b\")\\nvar c = require(\"./c\")',\n\t\t\t\t\"b.js\": \"var a = 1\\nmodule.exports = a\",\n\t\t\t\t\"c.js\": \"var a = [1, 2, 3]\\nvar arr = [0, ...a, 4]\\nmodule.exports = arr\",\n\t\t\t})\n\n\t\t\to(await bundle(p(\"a.js\"))).equals(\";(function() {\\nvar a = 1\\nvar b = a\\nvar a0 = [1, 2, 3]\\nvar arr = [0, ...a0, 4]\\nvar c = arr\\n}());\")\n\t\t})\n\t\to(\"array destructuring\", async () => {\n\t\t\tawait setup({\n\t\t\t\t\"a.js\": 'var b = require(\"./b\")\\nvar c = require(\"./c\")',\n\t\t\t\t\"b.js\": \"var a = 1\\nmodule.exports = a\",\n\t\t\t\t\"c.js\": \"var a = [1, 2, 3]\\nvar d\\n[d, ...a] = a\\nmodule.exports = a\",\n\t\t\t})\n\n\t\t\to(await bundle(p(\"a.js\"))).equals(\";(function() {\\nvar a = 1\\nvar b = a\\nvar a0 = [1, 2, 3]\\nvar d\\n[d, ...a0] = a0\\nvar c = a0\\n}());\")\n\t\t})\n\t\to(\"object spread\", async () => {\n\t\t\tawait setup({\n\t\t\t\t\"a.js\": 'var b = require(\"./b\")\\nvar c = require(\"./c\")',\n\t\t\t\t\"b.js\": \"var a = 1\\nmodule.exports = a\",\n\t\t\t\t\"c.js\": \"var a = { p: 1, q: 2, r: 3 }\\nvar d = {...a}\\nmodule.exports = d\",\n\t\t\t})\n\n\t\t\to(await bundle(p(\"a.js\"))).equals(\";(function() {\\nvar a = 1\\nvar b = a\\nvar a0 = { p: 1, q: 2, r: 3 }\\nvar d = {...a0}\\nvar c = d\\n}());\")\n\t\t})\n\t\to(\"object spread (merge)\", async () => {\n\t\t\tawait setup({\n\t\t\t\t\"a.js\": 'var b = require(\"./b\")\\nvar c = require(\"./c\")',\n\t\t\t\t\"b.js\": \"var a = 1\\nmodule.exports = a\",\n\t\t\t\t\"c.js\": \"var a = { p: 1, q: 2, r: 3 }\\nvar d = {o:0,...a}\\nmodule.exports = d\",\n\t\t\t})\n\n\t\t\to(await bundle(p(\"a.js\"))).equals(\";(function() {\\nvar a = 1\\nvar b = a\\nvar a0 = { p: 1, q: 2, r: 3 }\\nvar d = {o:0,...a0}\\nvar c = d\\n}());\")\n\t\t})\n\t\to(\"object destructuring\", async () => {\n\t\t\tawait setup({\n\t\t\t\t\"a.js\": 'var b = require(\"./b\")\\nvar c = require(\"./c\")',\n\t\t\t\t\"b.js\": \"var a = 1\\nmodule.exports = a\",\n\t\t\t\t\"c.js\": \"var obj = { p: 1, q: 2, r: 3 }\\nvar p,a\\n({p,...a}=obj)\\nmodule.exports = a\",\n\t\t\t})\n\n\t\t\to(await bundle(p(\"a.js\"))).equals(\";(function() {\\nvar a = 1\\nvar b = a\\nvar obj = { p: 1, q: 2, r: 3 }\\nvar p,a0\\n({p,...a0}=obj)\\nvar c = a0\\n}());\")\n\t\t})\n\t})\n})\n"
  },
  {
    "path": "stream/stream.js",
    "content": "/* eslint-disable */\n;(function() {\n\"use strict\"\n/* eslint-enable */\nStream.SKIP = {}\nStream.lift = lift\nStream.scan = scan\nStream.merge = merge\nStream.combine = combine\nStream.scanMerge = scanMerge\nStream[\"fantasy-land/of\"] = Stream\n\nvar warnedHalt = false\nObject.defineProperty(Stream, \"HALT\", {\n\tget: function() {\n\t\twarnedHalt || console.log(\"HALT is deprecated and has been renamed to SKIP\");\n\t\twarnedHalt = true\n\t\treturn Stream.SKIP\n\t}\n})\n\nfunction Stream(value) {\n\tvar dependentStreams = []\n\tvar dependentFns = []\n\n\tfunction stream(v) {\n\t\tif (arguments.length && v !== Stream.SKIP) {\n\t\t\tvalue = v\n\t\t\tif (open(stream)) {\n\t\t\t\tstream._changing()\n\t\t\t\tstream._state = \"active\"\n\t\t\t\t// Cloning the list to ensure it's still iterated in intended\n\t\t\t\t// order\n\t\t\t\tdependentStreams.slice().forEach(function(s, i) {\n\t\t\t\t\tif (open(s)) s(this[i](value))\n\t\t\t\t}, dependentFns.slice())\n\t\t\t}\n\t\t}\n\n\t\treturn value\n\t}\n\n\tstream.constructor = Stream\n\tstream._state = arguments.length && value !== Stream.SKIP ? \"active\" : \"pending\"\n\tstream._parents = []\n\n\tstream._changing = function() {\n\t\tif (open(stream)) stream._state = \"changing\"\n\t\tdependentStreams.forEach(function(s) {\n\t\t\ts._changing()\n\t\t})\n\t}\n\n\tstream._map = function(fn, ignoreInitial) {\n\t\tvar target = ignoreInitial ? Stream() : Stream(fn(value))\n\t\ttarget._parents.push(stream)\n\t\tdependentStreams.push(target)\n\t\tdependentFns.push(fn)\n\t\treturn target\n\t}\n\n\tstream.map = function(fn) {\n\t\treturn stream._map(fn, stream._state !== \"active\")\n\t}\n\n\tvar end\n\tfunction createEnd() {\n\t\tend = Stream()\n\t\tend.map(function(value) {\n\t\t\tif (value === true) {\n\t\t\t\tstream._parents.forEach(function (p) {p._unregisterChild(stream)})\n\t\t\t\tstream._state = \"ended\"\n\t\t\t\tstream._parents.length = dependentStreams.length = dependentFns.length = 0\n\t\t\t}\n\t\t\treturn value\n\t\t})\n\t\treturn end\n\t}\n\n\tstream.toJSON = function() { return value != null && typeof value.toJSON === \"function\" ? value.toJSON() : value }\n\n\tstream[\"fantasy-land/map\"] = stream.map\n\tstream[\"fantasy-land/ap\"] = function(x) { return combine(function(s1, s2) { return s1()(s2()) }, [x, stream]) }\n\n\tstream._unregisterChild = function(child) {\n\t\tvar childIndex = dependentStreams.indexOf(child)\n\t\tif (childIndex !== -1) {\n\t\t\tdependentStreams.splice(childIndex, 1)\n\t\t\tdependentFns.splice(childIndex, 1)\n\t\t}\n\t}\n\n\tObject.defineProperty(stream, \"end\", {\n\t\tget: function() { return end || createEnd() }\n\t})\n\n\treturn stream\n}\n\nfunction combine(fn, streams) {\n\tvar ready = streams.every(function(s) {\n\t\tif (s.constructor !== Stream)\n\t\t\tthrow new Error(\"Ensure that each item passed to stream.combine/stream.merge/lift is a stream.\")\n\t\treturn s._state === \"active\"\n\t})\n\tvar stream = ready\n\t\t? Stream(fn.apply(null, streams.concat([streams])))\n\t\t: Stream()\n\n\tvar changed = []\n\n\tvar mappers = streams.map(function(s) {\n\t\treturn s._map(function(value) {\n\t\t\tchanged.push(s)\n\t\t\tif (ready || streams.every(function(s) { return s._state !== \"pending\" })) {\n\t\t\t\tready = true\n\t\t\t\tstream(fn.apply(null, streams.concat([changed])))\n\t\t\t\tchanged = []\n\t\t\t}\n\t\t\treturn value\n\t\t}, true)\n\t})\n\n\tvar endStream = stream.end.map(function(value) {\n\t\tif (value === true) {\n\t\t\tmappers.forEach(function(mapper) { mapper.end(true) })\n\t\t\tendStream.end(true)\n\t\t}\n\t\treturn undefined\n\t})\n\n\treturn stream\n}\n\nfunction merge(streams) {\n\treturn combine(function() { return streams.map(function(s) { return s() }) }, streams)\n}\n\nfunction scan(fn, acc, origin) {\n\tvar stream = origin.map(function(v) {\n\t\tvar next = fn(acc, v)\n\t\tif (next !== Stream.SKIP) acc = next\n\t\treturn next\n\t})\n\tstream(acc)\n\treturn stream\n}\n\nfunction scanMerge(tuples, seed) {\n\tvar streams = tuples.map(function(tuple) { return tuple[0] })\n\n\tvar stream = combine(function() {\n\t\tvar changed = arguments[arguments.length - 1]\n\t\tstreams.forEach(function(stream, i) {\n\t\t\tif (changed.indexOf(stream) > -1)\n\t\t\t\tseed = tuples[i][1](seed, stream())\n\t\t})\n\n\t\treturn seed\n\t}, streams)\n\n\tstream(seed)\n\n\treturn stream\n}\n\nfunction lift() {\n\tvar fn = arguments[0]\n\tvar streams = Array.prototype.slice.call(arguments, 1)\n\treturn merge(streams).map(function(streams) {\n\t\treturn fn.apply(undefined, streams)\n\t})\n}\n\nfunction open(s) {\n\treturn s._state === \"pending\" || s._state === \"active\" || s._state === \"changing\"\n}\n\nif (typeof module !== \"undefined\") module[\"exports\"] = Stream\nelse if (typeof window.m === \"function\" && !(\"stream\" in window.m)) window.m.stream = Stream\nelse window.m = {stream : Stream}\n\n}());\n"
  },
  {
    "path": "stream/tests/test-scan.js",
    "content": "\"use strict\"\n\nvar o = require(\"ospec\")\nvar stream = require(\"../stream\")\n\no.spec(\"scan\", function() {\n\to(\"defaults to seed\", function() {\n\t\tvar parent = stream()\n\t\tvar child = stream.scan(function(out, p) {\n\t\t\treturn out - p\n\t\t}, 123, parent)\n\t\to(child()).equals(123)\n\t})\n\n\to(\"accumulates values as expected\", function() {\n\t\tvar parent = stream()\n\t\tvar child = stream.scan(function(arr, p) {\n\t\t\treturn arr.concat(p)\n\t\t}, [], parent)\n\n\t\tparent(7)\n\t\tparent(\"11\")\n\t\tparent(undefined)\n\t\tparent({a: 1})\n\t\tvar result = child()\n\n\t\t// deepEquals fails on arrays?\n\t\to(result[0]).equals(7)\n\t\to(result[1]).equals(\"11\")\n\t\to(result[2]).equals(undefined)\n\t\to(result[3]).deepEquals({a: 1})\n\t})\n\n\to(\"reducer can return SKIP to prevent child updates\", function() {\n\t\tvar count = 0\n\t\tvar action = stream()\n\t\tvar store = stream.scan(function (arr, value) {\n\t\t\tswitch (typeof value) {\n\t\t\t\tcase \"number\":\n\t\t\t\t\treturn arr.concat(value)\n\t\t\t\tdefault:\n\t\t\t\t\treturn stream.SKIP\n\t\t\t}\n\t\t}, [], action)\n\t\tvar child = store.map(function (p) {\n\t\t\tcount++\n\t\t\treturn p\n\t\t})\n\t\tvar result\n\n\t\taction(7)\n\t\taction(\"11\")\n\t\taction(undefined)\n\t\taction({a: 1})\n\t\taction(8) // assures we didn't break the accumulator\n\n\t\tresult = child()\n\n\t\t// check we got the expect result\n\t\to(result[0]).equals(7)\n\t\to(result[1]).equals(8)\n\n\t\t// check child received minimum # of updates\n\t\to(count).equals(3)\n\t})\n\n})\n"
  },
  {
    "path": "stream/tests/test-scanMerge.js",
    "content": "\"use strict\"\n\nvar o = require(\"ospec\")\nvar stream = require(\"../stream\")\n\no.spec(\"scanMerge\", function() {\n\to(\"defaults to seed\", function() {\n\t\tvar parent1 = stream()\n\t\tvar parent2 = stream()\n\n\t\tvar child = stream.scanMerge([\n\t\t\t[parent1, function(out, p1) {\n\t\t\t\treturn out + p1\n\t\t\t}],\n\t\t\t[parent2, function(out, p2) {\n\t\t\t\treturn out + p2\n\t\t\t}]\n\t\t], -10)\n\n\t\to(child()).equals(-10)\n\t})\n\n\to(\"accumulates as expected\", function() {\n\t\tvar parent1 = stream()\n\t\tvar parent2 = stream()\n\n\t\tvar child = stream.scanMerge([\n\t\t\t[parent1, function(out, p1) {\n\t\t\t\treturn out + p1\n\t\t\t}],\n\t\t\t[parent2, function(out, p2) {\n\t\t\t\treturn out + p2 + p2\n\t\t\t}]\n\t\t], \"a\")\n\n\t\tparent1(\"b\")\n\t\tparent2(\"c\")\n\t\tparent1(\"b\")\n\n\t\to(child()).equals(\"abccb\")\n\t})\n})\n"
  },
  {
    "path": "stream/tests/test-stream.js",
    "content": "\"use strict\"\n\nvar o = require(\"ospec\")\nvar Stream = require(\"../stream\")\n\no.spec(\"stream\", function() {\n\to.spec(\"stream\", function() {\n\t\to(\"works as getter/setter\", function() {\n\t\t\tvar stream = Stream(1)\n\t\t\tvar initialValue = stream()\n\t\t\tstream(2)\n\t\t\tvar newValue = stream()\n\n\t\t\to(initialValue).equals(1)\n\t\t\to(newValue).equals(2)\n\t\t})\n\t\to(\"has undefined value by default\", function() {\n\t\t\tvar stream = Stream()\n\n\t\t\to(stream()).equals(undefined)\n\t\t})\n\t\to(\"can update to undefined\", function() {\n\t\t\tvar stream = Stream(1)\n\t\t\tstream(undefined)\n\n\t\t\to(stream()).equals(undefined)\n\t\t})\n\t\to(\"can be stream of streams\", function() {\n\t\t\tvar stream = Stream(Stream(1))\n\n\t\t\to(stream()()).equals(1)\n\t\t})\n\t\to(\"can SKIP\", function() {\n\t\t\tvar a = Stream(2)\n\t\t\tvar b = a.map(function(value) {\n\t\t\t\treturn value === 5\n\t\t\t\t\t? Stream.SKIP\n\t\t\t\t\t: value\n\t\t\t})\n\n\t\t\ta(5)\n\n\t\t\to(b()).equals(2)\n\t\t})\n\t\t// NOTE: this *must* be the *only* uses of `Stream.HALT` in the entire\n\t\t// test suite.\n\t\to(\"HALT is a deprecated alias of SKIP and warns once\", function() {\n\t\t\tvar log = console.log\n\t\t\tvar warnings = []\n\t\t\tconsole.log = function(a) {\n\t\t\t\twarnings.push(a)\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\to(Stream.HALT).equals(Stream.SKIP)\n\t\t\t\to(warnings).deepEquals([\"HALT is deprecated and has been renamed to SKIP\"])\n\t\t\t\to(Stream.HALT).equals(Stream.SKIP)\n\t\t\t\to(warnings).deepEquals([\"HALT is deprecated and has been renamed to SKIP\"])\n\t\t\t\to(Stream.HALT).equals(Stream.SKIP)\n\t\t\t\to(warnings).deepEquals([\"HALT is deprecated and has been renamed to SKIP\"])\n\t\t\t} finally {\n\t\t\t\tconsole.log = log\n\t\t\t}\n\t\t})\n\t})\n\to.spec(\"combine\", function() {\n\t\to(\"transforms value\", function() {\n\t\t\tvar stream = Stream()\n\t\t\tvar doubled = Stream.combine(function(s) {return s() * 2}, [stream])\n\n\t\t\tstream(2)\n\n\t\t\to(doubled()).equals(4)\n\t\t})\n\t\to(\"transforms default value\", function() {\n\t\t\tvar stream = Stream(2)\n\t\t\tvar doubled = Stream.combine(function(s) {return s() * 2}, [stream])\n\n\t\t\to(doubled()).equals(4)\n\t\t})\n\t\to(\"transforms multiple values\", function() {\n\t\t\tvar s1 = Stream()\n\t\t\tvar s2 = Stream()\n\t\t\tvar added = Stream.combine(function(s1, s2) {return s1() + s2()}, [s1, s2])\n\n\t\t\ts1(2)\n\t\t\ts2(3)\n\n\t\t\to(added()).equals(5)\n\t\t})\n\t\to(\"transforms multiple default values\", function() {\n\t\t\tvar s1 = Stream(2)\n\t\t\tvar s2 = Stream(3)\n\t\t\tvar added = Stream.combine(function(s1, s2) {return s1() + s2()}, [s1, s2])\n\n\t\t\to(added()).equals(5)\n\t\t})\n\t\to(\"transforms mixed default and late-bound values\", function() {\n\t\t\tvar s1 = Stream(2)\n\t\t\tvar s2 = Stream()\n\t\t\tvar added = Stream.combine(function(s1, s2) {return s1() + s2()}, [s1, s2])\n\n\t\t\ts2(3)\n\n\t\t\to(added()).equals(5)\n\t\t})\n\t\to(\"combines atomically\", function() {\n\t\t\tvar count = 0\n\t\t\tvar a = Stream()\n\t\t\tvar b = Stream.combine(function(a) {return a() * 2}, [a])\n\t\t\tvar c = Stream.combine(function(a) {return a() * a()}, [a])\n\t\t\tvar d = Stream.combine(function(b, c) {\n\t\t\t\tcount++\n\t\t\t\treturn b() + c()\n\t\t\t}, [b, c])\n\n\t\t\ta(3)\n\n\t\t\to(d()).equals(15)\n\t\t\to(count).equals(1)\n\t\t})\n\n\t\to(\"combines default value atomically\", function() {\n\t\t\tvar count = 0\n\t\t\tvar a = Stream(3)\n\t\t\tvar b = Stream.combine(function(a) {return a() * 2}, [a])\n\t\t\tvar c = Stream.combine(function(a) {return a() * a()}, [a])\n\t\t\tvar d = Stream.combine(function(b, c) {\n\t\t\t\tcount++\n\t\t\t\treturn b() + c()\n\t\t\t}, [b, c])\n\n\t\t\to(d()).equals(15)\n\t\t\to(count).equals(1)\n\t\t})\n\t\to(\"combines and maps nested streams atomically\", function() {\n\t\t\tvar count = 0\n\t\t\tvar a = Stream(3)\n\t\t\tvar b = Stream.combine(function(a) {return a() * 2}, [a])\n\t\t\tvar c = Stream.combine(function(a) {return a() * a()}, [a])\n\t\t\tvar d = c.map(function(x){return x})\n\t\t\tvar e = Stream.combine(function(x) {return x()}, [d])\n\t\t\tvar f = Stream.combine(function(b, e) {\n\t\t\t\tcount++\n\t\t\t\treturn b() + e()\n\t\t\t}, [b, e])\n\n\t\t\to(f()).equals(15)\n\t\t\to(count).equals(1)\n\t\t})\n\t\to(\"combine lists only changed upstreams in last arg\", function() {\n\t\t\tvar streams = []\n\t\t\tvar a = Stream()\n\t\t\tvar b = Stream()\n\t\t\tStream.combine(function(a, b, changed) {\n\t\t\t\tstreams = changed\n\t\t\t}, [a, b])\n\n\t\t\ta(3)\n\t\t\tb(5)\n\n\t\t\to(streams.length).equals(2)\n\t\t\to(streams[0]).equals(a)\n\t\t\to(streams[1]).equals(b)\n\t\t})\n\t\to(\"combine continues with ended streams\", function() {\n\t\t\tvar a = Stream()\n\t\t\tvar b = Stream()\n\t\t\tvar combined = Stream.combine(function(a, b) {\n\t\t\t\treturn a() + b()\n\t\t\t}, [a, b])\n\n\t\t\ta(3)\n\t\t\ta.end(true)\n\t\t\tb(5)\n\n\t\t\to(combined()).equals(8)\n\t\t})\n\t\to(\"combine lists only changed upstreams in last arg with default value\", function() {\n\t\t\tvar streams = []\n\t\t\tvar a = Stream(3)\n\t\t\tvar b = Stream(5)\n\t\t\tStream.combine(function(a, b, changed) {\n\t\t\t\tstreams = changed\n\t\t\t}, [a, b])\n\n\t\t\ta(7)\n\n\t\t\to(streams.length).equals(1)\n\t\t\to(streams[0]).equals(a)\n\t\t})\n\t\to(\"combine can return undefined\", function() {\n\t\t\tvar a = Stream(1)\n\t\t\tvar b = Stream.combine(function() {\n\t\t\t\treturn undefined\n\t\t\t}, [a])\n\n\t\t\to(b()).equals(undefined)\n\t\t})\n\t\to(\"combine can return stream\", function() {\n\t\t\tvar a = Stream(1)\n\t\t\tvar b = Stream.combine(function() {\n\t\t\t\treturn Stream(2)\n\t\t\t}, [a])\n\n\t\t\to(b()()).equals(2)\n\t\t})\n\t\to(\"combine can return pending stream\", function() {\n\t\t\tvar a = Stream(1)\n\t\t\tvar b = Stream.combine(function() {\n\t\t\t\treturn Stream()\n\t\t\t}, [a])\n\n\t\t\to(b()()).equals(undefined)\n\t\t})\n\t\to(\"combine can skip\", function() {\n\t\t\tvar count = 0\n\t\t\tvar a = Stream(1)\n\t\t\tvar b = Stream.combine(function() {\n\t\t\t\treturn Stream.SKIP\n\t\t\t}, [a])[\"fantasy-land/map\"](function() {\n\t\t\t\tcount++\n\t\t\t\treturn 1\n\t\t\t})\n\n\t\t\to(b()).equals(undefined)\n\t\t\to(count).equals(0)\n\t\t})\n\t\to(\"combine can conditionaly skip\", function() {\n\t\t\tvar count = 0\n\t\t\tvar skip = false\n\t\t\tvar a = Stream(1)\n\t\t\tvar b = Stream.combine(function(a) {\n\t\t\t\tif (skip) {\n\t\t\t\t\treturn Stream.SKIP\n\t\t\t\t}\n\t\t\t\treturn a()\n\t\t\t}, [a])[\"fantasy-land/map\"](function(a) {\n\t\t\t\tcount++\n\t\t\t\treturn a\n\t\t\t})\n\t\t\to(b()).equals(1)\n\t\t\to(count).equals(1)\n\t\t\tskip = true\n\t\t\tcount = 0\n\t\t\ta(2)\n\t\t\to(b()).equals(1)\n\t\t\to(count).equals(0)\n\t\t})\n\t\to(\"combine will throw with a helpful error if given non-stream values\", function () {\n\t\t\tvar spy = o.spy()\n\t\t\tvar a = Stream(1)\n\t\t\tvar thrown = null;\n\t\t\ttry {\n\t\t\t\tStream.combine(spy, [a, \"\"])\n\t\t\t} catch (e) {\n\t\t\t\tthrown = e\n\t\t\t}\n\n\t\t\to(thrown).notEquals(null)\n\t\t\to(thrown.constructor === TypeError).equals(false)\n\t\t\to(spy.callCount).equals(0)\n\t\t})\n\t\to(\"combine callback not called when child stream was ended\", function () {\n\t\t\tvar spy = o.spy()\n\t\t\tvar a = Stream(1)\n\t\t\tvar b = Stream(2)\n\t\t\tvar mapped = Stream.combine(spy, [a, b])\n\t\t\tmapped.end(true)\n\t\t\ta(11)\n\t\t\to(spy.callCount).equals(1)\n\t\t})\n\t})\n\to.spec(\"lift\", function() {\n\t\to(\"transforms value\", function() {\n\t\t\tvar stream = Stream()\n\t\t\tvar doubled = Stream.lift(function(s) {return s * 2}, stream)\n\n\t\t\tstream(2)\n\n\t\t\to(doubled()).equals(4)\n\t\t})\n\t\to(\"transforms default value\", function() {\n\t\t\tvar stream = Stream(2)\n\t\t\tvar doubled = Stream.lift(function(s) {return s * 2}, stream)\n\n\t\t\to(doubled()).equals(4)\n\t\t})\n\t\to(\"transforms multiple values\", function() {\n\t\t\tvar s1 = Stream()\n\t\t\tvar s2 = Stream()\n\t\t\tvar added = Stream.lift(function(s1, s2) {return s1 + s2}, s1, s2)\n\n\t\t\ts1(2)\n\t\t\ts2(3)\n\n\t\t\to(added()).equals(5)\n\t\t})\n\t\to(\"transforms multiple default values\", function() {\n\t\t\tvar s1 = Stream(2)\n\t\t\tvar s2 = Stream(3)\n\t\t\tvar added = Stream.lift(function(s1, s2) {return s1 + s2}, s1, s2)\n\n\t\t\to(added()).equals(5)\n\t\t})\n\t\to(\"transforms mixed default and late-bound values\", function() {\n\t\t\tvar s1 = Stream(2)\n\t\t\tvar s2 = Stream()\n\t\t\tvar added = Stream.lift(function(s1, s2) {return s1 + s2}, s1, s2)\n\n\t\t\ts2(3)\n\n\t\t\to(added()).equals(5)\n\t\t})\n\t\to(\"lifts atomically\", function() {\n\t\t\tvar count = 0\n\t\t\tvar a = Stream()\n\t\t\tvar b = Stream.lift(function(a) {return a * 2}, a)\n\t\t\tvar c = Stream.lift(function(a) {return a * a}, a)\n\t\t\tvar d = Stream.lift(function(b, c) {\n\t\t\t\tcount++\n\t\t\t\treturn b + c\n\t\t\t}, b, c)\n\n\t\t\ta(3)\n\n\t\t\to(d()).equals(15)\n\t\t\to(count).equals(1)\n\t\t})\n\t\to(\"lifts default value atomically\", function() {\n\t\t\tvar count = 0\n\t\t\tvar a = Stream(3)\n\t\t\tvar b = Stream.lift(function(a) {return a * 2}, a)\n\t\t\tvar c = Stream.lift(function(a) {return a * a}, a)\n\t\t\tvar d = Stream.lift(function(b, c) {\n\t\t\t\tcount++\n\t\t\t\treturn b + c\n\t\t\t}, b, c)\n\n\t\t\to(d()).equals(15)\n\t\t\to(count).equals(1)\n\t\t})\n\t\to(\"lift can return undefined\", function() {\n\t\t\tvar a = Stream(1)\n\t\t\tvar b = Stream.lift(function() {\n\t\t\t\treturn undefined\n\t\t\t}, a)\n\n\t\t\to(b()).equals(undefined)\n\t\t})\n\t\to(\"lift can return stream\", function() {\n\t\t\tvar a = Stream(1)\n\t\t\tvar b = Stream.lift(function() {\n\t\t\t\treturn Stream(2)\n\t\t\t}, a)\n\n\t\t\to(b()()).equals(2)\n\t\t})\n\t\to(\"lift can return pending stream\", function() {\n\t\t\tvar a = Stream(1)\n\t\t\tvar b = Stream.lift(function() {\n\t\t\t\treturn Stream()\n\t\t\t}, a)\n\n\t\t\to(b()()).equals(undefined)\n\t\t})\n\t\to(\"lift can halt\", function() {\n\t\t\tvar count = 0\n\t\t\tvar a = Stream(1)\n\t\t\tvar b = Stream.lift(function() {\n\t\t\t\treturn Stream.SKIP\n\t\t\t}, a)[\"fantasy-land/map\"](function() {\n\t\t\t\tcount++\n\t\t\t\treturn 1\n\t\t\t})\n\n\t\t\to(b()).equals(undefined)\n\t\t\to(count).equals(0)\n\t\t})\n\t\to(\"lift will throw with a helpful error if given non-stream values\", function () {\n\t\t\tvar spy = o.spy()\n\t\t\tvar a = Stream(1)\n\t\t\tvar thrown = null;\n\t\t\ttry {\n\t\t\t\tStream.lift(spy, a, \"\")\n\t\t\t} catch (e) {\n\t\t\t\tthrown = e\n\t\t\t}\n\n\t\t\to(thrown).notEquals(null)\n\t\t\to(thrown.constructor === TypeError).equals(false)\n\t\t\to(spy.callCount).equals(0)\n\t\t})\n\t})\n\to.spec(\"merge\", function() {\n\t\to(\"transforms an array of streams to an array of values\", function() {\n\t\t\tvar all = Stream.merge([\n\t\t\t\tStream(10),\n\t\t\t\tStream(\"20\"),\n\t\t\t\tStream({value: 30}),\n\t\t\t])\n\n\t\t\to(all()).deepEquals([10, \"20\", {value: 30}])\n\t\t})\n\t\to(\"remains pending until all streams are active\", function() {\n\t\t\tvar straggler = Stream()\n\n\t\t\tvar all = Stream.merge([\n\t\t\t\tStream(10),\n\t\t\t\tStream(\"20\"),\n\t\t\t\tstraggler,\n\t\t\t])\n\n\t\t\to(all()).equals(undefined)\n\n\t\t\tstraggler(30)\n\t\t\to(all()).deepEquals([10, \"20\", 30])\n\t\t})\n\t\to(\"calls run callback after all parents are active\", function() {\n\t\t\tvar value = 0\n\t\t\tvar id = function(value) {return value}\n\t\t\tvar a = Stream()\n\t\t\tvar b = Stream()\n\n\t\t\tStream.merge([a.map(id), b.map(id)]).map(function(data) {\n\t\t\t\tvalue = data[0] + data[1]\n\t\t\t\treturn undefined\n\t\t\t})\n\n\t\t\ta(1)\n\t\t\tb(2)\n\t\t\to(value).equals(3)\n\n\t\t\ta(3)\n\t\t\tb(4)\n\t\t\to(value).equals(7)\n\t\t})\n\t})\n\to.spec(\"end\", function() {\n\t\to(\"end stream works\", function() {\n\t\t\tvar stream = Stream()\n\t\t\tvar doubled = Stream.combine(function(stream) {return stream() * 2}, [stream])\n\n\t\t\tstream.end(true)\n\n\t\t\tstream(3)\n\n\t\t\to(doubled()).equals(undefined)\n\t\t})\n\t\to(\"end stream works with default value\", function() {\n\t\t\tvar stream = Stream(2)\n\t\t\tvar doubled = Stream.combine(function(stream) {return stream() * 2}, [stream])\n\n\t\t\tstream.end(true)\n\n\t\t\tstream(3)\n\n\t\t\to(doubled()).equals(4)\n\t\t})\n\t\to(\"cannot add downstream to ended stream\", function() {\n\t\t\tvar stream = Stream(2)\n\t\t\tstream.end(true)\n\n\t\t\tvar doubled = Stream.combine(function(stream) {return stream() * 2}, [stream])\n\t\t\tstream(3)\n\n\t\t\to(doubled()).equals(undefined)\n\t\t})\n\t\to(\"upstream does not affect ended stream\", function() {\n\t\t\tvar stream = Stream(2)\n\t\t\tvar doubled = Stream.combine(function(stream) {return stream() * 2}, [stream])\n\n\t\t\tdoubled.end(true)\n\n\t\t\tstream(4)\n\n\t\t\to(doubled()).equals(4)\n\t\t})\n\t\to(\"end stream can be mapped to\", function() {\n\t\t\tvar stream = Stream()\n\t\t\tvar spy = o.spy()\n\n\t\t\tstream.end.map(spy)\n\n\t\t\to(spy.callCount).equals(0)\n\n\t\t\tstream.end(true)\n\n\t\t\to(spy.callCount).equals(1)\n\t\t})\n\t\to(\"ended stream works like a container\", function() {\n\t\t\tvar stream = Stream(1)\n\t\t\tstream.end(true)\n\t\t\tstream(2)\n\t\t\to(stream()).equals(2)\n\t\t})\n\t\t// https://github.com/MithrilJS/mithril.js/issues/2601\n\t\to(\"ended stream doesn't affect emit of subsequent streams\", function() {\n\t\t\tconst refreshing = Stream()\n\t\t\tconst o1Received = []\n\t\t\tconst waitingReceived = []\n\t\t\tconst o2Received = []\n\t\t\tconst o3Received = []\n\t\t\tconst o4Received = []\n\n\t\t\t/* eslint-disable array-callback-return */\n\t\t\trefreshing.map(function(v) { o1Received.push(v) })\n\n\t\t\tconst waiting = refreshing.map(function(v) {\n\t\t\t\twaitingReceived.push(v)\n\t\t\t\tif (v === false) {\n\t\t\t\t\twaiting.end(true)\n\t\t\t\t}\n\t\t\t})\n\n\t\t\trefreshing.map(function(v) { o2Received.push(v) })\n\t\t\trefreshing.map(function(v) { o3Received.push(v) })\n\t\t\trefreshing.map(function(v) { o4Received.push(v) })\n\t\t\t/* eslint-enable array-callback-return */\n\n\t\t\trefreshing(true)\n\t\t\trefreshing(false)\n\t\t\trefreshing(\"more\")\n\n\t\t\to(o1Received).deepEquals([true, false, \"more\"])\n\t\t\to(waitingReceived).deepEquals([true, false])\n\t\t\to(o2Received).deepEquals([true, false, \"more\"])\n\t\t\to(o3Received).deepEquals([true, false, \"more\"])\n\t\t\to(o4Received).deepEquals([true, false, \"more\"])\n\t\t})\n\t})\n\to.spec(\"toJSON\", function() {\n\t\to(\"works\", function() {\n\t\t\to(Stream(1).toJSON()).equals(1)\n\t\t\to(Stream(\"a\").toJSON()).equals(\"a\")\n\t\t\to(Stream(true).toJSON()).equals(true)\n\t\t\to(Stream(null).toJSON()).equals(null)\n\t\t\to(Stream(undefined).toJSON()).equals(undefined)\n\t\t\to(Stream({a: 1}).toJSON()).deepEquals({a: 1})\n\t\t\to(Stream([1, 2, 3]).toJSON()).deepEquals([1, 2, 3])\n\t\t\to(Stream().toJSON()).equals(undefined)\n\t\t\to(Stream(new Date(0)).toJSON()).equals(new Date(0).toJSON())\n\t\t})\n\t\to(\"works w/ JSON.stringify\", function() {\n\t\t\to(JSON.stringify(Stream(1))).equals(JSON.stringify(1))\n\t\t\to(JSON.stringify(Stream(\"a\"))).equals(JSON.stringify(\"a\"))\n\t\t\to(JSON.stringify(Stream(true))).equals(JSON.stringify(true))\n\t\t\to(JSON.stringify(Stream(null))).equals(JSON.stringify(null))\n\t\t\to(JSON.stringify(Stream(undefined))).equals(JSON.stringify(undefined))\n\t\t\to(JSON.stringify(Stream({a: 1}))).deepEquals(JSON.stringify({a: 1}))\n\t\t\to(JSON.stringify(Stream([1, 2, 3]))).deepEquals(JSON.stringify([1, 2, 3]))\n\t\t\to(JSON.stringify(Stream())).equals(JSON.stringify(undefined))\n\t\t\to(JSON.stringify(Stream(new Date(0)))).equals(JSON.stringify(new Date(0)))\n\t\t})\n\t})\n\to.spec(\"map\", function() {\n\t\to(\"works\", function() {\n\t\t\tvar stream = Stream()\n\t\t\tvar doubled = stream[\"fantasy-land/map\"](function(value) {return value * 2})\n\n\t\t\tstream(3)\n\n\t\t\to(doubled()).equals(6)\n\t\t})\n\t\to(\"works with default value\", function() {\n\t\t\tvar stream = Stream(3)\n\t\t\tvar doubled = stream[\"fantasy-land/map\"](function(value) {return value * 2})\n\n\t\t\to(doubled()).equals(6)\n\t\t})\n\t\to(\"works with undefined value\", function() {\n\t\t\tvar stream = Stream()\n\t\t\tvar mapped = stream[\"fantasy-land/map\"](function(value) {return String(value)})\n\n\t\t\tstream(undefined)\n\n\t\t\to(mapped()).equals(\"undefined\")\n\t\t})\n\t\to(\"works with default undefined value\", function() {\n\t\t\tvar stream = Stream(undefined)\n\t\t\tvar mapped = stream[\"fantasy-land/map\"](function(value) {return String(value)})\n\n\t\t\to(mapped()).equals(\"undefined\")\n\t\t})\n\t\to(\"works with pending stream\", function() {\n\t\t\tvar stream = Stream(undefined)\n\t\t\tvar mapped = stream[\"fantasy-land/map\"](function() {return Stream()})\n\n\t\t\to(mapped()()).equals(undefined)\n\t\t})\n\t\to(\"has alias\", function() {\n\t\t\tvar stream = Stream(undefined)\n\n\t\t\to(stream[\"fantasy-land/map\"]).equals(stream.map)\n\t\t})\n\t\to(\"mapping function is not invoked after ending\", function () {\n\t\t\tvar stream = Stream(undefined)\n\t\t\tvar fn = o.spy()\n\t\t\tvar mapped = stream.map(fn)\n\t\t\tmapped.end(true)\n\t\t\tstream(undefined)\n\t\t\to(fn.callCount).equals(1)\n\t\t})\n\t})\n\to.spec(\"ap\", function() {\n\t\to(\"works\", function() {\n\t\t\tvar apply = Stream(function(value) {return value * 2})\n\t\t\tvar stream = Stream(3)\n\t\t\tvar applied = stream[\"fantasy-land/ap\"](apply)\n\n\t\t\to(applied()).equals(6)\n\n\t\t\tapply(function(value) {return value / 3})\n\n\t\t\to(applied()).equals(1)\n\n\t\t\tstream(9)\n\n\t\t\to(applied()).equals(3)\n\t\t})\n\t\to(\"works with undefined value\", function() {\n\t\t\tvar apply = Stream(function(value) {return String(value)})\n\t\t\tvar stream = Stream(undefined)\n\t\t\tvar applied = stream[\"fantasy-land/ap\"](apply)\n\n\t\t\to(applied()).equals(\"undefined\")\n\n\t\t\tapply(function(value) {return String(value) + \"a\"})\n\n\t\t\to(applied()).equals(\"undefineda\")\n\t\t})\n\t})\n\to.spec(\"fantasy-land\", function() {\n\t\to.spec(\"functor\", function() {\n\t\t\to(\"identity\", function() {\n\t\t\t\tvar stream = Stream(3)\n\t\t\t\tvar mapped = stream[\"fantasy-land/map\"](function(value) {return value})\n\n\t\t\t\to(stream()).equals(mapped())\n\t\t\t})\n\t\t\to(\"composition\", function() {\n\t\t\t\tfunction f(x) {return x * 2}\n\t\t\t\tfunction g(x) {return x * x}\n\n\t\t\t\tvar stream = Stream(3)\n\n\t\t\t\tvar mapped = stream[\"fantasy-land/map\"](function(value) {return f(g(value))})\n\t\t\t\tvar composed = stream[\"fantasy-land/map\"](g)[\"fantasy-land/map\"](f)\n\n\t\t\t\to(mapped()).equals(18)\n\t\t\t\to(mapped()).equals(composed())\n\t\t\t})\n\t\t})\n\t\to.spec(\"apply\", function() {\n\t\t\to(\"composition\", function() {\n\t\t\t\tvar a = Stream(function(value) {return value * 2})\n\t\t\t\tvar u = Stream(function(value) {return value * 3})\n\t\t\t\tvar v = Stream(5)\n\n\t\t\t\tvar mapped = v[\"fantasy-land/ap\"](u[\"fantasy-land/ap\"](a[\"fantasy-land/map\"](function(f) {\n\t\t\t\t\treturn function(g) {\n\t\t\t\t\t\treturn function(x) {\n\t\t\t\t\t\t\treturn f(g(x))\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t})))\n\n\t\t\t\tvar composed = v[\"fantasy-land/ap\"](u)[\"fantasy-land/ap\"](a)\n\n\t\t\t\to(mapped()).equals(30)\n\t\t\t\to(mapped()).equals(composed())\n\t\t\t})\n\t\t})\n\t\to.spec(\"applicative\", function() {\n\t\t\to(\"identity\", function() {\n\t\t\t\tvar a = Stream[\"fantasy-land/of\"](function(value) {return value})\n\t\t\t\tvar v = Stream(5)\n\n\t\t\t\to(v[\"fantasy-land/ap\"](a)()).equals(5)\n\t\t\t\to(v[\"fantasy-land/ap\"](a)()).equals(v())\n\t\t\t})\n\t\t\to(\"homomorphism\", function() {\n\t\t\t\tvar a = Stream(0)\n\t\t\t\tvar f = function(value) {return value * 2}\n\t\t\t\tvar x = 3\n\n\t\t\t\to(a.constructor[\"fantasy-land/of\"](x)[\"fantasy-land/ap\"](a.constructor[\"fantasy-land/of\"](f))()).equals(6)\n\t\t\t\to(a.constructor[\"fantasy-land/of\"](x)[\"fantasy-land/ap\"](a.constructor[\"fantasy-land/of\"](f))()).equals(a.constructor[\"fantasy-land/of\"](f(x))())\n\t\t\t})\n\t\t\to(\"interchange\", function() {\n\t\t\t\tvar u = Stream(function(value) {return value * 2})\n\t\t\t\tvar a = Stream()\n\t\t\t\tvar y = 3\n\n\t\t\t\to(a.constructor[\"fantasy-land/of\"](y)[\"fantasy-land/ap\"](u)()).equals(6)\n\t\t\t\to(a.constructor[\"fantasy-land/of\"](y)[\"fantasy-land/ap\"](u)()).equals(u[\"fantasy-land/ap\"](a.constructor[\"fantasy-land/of\"](function(f) {return f(y)}))())\n\t\t\t})\n\t\t})\n\t})\n})\n"
  },
  {
    "path": "stream.js",
    "content": "\"use strict\"\n\nmodule.exports = require(\"./stream/stream\")\n"
  },
  {
    "path": "test-utils/browserMock.js",
    "content": "\"use strict\"\n\nvar pushStateMock = require(\"./pushStateMock\")\nvar domMock = require(\"./domMock\")\nvar xhrMock = require(\"./xhrMock\")\n\nmodule.exports = function(env) {\n\tenv = env || {}\n\tvar $window = env.window = {}\n\n\tvar dom = domMock()\n\tvar xhr = xhrMock()\n\tfor (var key in dom) if (!$window[key]) $window[key] = dom[key]\n\tfor (var key in xhr) if (!$window[key]) $window[key] = xhr[key]\n\tpushStateMock(env)\n\n\treturn $window\n}"
  },
  {
    "path": "test-utils/callAsync.js",
    "content": "\"use strict\"\n\nmodule.exports = setTimeout\n"
  },
  {
    "path": "test-utils/components.js",
    "content": "\"use strict\"\n\nvar m = require(\"../render/hyperscript\")\n\nmodule.exports = [\n\t{\n\t\tkind: \"POJO\",\n\t\tcreate: function(methods) {\n\t\t\tvar res = {view: function() {return m(\"div\")}}\n\t\t\tObject.keys(methods || {}).forEach(function(m){res[m] = methods[m]})\n\t\t\treturn res\n\t\t}\n\t}, {\n\t\tkind: \"constructible\",\n\t\tcreate: function(methods) {\n\t\t\tfunction res(){}\n\t\t\tres.prototype.view = function() {return m(\"div\")}\n\t\t\tObject.keys(methods || {}).forEach(function(m){res.prototype[m] = methods[m]})\n\t\t\treturn res\n\t\t}\n\t}, {\n\t\tkind: \"closure\",\n\t\tcreate: function(methods) {\n\t\t\treturn function() {\n\t\t\t\tvar res = {view: function() {return m(\"div\")}}\n\t\t\t\tObject.keys(methods || {}).forEach(function(m){res[m] = methods[m]})\n\t\t\t\treturn res\n\t\t\t}\n\t\t}\n\t}\n]\n"
  },
  {
    "path": "test-utils/domMock.js",
    "content": "\"use strict\"\n\n/*\nKnown limitations:\n- the innerHTML setter and the DOMParser only support a small subset of the true HTML/XML syntax.\n- `option.selected` can't be set/read when the option doesn't have a `select` parent\n- `element.attributes` is just a map of attribute names => Attr objects stubs\n- ...\n\n*/\n\n/*\noptions:\n- spy:(f: Function) => Function\n*/\n\nmodule.exports = function(options) {\n\toptions = options || {}\n\tvar spy = options.spy || function(f){return f}\n\tvar spymap = []\n\n\t// This way I'm not also implementing a partial `URL` polyfill. Based on the\n\t// regexp at https://urlregex.com/, but adapted to allow relative URLs and\n\t// care only about HTTP(S) URLs.\n\tvar urlHash = \"#[?!/+=&;%@.\\\\w_-]*\"\n\tvar urlQuery = \"\\\\?[!/+=&;%@.\\\\w_-]*\"\n\tvar urlPath = \"/[+~%/.\\\\w_-]*\"\n\tvar urlRelative = urlPath + \"(?:\" + urlQuery + \")?(?:\" + urlHash + \")?\"\n\tvar urlDomain = \"https?://[A-Za-z0-9][A-Za-z0-9.-]+[A-Za-z0-9]\"\n\tvar validURLRegex = new RegExp(\n\t\t\"^\" + urlDomain + \"(\" + urlRelative + \")?$|\" +\n\t\t\"^\" + urlRelative + \"$|\" +\n\t\t\"^\" + urlQuery + \"(?:\" + urlHash + \")?$|\" +\n\t\t\"^\" + urlHash + \"$\"\n\t)\n\n\tvar hasOwn = ({}.hasOwnProperty)\n\n\tfunction registerSpies(element, spies) {\n\t\tif(options.spy) {\n\t\t\tvar i = spymap.indexOf(element)\n\t\t\tif (i === -1) {\n\t\t\t\tspymap.push(element, spies)\n\t\t\t} else {\n\t\t\t\tvar existing = spymap[i + 1]\n\t\t\t\tfor (var k in spies) existing[k] = spies[k]\n\t\t\t}\n\t\t}\n\t}\n\tfunction getSpies(element) {\n\t\tif (element == null || typeof element !== \"object\") throw new Error(\"Element expected\")\n\t\tif(options.spy) return spymap[spymap.indexOf(element) + 1]\n\t}\n\n\tfunction isModernEvent(type) {\n\t\treturn type === \"transitionstart\" || type === \"transitionend\" || type === \"animationstart\" || type === \"animationend\"\n\t}\n\tfunction dispatchEvent(e) {\n\t\tvar stopped = false\n\t\te.stopImmediatePropagation = function() {\n\t\t\te.stopPropagation()\n\t\t\tstopped = true\n\t\t}\n\t\te.currentTarget = this\n\t\tif (this._events[e.type] != null) {\n\t\t\tfor (var i = 0; i < this._events[e.type].handlers.length; i++) {\n\t\t\t\tvar useCapture = this._events[e.type].options[i].capture\n\t\t\t\tif (useCapture && e.eventPhase < 3 || !useCapture && e.eventPhase > 1) {\n\t\t\t\t\tvar handler = this._events[e.type].handlers[i]\n\t\t\t\t\tif (typeof handler === \"function\") try {handler.call(this, e)} catch(e) {setTimeout(function(){throw e})}\n\t\t\t\t\telse try {handler.handleEvent(e)} catch(e) {setTimeout(function(){throw e})}\n\t\t\t\t\tif (stopped) return\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t// this is inaccurate. Normally the event fires in definition order, including legacy events\n\t\t// this would require getters/setters for each of them though and we haven't gotten around to\n\t\t// adding them since it would be at a high perf cost or would entail some heavy refactoring of\n\t\t// the mocks (prototypes instead of closures).\n\t\tif (e.eventPhase > 1 && typeof this[\"on\" + e.type] === \"function\" && !isModernEvent(e.type)) try {this[\"on\" + e.type](e)} catch(e) {setTimeout(function(){throw e})}\n\t}\n\tfunction appendChild(child) {\n\t\tvar ancestor = this\n\t\twhile (ancestor !== child && ancestor !== null) ancestor = ancestor.parentNode\n\t\tif (ancestor === child) throw new Error(\"Node cannot be inserted at the specified point in the hierarchy\")\n\n\t\tif (child.nodeType == null) throw new Error(\"Argument is not a DOM element\")\n\n\t\tvar index = this.childNodes.indexOf(child)\n\t\tif (index > -1) this.childNodes.splice(index, 1)\n\t\tif (child.nodeType === 11) {\n\t\t\twhile (child.firstChild != null) appendChild.call(this, child.firstChild)\n\t\t\tchild.childNodes = []\n\t\t}\n\t\telse {\n\t\t\tthis.childNodes.push(child)\n\t\t\tif (child.parentNode != null && child.parentNode !== this) removeChild.call(child.parentNode, child)\n\t\t\tchild.parentNode = this\n\t\t}\n\t}\n\tfunction removeChild(child) {\n\t\tif (child == null || typeof child !== \"object\" || !(\"nodeType\" in child)) {\n\t\t\tthrow new TypeError(\"Failed to execute removeChild, parameter is not of type 'Node'\")\n\t\t}\n\t\tvar index = this.childNodes.indexOf(child)\n\t\tif (index > -1) {\n\t\t\tthis.childNodes.splice(index, 1)\n\t\t\tchild.parentNode = null\n\t\t}\n\t\telse throw new TypeError(\"Failed to execute 'removeChild', child not found in parent\")\n\t}\n\tfunction insertBefore(child, reference) {\n\t\tvar ancestor = this\n\t\twhile (ancestor !== child && ancestor !== null) ancestor = ancestor.parentNode\n\t\tif (ancestor === child) throw new Error(\"Node cannot be inserted at the specified point in the hierarchy\")\n\n\t\tif (child.nodeType == null) throw new Error(\"Argument is not a DOM element\")\n\n\t\tvar refIndex = this.childNodes.indexOf(reference)\n\t\tvar index = this.childNodes.indexOf(child)\n\t\tif (reference !== null && refIndex < 0) throw new TypeError(\"Invalid argument\")\n\t\tif (index > -1) this.childNodes.splice(index, 1)\n\t\tif (reference === null) appendChild.call(this, child)\n\t\telse {\n\t\t\tif (index !== -1 && refIndex > index) refIndex--\n\t\t\tif (child.nodeType === 11) {\n\t\t\t\tthis.childNodes.splice.apply(this.childNodes, [refIndex, 0].concat(child.childNodes))\n\t\t\t\twhile (child.firstChild) {\n\t\t\t\t\tvar subchild = child.firstChild\n\t\t\t\t\tremoveChild.call(child, subchild)\n\t\t\t\t\tsubchild.parentNode = this\n\t\t\t\t}\n\t\t\t\tchild.childNodes = []\n\t\t\t}\n\t\t\telse {\n\t\t\t\tthis.childNodes.splice(refIndex, 0, child)\n\t\t\t\tif (child.parentNode != null && child.parentNode !== this) removeChild.call(child.parentNode, child)\n\t\t\t\tchild.parentNode = this\n\t\t\t}\n\t\t}\n\t}\n\tfunction getAttribute(name) {\n\t\tif (this.attributes[name] == null) return null\n\t\treturn this.attributes[name].value\n\t}\n\tfunction setAttribute(name, value) {\n\t\t/*eslint-disable no-implicit-coercion*/\n\t\t// this is the correct kind of conversion, passing a Symbol throws in browsers too.\n\t\tvar nodeValue = \"\" + value\n\t\t/*eslint-enable no-implicit-coercion*/\n\t\tthis.attributes[name] = {\n\t\t\tnamespaceURI: hasOwn.call(this.attributes, name) ? this.attributes[name].namespaceURI : null,\n\t\t\tget value() {return nodeValue},\n\t\t\tset value(value) {\n\t\t\t\t/*eslint-disable no-implicit-coercion*/\n\t\t\t\tnodeValue = \"\" + value\n\t\t\t\t/*eslint-enable no-implicit-coercion*/\n\t\t\t},\n\t\t\tget nodeValue() {return nodeValue},\n\t\t\tset nodeValue(value) {\n\t\t\t\tthis.value = value\n\t\t\t}\n\t\t}\n\t}\n\tfunction setAttributeNS(ns, name, value) {\n\t\tthis.setAttribute(name, value)\n\t\tthis.attributes[name].namespaceURI = ns\n\t}\n\tfunction removeAttribute(name) {\n\t\tdelete this.attributes[name]\n\t}\n\tfunction hasAttribute(name) {\n\t\treturn name in this.attributes\n\t}\n\tvar declListTokenizer = /;|\"(?:\\\\.|[^\"\\n])*\"|'(?:\\\\.|[^'\\n])*'/g\n\t/**\n\t * This will split a semicolon-separated CSS declaration list into an array of\n\t * individual declarations, ignoring semicolons in strings.\n\t *\n\t * Comments are also stripped.\n\t *\n\t * @param {string} declList\n\t * @return {string[]}\n\t */\n\tfunction splitDeclList(declList) {\n\t\tvar indices = [], res = [], match\n\n\t\t// remove comments, preserving comments in strings.\n\t\tdeclList = declList.replace(\n\t\t\t/(\"(?:\\\\.|[^\"\\n])*\"|'(?:\\\\.|[^'\\n])*')|\\/\\*[\\s\\S]*?\\*\\//g,\n\t\t\tfunction(m, str){\n\t\t\t\treturn str || \"\"\n\t\t\t}\n\t\t)\n\t\t/*eslint-disable no-cond-assign*/\n\t\twhile (match = declListTokenizer.exec(declList)) {\n\t\t\tif (match[0] === \";\") indices.push(match.index)\n\t\t}\n\t\t/*eslint-enable no-cond-assign*/\n\t\tfor (var i = indices.length; i--;){\n\t\t\tres.unshift(declList.slice(indices[i] + 1))\n\t\t\tdeclList = declList.slice(0, indices[i])\n\t\t}\n\t\tres.unshift(declList)\n\t\treturn res\n\t}\n\tfunction parseMarkup(value, root, voidElements, xmlns) {\n\t\tvar depth = 0, stack = [root]\n\t\tvalue.replace(/<([a-z0-9\\-]+?)((?:\\s+?[^=]+?=(?:\"[^\"]*?\"|'[^']*?'|[^\\s>]*))*?)(\\s*\\/)?>|<\\/([a-z0-9\\-]+?)>|([^<]+)/g, function(match, startTag, attrs, selfClosed, endTag, text) {\n\t\t\tif (startTag) {\n\t\t\t\tvar element = xmlns == null ? $window.document.createElement(startTag) : $window.document.createElementNS(xmlns, startTag)\n\t\t\t\tattrs.replace(/\\s+?([^=]+?)=(?:\"([^\"]*?)\"|'([^']*?)'|([^\\s>]*))/g, function(match, key, doubleQuoted, singleQuoted, unquoted) {\n\t\t\t\t\tvar keyParts = key.split(\":\")\n\t\t\t\t\tvar name = keyParts.pop()\n\t\t\t\t\tvar ns = keyParts[0]\n\t\t\t\t\tvar value = doubleQuoted || singleQuoted || unquoted || \"\"\n\t\t\t\t\tif (ns != null) element.setAttributeNS(ns, name, value)\n\t\t\t\t\telse element.setAttribute(name, value)\n\t\t\t\t})\n\t\t\t\tappendChild.call(stack[depth], element)\n\t\t\t\tif (!selfClosed && voidElements.indexOf(startTag.toLowerCase()) < 0) stack[++depth] = element\n\t\t\t}\n\t\t\telse if (endTag) {\n\t\t\t\tdepth--\n\t\t\t}\n\t\t\telse if (text) {\n\t\t\t\tappendChild.call(stack[depth], $window.document.createTextNode(text)) // FIXME handle html entities\n\t\t\t}\n\t\t})\n\t}\n\tfunction DOMParser() {}\n\tDOMParser.prototype.parseFromString = function(src, mime) {\n\t\tif (mime !== \"image/svg+xml\") throw new Error(\"The DOMParser mock only supports the \\\"image/svg+xml\\\" MIME type\")\n\t\tvar match = src.match(/^<svg xmlns=\"http:\\/\\/www\\.w3\\.org\\/2000\\/svg\">(.*)<\\/svg>$/)\n\t\tif (!match) throw new Error(\"Please provide a bare SVG tag with the xmlns as only attribute\")\n\t\tvar value = match[1]\n\t\tvar root = $window.document.createElementNS(\"http://www.w3.org/2000/svg\", \"svg\")\n\t\tparseMarkup(value, root, [], \"http://www.w3.org/2000/svg\")\n\t\treturn {documentElement: root}\n\t}\n\tfunction camelCase(string) {\n\t\treturn string.replace(/-\\D/g, function(match) {return match[1].toUpperCase()})\n\t}\n\tvar activeElement\n\tvar delay = 16, last = 0\n\tvar $window = {\n\t\tDOMParser: DOMParser,\n\t\trequestAnimationFrame: function(callback) {\n\t\t\tvar elapsed = Date.now() - last\n\t\t\treturn setTimeout(function() {\n\t\t\t\tcallback()\n\t\t\t\tlast = Date.now()\n\t\t\t}, delay - elapsed)\n\t\t},\n\t\tdocument: {\n\t\t\tcreateElement: function(tag) {\n\t\t\t\tvar cssText = \"\"\n\t\t\t\tvar style = {}\n\t\t\t\tObject.defineProperties(style, {\n\t\t\t\t\tcssText: {\n\t\t\t\t\t\tget: function() {return cssText},\n\t\t\t\t\t\tset: function (value) {\n\t\t\t\t\t\t\tvar buf = []\n\t\t\t\t\t\t\tif (typeof value === \"string\") {\n\t\t\t\t\t\t\t\tfor (var key in style) style[key] = \"\"\n\t\t\t\t\t\t\t\tvar rules = splitDeclList(value)\n\t\t\t\t\t\t\t\tfor (var i = 0; i < rules.length; i++) {\n\t\t\t\t\t\t\t\t\tvar rule = rules[i]\n\t\t\t\t\t\t\t\t\tvar colonIndex = rule.indexOf(\":\")\n\t\t\t\t\t\t\t\t\tif (colonIndex > -1) {\n\t\t\t\t\t\t\t\t\t\tvar rawKey = rule.slice(0, colonIndex).trim()\n\t\t\t\t\t\t\t\t\t\tvar key = camelCase(rawKey)\n\t\t\t\t\t\t\t\t\t\tvar value = rule.slice(colonIndex + 1).trim()\n\t\t\t\t\t\t\t\t\t\tif (key !== \"cssText\") {\n\t\t\t\t\t\t\t\t\t\t\tstyle[key] = style[rawKey] = value\n\t\t\t\t\t\t\t\t\t\t\tbuf.push(rawKey + \": \" + value + \";\")\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\telement.setAttribute(\"style\", cssText = buf.join(\" \"))\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\tgetPropertyValue: {value: function(key){\n\t\t\t\t\t\treturn style[key]\n\t\t\t\t\t}},\n\t\t\t\t\tremoveProperty: {\n\t\t\t\t\t\twritable: true,\n\t\t\t\t\t\tvalue: function(key){\n\t\t\t\t\t\t\tstyle[key] = style[camelCase(key)] = \"\"\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\tsetProperty: {\n\t\t\t\t\t\twritable: true,\n\t\t\t\t\t\tvalue: function(key, value){\n\t\t\t\t\t\t\tstyle[key] = style[camelCase(key)] = value\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t\tvar events = {}\n\t\t\t\tvar element = {\n\t\t\t\t\tnodeType: 1,\n\t\t\t\t\tnodeName: tag.toUpperCase(),\n\t\t\t\t\tnamespaceURI: \"http://www.w3.org/1999/xhtml\",\n\t\t\t\t\tappendChild: appendChild,\n\t\t\t\t\tremoveChild: removeChild,\n\t\t\t\t\tinsertBefore: insertBefore,\n\t\t\t\t\thasAttribute: hasAttribute,\n\t\t\t\t\tgetAttribute: getAttribute,\n\t\t\t\t\tsetAttribute: setAttribute,\n\t\t\t\t\tsetAttributeNS: setAttributeNS,\n\t\t\t\t\tremoveAttribute: removeAttribute,\n\t\t\t\t\tparentNode: null,\n\t\t\t\t\tchildNodes: [],\n\t\t\t\t\tattributes: {},\n\t\t\t\t\townerDocument: $window.document,\n\t\t\t\t\tcontains: function(child) {\n\t\t\t\t\t\twhile (child != null) {\n\t\t\t\t\t\t\tif (child === this) return true\n\t\t\t\t\t\t\tchild = child.parentNode\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn false\n\t\t\t\t\t},\n\t\t\t\t\tget firstChild() {\n\t\t\t\t\t\treturn this.childNodes[0] || null\n\t\t\t\t\t},\n\t\t\t\t\tget nextSibling() {\n\t\t\t\t\t\tif (this.parentNode == null) return null\n\t\t\t\t\t\tvar index = this.parentNode.childNodes.indexOf(this)\n\t\t\t\t\t\tif (index < 0) throw new TypeError(\"Parent's childNodes is out of sync\")\n\t\t\t\t\t\treturn this.parentNode.childNodes[index + 1] || null\n\t\t\t\t\t},\n\t\t\t\t\t// eslint-disable-next-line accessor-pairs\n\t\t\t\t\tset textContent(value) {\n\t\t\t\t\t\tthis.childNodes = []\n\t\t\t\t\t\tif (value !== \"\") appendChild.call(this, $window.document.createTextNode(value))\n\t\t\t\t\t},\n\t\t\t\t\t// eslint-disable-next-line accessor-pairs\n\t\t\t\t\tset innerHTML(value) {\n\t\t\t\t\t\tvar voidElements = [\"area\", \"base\", \"br\", \"col\", \"command\", \"embed\", \"hr\", \"img\", \"input\", \"keygen\", \"link\", \"meta\", \"param\", \"source\", \"track\", \"wbr\"]\n\t\t\t\t\t\twhile (this.firstChild) removeChild.call(this, this.firstChild)\n\t\t\t\t\t\tvar match = value.match(/^<svg xmlns=\"http:\\/\\/www\\.w3\\.org\\/2000\\/svg\">(.*)<\\/svg>$/), root, ns\n\t\t\t\t\t\tif (match) {\n\t\t\t\t\t\t\tvar value = match[1]\n\t\t\t\t\t\t\troot = $window.document.createElementNS(\"http://www.w3.org/2000/svg\", \"svg\")\n\t\t\t\t\t\t\tns = \"http://www.w3.org/2000/svg\"\n\t\t\t\t\t\t\tappendChild.call(this, root)\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\troot = this\n\t\t\t\t\t\t}\n\t\t\t\t\t\tparseMarkup(value, root, voidElements, ns)\n\t\t\t\t\t},\n\t\t\t\t\tget style() {\n\t\t\t\t\t\treturn style\n\t\t\t\t\t},\n\t\t\t\t\tset style(value){\n\t\t\t\t\t\tthis.style.cssText = value\n\t\t\t\t\t},\n\t\t\t\t\tget className() {\n\t\t\t\t\t\treturn this.attributes[\"class\"] ? this.attributes[\"class\"].value : \"\"\n\t\t\t\t\t},\n\t\t\t\t\tset className(value) {\n\t\t\t\t\t\tif (this.namespaceURI === \"http://www.w3.org/2000/svg\") throw new Error(\"Cannot set property className of SVGElement\")\n\t\t\t\t\t\telse this.setAttribute(\"class\", value)\n\t\t\t\t\t},\n\t\t\t\t\tfocus: function() {activeElement = this},\n\t\t\t\t\taddEventListener: function(type, handler, options) {\n\t\t\t\t\t\tif (arguments.length > 2) {\n\t\t\t\t\t\t\tif (typeof options === \"object\" && options != null) throw new TypeError(\"NYI: addEventListener options\")\n\t\t\t\t\t\t\telse if (typeof options !== \"boolean\") throw new TypeError(\"boolean expected for useCapture\")\n\t\t\t\t\t\t\telse options = {capture: options}\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\toptions = {capture: false}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (events[type] == null) events[type] = {handlers: [handler], options: [options]}\n\t\t\t\t\t\telse {\n\t\t\t\t\t\t\tvar found = false\n\t\t\t\t\t\t\tfor (var i = 0; i < events[type].handlers.length; i++) {\n\t\t\t\t\t\t\t\tif (events[type].handlers[i] === handler && events[type].options[i].capture === options.capture) {\n\t\t\t\t\t\t\t\t\tfound = true\n\t\t\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (!found) {\n\t\t\t\t\t\t\t\tevents[type].handlers.push(handler)\n\t\t\t\t\t\t\t\tevents[type].options.push(options)\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\tremoveEventListener: function(type, handler, options) {\n\t\t\t\t\t\tif (arguments.length > 2) {\n\t\t\t\t\t\t\tif (typeof options === \"object\" && options != null) throw new TypeError(\"NYI: addEventListener options\")\n\t\t\t\t\t\t\telse if (typeof options !== \"boolean\") throw new TypeError(\"boolean expected for useCapture\")\n\t\t\t\t\t\t\telse options = {capture: options}\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\toptions = {capture: false}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (events[type] != null) {\n\t\t\t\t\t\t\tfor (var i = 0; i < events[type].handlers.length; i++) {\n\t\t\t\t\t\t\t\tif (events[type].handlers[i] === handler && events[type].options[i].capture === options.capture) {\n\t\t\t\t\t\t\t\t\tevents[type].handlers.splice(i, 1)\n\t\t\t\t\t\t\t\t\tevents[type].options.splice(i, 1)\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\tdispatchEvent: function(e) {\n\t\t\t\t\t\tvar parents = []\n\t\t\t\t\t\tif (this.parentNode != null) {\n\t\t\t\t\t\t\tvar parent = this.parentNode\n\t\t\t\t\t\t\tdo {\n\t\t\t\t\t\t\t\tparents.push(parent)\n\t\t\t\t\t\t\t\tparent = parent.parentNode\n\t\t\t\t\t\t\t} while (parent != null)\n\t\t\t\t\t\t}\n\t\t\t\t\t\te.target = this\n\t\t\t\t\t\tvar prevented = false\n\t\t\t\t\t\te.preventDefault = function() {\n\t\t\t\t\t\t\tprevented = true\n\t\t\t\t\t\t}\n\t\t\t\t\t\tObject.defineProperty(e, \"defaultPrevented\", {\n\t\t\t\t\t\t\tconfigurable: true,\n\t\t\t\t\t\t\tget: function () { return prevented }\n\t\t\t\t\t\t})\n\t\t\t\t\t\tvar stopped = false\n\t\t\t\t\t\te.stopPropagation = function() {\n\t\t\t\t\t\t\tstopped = true\n\t\t\t\t\t\t}\n\t\t\t\t\t\te.eventPhase = 1\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tfor (var i = parents.length - 1; 0 <= i; i--) {\n\t\t\t\t\t\t\t\tdispatchEvent.call(parents[i], e)\n\t\t\t\t\t\t\t\tif (stopped) {\n\t\t\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\te.eventPhase = 2\n\t\t\t\t\t\t\tdispatchEvent.call(this, e)\n\t\t\t\t\t\t\tif (stopped) {\n\t\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\te.eventPhase = 3\n\t\t\t\t\t\t\tfor (var i = 0; i < parents.length; i++) {\n\t\t\t\t\t\t\t\tdispatchEvent.call(parents[i], e)\n\t\t\t\t\t\t\t\tif (stopped) {\n\t\t\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} finally {\n\t\t\t\t\t\t\te.eventPhase = 0\n\t\t\t\t\t\t\tif (!prevented) {\n\t\t\t\t\t\t\t\tif (this.nodeName === \"INPUT\" && this.attributes[\"type\"] != null && this.attributes[\"type\"].value === \"checkbox\" && e.type === \"click\") {\n\t\t\t\t\t\t\t\t\tthis.checked = !this.checked\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t},\n\t\t\t\t\tonclick: null,\n\t\t\t\t\t_events: events\n\t\t\t\t}\n\n\t\t\t\tif (element.nodeName === \"A\") {\n\t\t\t\t\tObject.defineProperty(element, \"href\", {\n\t\t\t\t\t\tget: function() {\n\t\t\t\t\t\t\tif (this.namespaceURI === \"http://www.w3.org/2000/svg\") {\n\t\t\t\t\t\t\t\tvar val = this.hasAttribute(\"href\") ? this.attributes.href.value : \"\"\n\t\t\t\t\t\t\t\treturn {baseVal: val, animVal: val}\n\t\t\t\t\t\t\t} else if (this.namespaceURI === \"http://www.w3.org/1999/xhtml\") {\n\t\t\t\t\t\t\t\tif (!this.hasAttribute(\"href\")) return \"\"\n\t\t\t\t\t\t\t\t// HACK: if it's valid already, there's nothing to implement.\n\t\t\t\t\t\t\t\tvar value = this.attributes.href.value\n\t\t\t\t\t\t\t\tif (validURLRegex.test(encodeURI(value))) return value\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\treturn \"[FIXME implement]\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\tset: function(value) {\n\t\t\t\t\t\t\t// This is a readonly attribute for SVG, todo investigate MathML which may have yet another IDL\n\t\t\t\t\t\t\tif (this.namespaceURI !== \"http://www.w3.org/2000/svg\") this.setAttribute(\"href\", value)\n\t\t\t\t\t\t},\n\t\t\t\t\t\tenumerable: true,\n\t\t\t\t\t})\n\t\t\t\t}\n\n\t\t\t\tif (element.nodeName === \"INPUT\") {\n\t\t\t\t\tvar checked\n\t\t\t\t\tObject.defineProperty(element, \"checked\", {\n\t\t\t\t\t\tget: function() {return checked === undefined ? this.attributes[\"checked\"] !== undefined : checked},\n\t\t\t\t\t\tset: function(value) {checked = Boolean(value)},\n\t\t\t\t\t\tenumerable: true,\n\t\t\t\t\t})\n\n\t\t\t\t\tvar value = \"\"\n\t\t\t\t\tvar valueSetter = spy(function(v) {\n\t\t\t\t\t\t/*eslint-disable no-implicit-coercion*/\n\t\t\t\t\t\tvalue = v === null ? \"\" : \"\" + v\n\t\t\t\t\t\t/*eslint-enable no-implicit-coercion*/\n\t\t\t\t\t})\n\t\t\t\t\tObject.defineProperty(element, \"value\", {\n\t\t\t\t\t\tget: function() {\n\t\t\t\t\t\t\treturn value\n\t\t\t\t\t\t},\n\t\t\t\t\t\tset: valueSetter,\n\t\t\t\t\t\tenumerable: true,\n\t\t\t\t\t})\n\t\t\t\t\tObject.defineProperty(element, \"valueAsDate\", {\n\t\t\t\t\t\tget: function() {\n\t\t\t\t\t\t\tif (this.getAttribute(\"type\") !== \"date\") return null\n\t\t\t\t\t\t\treturn new Date(value).getTime()\n\t\t\t\t\t\t},\n\t\t\t\t\t\tset: function(v) {\n\t\t\t\t\t\t\tif (this.getAttribute(\"type\") !== \"date\") throw new Error(\"invalid state\")\n\t\t\t\t\t\t\tvar time = new Date(v).getTime()\n\t\t\t\t\t\t\tvalueSetter(isNaN(time) ? \"\" : new Date(time).toUTCString())\n\t\t\t\t\t\t},\n\t\t\t\t\t\tenumerable: true,\n\t\t\t\t\t})\n\t\t\t\t\tObject.defineProperty(element, \"valueAsNumber\", {\n\t\t\t\t\t\tget: function() {\n\t\t\t\t\t\t\tswitch (this.getAttribute(\"type\")) {\n\t\t\t\t\t\t\t\tcase \"date\": return new Date(value).getTime()\n\t\t\t\t\t\t\t\tcase \"number\": return new Date(value).getTime()\n\t\t\t\t\t\t\t\tdefault: return NaN\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t},\n\t\t\t\t\t\tset: function(v) {\n\t\t\t\t\t\t\tv = Number(v)\n\t\t\t\t\t\t\tif (!isNaN(v) && !isFinite(v)) throw new TypeError(\"infinite value\")\n\t\t\t\t\t\t\tswitch (this.getAttribute(\"type\")) {\n\t\t\t\t\t\t\t\tcase \"date\": valueSetter(isNaN(v) ? \"\" : new Date(v).toUTCString()); break;\n\t\t\t\t\t\t\t\tcase \"number\": valueSetter(String(value)); break;\n\t\t\t\t\t\t\t\tdefault: throw new Error(\"invalid state\")\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t},\n\t\t\t\t\t\tenumerable: true,\n\t\t\t\t\t})\n\n\t\t\t\t\t// we currently emulate the non-ie behavior, but emulating ie may be more useful (throw when an invalid type is set)\n\t\t\t\t\tvar typeSetter = spy(function(v) {\n\t\t\t\t\t\tthis.setAttribute(\"type\", v)\n\t\t\t\t\t})\n\t\t\t\t\tObject.defineProperty(element, \"type\", {\n\t\t\t\t\t\tget: function() {\n\t\t\t\t\t\t\tif (!this.hasAttribute(\"type\")) return \"text\"\n\t\t\t\t\t\t\tvar type = this.getAttribute(\"type\")\n\t\t\t\t\t\t\treturn (/^(?:radio|button|checkbox|color|date|datetime|datetime-local|email|file|hidden|month|number|password|range|research|search|submit|tel|text|url|week|image)$/)\n\t\t\t\t\t\t\t\t.test(type)\n\t\t\t\t\t\t\t\t? type\n\t\t\t\t\t\t\t\t: \"text\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\tset: typeSetter,\n\t\t\t\t\t\tenumerable: true,\n\t\t\t\t\t})\n\t\t\t\t\tregisterSpies(element, {\n\t\t\t\t\t\tvalueSetter: valueSetter,\n\t\t\t\t\t\ttypeSetter: typeSetter\n\t\t\t\t\t})\n\t\t\t\t}\n\n\n\t\t\t\tif (element.nodeName === \"TEXTAREA\") {\n\t\t\t\t\tvar wasNeverSet = true\n\t\t\t\t\tvar value = \"\"\n\t\t\t\t\tvar valueSetter = spy(function(v) {\n\t\t\t\t\t\twasNeverSet = false\n\t\t\t\t\t\t/*eslint-disable no-implicit-coercion*/\n\t\t\t\t\t\tvalue = v === null ? \"\" : \"\" + v\n\t\t\t\t\t\t/*eslint-enable no-implicit-coercion*/\n\t\t\t\t\t})\n\t\t\t\t\tObject.defineProperty(element, \"value\", {\n\t\t\t\t\t\tget: function() {\n\t\t\t\t\t\t\treturn wasNeverSet && this.firstChild ? this.firstChild.nodeValue : value\n\t\t\t\t\t\t},\n\t\t\t\t\t\tset: valueSetter,\n\t\t\t\t\t\tenumerable: true,\n\t\t\t\t\t})\n\t\t\t\t\tregisterSpies(element, {\n\t\t\t\t\t\tvalueSetter: valueSetter\n\t\t\t\t\t})\n\t\t\t\t}\n\n\t\t\t\t/* eslint-disable radix */\n\n\t\t\t\tif (element.nodeName === \"CANVAS\") {\n\t\t\t\t\tObject.defineProperty(element, \"width\", {\n\t\t\t\t\t\tget: function() {return this.attributes[\"width\"] ? Math.floor(parseInt(this.attributes[\"width\"].value) || 0) : 300},\n\t\t\t\t\t\tset: function(value) {this.setAttribute(\"width\", Math.floor(Number(value) || 0).toString())},\n\t\t\t\t\t})\n\t\t\t\t\tObject.defineProperty(element, \"height\", {\n\t\t\t\t\t\tget: function() {return this.attributes[\"height\"] ? Math.floor(parseInt(this.attributes[\"height\"].value) || 0) : 300},\n\t\t\t\t\t\tset: function(value) {this.setAttribute(\"height\", Math.floor(Number(value) || 0).toString())},\n\t\t\t\t\t})\n\t\t\t\t}\n\n\t\t\t\t/* eslint-enable radix */\n\n\t\t\t\tfunction getOptions(element) {\n\t\t\t\t\tvar options = []\n\t\t\t\t\tfor (var i = 0; i < element.childNodes.length; i++) {\n\t\t\t\t\t\tif (element.childNodes[i].nodeName === \"OPTION\") options.push(element.childNodes[i])\n\t\t\t\t\t\telse if (element.childNodes[i].nodeName === \"OPTGROUP\") options = options.concat(getOptions(element.childNodes[i]))\n\t\t\t\t\t}\n\t\t\t\t\treturn options\n\t\t\t\t}\n\t\t\t\tfunction getOptionValue(element) {\n\t\t\t\t\treturn element.attributes[\"value\"] != null ?\n\t\t\t\t\t\telement.attributes[\"value\"].value :\n\t\t\t\t\t\telement.firstChild != null ? element.firstChild.nodeValue : \"\"\n\t\t\t\t}\n\t\t\t\tif (element.nodeName === \"SELECT\") {\n\t\t\t\t\t// var selectedValue\n\t\t\t\t\tvar selectedIndex = 0\n\t\t\t\t\tObject.defineProperty(element, \"selectedIndex\", {\n\t\t\t\t\t\tget: function() {return getOptions(this).length > 0 ? selectedIndex : -1},\n\t\t\t\t\t\tset: function(value) {\n\t\t\t\t\t\t\tvar options = getOptions(this)\n\t\t\t\t\t\t\tif (value >= 0 && value < options.length) {\n\t\t\t\t\t\t\t\t// selectedValue = getOptionValue(options[selectedIndex])\n\t\t\t\t\t\t\t\tselectedIndex = value\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse {\n\t\t\t\t\t\t\t\t// selectedValue = \"\"\n\t\t\t\t\t\t\t\tselectedIndex = -1\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t},\n\t\t\t\t\t\tenumerable: true,\n\t\t\t\t\t})\n\t\t\t\t\tvar valueSetter = spy(function(value) {\n\t\t\t\t\t\tif (value === null) {\n\t\t\t\t\t\t\tselectedIndex = -1\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tvar options = getOptions(this)\n\t\t\t\t\t\t\t/*eslint-disable no-implicit-coercion*/\n\t\t\t\t\t\t\tvar stringValue = \"\" + value\n\t\t\t\t\t\t\t/*eslint-enable no-implicit-coercion*/\n\t\t\t\t\t\t\tfor (var i = 0; i < options.length; i++) {\n\t\t\t\t\t\t\t\tif (getOptionValue(options[i]) === stringValue) {\n\t\t\t\t\t\t\t\t\t// selectedValue = stringValue\n\t\t\t\t\t\t\t\t\tselectedIndex = i\n\t\t\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t// selectedValue = stringValue\n\t\t\t\t\t\t\tselectedIndex = -1\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\t\t\t\t\tObject.defineProperty(element, \"value\", {\n\t\t\t\t\t\tget: function() {\n\t\t\t\t\t\t\tif (this.selectedIndex > -1) return getOptionValue(getOptions(this)[this.selectedIndex])\n\t\t\t\t\t\t\treturn \"\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\tset: valueSetter,\n\t\t\t\t\t\tenumerable: true,\n\t\t\t\t\t})\n\t\t\t\t\tregisterSpies(element, {\n\t\t\t\t\t\tvalueSetter: valueSetter\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t\tif (element.nodeName === \"OPTION\") {\n\t\t\t\t\tvar valueSetter = spy(function(value) {\n\t\t\t\t\t\t/*eslint-disable no-implicit-coercion*/\n\t\t\t\t\t\tthis.setAttribute(\"value\", \"\" + value)\n\t\t\t\t\t\t/*eslint-enable no-implicit-coercion*/\n\t\t\t\t\t})\n\t\t\t\t\tObject.defineProperty(element, \"value\", {\n\t\t\t\t\t\tget: function() {return getOptionValue(this)},\n\t\t\t\t\t\tset: valueSetter,\n\t\t\t\t\t\tenumerable: true,\n\t\t\t\t\t})\n\t\t\t\t\tregisterSpies(element, {\n\t\t\t\t\t\tvalueSetter: valueSetter\n\t\t\t\t\t})\n\n\t\t\t\t\tObject.defineProperty(element, \"selected\", {\n\t\t\t\t\t\t// TODO? handle `selected` without a parent (works in browsers)\n\t\t\t\t\t\tget: function() {\n\t\t\t\t\t\t\tvar options = getOptions(this.parentNode)\n\t\t\t\t\t\t\tvar index = options.indexOf(this)\n\t\t\t\t\t\t\treturn index === this.parentNode.selectedIndex\n\t\t\t\t\t\t},\n\t\t\t\t\t\tset: function(value) {\n\t\t\t\t\t\t\tif (value) {\n\t\t\t\t\t\t\t\tvar options = getOptions(this.parentNode)\n\t\t\t\t\t\t\t\tvar index = options.indexOf(this)\n\t\t\t\t\t\t\t\tif (index > -1) this.parentNode.selectedIndex = index\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse this.parentNode.selectedIndex = 0\n\t\t\t\t\t\t},\n\t\t\t\t\t\tenumerable: true,\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t\treturn element\n\t\t\t},\n\t\t\tcreateElementNS: function(ns, tag, is) {\n\t\t\t\tvar element = this.createElement(tag, is)\n\t\t\t\telement.nodeName = tag\n\t\t\t\telement.namespaceURI = ns\n\t\t\t\treturn element\n\t\t\t},\n\t\t\tcreateTextNode: function(text) {\n\t\t\t\t/*eslint-disable no-implicit-coercion*/\n\t\t\t\tvar nodeValue = \"\" + text\n\t\t\t\t/*eslint-enable no-implicit-coercion*/\n\t\t\t\treturn {\n\t\t\t\t\tnodeType: 3,\n\t\t\t\t\tnodeName: \"#text\",\n\t\t\t\t\tparentNode: null,\n\t\t\t\t\tget childNodes() { return [] },\n\t\t\t\t\tget firstChild() { return null },\n\t\t\t\t\tget nodeValue() {return nodeValue},\n\t\t\t\t\tset nodeValue(value) {\n\t\t\t\t\t\t/*eslint-disable no-implicit-coercion*/\n\t\t\t\t\t\tnodeValue = \"\" + value\n\t\t\t\t\t\t/*eslint-enable no-implicit-coercion*/\n\t\t\t\t\t},\n\t\t\t\t\tget nextSibling() {\n\t\t\t\t\t\tif (this.parentNode == null) return null\n\t\t\t\t\t\tvar index = this.parentNode.childNodes.indexOf(this)\n\t\t\t\t\t\tif (index < 0) throw new TypeError(\"Parent's childNodes is out of sync\")\n\t\t\t\t\t\treturn this.parentNode.childNodes[index + 1] || null\n\t\t\t\t\t},\n\t\t\t\t}\n\t\t\t},\n\t\t\tcreateDocumentFragment: function() {\n\t\t\t\treturn {\n\t\t\t\t\townerDocument: $window.document,\n\t\t\t\t\tnodeType: 11,\n\t\t\t\t\tnodeName: \"#document-fragment\",\n\t\t\t\t\tappendChild: appendChild,\n\t\t\t\t\tinsertBefore: insertBefore,\n\t\t\t\t\tremoveChild: removeChild,\n\t\t\t\t\tparentNode: null,\n\t\t\t\t\tchildNodes: [],\n\t\t\t\t\tget firstChild() {\n\t\t\t\t\t\treturn this.childNodes[0] || null\n\t\t\t\t\t},\n\t\t\t\t}\n\t\t\t},\n\t\t\tcreateEvent: function() {\n\t\t\t\treturn {\n\t\t\t\t\teventPhase: 0,\n\t\t\t\t\tinitEvent: function(type) {this.type = type}\n\t\t\t\t}\n\t\t\t},\n\t\t\tget activeElement() {return activeElement},\n\t\t},\n\t}\n\t$window.document.defaultView = $window\n\t$window.document.documentElement = $window.document.createElement(\"html\")\n\tappendChild.call($window.document.documentElement, $window.document.createElement(\"head\"))\n\t$window.document.body = $window.document.createElement(\"body\")\n\tappendChild.call($window.document.documentElement, $window.document.body)\n\tactiveElement = $window.document.body\n\n\tif (options.spy) $window.__getSpies = getSpies\n\n\treturn $window\n}\n"
  },
  {
    "path": "test-utils/parseURL.js",
    "content": "\"use strict\"\n\nmodule.exports = function parseURL(url, root) {\n\tvar data = {}\n\tvar protocolIndex = url.indexOf(\"://\")\n\tvar pathnameIndex = protocolIndex > -1 ? url.indexOf(\"/\", protocolIndex + 3) : url.indexOf(\"/\")\n\tvar searchIndex = url.indexOf(\"?\")\n\tvar hashIndex = url.indexOf(\"#\")\n\tif ((pathnameIndex > searchIndex && searchIndex > -1) || (pathnameIndex > hashIndex && hashIndex > -1)) pathnameIndex = -1\n\tif (searchIndex > hashIndex && hashIndex > -1) searchIndex = -1\n\tvar pathnameEnd = searchIndex > -1 ? searchIndex : hashIndex > -1 ? hashIndex : url.length\n\tif (protocolIndex > -1) {\n\t\t//it's a full URL\n\t\tif (pathnameIndex < 0) pathnameIndex = url.length\n\t\tvar portIndex = url.indexOf(\":\", protocolIndex + 1)\n\t\tif (portIndex < 0) portIndex = pathnameIndex\n\t\tdata.protocol = url.slice(0, protocolIndex + 1)\n\t\tdata.hostname = url.slice(protocolIndex + 3, portIndex)\n\t\tdata.port = url.slice(portIndex + 1, pathnameIndex)\n\t\tdata.pathname = url.slice(pathnameIndex, pathnameEnd) || \"/\"\n\t}\n\telse {\n\t\tdata.protocol = root.protocol\n\t\tdata.hostname = root.hostname\n\t\tdata.port = root.port\n\t\tif (pathnameIndex === 0) {\n\t\t\t//it's an absolute path\n\t\t\tdata.pathname = url.slice(pathnameIndex, pathnameEnd) || \"/\"\n\t\t}\n\t\telse if (searchIndex !== 0 && hashIndex !== 0) {\n\t\t\t//it's a relative path\n\t\t\tvar slashIndex = root.pathname.lastIndexOf(\"/\")\n\t\t\tvar path = slashIndex > -1 ? root.pathname.slice(0, slashIndex + 1) : \"./\"\n\t\t\tvar normalized = url.slice(0, pathnameEnd).replace(/^\\.$/, root.pathname.slice(slashIndex + 1)).replace(/^\\.\\//, \"\")\n\t\t\tvar dotdot = /\\/[^\\/]+?\\/\\.{2}/g\n\t\t\tvar pathname = path + normalized\n\t\t\tpathname = path + normalized\n\t\t\twhile (dotdot.test(pathname)) pathname = pathname.replace(dotdot, \"\")\n\t\t\tpathname = pathname.replace(/\\/\\.\\//g, \"/\").replace(/^(\\/\\.{2})+/, \"\") || \"/\"\n\t\t\tdata.pathname = pathname\n\t\t}\n\t}\n\tvar searchEnd = hashIndex > -1 ? hashIndex : url.length\n\tdata.search = searchIndex > -1 ? url.slice(searchIndex, searchEnd) : \"\"\n\tdata.hash = hashIndex > -1 ? url.slice(hashIndex) : \"\"\n\treturn data\n}\n"
  },
  {
    "path": "test-utils/pushStateMock.js",
    "content": "\"use strict\"\n\nvar parseURL = require(\"../test-utils/parseURL\")\nvar callAsync = require(\"../test-utils/callAsync\")\n\nfunction debouncedAsync(f) {\n\tvar ref\n\treturn function() {\n\t\tif (ref != null) return\n\t\tref = callAsync(function(){\n\t\t\tref = null\n\t\t\tf()\n\t\t})\n\t}\n}\n\nmodule.exports = function(options) {\n\tif (options == null) options = {}\n\n\tvar $window = options.window || {}\n\tvar protocol = options.protocol || \"http:\"\n\tvar hostname = options.hostname || \"localhost\"\n\tvar port = \"\"\n\tvar pathname = \"/\"\n\tvar search = \"\"\n\tvar hash = \"\"\n\n\tvar past = [{url: getURL(), isNew: true, state: null, title: null}], future = []\n\n\tfunction getURL() {\n\t\tif (protocol === \"file:\") return protocol + \"//\" + pathname + search + hash\n\t\treturn protocol + \"//\" + hostname + prefix(\":\", port) + pathname + search + hash\n\t}\n\tfunction setURL(value) {\n\t\tvar data = parseURL(value, {protocol: protocol, hostname: hostname, port: port, pathname: pathname})\n\t\tvar isNew = false\n\t\tif (data.protocol != null && data.protocol !== protocol) protocol = data.protocol, isNew = true\n\t\tif (data.hostname != null && data.hostname !== hostname) hostname = data.hostname, isNew = true\n\t\tif (data.port != null && data.port !== port) port = data.port, isNew = true\n\t\tif (data.pathname != null && data.pathname !== pathname) pathname = data.pathname, isNew = true\n\t\tif (data.search != null && data.search !== search) search = data.search, isNew = true\n\t\tif (data.hash != null && data.hash !== hash) {\n\t\t\thash = data.hash\n\t\t\tif (!isNew) {\n\t\t\t\thashchange()\n\t\t\t}\n\t\t}\n\t\treturn isNew\n\t}\n\n\tfunction prefix(prefix, value) {\n\t\tif (value === \"\") return \"\"\n\t\treturn (value.charAt(0) !== prefix ? prefix : \"\") + value\n\t}\n\tfunction _hashchange() {\n\t\tif (typeof $window.onhashchange === \"function\") $window.onhashchange({type: \"hashchange\"})\n\t}\n\tvar hashchange = debouncedAsync(_hashchange)\n\tfunction popstate() {\n\t\tif (typeof $window.onpopstate === \"function\") $window.onpopstate({type: \"popstate\", state: $window.history.state})\n\t}\n\tfunction unload() {\n\t\tif (typeof $window.onunload === \"function\") $window.onunload({type: \"unload\"})\n\t}\n\n\t$window.location = {\n\t\tget protocol() {\n\t\t\treturn protocol\n\t\t},\n\t\tget hostname() {\n\t\t\treturn hostname\n\t\t},\n\t\tget port() {\n\t\t\treturn port\n\t\t},\n\t\tget pathname() {\n\t\t\treturn pathname\n\t\t},\n\t\tget search() {\n\t\t\treturn search\n\t\t},\n\t\tget hash() {\n\t\t\treturn hash\n\t\t},\n\t\tget origin() {\n\t\t\tif (protocol === \"file:\") return \"null\"\n\t\t\treturn protocol + \"//\" + hostname + prefix(\":\", port)\n\t\t},\n\t\tget host() {\n\t\t\tif (protocol === \"file:\") return \"\"\n\t\t\treturn hostname + prefix(\":\", port)\n\t\t},\n\t\tget href() {\n\t\t\treturn getURL()\n\t\t},\n\n\t\tset protocol(value) {\n\t\t\tthrow new Error(\"Protocol is read-only\")\n\t\t},\n\t\tset hostname(value) {\n\t\t\tunload()\n\t\t\tpast.push({url: getURL(), isNew: true})\n\t\t\tfuture = []\n\t\t\thostname = value\n\t\t},\n\t\tset port(value) {\n\t\t\tif (protocol === \"file:\") throw new Error(\"Port is read-only under `file://` protocol\")\n\t\t\tunload()\n\t\t\tpast.push({url: getURL(), isNew: true})\n\t\t\tfuture = []\n\t\t\tport = value\n\t\t},\n\t\tset pathname(value) {\n\t\t\tif (protocol === \"file:\") throw new Error(\"Pathname is read-only under `file://` protocol\")\n\t\t\tunload()\n\t\t\tpast.push({url: getURL(), isNew: true})\n\t\t\tfuture = []\n\t\t\tpathname = prefix(\"/\", value)\n\t\t},\n\t\tset search(value) {\n\t\t\tunload()\n\t\t\tpast.push({url: getURL(), isNew: true})\n\t\t\tfuture = []\n\t\t\tsearch = prefix(\"?\", value)\n\t\t},\n\t\tset hash(value) {\n\t\t\tvar oldHash = hash\n\t\t\tpast.push({url: getURL(), isNew: false})\n\t\t\tfuture = []\n\t\t\thash = prefix(\"#\", value)\n\t\t\tif (oldHash != hash) hashchange()\n\t\t},\n\n\t\tset origin(value) {\n\t\t\t//origin is writable but ignored\n\t\t},\n\t\tset host(value) {\n\t\t\t//host is writable but ignored in Chrome\n\t\t},\n\t\tset href(value) {\n\t\t\tvar url = getURL()\n\t\t\tvar isNew = setURL(value)\n\t\t\tif (isNew) {\n\t\t\t\tsetURL(url)\n\t\t\t\tunload()\n\t\t\t\tsetURL(value)\n\t\t\t}\n\t\t\tpast.push({url: url, isNew: isNew})\n\t\t\tfuture = []\n\t\t},\n\t}\n\t$window.history = {\n\t\tpushState: function(state, title, url) {\n\t\t\tpast.push({url: getURL(), isNew: false, state: state, title: title})\n\t\t\tfuture = []\n\t\t\tsetURL(url)\n\t\t},\n\t\treplaceState: function(state, title, url) {\n\t\t\tvar entry = past[past.length - 1]\n\t\t\tentry.state = state\n\t\t\tentry.title = title\n\t\t\tsetURL(url)\n\t\t},\n\t\tback: function() {\n\t\t\tif (past.length > 1) {\n\t\t\t\tvar entry = past.pop()\n\t\t\t\tif (entry.isNew) unload()\n\t\t\t\tfuture.push({url: getURL(), isNew: false, state: entry.state, title: entry.title})\n\t\t\t\tsetURL(entry.url)\n\t\t\t\tif (!entry.isNew) popstate()\n\t\t\t}\n\t\t},\n\t\tforward: function() {\n\t\t\tvar entry = future.pop()\n\t\t\tif (entry != null) {\n\t\t\t\tif (entry.isNew) unload()\n\t\t\t\tpast.push({url: getURL(), isNew: false, state: entry.state, title: entry.title})\n\t\t\t\tsetURL(entry.url)\n\t\t\t\tif (!entry.isNew) popstate()\n\t\t\t}\n\t\t},\n\t\tget state() {\n\t\t\treturn past.length === 0 ? null : past[past.length - 1].state\n\t\t},\n\t}\n\t$window.onpopstate = null,\n\t$window.onhashchange = null,\n\t$window.onunload = null\n\n\t$window.addEventListener = function (name, handler) {\n\t\t$window[\"on\" + name] = handler\n\t}\n\n\t$window.removeEventListener = function (name, handler) {\n\t\t$window[\"on\" + name] = handler\n\t}\n\n\treturn $window\n}\n"
  },
  {
    "path": "test-utils/tests/test-browserMock.js",
    "content": "\"use strict\"\n\nvar o = require(\"ospec\")\nvar browserMock = require(\"../../test-utils/browserMock\")\nvar callAsync = require(\"../../test-utils/callAsync\")\no.spec(\"browserMock\", function() {\n\n\tvar $window\n\to.beforeEach(function() {\n\t\t$window = browserMock()\n\t})\n\n\to(\"Mocks DOM, pushState and XHR\", function() {\n\t\to($window.location).notEquals(undefined)\n\t\to($window.document).notEquals(undefined)\n\t\to($window.XMLHttpRequest).notEquals(undefined)\n\t})\n\to(\"$window.onhashchange can be reached from the pushStateMock functions\", function(done) {\n\t\t$window.onhashchange = o.spy()\n\t\t$window.location.hash = \"#a\"\n\n\t\tcallAsync(function(){\n\t\t\to($window.onhashchange.callCount).equals(1)\n\t\t\tdone()\n\t\t})\n\t})\n\to(\"$window.onpopstate can be reached from the pushStateMock functions\", function() {\n\t\t$window.onpopstate = o.spy()\n\t\t$window.history.pushState(null, null, \"#a\")\n\t\t$window.history.back()\n\n\t\to($window.onpopstate.callCount).equals(1)\n\t})\n\to(\"$window.onunload can be reached from the pushStateMock functions\", function() {\n\t\t$window.onunload = o.spy()\n\t\t$window.location.href = \"/a\"\n\n\t\to($window.onunload.callCount).equals(1)\n\t})\n})\n"
  },
  {
    "path": "test-utils/tests/test-callAsync.js",
    "content": "\"use strict\"\n\nvar o = require(\"ospec\")\nvar callAsync = require(\"../../test-utils/callAsync\")\n\no.spec(\"callAsync\", function() {\n\to(\"works\", function(done) {\n\t\tvar count = 0\n\t\tcallAsync(function() {\n\t\t\to(count).equals(1)\n\t\t\tdone()\n\t\t})\n\t\tcount++\n\t})\n\to(\"gets called before setTimeout\", function(done) {\n\t\tvar timeout\n\t\tcallAsync(function() {\n\t\t\tclearTimeout(timeout)\n\t\t\tdone()\n\t\t})\n\t\ttimeout = setTimeout(function() {\n\t\t\tthrow new Error(\"callAsync was called too slow\")\n\t\t}, 5)\n\t})\n})\n"
  },
  {
    "path": "test-utils/tests/test-components.js",
    "content": "\"use strict\"\n\nvar o = require(\"ospec\")\nvar components = require(\"../../test-utils/components\")\nvar m = require(\"../../render/hyperscript\")\n\no.spec(\"test-utils/components\", function() {\n\tvar test = o.spy(function(component) {\n\t\treturn function() {\n\t\t\to(\"works\", function() {\n\t\t\t\to(typeof component.kind).equals(\"string\")\n\n\t\t\t\tvar methods = {oninit: function(){}, view: function(){}}\n\n\t\t\t\tvar cmp1, cmp2\n\n\t\t\t\tif (component.kind === \"POJO\") {\n\t\t\t\t\tcmp1 = component.create()\n\t\t\t\t\tcmp2 = component.create(methods)\n\t\t\t\t} else if (component.kind === \"constructible\") {\n\t\t\t\t\tcmp1 = new (component.create())\n\t\t\t\t\tcmp2 = new (component.create(methods))\n\t\t\t\t} else if (component.kind === \"closure\") {\n\t\t\t\t\tcmp1 = component.create()()\n\t\t\t\t\tcmp2 = component.create(methods)()\n\t\t\t\t} else {\n\t\t\t\t\tthrow new Error(\"unexpected component kind\")\n\t\t\t\t}\n\n\t\t\t\to(cmp1 != null).equals(true)\n\t\t\t\to(typeof cmp1.view).equals(\"function\")\n\n\t\t\t\tvar vnode = cmp1.view()\n\n\t\t\t\to(vnode != null).equals(true)\n\t\t\t\to(vnode).deepEquals(m(\"div\"))\n\n\t\t\t\tif (component.kind !== \"constructible\") {\n\t\t\t\t\to(cmp2).deepEquals(methods)\n\t\t\t\t} else {\n\t\t\t\t\t// deepEquals doesn't search the prototype, do it manually\n\t\t\t\t\to(cmp2 != null).equals(true)\n\t\t\t\t\to(cmp2.view).equals(methods.view)\n\t\t\t\t\to(cmp2.oninit).equals(methods.oninit)\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\t})\n\to.after(function(){\n\t\to(test.callCount).equals(3)\n\t})\n\tcomponents.forEach(function(component) {\n\t\to.spec(component.kind, test(component))\n\t})\n})\n"
  },
  {
    "path": "test-utils/tests/test-domMock.js",
    "content": "\"use strict\"\n\nvar o = require(\"ospec\")\nvar domMock = require(\"../../test-utils/domMock\")\n\no.spec(\"domMock\", function() {\n\tvar $document, $window\n\to.beforeEach(function() {\n\t\t$window = domMock()\n\t\t$document = $window.document\n\t})\n\n\to.spec(\"createElement\", function() {\n\t\to(\"works\", function() {\n\t\t\tvar node = $document.createElement(\"div\")\n\n\t\t\to(node.nodeType).equals(1)\n\t\t\to(node.nodeName).equals(\"DIV\")\n\t\t\to(node.namespaceURI).equals(\"http://www.w3.org/1999/xhtml\")\n\t\t\to(node.parentNode).equals(null)\n\t\t\to(node.childNodes.length).equals(0)\n\t\t\to(node.firstChild).equals(null)\n\t\t\to(node.nextSibling).equals(null)\n\t\t})\n\t})\n\n\to.spec(\"createElementNS\", function() {\n\t\to(\"works\", function() {\n\t\t\tvar node = $document.createElementNS(\"http://www.w3.org/2000/svg\", \"svg\")\n\n\t\t\to(node.nodeType).equals(1)\n\t\t\to(node.nodeName).equals(\"svg\")\n\t\t\to(node.namespaceURI).equals(\"http://www.w3.org/2000/svg\")\n\t\t\to(node.parentNode).equals(null)\n\t\t\to(node.childNodes.length).equals(0)\n\t\t\to(node.firstChild).equals(null)\n\t\t\to(node.nextSibling).equals(null)\n\t\t})\n\t})\n\n\to.spec(\"createTextNode\", function() {\n\t\to(\"works\", function() {\n\t\t\tvar node = $document.createTextNode(\"abc\")\n\n\t\t\to(node.nodeType).equals(3)\n\t\t\to(node.nodeName).equals(\"#text\")\n\t\t\to(node.parentNode).equals(null)\n\t\t\to(node.nodeValue).equals(\"abc\")\n\t\t})\n\t\to(\"works w/ number\", function() {\n\t\t\tvar node = $document.createTextNode(123)\n\n\t\t\to(node.nodeValue).equals(\"123\")\n\t\t})\n\t\to(\"works w/ null\", function() {\n\t\t\tvar node = $document.createTextNode(null)\n\n\t\t\to(node.nodeValue).equals(\"null\")\n\t\t})\n\t\to(\"works w/ undefined\", function() {\n\t\t\tvar node = $document.createTextNode(undefined)\n\n\t\t\to(node.nodeValue).equals(\"undefined\")\n\t\t})\n\t\to(\"works w/ object\", function() {\n\t\t\tvar node = $document.createTextNode({})\n\n\t\t\to(node.nodeValue).equals(\"[object Object]\")\n\t\t})\n\t\to(\"does not unescape HTML\", function() {\n\t\t\tvar node = $document.createTextNode(\"<a>&amp;</a>\")\n\n\t\t\to(node.nodeValue).equals(\"<a>&amp;</a>\")\n\t\t})\n\t\to(\"nodeValue casts to string\", function() {\n\t\t\tvar node = $document.createTextNode(\"a\")\n\t\t\tnode.nodeValue = true\n\n\t\t\to(node.nodeValue).equals(\"true\")\n\t\t})\n\t\tif (typeof Symbol === \"function\") {\n\t\t\to(\"doesn't work with symbols\", function(){\n\t\t\t\tvar threw = false\n\t\t\t\ttry {\n\t\t\t\t\t$document.createTextNode(Symbol(\"nono\"))\n\t\t\t\t} catch(e) {\n\t\t\t\t\tthrew = true\n\t\t\t\t}\n\t\t\t\to(threw).equals(true)\n\t\t\t})\n\t\t\to(\"symbols can't be used as nodeValue\", function(){\n\t\t\t\tvar threw = false\n\t\t\t\ttry {\n\t\t\t\t\tvar node = $document.createTextNode(\"a\")\n\t\t\t\t\tnode.nodeValue = Symbol(\"nono\")\n\t\t\t\t} catch(e) {\n\t\t\t\t\tthrew = true\n\t\t\t\t}\n\t\t\t\to(threw).equals(true)\n\t\t\t})\n\t\t}\n\t})\n\n\to.spec(\"createDocumentFragment\", function() {\n\t\to(\"works\", function() {\n\t\t\tvar node = $document.createDocumentFragment()\n\n\t\t\to(node.nodeType).equals(11)\n\t\t\to(node.nodeName).equals(\"#document-fragment\")\n\t\t\to(node.parentNode).equals(null)\n\t\t\to(node.childNodes.length).equals(0)\n\t\t\to(node.firstChild).equals(null)\n\t\t})\n\t})\n\n\to.spec(\"appendChild\", function() {\n\t\to(\"works\", function() {\n\t\t\tvar parent = $document.createElement(\"div\")\n\t\t\tvar child = $document.createElement(\"a\")\n\t\t\tparent.appendChild(child)\n\n\t\t\to(parent.childNodes.length).equals(1)\n\t\t\to(parent.childNodes[0]).equals(child)\n\t\t\to(parent.firstChild).equals(child)\n\t\t\to(child.parentNode).equals(parent)\n\t\t})\n\t\to(\"moves existing\", function() {\n\t\t\tvar parent = $document.createElement(\"div\")\n\t\t\tvar a = $document.createElement(\"a\")\n\t\t\tvar b = $document.createElement(\"b\")\n\t\t\tparent.appendChild(a)\n\t\t\tparent.appendChild(b)\n\t\t\tparent.appendChild(a)\n\n\t\t\to(parent.childNodes.length).equals(2)\n\t\t\to(parent.childNodes[0]).equals(b)\n\t\t\to(parent.childNodes[1]).equals(a)\n\t\t\to(parent.firstChild).equals(b)\n\t\t\to(parent.firstChild.nextSibling).equals(a)\n\t\t\to(a.parentNode).equals(parent)\n\t\t\to(b.parentNode).equals(parent)\n\t\t})\n\t\to(\"removes from old parent\", function() {\n\t\t\tvar parent = $document.createElement(\"div\")\n\t\t\tvar source = $document.createElement(\"span\")\n\t\t\tvar a = $document.createElement(\"a\")\n\t\t\tvar b = $document.createElement(\"b\")\n\t\t\tparent.appendChild(a)\n\t\t\tsource.appendChild(b)\n\t\t\tparent.appendChild(b)\n\n\t\t\to(source.childNodes.length).equals(0)\n\t\t})\n\t\to(\"transfers from fragment\", function() {\n\t\t\tvar parent = $document.createElement(\"div\")\n\t\t\tvar a = $document.createDocumentFragment(\"a\")\n\t\t\tvar b = $document.createElement(\"b\")\n\t\t\tvar c = $document.createElement(\"c\")\n\t\t\ta.appendChild(b)\n\t\t\ta.appendChild(c)\n\t\t\tparent.appendChild(a)\n\n\t\t\to(parent.childNodes.length).equals(2)\n\t\t\to(parent.childNodes[0]).equals(b)\n\t\t\to(parent.childNodes[1]).equals(c)\n\t\t\to(parent.firstChild).equals(b)\n\t\t\to(parent.firstChild.nextSibling).equals(c)\n\t\t\to(a.childNodes.length).equals(0)\n\t\t\to(a.firstChild).equals(null)\n\t\t\to(a.parentNode).equals(null)\n\t\t\to(b.parentNode).equals(parent)\n\t\t\to(c.parentNode).equals(parent)\n\t\t})\n\t\to(\"throws if appended to self\", function(done) {\n\t\t\tvar div = $document.createElement(\"div\")\n\t\t\ttry {div.appendChild(div)}\n\t\t\tcatch (e) {done()}\n\t\t})\n\t\to(\"throws if appended to child\", function(done) {\n\t\t\tvar parent = $document.createElement(\"div\")\n\t\t\tvar child = $document.createElement(\"a\")\n\t\t\tparent.appendChild(child)\n\t\t\ttry {child.appendChild(parent)}\n\t\t\tcatch (e) {done()}\n\t\t})\n\t\to(\"throws if child is not element\", function(done) {\n\t\t\tvar parent = $document.createElement(\"div\")\n\t\t\tvar child = 1\n\t\t\ttry {parent.appendChild(child)}\n\t\t\tcatch (e) {done()}\n\t\t})\n\t})\n\n\to.spec(\"removeChild\", function() {\n\t\to(\"works\", function() {\n\t\t\tvar parent = $document.createElement(\"div\")\n\t\t\tvar child = $document.createElement(\"a\")\n\t\t\tparent.appendChild(child)\n\t\t\tparent.removeChild(child)\n\n\t\t\to(parent.childNodes.length).equals(0)\n\t\t\to(parent.firstChild).equals(null)\n\t\t\to(child.parentNode).equals(null)\n\t\t})\n\t\to(\"throws if not a child\", function(done) {\n\t\t\tvar parent = $document.createElement(\"div\")\n\t\t\tvar child = $document.createElement(\"a\")\n\t\t\ttry {parent.removeChild(child)}\n\t\t\tcatch (e) {done()}\n\t\t})\n\t})\n\n\to.spec(\"insertBefore\", function() {\n\t\to(\"works\", function() {\n\t\t\tvar parent = $document.createElement(\"div\")\n\t\t\tvar a = $document.createElement(\"a\")\n\t\t\tvar b = $document.createElement(\"b\")\n\t\t\tparent.appendChild(a)\n\t\t\tparent.insertBefore(b, a)\n\n\t\t\to(parent.childNodes.length).equals(2)\n\t\t\to(parent.childNodes[0]).equals(b)\n\t\t\to(parent.childNodes[1]).equals(a)\n\t\t\to(parent.firstChild).equals(b)\n\t\t\to(parent.firstChild.nextSibling).equals(a)\n\t\t\to(a.parentNode).equals(parent)\n\t\t\to(b.parentNode).equals(parent)\n\t\t})\n\t\to(\"moves existing\", function() {\n\t\t\tvar parent = $document.createElement(\"div\")\n\t\t\tvar a = $document.createElement(\"a\")\n\t\t\tvar b = $document.createElement(\"b\")\n\t\t\tparent.appendChild(a)\n\t\t\tparent.appendChild(b)\n\t\t\tparent.insertBefore(b, a)\n\n\t\t\to(parent.childNodes.length).equals(2)\n\t\t\to(parent.childNodes[0]).equals(b)\n\t\t\to(parent.childNodes[1]).equals(a)\n\t\t\to(parent.firstChild).equals(b)\n\t\t\to(parent.firstChild.nextSibling).equals(a)\n\t\t\to(a.parentNode).equals(parent)\n\t\t\to(b.parentNode).equals(parent)\n\t\t})\n\t\to(\"moves existing node forward but not at the end\", function() {\n\t\t\tvar parent = $document.createElement(\"div\")\n\t\t\tvar a = $document.createElement(\"a\")\n\t\t\tvar b = $document.createElement(\"b\")\n\t\t\tvar c = $document.createElement(\"c\")\n\t\t\tparent.appendChild(a)\n\t\t\tparent.appendChild(b)\n\t\t\tparent.appendChild(c)\n\t\t\tparent.insertBefore(a, c)\n\n\t\t\to(parent.childNodes.length).equals(3)\n\t\t\to(parent.childNodes[0]).equals(b)\n\t\t\to(parent.childNodes[1]).equals(a)\n\t\t\to(parent.childNodes[2]).equals(c)\n\t\t\to(parent.firstChild).equals(b)\n\t\t\to(parent.firstChild.nextSibling).equals(a)\n\t\t\to(parent.firstChild.nextSibling.nextSibling).equals(c)\n\t\t\to(a.parentNode).equals(parent)\n\t\t\to(b.parentNode).equals(parent)\n\t\t\to(c.parentNode).equals(parent)\n\n\t\t})\n\t\to(\"removes from old parent\", function() {\n\t\t\tvar parent = $document.createElement(\"div\")\n\t\t\tvar source = $document.createElement(\"span\")\n\t\t\tvar a = $document.createElement(\"a\")\n\t\t\tvar b = $document.createElement(\"b\")\n\t\t\tparent.appendChild(a)\n\t\t\tsource.appendChild(b)\n\t\t\tparent.insertBefore(b, a)\n\n\t\t\to(source.childNodes.length).equals(0)\n\t\t})\n\t\to(\"transfers from fragment\", function() {\n\t\t\tvar parent = $document.createElement(\"div\")\n\t\t\tvar ref = $document.createElement(\"span\")\n\t\t\tvar a = $document.createDocumentFragment(\"a\")\n\t\t\tvar b = $document.createElement(\"b\")\n\t\t\tvar c = $document.createElement(\"c\")\n\t\t\tparent.appendChild(ref)\n\t\t\ta.appendChild(b)\n\t\t\ta.appendChild(c)\n\t\t\tparent.insertBefore(a, ref)\n\n\t\t\to(parent.childNodes.length).equals(3)\n\t\t\to(parent.childNodes[0]).equals(b)\n\t\t\to(parent.childNodes[1]).equals(c)\n\t\t\to(parent.childNodes[2]).equals(ref)\n\t\t\to(parent.firstChild).equals(b)\n\t\t\to(parent.firstChild.nextSibling).equals(c)\n\t\t\to(parent.firstChild.nextSibling.nextSibling).equals(ref)\n\t\t\to(a.childNodes.length).equals(0)\n\t\t\to(a.firstChild).equals(null)\n\t\t\to(a.parentNode).equals(null)\n\t\t\to(b.parentNode).equals(parent)\n\t\t\to(c.parentNode).equals(parent)\n\t\t})\n\t\to(\"appends if second arg is null\", function() {\n\t\t\tvar parent = $document.createElement(\"div\")\n\t\t\tvar a = $document.createElement(\"a\")\n\t\t\tvar b = $document.createElement(\"b\")\n\t\t\tparent.appendChild(a)\n\t\t\tparent.insertBefore(b, null)\n\n\t\t\to(parent.childNodes.length).equals(2)\n\t\t\to(parent.childNodes[0]).equals(a)\n\t\t\to(parent.childNodes[1]).equals(b)\n\t\t\to(parent.firstChild).equals(a)\n\t\t\to(parent.firstChild.nextSibling).equals(b)\n\t\t\to(a.parentNode).equals(parent)\n\t\t})\n\t\to(\"throws if appended to self\", function(done) {\n\t\t\tvar div = $document.createElement(\"div\")\n\t\t\tvar a = $document.createElement(\"a\")\n\t\t\tdiv.appendChild(a)\n\t\t\ttry {div.isnertBefore(div, a)}\n\t\t\tcatch (e) {done()}\n\t\t})\n\t\to(\"throws if appended to child\", function(done) {\n\t\t\tvar parent = $document.createElement(\"div\")\n\t\t\tvar a = $document.createElement(\"a\")\n\t\t\tvar b = $document.createElement(\"b\")\n\t\t\tparent.appendChild(a)\n\t\t\ta.appendChild(b)\n\t\t\ttry {a.insertBefore(parent, b)}\n\t\t\tcatch (e) {done()}\n\t\t})\n\t\to(\"throws if child is not element\", function(done) {\n\t\t\tvar parent = $document.createElement(\"div\")\n\t\t\tvar a = $document.createElement(\"a\")\n\t\t\tparent.appendChild(a)\n\t\t\ttry {parent.insertBefore(1, a)}\n\t\t\tcatch (e) {done()}\n\t\t})\n\t\to(\"throws if inserted before itself\", function(done) {\n\t\t\tvar parent = $document.createElement(\"div\")\n\t\t\tvar a = $document.createElement(\"a\")\n\t\t\ttry {parent.insertBefore(a, a)}\n\t\t\tcatch (e) {done()}\n\t\t})\n\t\to(\"throws if second arg is undefined\", function(done) {\n\t\t\tvar parent = $document.createElement(\"div\")\n\t\t\tvar a = $document.createElement(\"a\")\n\t\t\ttry {parent.insertBefore(a)}\n\t\t\tcatch (e) {done()}\n\t\t})\n\t\to(\"throws if reference is not child\", function(done) {\n\t\t\tvar parent = $document.createElement(\"div\")\n\t\t\tvar a = $document.createElement(\"a\")\n\t\t\tvar b = $document.createElement(\"b\")\n\t\t\ttry {parent.insertBefore(a, b)}\n\t\t\tcatch (e) {done()}\n\t\t})\n\t})\n\n\to.spec(\"getAttribute\", function() {\n\t\to(\"works\", function() {\n\t\t\tvar div = $document.createElement(\"div\")\n\t\t\tdiv.setAttribute(\"id\", \"aaa\")\n\n\t\t\to(div.getAttribute(\"id\")).equals(\"aaa\")\n\t\t})\n\t\to(\"works for attributes with a namespace\", function() {\n\t\t\tvar div = $document.createElement(\"div\")\n\t\t\tdiv.setAttributeNS(\"http://www.w3.org/1999/xlink\", \"href\", \"aaa\")\n\n\t\t\to(div.getAttribute(\"href\")).equals(\"aaa\")\n\t\t})\n\t})\n\n\to.spec(\"setAttribute\", function() {\n\t\to(\"works\", function() {\n\t\t\tvar div = $document.createElement(\"div\")\n\t\t\tdiv.setAttribute(\"id\", \"aaa\")\n\n\t\t\to(div.attributes[\"id\"].value).equals(\"aaa\")\n\t\t\to(div.attributes[\"id\"].nodeValue).equals(\"aaa\")\n\t\t\to(div.attributes[\"id\"].namespaceURI).equals(null)\n\t\t})\n\t\to(\"works w/ number\", function() {\n\t\t\tvar div = $document.createElement(\"div\")\n\t\t\tdiv.setAttribute(\"id\", 123)\n\n\t\t\to(div.attributes[\"id\"].value).equals(\"123\")\n\t\t})\n\t\to(\"works w/ null\", function() {\n\t\t\tvar div = $document.createElement(\"div\")\n\t\t\tdiv.setAttribute(\"id\", null)\n\n\t\t\to(div.attributes[\"id\"].value).equals(\"null\")\n\t\t})\n\t\to(\"works w/ undefined\", function() {\n\t\t\tvar div = $document.createElement(\"div\")\n\t\t\tdiv.setAttribute(\"id\", undefined)\n\n\t\t\to(div.attributes[\"id\"].value).equals(\"undefined\")\n\t\t})\n\t\to(\"works w/ object\", function() {\n\t\t\tvar div = $document.createElement(\"div\")\n\t\t\tdiv.setAttribute(\"id\", {})\n\n\t\t\to(div.attributes[\"id\"].value).equals(\"[object Object]\")\n\t\t})\n\t\to(\"setting via attributes map stringifies\", function() {\n\t\t\tvar div = $document.createElement(\"div\")\n\t\t\tdiv.setAttribute(\"id\", \"a\")\n\t\t\tdiv.attributes[\"id\"].value = 123\n\n\t\t\to(div.attributes[\"id\"].value).equals(\"123\")\n\n\t\t\tdiv.attributes[\"id\"].nodeValue = 456\n\n\t\t\to(div.attributes[\"id\"].value).equals(\"456\")\n\t\t})\n\t})\n\to.spec(\"hasAttribute\", function() {\n\t\to(\"works\", function() {\n\t\t\tvar div = $document.createElement(\"div\")\n\n\t\t\to(div.hasAttribute(\"id\")).equals(false)\n\n\t\t\tdiv.setAttribute(\"id\", \"aaa\")\n\n\t\t\to(div.hasAttribute(\"id\")).equals(true)\n\n\t\t\tdiv.removeAttribute(\"id\")\n\n\t\t\to(div.hasAttribute(\"id\")).equals(false)\n\t\t})\n\t})\n\n\to.spec(\"setAttributeNS\", function() {\n\t\to(\"works\", function() {\n\t\t\tvar a = $document.createElementNS(\"http://www.w3.org/2000/svg\", \"a\")\n\t\t\ta.setAttributeNS(\"http://www.w3.org/1999/xlink\", \"href\", \"/aaa\")\n\n\t\t\to(a.href).deepEquals({baseVal: \"/aaa\", animVal: \"/aaa\"})\n\t\t\to(a.attributes[\"href\"].value).equals(\"/aaa\")\n\t\t\to(a.attributes[\"href\"].namespaceURI).equals(\"http://www.w3.org/1999/xlink\")\n\t\t})\n\t\to(\"works w/ number\", function() {\n\t\t\tvar a = $document.createElementNS(\"http://www.w3.org/2000/svg\", \"a\")\n\t\t\ta.setAttributeNS(\"http://www.w3.org/1999/xlink\", \"href\", 123)\n\n\t\t\to(a.href).deepEquals({baseVal: \"123\", animVal: \"123\"})\n\t\t\to(a.attributes[\"href\"].value).equals(\"123\")\n\t\t\to(a.attributes[\"href\"].namespaceURI).equals(\"http://www.w3.org/1999/xlink\")\n\t\t})\n\t\to(\"attributes with a namespace can be querried, updated and removed with non-NS functions\", function() {\n\t\t\tvar a = $document.createElementNS(\"http://www.w3.org/2000/svg\", \"a\")\n\t\t\ta.setAttributeNS(\"http://www.w3.org/1999/xlink\", \"href\", \"/aaa\")\n\n\t\t\to(a.hasAttribute(\"href\")).equals(true)\n\t\t\to(a.getAttribute(\"href\")).equals(\"/aaa\")\n\n\t\t\ta.setAttribute(\"href\", \"/bbb\")\n\n\t\t\to(a.href).deepEquals({baseVal: \"/bbb\", animVal: \"/bbb\"})\n\t\t\to(a.getAttribute(\"href\")).equals(\"/bbb\")\n\t\t\to(a.attributes[\"href\"].value).equals(\"/bbb\")\n\t\t\to(a.attributes[\"href\"].namespaceURI).equals(\"http://www.w3.org/1999/xlink\")\n\n\t\t\ta.removeAttribute(\"href\")\n\n\t\t\to(a.hasAttribute(\"href\")).equals(false)\n\t\t\to(a.getAttribute(\"href\")).equals(null)\n\t\t\to(\"href\" in a.attributes).equals(false)\n\t\t})\n\t})\n\n\to.spec(\"removeAttribute\", function() {\n\t\to(\"works\", function() {\n\t\t\tvar div = $document.createElement(\"div\")\n\t\t\tdiv.setAttribute(\"id\", \"aaa\")\n\t\t\tdiv.removeAttribute(\"id\")\n\n\t\t\to(\"id\" in div.attributes).equals(false)\n\t\t})\n\t})\n\n\to.spec(\"textContent\", function() {\n\t\to(\"works\", function() {\n\t\t\tvar div = $document.createElement(\"div\")\n\t\t\tdiv.textContent = \"aaa\"\n\n\t\t\to(div.childNodes.length).equals(1)\n\t\t\to(div.firstChild.nodeType).equals(3)\n\t\t\to(div.firstChild.nodeValue).equals(\"aaa\")\n\t\t})\n\t\to(\"works with empty string\", function() {\n\t\t\tvar div = $document.createElement(\"div\")\n\t\t\tdiv.textContent = \"\"\n\n\t\t\to(div.childNodes.length).equals(0)\n\t\t})\n\t})\n\n\to.spec(\"innerHTML\", function() {\n\t\to(\"works\", function() {\n\t\t\tvar div = $document.createElement(\"div\")\n\t\t\tdiv.innerHTML = \"<br /><a class='aaa' id='xyz'>123<b class=\\\"bbb\\\"></b>234<br class=ccc>345</a>\"\n\t\t\to(div.childNodes.length).equals(2)\n\t\t\to(div.childNodes[0].nodeType).equals(1)\n\t\t\to(div.childNodes[0].nodeName).equals(\"BR\")\n\t\t\to(div.childNodes[1].nodeType).equals(1)\n\t\t\to(div.childNodes[1].nodeName).equals(\"A\")\n\t\t\to(div.childNodes[1].attributes[\"class\"].value).equals(\"aaa\")\n\t\t\to(div.childNodes[1].attributes[\"id\"].value).equals(\"xyz\")\n\t\t\to(div.childNodes[1].childNodes[0].nodeType).equals(3)\n\t\t\to(div.childNodes[1].childNodes[0].nodeValue).equals(\"123\")\n\t\t\to(div.childNodes[1].childNodes[1].nodeType).equals(1)\n\t\t\to(div.childNodes[1].childNodes[1].nodeName).equals(\"B\")\n\t\t\to(div.childNodes[1].childNodes[1].attributes[\"class\"].value).equals(\"bbb\")\n\t\t\to(div.childNodes[1].childNodes[2].nodeType).equals(3)\n\t\t\to(div.childNodes[1].childNodes[2].nodeValue).equals(\"234\")\n\t\t\to(div.childNodes[1].childNodes[3].nodeType).equals(1)\n\t\t\to(div.childNodes[1].childNodes[3].nodeName).equals(\"BR\")\n\t\t\to(div.childNodes[1].childNodes[3].attributes[\"class\"].value).equals(\"ccc\")\n\t\t\to(div.childNodes[1].childNodes[4].nodeType).equals(3)\n\t\t\to(div.childNodes[1].childNodes[4].nodeValue).equals(\"345\")\n\t\t})\n\t\to(\"headers work\", function() {\n\t\t\tvar div = $document.createElement(\"div\")\n\t\t\tdiv.innerHTML = \"<h1></h1><h2></h2><h3></h3><h4></h4><h5></h5><h6></h6>\"\n\t\t\to(div.childNodes.length).equals(6)\n\t\t\to(div.childNodes[0].nodeType).equals(1)\n\t\t\to(div.childNodes[0].nodeName).equals(\"H1\")\n\t\t\to(div.childNodes[1].nodeType).equals(1)\n\t\t\to(div.childNodes[1].nodeName).equals(\"H2\")\n\t\t\to(div.childNodes[2].nodeType).equals(1)\n\t\t\to(div.childNodes[2].nodeName).equals(\"H3\")\n\t\t\to(div.childNodes[3].nodeType).equals(1)\n\t\t\to(div.childNodes[3].nodeName).equals(\"H4\")\n\t\t\to(div.childNodes[4].nodeType).equals(1)\n\t\t\to(div.childNodes[4].nodeName).equals(\"H5\")\n\t\t\to(div.childNodes[5].nodeType).equals(1)\n\t\t\to(div.childNodes[5].nodeName).equals(\"H6\")\n\t\t})\n\t\to(\"detaches old elements\", function() {\n\t\t\tvar div = $document.createElement(\"div\")\n\t\t\tvar a = $document.createElement(\"a\")\n\t\t\tdiv.appendChild(a)\n\t\t\tdiv.innerHTML = \"<b></b>\"\n\n\t\t\to(a.parentNode).equals(null)\n\t\t})\n\t\to(\"empty SVG document\", function() {\n\t\t\tvar div = $document.createElement(\"div\")\n\t\t\tdiv.innerHTML = \"<svg xmlns=\\\"http://www.w3.org/2000/svg\\\"></svg>\"\n\n\t\t\to(typeof div.firstChild).notEquals(undefined)\n\t\t\to(div.firstChild.nodeName).equals(\"svg\")\n\t\t\to(div.firstChild.namespaceURI).equals(\"http://www.w3.org/2000/svg\")\n\t\t\to(div.firstChild.childNodes.length).equals(0)\n\t\t})\n\t\to(\"text elements\", function() {\n\t\t\tvar div = $document.createElement(\"div\")\n\t\t\tdiv.innerHTML =\n\t\t\t\t\"<svg xmlns=\\\"http://www.w3.org/2000/svg\\\">\"\n\t\t\t\t\t+ \"<text>hello</text>\"\n\t\t\t\t\t+ \"<text> </text>\"\n\t\t\t\t\t+ \"<text>world</text>\"\n\t\t\t\t+ \"</svg>\"\n\n\t\t\to(div.firstChild.nodeName).equals(\"svg\")\n\t\t\to(div.firstChild.namespaceURI).equals(\"http://www.w3.org/2000/svg\")\n\n\t\t\tvar nodes = div.firstChild.childNodes\n\t\t\to(nodes.length).equals(3)\n\t\t\to(nodes[0].nodeName).equals(\"text\")\n\t\t\to(nodes[0].namespaceURI).equals(\"http://www.w3.org/2000/svg\")\n\t\t\to(nodes[0].childNodes.length).equals(1)\n\t\t\to(nodes[0].childNodes[0].nodeName).equals(\"#text\")\n\t\t\to(nodes[0].childNodes[0].nodeValue).equals(\"hello\")\n\t\t\to(nodes[1].nodeName).equals(\"text\")\n\t\t\to(nodes[1].namespaceURI).equals(\"http://www.w3.org/2000/svg\")\n\t\t\to(nodes[1].childNodes.length).equals(1)\n\t\t\to(nodes[1].childNodes[0].nodeName).equals(\"#text\")\n\t\t\to(nodes[1].childNodes[0].nodeValue).equals(\" \")\n\t\t\to(nodes[2].nodeName).equals(\"text\")\n\t\t\to(nodes[2].namespaceURI).equals(\"http://www.w3.org/2000/svg\")\n\t\t\to(nodes[2].childNodes.length).equals(1)\n\t\t\to(nodes[2].childNodes[0].nodeName).equals(\"#text\")\n\t\t\to(nodes[2].childNodes[0].nodeValue).equals(\"world\")\n\t\t})\n\t})\n\to.spec(\"focus\", function() {\n\t\to(\"body is active by default\", function() {\n\t\t\to($document.documentElement.nodeName).equals(\"HTML\")\n\t\t\to($document.body.nodeName).equals(\"BODY\")\n\t\t\to($document.documentElement.firstChild.nodeName).equals(\"HEAD\")\n\t\t\to($document.documentElement).equals($document.body.parentNode)\n\t\t\to($document.activeElement).equals($document.body)\n\t\t})\n\t\to(\"focus changes activeElement\", function() {\n\t\t\tvar input = $document.createElement(\"input\")\n\t\t\t$document.body.appendChild(input)\n\t\t\tinput.focus()\n\n\t\t\to($document.activeElement).equals(input)\n\n\t\t\t$document.body.removeChild(input)\n\t\t})\n\t})\n\to.spec(\"style\", function() {\n\t\to(\"has style property\", function() {\n\t\t\tvar div = $document.createElement(\"div\")\n\n\t\t\to(typeof div.style).equals(\"object\")\n\t\t})\n\t\to(\"setting style.cssText string works\", function() {\n\t\t\tvar div = $document.createElement(\"div\")\n\t\t\tdiv.style.cssText = \"background-color: red; border-bottom: 1px solid red;\"\n\n\t\t\to(div.style.backgroundColor).equals(\"red\")\n\t\t\to(div.style.borderBottom).equals(\"1px solid red\")\n\t\t\to(div.attributes.style.value).equals(\"background-color: red; border-bottom: 1px solid red;\")\n\t\t})\n\t\to(\"removing via setting style.cssText string works\", function() {\n\t\t\tvar div = $document.createElement(\"div\")\n\t\t\tdiv.style.cssText = \"background: red;\"\n\t\t\tdiv.style.cssText = \"\"\n\n\t\t\to(div.style.background).equals(\"\")\n\t\t\to(div.attributes.style.value).equals(\"\")\n\t\t})\n\t\to(\"the final semicolon is optional when setting style.cssText\", function() {\n\t\t\tvar div = $document.createElement(\"div\")\n\t\t\tdiv.style.cssText = \"background: red\"\n\n\t\t\to(div.style.background).equals(\"red\")\n\t\t\to(div.style.cssText).equals(\"background: red;\")\n\t\t\to(div.attributes.style.value).equals(\"background: red;\")\n\t\t})\n\t\to(\"'cssText' as a property name is ignored when setting style.cssText\", function(){\n\t\t\tvar div = $document.createElement(\"div\")\n\t\t\tdiv.style.cssText = \"cssText: red;\"\n\n\t\t\to(div.style.cssText).equals(\"\")\n\t\t})\n\t\to(\"setting style.cssText that has a semi-colon in a strings\", function(){\n\t\t\tvar div = $document.createElement(\"div\")\n\t\t\tdiv.style.cssText = \"background: url(';'); font-family: \\\";\\\"\"\n\n\t\t\to(div.style.background).equals(\"url(';')\")\n\t\t\to(div.style.fontFamily).equals('\";\"')\n\t\t\to(div.style.cssText).equals(\"background: url(';'); font-family: \\\";\\\";\")\n\t\t})\n\t\to(\"comments in style.cssText are stripped\", function(){\n\t\t\tvar div = $document.createElement(\"div\")\n\t\t\tdiv.style.cssText = \"/**/background/*:*/: /*>;)*/red/**/;/**/\"\n\n\t\t\to(div.style.background).equals(\"red\")\n\t\t\to(div.style.cssText).equals(\"background: red;\")\n\n\t\t})\n\t\to(\"comments in strings in style.cssText are preserved\", function(){\n\t\t\tvar div = $document.createElement(\"div\")\n\t\t\tdiv.style.cssText = \"background: url('/*foo*/')\"\n\n\t\t\to(div.style.background).equals(\"url('/*foo*/')\")\n\n\t\t})\n\t\to(\"setting style updates style.cssText\", function () {\n\t\t\tvar div = $document.createElement(\"div\")\n\t\t\tdiv.style = \"background: red;\"\n\n\t\t\to(div.style.background).equals(\"red\")\n\t\t\to(div.style.cssText).equals(\"background: red;\")\n\n\t\t})\n\t})\n\to.spec(\"events\", function() {\n\t\to.spec(\"click\", function() {\n\t\t\tvar spy, div, e\n\t\t\to.beforeEach(function() {\n\t\t\t\tspy = o.spy()\n\t\t\t\tdiv = $document.createElement(\"div\")\n\t\t\t\te = $document.createEvent(\"MouseEvents\")\n\t\t\t\te.initEvent(\"click\", true, true)\n\n\t\t\t\t$document.body.appendChild(div)\n\t\t\t})\n\t\t\to.afterEach(function() {\n\t\t\t\t$document.body.removeChild(div)\n\t\t\t})\n\n\t\t\to(\"has onclick\", function() {\n\t\t\t\to(\"onclick\" in div).equals(true)\n\t\t\t})\n\t\t\to(\"addEventListener works\", function() {\n\t\t\t\tdiv.addEventListener(\"click\", spy, false)\n\t\t\t\tdiv.dispatchEvent(e)\n\n\t\t\t\to(spy.callCount).equals(1)\n\t\t\t\to(spy.this).equals(div)\n\t\t\t\to(spy.args[0].type).equals(\"click\")\n\t\t\t\to(spy.args[0].target).equals(div)\n\t\t\t})\n\t\t\to(\"removeEventListener works (bubbling phase)\", function() {\n\t\t\t\tdiv.addEventListener(\"click\", spy, false)\n\t\t\t\tdiv.removeEventListener(\"click\", spy, false)\n\t\t\t\tdiv.dispatchEvent(e)\n\n\t\t\t\to(spy.callCount).equals(0)\n\t\t\t})\n\t\t\to(\"removeEventListener works (capture phase)\", function() {\n\t\t\t\tdiv.addEventListener(\"click\", spy, true)\n\t\t\t\tdiv.removeEventListener(\"click\", spy, true)\n\t\t\t\tdiv.dispatchEvent(e)\n\n\t\t\t\to(spy.callCount).equals(0)\n\t\t\t})\n\t\t\to(\"removeEventListener is selective (bubbling phase)\", function() {\n\t\t\t\tvar other = o.spy()\n\t\t\t\tdiv.addEventListener(\"click\", spy, false)\n\t\t\t\tdiv.addEventListener(\"click\", other, false)\n\t\t\t\tdiv.removeEventListener(\"click\", spy, false)\n\t\t\t\tdiv.dispatchEvent(e)\n\n\t\t\t\to(spy.callCount).equals(0)\n\t\t\t\to(other.callCount).equals(1)\n\t\t\t})\n\t\t\to(\"removeEventListener is selective (capture phase)\", function() {\n\t\t\t\tvar other = o.spy()\n\t\t\t\tdiv.addEventListener(\"click\", spy, true)\n\t\t\t\tdiv.addEventListener(\"click\", other, true)\n\t\t\t\tdiv.removeEventListener(\"click\", spy, true)\n\t\t\t\tdiv.dispatchEvent(e)\n\n\t\t\t\to(spy.callCount).equals(0)\n\t\t\t\to(other.callCount).equals(1)\n\t\t\t})\n\t\t\to(\"removeEventListener only removes the handler related to a given phase (1/2)\", function() {\n\t\t\t\tspy = o.spy(function(e) {o(e.eventPhase).equals(3)})\n\t\t\t\t$document.body.addEventListener(\"click\", spy, true)\n\t\t\t\t$document.body.addEventListener(\"click\", spy, false)\n\t\t\t\t$document.body.removeEventListener(\"click\", spy, true)\n\t\t\t\tdiv.dispatchEvent(e)\n\n\t\t\t\to(spy.callCount).equals(1)\n\t\t\t})\n\t\t\to(\"removeEventListener only removes the handler related to a given phase (2/2)\", function() {\n\t\t\t\tspy = o.spy(function(e) {o(e.eventPhase).equals(1)})\n\t\t\t\t$document.body.addEventListener(\"click\", spy, true)\n\t\t\t\t$document.body.addEventListener(\"click\", spy, false)\n\t\t\t\t$document.body.removeEventListener(\"click\", spy, false)\n\t\t\t\tdiv.dispatchEvent(e)\n\n\t\t\t\to(spy.callCount).equals(1)\n\t\t\t})\n\t\t\to(\"click fires onclick\", function() {\n\t\t\t\tdiv.onclick = spy\n\t\t\t\tdiv.dispatchEvent(e)\n\n\t\t\t\to(spy.callCount).equals(1)\n\t\t\t\to(spy.this).equals(div)\n\t\t\t\to(spy.args[0].type).equals(\"click\")\n\t\t\t\to(spy.args[0].target).equals(div)\n\t\t\t})\n\t\t\to(\"click without onclick doesn't throw\", function(done) {\n\t\t\t\tdiv.dispatchEvent(e)\n\t\t\t\tdone()\n\t\t\t})\n\t\t})\n\t\to.spec(\"transitionend\", function() {\n\t\t\tvar spy, div, e\n\t\t\to.beforeEach(function() {\n\t\t\t\tspy = o.spy()\n\t\t\t\tdiv = $document.createElement(\"div\")\n\t\t\t\te = $document.createEvent(\"HTMLEvents\")\n\t\t\t\te.initEvent(\"transitionend\", true, true)\n\n\t\t\t\t$document.body.appendChild(div)\n\t\t\t})\n\t\t\to.afterEach(function() {\n\t\t\t\t$document.body.removeChild(div)\n\t\t\t})\n\n\t\t\to(\"ontransitionend does not fire\", function(done) {\n\t\t\t\tdiv.ontransitionend = spy\n\t\t\t\tdiv.dispatchEvent(e)\n\n\t\t\t\to(spy.callCount).equals(0)\n\t\t\t\tdone()\n\t\t\t})\n\t\t})\n\t\to.spec(\"capture and bubbling phases\", function() {\n\t\t\tvar div, e\n\t\t\to.beforeEach(function() {\n\t\t\t\tdiv = $document.createElement(\"div\")\n\t\t\t\te = $document.createEvent(\"MouseEvents\")\n\t\t\t\te.initEvent(\"click\", true, true)\n\n\t\t\t\t$document.body.appendChild(div)\n\t\t\t})\n\t\t\to.afterEach(function() {\n\t\t\t\t$document.body.removeChild(div)\n\t\t\t})\n\t\t\to(\"capture and bubbling events both fire on the target in the order they were defined, regardless of the phase\", function () {\n\t\t\t\tvar sequence = []\n\t\t\t\tvar capture = o.spy(function(ev){\n\t\t\t\t\tsequence.push(\"capture\")\n\n\t\t\t\t\to(ev).equals(e)\n\t\t\t\t\to(ev.eventPhase).equals(2)\n\t\t\t\t\to(ev.target).equals(div)\n\t\t\t\t\to(ev.currentTarget).equals(div)\n\t\t\t\t})\n\t\t\t\tvar bubble = o.spy(function(ev){\n\t\t\t\t\tsequence.push(\"bubble\")\n\n\t\t\t\t\to(ev).equals(e)\n\t\t\t\t\to(ev.eventPhase).equals(2)\n\t\t\t\t\to(ev.target).equals(div)\n\t\t\t\t\to(ev.currentTarget).equals(div)\n\t\t\t\t})\n\n\t\t\t\tdiv.addEventListener(\"click\", bubble, false)\n\t\t\t\tdiv.addEventListener(\"click\", capture, true)\n\t\t\t\tdiv.dispatchEvent(e)\n\n\t\t\t\to(capture.callCount).equals(1)\n\t\t\t\to(bubble.callCount).equals(1)\n\t\t\t\to(sequence).deepEquals([\"bubble\", \"capture\"])\n\t\t\t})\n\t\t\to(\"capture and bubbling events both fire on the parent\", function () {\n\t\t\t\tvar sequence = []\n\t\t\t\tvar capture = o.spy(function(ev){\n\t\t\t\t\tsequence.push(\"capture\")\n\n\t\t\t\t\to(ev).equals(e)\n\t\t\t\t\to(ev.eventPhase).equals(1)\n\t\t\t\t\to(ev.target).equals(div)\n\t\t\t\t\to(ev.currentTarget).equals($document.body)\n\t\t\t\t})\n\t\t\t\tvar bubble = o.spy(function(ev){\n\t\t\t\t\tsequence.push(\"bubble\")\n\n\t\t\t\t\to(ev).equals(e)\n\t\t\t\t\to(ev.eventPhase).equals(3)\n\t\t\t\t\to(ev.target).equals(div)\n\t\t\t\t\to(ev.currentTarget).equals($document.body)\n\t\t\t\t})\n\n\t\t\t\t$document.body.addEventListener(\"click\", bubble, false)\n\t\t\t\t$document.body.addEventListener(\"click\", capture, true)\n\t\t\t\tdiv.dispatchEvent(e)\n\n\t\t\t\to(capture.callCount).equals(1)\n\t\t\t\to(bubble.callCount).equals(1)\n\t\t\t\to(sequence).deepEquals([\"capture\", \"bubble\"])\n\t\t\t})\n\t\t\to(\"useCapture defaults to false\", function () {\n\t\t\t\tvar sequence = []\n\t\t\t\tvar parent = o.spy(function(ev){\n\t\t\t\t\tsequence.push(\"parent\")\n\n\t\t\t\t\to(ev).equals(e)\n\t\t\t\t\to(ev.eventPhase).equals(3)\n\t\t\t\t\to(ev.target).equals(div)\n\t\t\t\t\to(ev.currentTarget).equals($document.body)\n\t\t\t\t})\n\t\t\t\tvar target = o.spy(function(ev){\n\t\t\t\t\tsequence.push(\"target\")\n\n\t\t\t\t\to(ev).equals(e)\n\t\t\t\t\to(ev.eventPhase).equals(2)\n\t\t\t\t\to(ev.target).equals(div)\n\t\t\t\t\to(ev.currentTarget).equals(div)\n\t\t\t\t})\n\n\t\t\t\t$document.body.addEventListener(\"click\", parent)\n\t\t\t\tdiv.addEventListener(\"click\", target)\n\t\t\t\tdiv.dispatchEvent(e)\n\n\t\t\t\to(parent.callCount).equals(1)\n\t\t\t\to(target.callCount).equals(1)\n\t\t\t\to(sequence).deepEquals([\"target\", \"parent\"])\n\t\t\t})\n\t\t\to(\"legacy handlers fire on the bubbling phase\", function () {\n\t\t\t\tvar sequence = []\n\t\t\t\tvar parent = o.spy(function(ev){\n\t\t\t\t\tsequence.push(\"parent\")\n\n\t\t\t\t\to(ev).equals(e)\n\t\t\t\t\to(ev.eventPhase).equals(3)\n\t\t\t\t\to(ev.target).equals(div)\n\t\t\t\t\to(ev.currentTarget).equals($document.body)\n\t\t\t\t})\n\t\t\t\tvar target = o.spy(function(ev){\n\t\t\t\t\tsequence.push(\"target\")\n\n\t\t\t\t\to(ev).equals(e)\n\t\t\t\t\to(ev.eventPhase).equals(2)\n\t\t\t\t\to(ev.target).equals(div)\n\t\t\t\t\to(ev.currentTarget).equals(div)\n\t\t\t\t})\n\n\t\t\t\t$document.body.addEventListener(\"click\", parent)\n\t\t\t\t$document.body.onclick = parent\n\t\t\t\tdiv.addEventListener(\"click\", target)\n\t\t\t\tdiv.dispatchEvent(e)\n\n\t\t\t\to(parent.callCount).equals(2)\n\t\t\t\to(target.callCount).equals(1)\n\t\t\t\to(sequence).deepEquals([\"target\", \"parent\", \"parent\"])\n\t\t\t})\n\t\t\to(\"events do not propagate to child nodes\", function() {\n\t\t\t\tvar target = o.spy(function(ev){\n\t\t\t\t\to(ev).equals(e)\n\t\t\t\t\to(ev.eventPhase).equals(2)\n\t\t\t\t\to(ev.target).equals($document.body)\n\t\t\t\t\to(ev.currentTarget).equals($document.body)\n\t\t\t\t})\n\t\t\t\tvar child = o.spy(function(){\n\t\t\t\t})\n\n\t\t\t\t$document.body.addEventListener(\"click\", target)\n\t\t\t\tdiv.addEventListener(\"click\", child)\n\t\t\t\t$document.body.dispatchEvent(e)\n\n\t\t\t\to(target.callCount).equals(1)\n\t\t\t\to(child.callCount).equals(0)\n\t\t\t})\n\t\t\to(\"e.stopPropagation 1/6\", function () {\n\t\t\t\tvar capParent = o.spy(function(e){e.stopPropagation()})\n\t\t\t\tvar capTarget = o.spy()\n\t\t\t\tvar bubTarget = o.spy()\n\t\t\t\tvar legacyTarget = o.spy()\n\t\t\t\tvar bubParent = o.spy()\n\t\t\t\tvar legacyParent = o.spy()\n\n\t\t\t\t$document.body.addEventListener(\"click\", capParent, true)\n\t\t\t\t$document.body.addEventListener(\"click\", bubParent, false)\n\t\t\t\t$document.body.onclick = legacyParent\n\n\t\t\t\tdiv.addEventListener(\"click\", capTarget, true)\n\t\t\t\tdiv.addEventListener(\"click\", bubTarget, false)\n\t\t\t\tdiv.onclick = legacyTarget\n\n\t\t\t\tdiv.dispatchEvent(e)\n\n\t\t\t\to(capParent.callCount).equals(1)\n\t\t\t\to(capTarget.callCount).equals(0)\n\t\t\t\to(bubTarget.callCount).equals(0)\n\t\t\t\to(legacyTarget.callCount).equals(0)\n\t\t\t\to(bubParent.callCount).equals(0)\n\t\t\t\to(legacyParent.callCount).equals(0)\n\t\t\t})\n\t\t\to(\"e.stopPropagation 2/6\", function () {\n\t\t\t\tvar capParent = o.spy()\n\t\t\t\tvar capTarget = o.spy(function(e){e.stopPropagation()})\n\t\t\t\tvar bubTarget = o.spy()\n\t\t\t\tvar legacyTarget = o.spy()\n\t\t\t\tvar bubParent = o.spy()\n\t\t\t\tvar legacyParent = o.spy()\n\n\t\t\t\t$document.body.addEventListener(\"click\", capParent, true)\n\t\t\t\t$document.body.addEventListener(\"click\", bubParent, false)\n\t\t\t\t$document.body.onclick = legacyParent\n\n\t\t\t\tdiv.addEventListener(\"click\", capTarget, true)\n\t\t\t\tdiv.addEventListener(\"click\", bubTarget, false)\n\t\t\t\tdiv.onclick = legacyTarget\n\n\t\t\t\tdiv.dispatchEvent(e)\n\n\t\t\t\to(capParent.callCount).equals(1)\n\t\t\t\to(capTarget.callCount).equals(1)\n\t\t\t\to(bubTarget.callCount).equals(1)\n\t\t\t\to(legacyTarget.callCount).equals(1)\n\t\t\t\to(bubParent.callCount).equals(0)\n\t\t\t\to(legacyParent.callCount).equals(0)\n\t\t\t})\n\n\t\t\to(\"e.stopPropagation 3/6\", function () {\n\t\t\t\tvar capParent = o.spy()\n\t\t\t\tvar capTarget = o.spy()\n\t\t\t\tvar bubTarget = o.spy(function(e){e.stopPropagation()})\n\t\t\t\tvar legacyTarget = o.spy()\n\t\t\t\tvar bubParent = o.spy()\n\t\t\t\tvar legacyParent = o.spy()\n\n\t\t\t\t$document.body.addEventListener(\"click\", capParent, true)\n\t\t\t\t$document.body.addEventListener(\"click\", bubParent, false)\n\t\t\t\t$document.body.onclick = legacyParent\n\n\t\t\t\tdiv.addEventListener(\"click\", capTarget, true)\n\t\t\t\tdiv.addEventListener(\"click\", bubTarget, false)\n\t\t\t\tdiv.onclick = legacyTarget\n\n\t\t\t\tdiv.dispatchEvent(e)\n\n\t\t\t\to(capParent.callCount).equals(1)\n\t\t\t\to(capTarget.callCount).equals(1)\n\t\t\t\to(bubTarget.callCount).equals(1)\n\t\t\t\to(legacyTarget.callCount).equals(1)\n\t\t\t\to(bubParent.callCount).equals(0)\n\t\t\t\to(legacyParent.callCount).equals(0)\n\t\t\t})\n\t\t\to(\"e.stopPropagation 4/6\", function () {\n\t\t\t\tvar capParent = o.spy()\n\t\t\t\tvar capTarget = o.spy()\n\t\t\t\tvar bubTarget = o.spy()\n\t\t\t\tvar legacyTarget = o.spy(function(e){e.stopPropagation()})\n\t\t\t\tvar bubParent = o.spy()\n\t\t\t\tvar legacyParent = o.spy()\n\n\t\t\t\t$document.body.addEventListener(\"click\", capParent, true)\n\t\t\t\t$document.body.addEventListener(\"click\", bubParent, false)\n\t\t\t\t$document.body.onclick = legacyParent\n\n\t\t\t\tdiv.addEventListener(\"click\", capTarget, true)\n\t\t\t\tdiv.addEventListener(\"click\", bubTarget, false)\n\t\t\t\tdiv.onclick = legacyTarget\n\n\t\t\t\tdiv.dispatchEvent(e)\n\n\t\t\t\to(capParent.callCount).equals(1)\n\t\t\t\to(capTarget.callCount).equals(1)\n\t\t\t\to(bubTarget.callCount).equals(1)\n\t\t\t\to(legacyTarget.callCount).equals(1)\n\t\t\t\to(bubParent.callCount).equals(0)\n\t\t\t\to(legacyParent.callCount).equals(0)\n\t\t\t})\n\t\t\to(\"e.stopPropagation 5/6\", function () {\n\t\t\t\tvar capParent = o.spy()\n\t\t\t\tvar capTarget = o.spy()\n\t\t\t\tvar bubTarget = o.spy()\n\t\t\t\tvar legacyTarget = o.spy()\n\t\t\t\tvar bubParent = o.spy(function(e){e.stopPropagation()})\n\t\t\t\tvar legacyParent = o.spy()\n\n\t\t\t\t$document.body.addEventListener(\"click\", capParent, true)\n\t\t\t\t$document.body.addEventListener(\"click\", bubParent, false)\n\t\t\t\t$document.body.onclick = legacyParent\n\n\t\t\t\tdiv.addEventListener(\"click\", capTarget, true)\n\t\t\t\tdiv.addEventListener(\"click\", bubTarget, false)\n\t\t\t\tdiv.onclick = legacyTarget\n\n\t\t\t\tdiv.dispatchEvent(e)\n\n\t\t\t\to(capParent.callCount).equals(1)\n\t\t\t\to(capTarget.callCount).equals(1)\n\t\t\t\to(bubTarget.callCount).equals(1)\n\t\t\t\to(legacyTarget.callCount).equals(1)\n\t\t\t\to(bubParent.callCount).equals(1)\n\t\t\t\to(legacyParent.callCount).equals(1)\n\t\t\t})\n\t\t\to(\"e.stopPropagation 6/6\", function () {\n\t\t\t\tvar capParent = o.spy()\n\t\t\t\tvar capTarget = o.spy()\n\t\t\t\tvar legacyTarget = o.spy()\n\t\t\t\tvar bubTarget = o.spy()\n\t\t\t\tvar bubParent = o.spy()\n\t\t\t\tvar legacyParent = o.spy(function(e){e.stopPropagation()})\n\n\t\t\t\t$document.body.addEventListener(\"click\", capParent, true)\n\t\t\t\t$document.body.addEventListener(\"click\", bubParent, false)\n\t\t\t\t$document.body.onclick = legacyParent\n\n\t\t\t\tdiv.addEventListener(\"click\", capTarget, true)\n\t\t\t\tdiv.addEventListener(\"click\", bubTarget, false)\n\t\t\t\tdiv.onclick = legacyTarget\n\n\t\t\t\tdiv.dispatchEvent(e)\n\n\t\t\t\to(capParent.callCount).equals(1)\n\t\t\t\to(capTarget.callCount).equals(1)\n\t\t\t\to(bubTarget.callCount).equals(1)\n\t\t\t\to(legacyTarget.callCount).equals(1)\n\t\t\t\to(bubParent.callCount).equals(1)\n\t\t\t\to(legacyParent.callCount).equals(1)\n\t\t\t})\n\t\t\to(\"e.stopImmediatePropagation 1/6\", function () {\n\t\t\t\tvar capParent = o.spy(function(e){e.stopImmediatePropagation()})\n\t\t\t\tvar capTarget = o.spy()\n\t\t\t\tvar bubTarget = o.spy()\n\t\t\t\tvar legacyTarget = o.spy()\n\t\t\t\tvar bubParent = o.spy()\n\t\t\t\tvar legacyParent = o.spy()\n\n\t\t\t\t$document.body.addEventListener(\"click\", capParent, true)\n\t\t\t\t$document.body.addEventListener(\"click\", bubParent, false)\n\t\t\t\t$document.body.onclick = legacyParent\n\n\t\t\t\tdiv.addEventListener(\"click\", capTarget, true)\n\t\t\t\tdiv.addEventListener(\"click\", bubTarget, false)\n\t\t\t\tdiv.onclick = legacyTarget\n\n\t\t\t\tdiv.dispatchEvent(e)\n\n\t\t\t\to(capParent.callCount).equals(1)\n\t\t\t\to(capTarget.callCount).equals(0)\n\t\t\t\to(bubTarget.callCount).equals(0)\n\t\t\t\to(legacyTarget.callCount).equals(0)\n\t\t\t\to(bubParent.callCount).equals(0)\n\t\t\t\to(legacyParent.callCount).equals(0)\n\t\t\t})\n\t\t\to(\"e.stopImmediatePropagation 2/6\", function () {\n\t\t\t\tvar capParent = o.spy()\n\t\t\t\tvar capTarget = o.spy(function(e){e.stopImmediatePropagation()})\n\t\t\t\tvar bubTarget = o.spy()\n\t\t\t\tvar legacyTarget = o.spy()\n\t\t\t\tvar bubParent = o.spy()\n\t\t\t\tvar legacyParent = o.spy()\n\n\t\t\t\t$document.body.addEventListener(\"click\", capParent, true)\n\t\t\t\t$document.body.addEventListener(\"click\", bubParent, false)\n\t\t\t\t$document.body.onclick = legacyParent\n\n\t\t\t\tdiv.addEventListener(\"click\", capTarget, true)\n\t\t\t\tdiv.addEventListener(\"click\", bubTarget, false)\n\t\t\t\tdiv.onclick = legacyTarget\n\n\t\t\t\tdiv.dispatchEvent(e)\n\n\t\t\t\to(capParent.callCount).equals(1)\n\t\t\t\to(capTarget.callCount).equals(1)\n\t\t\t\to(bubTarget.callCount).equals(0)\n\t\t\t\to(legacyTarget.callCount).equals(0)\n\t\t\t\to(bubParent.callCount).equals(0)\n\t\t\t\to(legacyParent.callCount).equals(0)\n\t\t\t})\n\n\t\t\to(\"e.stopImmediatePropagation 3/6\", function () {\n\t\t\t\tvar capParent = o.spy()\n\t\t\t\tvar capTarget = o.spy()\n\t\t\t\tvar bubTarget = o.spy(function(e){e.stopImmediatePropagation()})\n\t\t\t\tvar legacyTarget = o.spy()\n\t\t\t\tvar bubParent = o.spy()\n\t\t\t\tvar legacyParent = o.spy()\n\n\t\t\t\t$document.body.addEventListener(\"click\", capParent, true)\n\t\t\t\t$document.body.addEventListener(\"click\", bubParent, false)\n\t\t\t\t$document.body.onclick = legacyParent\n\n\t\t\t\tdiv.addEventListener(\"click\", capTarget, true)\n\t\t\t\tdiv.addEventListener(\"click\", bubTarget, false)\n\t\t\t\tdiv.onclick = legacyTarget\n\n\t\t\t\tdiv.dispatchEvent(e)\n\n\t\t\t\to(capParent.callCount).equals(1)\n\t\t\t\to(capTarget.callCount).equals(1)\n\t\t\t\to(bubTarget.callCount).equals(1)\n\t\t\t\to(legacyTarget.callCount).equals(0)\n\t\t\t\to(bubParent.callCount).equals(0)\n\t\t\t\to(legacyParent.callCount).equals(0)\n\t\t\t})\n\t\t\to(\"e.stopImmediatePropagation 4/6\", function () {\n\t\t\t\tvar capParent = o.spy()\n\t\t\t\tvar capTarget = o.spy()\n\t\t\t\tvar bubTarget = o.spy()\n\t\t\t\tvar legacyTarget = o.spy(function(e){e.stopImmediatePropagation()})\n\t\t\t\tvar bubParent = o.spy()\n\t\t\t\tvar legacyParent = o.spy()\n\n\t\t\t\t$document.body.addEventListener(\"click\", capParent, true)\n\t\t\t\t$document.body.addEventListener(\"click\", bubParent, false)\n\t\t\t\t$document.body.onclick = legacyParent\n\n\t\t\t\tdiv.addEventListener(\"click\", capTarget, true)\n\t\t\t\tdiv.addEventListener(\"click\", bubTarget, false)\n\t\t\t\tdiv.onclick = legacyTarget\n\n\t\t\t\tdiv.dispatchEvent(e)\n\n\t\t\t\to(capParent.callCount).equals(1)\n\t\t\t\to(capTarget.callCount).equals(1)\n\t\t\t\to(bubTarget.callCount).equals(1)\n\t\t\t\to(legacyTarget.callCount).equals(1)\n\t\t\t\to(bubParent.callCount).equals(0)\n\t\t\t\to(legacyParent.callCount).equals(0)\n\t\t\t})\n\t\t\to(\"e.stopImmediatePropagation 5/6\", function () {\n\t\t\t\tvar capParent = o.spy()\n\t\t\t\tvar capTarget = o.spy()\n\t\t\t\tvar bubTarget = o.spy()\n\t\t\t\tvar legacyTarget = o.spy()\n\t\t\t\tvar bubParent = o.spy(function(e){e.stopImmediatePropagation()})\n\t\t\t\tvar legacyParent = o.spy()\n\n\t\t\t\t$document.body.addEventListener(\"click\", capParent, true)\n\t\t\t\t$document.body.addEventListener(\"click\", bubParent, false)\n\t\t\t\t$document.body.onclick = legacyParent\n\n\t\t\t\tdiv.addEventListener(\"click\", capTarget, true)\n\t\t\t\tdiv.addEventListener(\"click\", bubTarget, false)\n\t\t\t\tdiv.onclick = legacyTarget\n\n\t\t\t\tdiv.dispatchEvent(e)\n\n\t\t\t\to(capParent.callCount).equals(1)\n\t\t\t\to(capTarget.callCount).equals(1)\n\t\t\t\to(bubTarget.callCount).equals(1)\n\t\t\t\to(legacyTarget.callCount).equals(1)\n\t\t\t\to(bubParent.callCount).equals(1)\n\t\t\t\to(legacyParent.callCount).equals(0)\n\t\t\t})\n\t\t\to(\"e.stopImmediatePropagation 6/6\", function () {\n\t\t\t\tvar capParent = o.spy()\n\t\t\t\tvar capTarget = o.spy()\n\t\t\t\tvar legacyTarget = o.spy()\n\t\t\t\tvar bubTarget = o.spy()\n\t\t\t\tvar bubParent = o.spy()\n\t\t\t\tvar legacyParent = o.spy(function(e){e.stopImmediatePropagation()})\n\n\t\t\t\t$document.body.addEventListener(\"click\", capParent, true)\n\t\t\t\t$document.body.addEventListener(\"click\", bubParent, false)\n\t\t\t\t$document.body.onclick = legacyParent\n\n\t\t\t\tdiv.addEventListener(\"click\", capTarget, true)\n\t\t\t\tdiv.addEventListener(\"click\", bubTarget, false)\n\t\t\t\tdiv.onclick = legacyTarget\n\n\t\t\t\tdiv.dispatchEvent(e)\n\n\t\t\t\to(capParent.callCount).equals(1)\n\t\t\t\to(capTarget.callCount).equals(1)\n\t\t\t\to(bubTarget.callCount).equals(1)\n\t\t\t\to(legacyTarget.callCount).equals(1)\n\t\t\t\to(bubParent.callCount).equals(1)\n\t\t\t\to(legacyParent.callCount).equals(1)\n\t\t\t})\n\t\t\to(\"errors thrown in handlers don't interrupt the chain\", function(done) {\n\t\t\t\tvar errMsg = \"The presence of these six errors in the log is expected in non-Node.js environments\"\n\t\t\t\tvar handler = o.spy(function(){throw errMsg})\n\n\t\t\t\t$document.body.addEventListener(\"click\", handler, true)\n\t\t\t\t$document.body.addEventListener(\"click\", handler, false)\n\t\t\t\t$document.body.onclick = handler\n\n\t\t\t\tdiv.addEventListener(\"click\", handler, true)\n\t\t\t\tdiv.addEventListener(\"click\", handler, false)\n\t\t\t\tdiv.onclick = handler\n\n\t\t\t\tdiv.dispatchEvent(e)\n\n\t\t\t\to(handler.callCount).equals(6)\n\n\t\t\t\t// Swallow the async errors in NodeJS\n\t\t\t\tif (typeof process !== \"undefined\" && typeof process.once === \"function\"){\n\t\t\t\t\tprocess.once(\"uncaughtException\", function(e) {\n\t\t\t\t\t\tif (e !== errMsg) throw e\n\t\t\t\t\t\tprocess.once(\"uncaughtException\", function(e) {\n\t\t\t\t\t\t\tif (e !== errMsg) throw e\n\t\t\t\t\t\t\tprocess.once(\"uncaughtException\", function(e) {\n\t\t\t\t\t\t\t\tif (e !== errMsg) throw e\n\t\t\t\t\t\t\t\tprocess.once(\"uncaughtException\", function(e) {\n\t\t\t\t\t\t\t\t\tif (e !== errMsg) throw e\n\t\t\t\t\t\t\t\t\tprocess.once(\"uncaughtException\", function(e) {\n\t\t\t\t\t\t\t\t\t\tif (e !== errMsg) throw e\n\t\t\t\t\t\t\t\t\t\tprocess.once(\"uncaughtException\", function(e) {\n\t\t\t\t\t\t\t\t\t\t\tif (e !== errMsg) throw e\n\t\t\t\t\t\t\t\t\t\t\tdone()\n\t\t\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t})\n\t\t\t\t\t})\n\t\t\t\t} else {\n\t\t\t\t\tdone()\n\t\t\t\t}\n\t\t\t})\n\t\t})\n\t})\n\to.spec(\"attributes\", function() {\n\t\to.spec(\"a[href]\", function() {\n\t\t\to(\"is empty string if no attribute\", function() {\n\t\t\t\tvar a = $document.createElement(\"a\")\n\n\t\t\t\to(a.href).equals(\"\")\n\t\t\t\to(a.attributes[\"href\"]).equals(undefined)\n\t\t\t})\n\t\t\to(\"is path if attribute is set\", function() {\n\t\t\t\tvar a = $document.createElement(\"a\")\n\t\t\t\ta.setAttribute(\"href\", \"\")\n\n\t\t\t\to(a.href).notEquals(\"\")\n\t\t\t\to(a.attributes[\"href\"].value).equals(\"\")\n\t\t\t})\n\t\t\to(\"is path if property is set\", function() {\n\t\t\t\tvar a = $document.createElement(\"a\")\n\t\t\t\ta.href = \"\"\n\n\t\t\t\to(a.href).notEquals(\"\")\n\t\t\t\to(a.attributes[\"href\"].value).equals(\"\")\n\t\t\t})\n\t\t\to(\"property is read-only for SVG elements\", function() {\n\t\t\t\tvar a = $document.createElementNS(\"http://www.w3.org/2000/svg\", \"a\")\n\t\t\t\ta.href = \"/foo\"\n\n\t\t\t\to(a.href).deepEquals({baseVal: \"\", animVal: \"\"})\n\t\t\t\to(\"href\" in a.attributes).equals(false)\n\t\t\t})\n\t\t})\n\t\to.spec(\"input[checked]\", function() {\n\t\t\to(\"only exists in input elements\", function() {\n\t\t\t\tvar input = $document.createElement(\"input\")\n\t\t\t\tvar a = $document.createElement(\"a\")\n\n\t\t\t\to(\"checked\" in input).equals(true)\n\t\t\t\to(\"checked\" in a).equals(false)\n\t\t\t})\n\t\t\to(\"tracks attribute value when unset\", function() {\n\t\t\t\tvar input = $document.createElement(\"input\")\n\t\t\t\tinput.setAttribute(\"type\", \"checkbox\")\n\n\t\t\t\to(input.checked).equals(false)\n\t\t\t\to(input.attributes[\"checked\"]).equals(undefined)\n\n\t\t\t\tinput.setAttribute(\"checked\", \"\")\n\n\t\t\t\to(input.checked).equals(true)\n\t\t\t\to(input.attributes[\"checked\"].value).equals(\"\")\n\n\t\t\t\tinput.removeAttribute(\"checked\")\n\n\t\t\t\to(input.checked).equals(false)\n\t\t\t\to(input.attributes[\"checked\"]).equals(undefined)\n\t\t\t})\n\t\t\to(\"does not track attribute value when set\", function() {\n\t\t\t\tvar input = $document.createElement(\"input\")\n\t\t\t\tinput.setAttribute(\"type\", \"checkbox\")\n\t\t\t\tinput.checked = true\n\n\t\t\t\to(input.checked).equals(true)\n\t\t\t\to(input.attributes[\"checked\"]).equals(undefined)\n\n\t\t\t\tinput.checked = false\n\t\t\t\tinput.setAttribute(\"checked\", \"\")\n\n\t\t\t\tinput.checked = true\n\t\t\t\tinput.removeAttribute(\"checked\")\n\n\t\t\t\to(input.checked).equals(true)\n\t\t\t})\n\t\t\to(\"toggles on click\", function() {\n\t\t\t\tvar input = $document.createElement(\"input\")\n\t\t\t\tinput.setAttribute(\"type\", \"checkbox\")\n\t\t\t\tinput.checked = false\n\n\t\t\t\tvar e = $document.createEvent(\"MouseEvents\")\n\t\t\t\te.initEvent(\"click\", true, true)\n\t\t\t\tinput.dispatchEvent(e)\n\n\t\t\t\to(input.checked).equals(true)\n\t\t\t})\n\t\t\to(\"doesn't toggle on click when preventDefault() is used\", function() {\n\t\t\t\tvar input = $document.createElement(\"input\")\n\t\t\t\tinput.setAttribute(\"type\", \"checkbox\")\n\t\t\t\tinput.checked = false\n\t\t\t\tinput.onclick = function(e) {e.preventDefault()}\n\n\t\t\t\tvar e = $document.createEvent(\"MouseEvents\")\n\t\t\t\te.initEvent(\"click\", true, true)\n\t\t\t\tinput.dispatchEvent(e)\n\n\t\t\t\to(input.checked).equals(false)\n\t\t\t})\n\t\t})\n\t\to.spec(\"input[value]\", function() {\n\t\t\to(\"only exists in input elements\", function() {\n\t\t\t\tvar input = $document.createElement(\"input\")\n\t\t\t\tvar a = $document.createElement(\"a\")\n\n\t\t\t\to(\"value\" in input).equals(true)\n\t\t\t\to(\"value\" in a).equals(false)\n\t\t\t})\n\t\t\to(\"converts null to ''\", function() {\n\t\t\t\tvar input = $document.createElement(\"input\")\n\t\t\t\tinput.value = \"x\"\n\n\t\t\t\to(input.value).equals(\"x\")\n\n\t\t\t\tinput.value = null\n\n\t\t\t\to(input.value).equals(\"\")\n\t\t\t})\n\t\t\to(\"converts values to strings\", function() {\n\t\t\t\tvar input = $document.createElement(\"input\")\n\t\t\t\tinput.value = 5\n\n\t\t\t\to(input.value).equals(\"5\")\n\n\t\t\t\tinput.value = 0\n\n\t\t\t\to(input.value).equals(\"0\")\n\n\t\t\t\tinput.value = undefined\n\n\t\t\t\to(input.value).equals(\"undefined\")\n\t\t\t})\n\t\t\tif (typeof Symbol === \"function\") o(\"throws when set to a symbol\", function() {\n\t\t\t\tvar threw = false\n\t\t\t\tvar input = $document.createElement(\"input\")\n\t\t\t\ttry {\n\t\t\t\t\tinput.value = Symbol(\"\")\n\t\t\t\t} catch (e) {\n\t\t\t\t\to(e instanceof TypeError).equals(true)\n\t\t\t\t\tthrew = true\n\t\t\t\t}\n\n\t\t\t\to(input.value).equals(\"\")\n\t\t\t\to(threw).equals(true)\n\t\t\t})\n\t\t})\n\t\to.spec(\"input[type]\", function(){\n\t\t\to(\"only exists in input elements\", function() {\n\t\t\t\tvar input = $document.createElement(\"input\")\n\t\t\t\tvar a = $document.createElement(\"a\")\n\n\t\t\t\to(\"type\" in input).equals(true)\n\t\t\t\to(\"type\" in a).equals(false)\n\t\t\t})\n\t\t\to(\"is 'text' by default\", function() {\n\t\t\t\tvar input = $document.createElement(\"input\")\n\n\t\t\t\to(input.type).equals(\"text\")\n\t\t\t})\n\t\t\t\"radio|button|checkbox|color|date|datetime|datetime-local|email|file|hidden|month|number|password|range|research|search|submit|tel|text|url|week|image\"\n\t\t\t\t.split(\"|\").forEach(function(type) {\n\t\t\t\t\to(\"can be set to \" + type, function(){\n\t\t\t\t\t\tvar input = $document.createElement(\"input\")\n\t\t\t\t\t\tinput.type = type\n\n\t\t\t\t\t\to(input.getAttribute(\"type\")).equals(type)\n\t\t\t\t\t\to(input.type).equals(type)\n\t\t\t\t\t})\n\t\t\t\t\to(\"bad values set the attribute, but the getter corrects to 'text', \" + type, function(){\n\t\t\t\t\t\tvar input = $document.createElement(\"input\")\n\t\t\t\t\t\tinput.type = \"badbad\" + type\n\n\t\t\t\t\t\to(input.getAttribute(\"type\")).equals(\"badbad\" + type)\n\t\t\t\t\t\to(input.type).equals(\"text\")\n\t\t\t\t\t})\n\t\t\t\t})\n\t\t})\n\t\to.spec(\"textarea[value]\", function() {\n\t\t\to(\"reads from child if no value was ever set\", function() {\n\t\t\t\tvar textarea = $document.createElement(\"textarea\")\n\t\t\t\ttextarea.appendChild($document.createTextNode(\"aaa\"))\n\n\t\t\t\to(textarea.value).equals(\"aaa\")\n\t\t\t})\n\t\t\to(\"ignores child if value set\", function() {\n\t\t\t\tvar textarea = $document.createElement(\"textarea\")\n\t\t\t\ttextarea.value = null\n\t\t\t\ttextarea.appendChild($document.createTextNode(\"aaa\"))\n\n\t\t\t\to(textarea.value).equals(\"\")\n\t\t\t})\n\t\t\to(\"textarea[value] doesn't reflect `attributes.value`\", function() {\n\t\t\t\tvar textarea = $document.createElement(\"textarea\")\n\t\t\t\ttextarea.value = \"aaa\"\n\t\t\t\ttextarea.setAttribute(\"value\", \"bbb\")\n\n\t\t\t\to(textarea.value).equals(\"aaa\")\n\t\t\t})\n\t\t})\n\t\to.spec(\"select[value] and select[selectedIndex]\", function() {\n\t\t\to(\"only exist in select elements\", function() {\n\t\t\t\tvar select = $document.createElement(\"select\")\n\t\t\t\tvar a = $document.createElement(\"a\")\n\n\t\t\t\to(\"value\" in select).equals(true)\n\t\t\t\to(\"value\" in a).equals(false)\n\n\t\t\t\to(\"selectedIndex\" in select).equals(true)\n\t\t\t\to(\"selectedIndex\" in a).equals(false)\n\t\t\t})\n\t\t\to(\"value defaults to value at first index\", function() {\n\t\t\t\tvar select = $document.createElement(\"select\")\n\n\t\t\t\tvar option1 = $document.createElement(\"option\")\n\t\t\t\toption1.setAttribute(\"value\", \"a\")\n\t\t\t\tselect.appendChild(option1)\n\n\t\t\t\tvar option2 = $document.createElement(\"option\")\n\t\t\t\toption2.setAttribute(\"value\", \"b\")\n\t\t\t\tselect.appendChild(option2)\n\n\t\t\t\to(select.value).equals(\"a\")\n\t\t\t\to(select.selectedIndex).equals(0)\n\t\t\t})\n\t\t\to(\"value falls back to child nodeValue if no attribute\", function() {\n\t\t\t\tvar select = $document.createElement(\"select\")\n\n\t\t\t\tvar option1 = $document.createElement(\"option\")\n\t\t\t\toption1.appendChild($document.createTextNode(\"a\"))\n\t\t\t\tvar option2 = $document.createElement(\"option\")\n\t\t\t\toption2.appendChild($document.createTextNode(\"b\"))\n\t\t\t\tselect.appendChild(option1)\n\t\t\t\tselect.appendChild(option2)\n\n\t\t\t\to(select.value).equals(\"a\")\n\t\t\t\to(select.selectedIndex).equals(0)\n\t\t\t\to(select.childNodes[0].selected).equals(true)\n\t\t\t\to(select.childNodes[0].value).equals(\"a\")\n\t\t\t\to(select.childNodes[1].value).equals(\"b\")\n\t\t\t})\n\t\t\to(\"value defaults to invalid if no options\", function() {\n\t\t\t\tvar select = $document.createElement(\"select\")\n\n\t\t\t\to(select.value).equals(\"\")\n\t\t\t\to(select.selectedIndex).equals(-1)\n\t\t\t})\n\t\t\to(\"setting valid value works\", function() {\n\t\t\t\tvar select = $document.createElement(\"select\")\n\n\t\t\t\tvar option1 = $document.createElement(\"option\")\n\t\t\t\toption1.setAttribute(\"value\", \"a\")\n\t\t\t\tselect.appendChild(option1)\n\n\t\t\t\tvar option2 = $document.createElement(\"option\")\n\t\t\t\toption2.setAttribute(\"value\", \"b\")\n\t\t\t\tselect.appendChild(option2)\n\n\t\t\t\tvar option3 = $document.createElement(\"option\")\n\t\t\t\toption3.setAttribute(\"value\", \"\")\n\t\t\t\tselect.appendChild(option3)\n\n\t\t\t\tvar option4 = $document.createElement(\"option\")\n\t\t\t\toption4.setAttribute(\"value\", \"null\")\n\t\t\t\tselect.appendChild(option4)\n\n\t\t\t\tselect.value = \"b\"\n\n\t\t\t\to(select.value).equals(\"b\")\n\t\t\t\to(select.selectedIndex).equals(1)\n\n\t\t\t\tselect.value = \"\"\n\n\t\t\t\to(select.value).equals(\"\")\n\t\t\t\to(select.selectedIndex).equals(2)\n\n\t\t\t\tselect.value = \"null\"\n\n\t\t\t\to(select.value).equals(\"null\")\n\t\t\t\to(select.selectedIndex).equals(3)\n\n\t\t\t\tselect.value = null\n\n\t\t\t\to(select.value).equals(\"\")\n\t\t\t\to(select.selectedIndex).equals(-1)\n\t\t\t})\n\t\t\to(\"setting valid value works with type conversion\", function() {\n\t\t\t\tvar select = $document.createElement(\"select\")\n\n\t\t\t\tvar option1 = $document.createElement(\"option\")\n\t\t\t\toption1.setAttribute(\"value\", \"0\")\n\t\t\t\tselect.appendChild(option1)\n\n\t\t\t\tvar option2 = $document.createElement(\"option\")\n\t\t\t\toption2.setAttribute(\"value\", \"undefined\")\n\t\t\t\tselect.appendChild(option2)\n\n\t\t\t\tvar option3 = $document.createElement(\"option\")\n\t\t\t\toption3.setAttribute(\"value\", \"\")\n\t\t\t\tselect.appendChild(option3)\n\n\t\t\t\tselect.value = 0\n\n\t\t\t\to(select.value).equals(\"0\")\n\t\t\t\to(select.selectedIndex).equals(0)\n\n\t\t\t\tselect.value = undefined\n\n\t\t\t\to(select.value).equals(\"undefined\")\n\t\t\t\to(select.selectedIndex).equals(1)\n\n\t\t\t\tif (typeof Symbol === \"function\") {\n\t\t\t\t\tvar threw = false\n\t\t\t\t\ttry {\n\t\t\t\t\t\tselect.value = Symbol(\"x\")\n\t\t\t\t\t} catch (e) {\n\t\t\t\t\t\tthrew = true\n\t\t\t\t\t}\n\t\t\t\t\to(threw).equals(true)\n\t\t\t\t\to(select.value).equals(\"undefined\")\n\t\t\t\t\to(select.selectedIndex).equals(1)\n\t\t\t\t}\n\t\t\t})\n\t\t\to(\"option.value = null is converted to 'null'\", function() {\n\t\t\t\tvar option = $document.createElement(\"option\")\n\t\t\t\toption.value = null\n\n\t\t\t\to(option.value).equals(\"null\")\n\t\t\t})\n\t\t\to(\"setting valid value works with optgroup\", function() {\n\t\t\t\tvar select = $document.createElement(\"select\")\n\n\t\t\t\tvar option1 = $document.createElement(\"option\")\n\t\t\t\toption1.setAttribute(\"value\", \"a\")\n\n\t\t\t\tvar option2 = $document.createElement(\"option\")\n\t\t\t\toption2.setAttribute(\"value\", \"b\")\n\n\t\t\t\tvar option3 = $document.createElement(\"option\")\n\t\t\t\toption3.setAttribute(\"value\", \"c\")\n\n\t\t\t\tvar optgroup = $document.createElement(\"optgroup\")\n\t\t\t\toptgroup.appendChild(option1)\n\t\t\t\toptgroup.appendChild(option2)\n\t\t\t\tselect.appendChild(optgroup)\n\t\t\t\tselect.appendChild(option3)\n\n\t\t\t\tselect.value = \"b\"\n\n\t\t\t\to(select.value).equals(\"b\")\n\t\t\t\to(select.selectedIndex).equals(1)\n\t\t\t})\n\t\t\to(\"setting valid selectedIndex works\", function() {\n\t\t\t\tvar select = $document.createElement(\"select\")\n\n\t\t\t\tvar option1 = $document.createElement(\"option\")\n\t\t\t\toption1.setAttribute(\"value\", \"a\")\n\t\t\t\tselect.appendChild(option1)\n\n\t\t\t\tvar option2 = $document.createElement(\"option\")\n\t\t\t\toption2.setAttribute(\"value\", \"b\")\n\t\t\t\tselect.appendChild(option2)\n\n\t\t\t\tselect.selectedIndex = 1\n\n\t\t\t\to(select.value).equals(\"b\")\n\t\t\t\to(select.selectedIndex).equals(1)\n\t\t\t})\n\t\t\to(\"setting option[selected] works\", function() {\n\t\t\t\tvar select = $document.createElement(\"select\")\n\n\t\t\t\tvar option1 = $document.createElement(\"option\")\n\t\t\t\toption1.setAttribute(\"value\", \"a\")\n\t\t\t\tselect.appendChild(option1)\n\n\t\t\t\tvar option2 = $document.createElement(\"option\")\n\t\t\t\toption2.setAttribute(\"value\", \"b\")\n\t\t\t\tselect.appendChild(option2)\n\n\t\t\t\tselect.childNodes[1].selected = true\n\n\t\t\t\to(select.value).equals(\"b\")\n\t\t\t\to(select.selectedIndex).equals(1)\n\t\t\t})\n\t\t\to(\"unsetting option[selected] works\", function() {\n\t\t\t\tvar select = $document.createElement(\"select\")\n\n\t\t\t\tvar option1 = $document.createElement(\"option\")\n\t\t\t\toption1.setAttribute(\"value\", \"a\")\n\t\t\t\tselect.appendChild(option1)\n\n\t\t\t\tvar option2 = $document.createElement(\"option\")\n\t\t\t\toption2.setAttribute(\"value\", \"b\")\n\t\t\t\tselect.appendChild(option2)\n\n\t\t\t\tselect.childNodes[1].selected = true\n\t\t\t\tselect.childNodes[1].selected = false\n\n\t\t\t\to(select.value).equals(\"a\")\n\t\t\t\to(select.selectedIndex).equals(0)\n\t\t\t})\n\t\t\to(\"setting invalid value yields a selectedIndex of -1 and value of empty string\", function() {\n\t\t\t\tvar select = $document.createElement(\"select\")\n\n\t\t\t\tvar option1 = $document.createElement(\"option\")\n\t\t\t\toption1.setAttribute(\"value\", \"a\")\n\t\t\t\tselect.appendChild(option1)\n\n\t\t\t\tvar option2 = $document.createElement(\"option\")\n\t\t\t\toption2.setAttribute(\"value\", \"b\")\n\t\t\t\tselect.appendChild(option2)\n\n\t\t\t\tselect.value = \"c\"\n\n\t\t\t\to(select.value).equals(\"\")\n\t\t\t\to(select.selectedIndex).equals(-1)\n\t\t\t})\n\t\t\to(\"setting invalid selectedIndex yields a selectedIndex of -1 and value of empty string\", function() {\n\t\t\t\tvar select = $document.createElement(\"select\")\n\n\t\t\t\tvar option1 = $document.createElement(\"option\")\n\t\t\t\toption1.setAttribute(\"value\", \"a\")\n\t\t\t\tselect.appendChild(option1)\n\n\t\t\t\tvar option2 = $document.createElement(\"option\")\n\t\t\t\toption2.setAttribute(\"value\", \"b\")\n\t\t\t\tselect.appendChild(option2)\n\n\t\t\t\tselect.selectedIndex = -2\n\n\t\t\t\to(select.value).equals(\"\")\n\t\t\t\to(select.selectedIndex).equals(-1)\n\t\t\t})\n\t\t\to(\"setting invalid value yields a selectedIndex of -1 and value of empty string even when there's an option whose value is empty string\", function() {\n\t\t\t\tvar select = $document.createElement(\"select\")\n\n\t\t\t\tvar option1 = $document.createElement(\"option\")\n\t\t\t\toption1.setAttribute(\"value\", \"a\")\n\t\t\t\tselect.appendChild(option1)\n\n\t\t\t\tvar option2 = $document.createElement(\"option\")\n\t\t\t\toption2.setAttribute(\"value\", \"\")\n\t\t\t\tselect.appendChild(option2)\n\n\t\t\t\tselect.value = \"c\"\n\n\t\t\t\to(select.value).equals(\"\")\n\t\t\t\to(select.selectedIndex).equals(-1)\n\t\t\t})\n\t\t\to(\"setting invalid selectedIndex yields a selectedIndex of -1 and value of empty string even when there's an option whose value is empty string\", function() {\n\t\t\t\tvar select = $document.createElement(\"select\")\n\n\t\t\t\tvar option1 = $document.createElement(\"option\")\n\t\t\t\toption1.setAttribute(\"value\", \"a\")\n\t\t\t\tselect.appendChild(option1)\n\n\t\t\t\tvar option2 = $document.createElement(\"option\")\n\t\t\t\toption2.setAttribute(\"value\", \"\")\n\t\t\t\tselect.appendChild(option2)\n\n\t\t\t\tselect.selectedIndex = -2\n\n\t\t\t\to(select.value).equals(\"\")\n\t\t\t\to(select.selectedIndex).equals(-1)\n\t\t\t})\n\t\t})\n\t\to.spec(\"canvas width and height\", function() {\n\t\t\to(\"setting property works\", function() {\n\t\t\t\tvar canvas = $document.createElement(\"canvas\")\n\n\t\t\t\tcanvas.width = 100\n\t\t\t\to(canvas.attributes[\"width\"].value).equals(\"100\")\n\t\t\t\to(canvas.width).equals(100)\n\n\t\t\t\tcanvas.height = 100\n\t\t\t\to(canvas.attributes[\"height\"].value).equals(\"100\")\n\t\t\t\to(canvas.height).equals(100)\n\t\t\t})\n\t\t\to(\"setting string casts to number\", function() {\n\t\t\t\tvar canvas = $document.createElement(\"canvas\")\n\n\t\t\t\tcanvas.width = \"100\"\n\t\t\t\to(canvas.attributes[\"width\"].value).equals(\"100\")\n\t\t\t\to(canvas.width).equals(100)\n\n\t\t\t\tcanvas.height = \"100\"\n\t\t\t\to(canvas.attributes[\"height\"].value).equals(\"100\")\n\t\t\t\to(canvas.height).equals(100)\n\t\t\t})\n\t\t\to(\"setting float casts to int\", function() {\n\t\t\t\tvar canvas = $document.createElement(\"canvas\")\n\n\t\t\t\tcanvas.width = 1.2\n\t\t\t\to(canvas.attributes[\"width\"].value).equals(\"1\")\n\t\t\t\to(canvas.width).equals(1)\n\n\t\t\t\tcanvas.height = 1.2\n\t\t\t\to(canvas.attributes[\"height\"].value).equals(\"1\")\n\t\t\t\to(canvas.height).equals(1)\n\t\t\t})\n\t\t\to(\"setting percentage fails\", function() {\n\t\t\t\tvar canvas = $document.createElement(\"canvas\")\n\n\t\t\t\tcanvas.width = \"100%\"\n\t\t\t\to(canvas.attributes[\"width\"].value).equals(\"0\")\n\t\t\t\to(canvas.width).equals(0)\n\n\t\t\t\tcanvas.height = \"100%\"\n\t\t\t\to(canvas.attributes[\"height\"].value).equals(\"0\")\n\t\t\t\to(canvas.height).equals(0)\n\t\t\t})\n\t\t\to(\"setting attribute works\", function() {\n\t\t\t\tvar canvas = $document.createElement(\"canvas\")\n\n\t\t\t\tcanvas.setAttribute(\"width\", \"100%\")\n\t\t\t\to(canvas.attributes[\"width\"].value).equals(\"100%\")\n\t\t\t\to(canvas.width).equals(100)\n\n\t\t\t\tcanvas.setAttribute(\"height\", \"100%\")\n\t\t\t\to(canvas.attributes[\"height\"].value).equals(\"100%\")\n\t\t\t\to(canvas.height).equals(100)\n\t\t\t})\n\t\t})\n\t})\n\to.spec(\"className\", function() {\n\t\to(\"works\", function() {\n\t\t\tvar el = $document.createElement(\"div\")\n\t\t\tel.className = \"a\"\n\n\t\t\to(el.className).equals(\"a\")\n\t\t\to(el.attributes[\"class\"].value).equals(\"a\")\n\t\t})\n\t\to(\"setter throws in svg\", function(done) {\n\t\t\tvar el = $document.createElementNS(\"http://www.w3.org/2000/svg\", \"svg\")\n\t\t\ttry {\n\t\t\t\tel.className = \"a\"\n\t\t\t}\n\t\t\tcatch (e) {\n\t\t\t\tdone()\n\t\t\t}\n\t\t})\n\t})\n\to.spec(\"spies\", function() {\n\t\tvar $window\n\t\to.beforeEach(function() {\n\t\t\t$window = domMock({spy: o.spy})\n\t\t})\n\t\to(\"basics\", function() {\n\t\t\to(typeof $window.__getSpies).equals(\"function\")\n\t\t\to(\"__getSpies\" in domMock()).equals(false)\n\t\t})\n\t\to(\"input elements have spies on value and type setters\", function() {\n\t\t\tvar input = $window.document.createElement(\"input\")\n\n\t\t\tvar spies = $window.__getSpies(input)\n\n\t\t\to(typeof spies).equals(\"object\")\n\t\t\to(spies).notEquals(null)\n\t\t\to(typeof spies.valueSetter).equals(\"function\")\n\t\t\to(typeof spies.typeSetter).equals(\"function\")\n\t\t\to(spies.valueSetter.callCount).equals(0)\n\t\t\to(spies.typeSetter.callCount).equals(0)\n\n\t\t\tinput.value = \"aaa\"\n\t\t\tinput.type = \"radio\"\n\n\t\t\to(spies.valueSetter.callCount).equals(1)\n\t\t\to(spies.valueSetter.this).equals(input)\n\t\t\to(spies.valueSetter.args[0]).equals(\"aaa\")\n\n\t\t\to(spies.typeSetter.callCount).equals(1)\n\t\t\to(spies.typeSetter.this).equals(input)\n\t\t\to(spies.typeSetter.args[0]).equals(\"radio\")\n\t\t})\n\t\to(\"select elements have spies on value setters\", function() {\n\t\t\tvar select = $window.document.createElement(\"select\")\n\n\t\t\tvar spies = $window.__getSpies(select)\n\n\t\t\to(typeof spies).equals(\"object\")\n\t\t\to(spies).notEquals(null)\n\t\t\to(typeof spies.valueSetter).equals(\"function\")\n\t\t\to(spies.valueSetter.callCount).equals(0)\n\n\t\t\tselect.value = \"aaa\"\n\n\t\t\to(spies.valueSetter.callCount).equals(1)\n\t\t\to(spies.valueSetter.this).equals(select)\n\t\t\to(spies.valueSetter.args[0]).equals(\"aaa\")\n\t\t})\n\t\to(\"option elements have spies on value setters\", function() {\n\t\t\tvar option = $window.document.createElement(\"option\")\n\n\t\t\tvar spies = $window.__getSpies(option)\n\n\t\t\to(typeof spies).equals(\"object\")\n\t\t\to(spies).notEquals(null)\n\t\t\to(typeof spies.valueSetter).equals(\"function\")\n\t\t\to(spies.valueSetter.callCount).equals(0)\n\n\t\t\toption.value = \"aaa\"\n\n\t\t\to(spies.valueSetter.callCount).equals(1)\n\t\t\to(spies.valueSetter.this).equals(option)\n\t\t\to(spies.valueSetter.args[0]).equals(\"aaa\")\n\t\t})\n\t\to(\"textarea elements have spies on value setters\", function() {\n\t\t\tvar textarea = $window.document.createElement(\"textarea\")\n\n\t\t\tvar spies = $window.__getSpies(textarea)\n\n\t\t\to(typeof spies).equals(\"object\")\n\t\t\to(spies).notEquals(null)\n\t\t\to(typeof spies.valueSetter).equals(\"function\")\n\t\t\to(spies.valueSetter.callCount).equals(0)\n\n\t\t\ttextarea.value = \"aaa\"\n\n\t\t\to(spies.valueSetter.callCount).equals(1)\n\t\t\to(spies.valueSetter.this).equals(textarea)\n\t\t\to(spies.valueSetter.args[0]).equals(\"aaa\")\n\t\t})\n\t})\n\to.spec(\"DOMParser for SVG\", function(){\n\t\tvar $DOMParser\n\t\to.beforeEach(function() {\n\t\t\t$DOMParser = $window.DOMParser\n\t\t})\n\t\to(\"basics\", function(){\n\t\t\to(typeof $DOMParser).equals(\"function\")\n\n\t\t\tvar parser = new $DOMParser()\n\n\t\t\to(parser instanceof $DOMParser).equals(true)\n\t\t\to(typeof parser.parseFromString).equals(\"function\")\n\t\t})\n\t\to(\"empty document\", function() {\n\t\t\tvar parser = new $DOMParser()\n\t\t\tvar doc = parser.parseFromString(\n\t\t\t\t\"<svg xmlns=\\\"http://www.w3.org/2000/svg\\\"></svg>\",\n\t\t\t\t\"image/svg+xml\"\n\t\t\t)\n\n\t\t\to(typeof doc.documentElement).notEquals(undefined)\n\t\t\to(doc.documentElement.nodeName).equals(\"svg\")\n\t\t\to(doc.documentElement.namespaceURI).equals(\"http://www.w3.org/2000/svg\")\n\t\t\to(doc.documentElement.childNodes.length).equals(0)\n\t\t})\n\t\to(\"text elements\", function() {\n\t\t\tvar parser = new $DOMParser()\n\t\t\tvar doc = parser.parseFromString(\n\t\t\t\t\"<svg xmlns=\\\"http://www.w3.org/2000/svg\\\">\"\n\t\t\t\t\t+ \"<text>hello</text>\"\n\t\t\t\t\t+ \"<text> </text>\"\n\t\t\t\t\t+ \"<text>world</text>\"\n\t\t\t\t+ \"</svg>\",\n\t\t\t\t\"image/svg+xml\"\n\t\t\t)\n\n\t\t\to(doc.documentElement.nodeName).equals(\"svg\")\n\t\t\to(doc.documentElement.namespaceURI).equals(\"http://www.w3.org/2000/svg\")\n\n\t\t\tvar nodes = doc.documentElement.childNodes\n\t\t\to(nodes.length).equals(3)\n\t\t\to(nodes[0].nodeName).equals(\"text\")\n\t\t\to(nodes[0].namespaceURI).equals(\"http://www.w3.org/2000/svg\")\n\t\t\to(nodes[0].childNodes.length).equals(1)\n\t\t\to(nodes[0].childNodes[0].nodeName).equals(\"#text\")\n\t\t\to(nodes[0].childNodes[0].nodeValue).equals(\"hello\")\n\t\t\to(nodes[1].nodeName).equals(\"text\")\n\t\t\to(nodes[1].namespaceURI).equals(\"http://www.w3.org/2000/svg\")\n\t\t\to(nodes[1].childNodes.length).equals(1)\n\t\t\to(nodes[1].childNodes[0].nodeName).equals(\"#text\")\n\t\t\to(nodes[1].childNodes[0].nodeValue).equals(\" \")\n\t\t\to(nodes[2].nodeName).equals(\"text\")\n\t\t\to(nodes[2].namespaceURI).equals(\"http://www.w3.org/2000/svg\")\n\t\t\to(nodes[2].childNodes.length).equals(1)\n\t\t\to(nodes[2].childNodes[0].nodeName).equals(\"#text\")\n\t\t\to(nodes[2].childNodes[0].nodeValue).equals(\"world\")\n\t\t})\n\t})\n})\n"
  },
  {
    "path": "test-utils/tests/test-parseURL.js",
    "content": "\"use strict\"\n\nvar o = require(\"ospec\")\nvar parseURL = require(\"../../test-utils/parseURL\")\n\no.spec(\"parseURL\", function() {\n\tvar root = {protocol: \"http:\", hostname: \"localhost\", port: \"\", pathname: \"/\"}\n\n\to.spec(\"full URL\", function() {\n\t\to(\"parses full http URL\", function() {\n\t\t\tvar data = parseURL(\"http://www.google.com:80/test?a=b#c\")\n\t\t\to(data.protocol).equals(\"http:\")\n\t\t\to(data.hostname).equals(\"www.google.com\")\n\t\t\to(data.port).equals(\"80\")\n\t\t\to(data.pathname).equals(\"/test\")\n\t\t\to(data.search).equals(\"?a=b\")\n\t\t\to(data.hash).equals(\"#c\")\n\t\t})\n\t\to(\"parses full websocket URL\", function() {\n\t\t\tvar data = parseURL(\"ws://www.google.com:80/test?a=b#c\")\n\t\t\to(data.protocol).equals(\"ws:\")\n\t\t\to(data.hostname).equals(\"www.google.com\")\n\t\t\to(data.port).equals(\"80\")\n\t\t\to(data.pathname).equals(\"/test\")\n\t\t\to(data.search).equals(\"?a=b\")\n\t\t\to(data.hash).equals(\"#c\")\n\t\t})\n\t\to(\"parses full URL omitting optionals\", function() {\n\t\t\tvar data = parseURL(\"http://www.google.com\")\n\t\t\to(data.protocol).equals(\"http:\")\n\t\t\to(data.hostname).equals(\"www.google.com\")\n\t\t\to(data.port).equals(\"\")\n\t\t\to(data.pathname).equals(\"/\")\n\t\t\to(data.search).equals(\"\")\n\t\t\to(data.hash).equals(\"\")\n\t\t})\n\t})\n\to.spec(\"absolute path\", function() {\n\t\to(\"parses absolute path\", function() {\n\t\t\tvar data = parseURL(\"/test?a=b#c\", root)\n\t\t\to(data.protocol).equals(root.protocol)\n\t\t\to(data.hostname).equals(root.hostname)\n\t\t\to(data.port).equals(root.port)\n\t\t\to(data.pathname).equals(\"/test\")\n\t\t\to(data.search).equals(\"?a=b\")\n\t\t\to(data.hash).equals(\"#c\")\n\t\t})\n\t\to(\"parses absolute path omitting optionals\", function() {\n\t\t\tvar data = parseURL(\"/test?a=b#c\", root)\n\t\t\to(data.protocol).equals(root.protocol)\n\t\t\to(data.hostname).equals(root.hostname)\n\t\t\to(data.port).equals(root.port)\n\t\t\to(data.pathname).equals(\"/test\")\n\t\t\to(data.search).equals(\"?a=b\")\n\t\t\to(data.hash).equals(\"#c\")\n\t\t})\n\t})\n\to.spec(\"relative path\", function() {\n\t\to(\"parses relative URL\", function() {\n\t\t\tvar data = parseURL(\"test?a=b#c\", root)\n\t\t\to(data.protocol).equals(root.protocol)\n\t\t\to(data.hostname).equals(root.hostname)\n\t\t\to(data.port).equals(root.port)\n\t\t\to(data.pathname).equals(\"/test\")\n\t\t\to(data.search).equals(\"?a=b\")\n\t\t\to(data.hash).equals(\"#c\")\n\t\t})\n\t\to(\"parses relative URL omitting optionals\", function() {\n\t\t\tvar data = parseURL(\"test\", root)\n\t\t\to(data.protocol).equals(root.protocol)\n\t\t\to(data.hostname).equals(root.hostname)\n\t\t\to(data.port).equals(root.port)\n\t\t\to(data.pathname).equals(\"/test\")\n\t\t\to(data.search).equals(\"\")\n\t\t\to(data.hash).equals(\"\")\n\t\t})\n\t\to(\"parses relative URL with dot\", function() {\n\t\t\tvar data = parseURL(\"././test?a=b#c\", root)\n\t\t\to(data.protocol).equals(root.protocol)\n\t\t\to(data.hostname).equals(root.hostname)\n\t\t\to(data.port).equals(root.port)\n\t\t\to(data.pathname).equals(\"/test\")\n\t\t\to(data.search).equals(\"?a=b\")\n\t\t\to(data.hash).equals(\"#c\")\n\t\t})\n\t\to(\"parses relative URL with dotdot\", function() {\n\t\t\tvar data = parseURL(\"foo/bar/../../test?a=b#c\", root)\n\t\t\to(data.protocol).equals(root.protocol)\n\t\t\to(data.hostname).equals(root.hostname)\n\t\t\to(data.port).equals(root.port)\n\t\t\to(data.pathname).equals(\"/test\")\n\t\t\to(data.search).equals(\"?a=b\")\n\t\t\to(data.hash).equals(\"#c\")\n\t\t})\n\t\to(\"clamps invalid dotdot\", function() {\n\t\t\tvar data = parseURL(\"../../test?a=b#c\", root)\n\t\t\to(data.protocol).equals(root.protocol)\n\t\t\to(data.hostname).equals(root.hostname)\n\t\t\to(data.port).equals(root.port)\n\t\t\to(data.pathname).equals(\"/test\")\n\t\t\to(data.search).equals(\"?a=b\")\n\t\t\to(data.hash).equals(\"#c\")\n\t\t})\n\t\to(\"clamps invalid dotdot after dot\", function() {\n\t\t\tvar data = parseURL(\"./../../test?a=b#c\", root)\n\t\t\to(data.protocol).equals(root.protocol)\n\t\t\to(data.hostname).equals(root.hostname)\n\t\t\to(data.port).equals(root.port)\n\t\t\to(data.pathname).equals(\"/test\")\n\t\t\to(data.search).equals(\"?a=b\")\n\t\t\to(data.hash).equals(\"#c\")\n\t\t})\n\t\to(\"clamps invalid dotdot after valid path\", function() {\n\t\t\tvar data = parseURL(\"a/../../test?a=b#c\", root)\n\t\t\to(data.protocol).equals(root.protocol)\n\t\t\to(data.hostname).equals(root.hostname)\n\t\t\to(data.port).equals(root.port)\n\t\t\to(data.pathname).equals(\"/test\")\n\t\t\to(data.search).equals(\"?a=b\")\n\t\t\to(data.hash).equals(\"#c\")\n\t\t})\n\t})\n\to.spec(\"edge cases\", function() {\n\t\to(\"handles hash w/ question mark\", function() {\n\t\t\tvar data = parseURL(\"http://www.google.com/test#a?c\")\n\t\t\to(data.pathname).equals(\"/test\")\n\t\t\to(data.search).equals(\"\")\n\t\t\to(data.hash).equals(\"#a?c\")\n\t\t})\n\t\to(\"handles hash w/ slash\", function() {\n\t\t\tvar data = parseURL(\"http://www.google.com/test#a/c\")\n\t\t\to(data.pathname).equals(\"/test\")\n\t\t\to(data.search).equals(\"\")\n\t\t\to(data.hash).equals(\"#a/c\")\n\t\t})\n\t\to(\"handles hash w/ colon\", function() {\n\t\t\tvar data = parseURL(\"http://www.google.com/test#a:c\")\n\t\t\to(data.pathname).equals(\"/test\")\n\t\t\to(data.search).equals(\"\")\n\t\t\to(data.hash).equals(\"#a:c\")\n\t\t})\n\t\to(\"handles search w/ slash\", function() {\n\t\t\tvar data = parseURL(\"http://www.google.com/test?a/c\")\n\t\t\to(data.pathname).equals(\"/test\")\n\t\t\to(data.search).equals(\"?a/c\")\n\t\t\to(data.hash).equals(\"\")\n\t\t})\n\t\to(\"handles search w/ colon\", function() {\n\t\t\tvar data = parseURL(\"http://www.google.com/test?a:c\")\n\t\t\to(data.pathname).equals(\"/test\")\n\t\t\to(data.search).equals(\"?a:c\")\n\t\t\to(data.hash).equals(\"\")\n\t\t})\n\t\to(\"handles pathname w/ colon\", function() {\n\t\t\tvar data = parseURL(\"http://www.google.com/a:b\")\n\t\t\to(data.pathname).equals(\"/a:b\")\n\t\t})\n\t})\n})\n"
  },
  {
    "path": "test-utils/tests/test-pushStateMock.js",
    "content": "\"use strict\"\n\nvar o = require(\"ospec\")\nvar pushStateMock = require(\"../../test-utils/pushStateMock\")\nvar callAsync = require(\"../../test-utils/callAsync\")\no.spec(\"pushStateMock\", function() {\n\n\tvar $window\n\to.beforeEach(function() {\n\t\t$window = pushStateMock()\n\t})\n\n\to.spec(\"initial state\", function() {\n\t\to(\"has url on page load\", function() {\n\t\t\to($window.location.href).equals(\"http://localhost/\")\n\t\t})\n\t})\n\n\to.spec(\"set href\", function() {\n\t\to(\"changes url on location.href change\", function() {\n\t\t\tvar old = $window.location.href\n\t\t\t$window.location.href = \"http://localhost/a\"\n\n\t\t\to(old).equals(\"http://localhost/\")\n\t\t\to($window.location.href).equals(\"http://localhost/a\")\n\t\t})\n\t\to(\"changes url on relative location.href change\", function() {\n\t\t\tvar old = $window.location.href\n\t\t\t$window.location.href = \"a\"\n\n\t\t\to(old).equals(\"http://localhost/\")\n\t\t\to($window.location.href).equals(\"http://localhost/a\")\n\t\t\to($window.location.pathname).equals(\"/a\")\n\t\t})\n\t\to(\"changes url on dotdot location.href change\", function() {\n\t\t\t$window.location.href = \"a\"\n\t\t\tvar old = $window.location.href\n\t\t\t$window.location.href = \"..\"\n\n\t\t\to(old).equals(\"http://localhost/a\")\n\t\t\to($window.location.href).equals(\"http://localhost/\")\n\t\t\to($window.location.pathname).equals(\"/\")\n\t\t})\n\t\to(\"changes url on deep dotdot location.href change\", function() {\n\t\t\t$window.location.href = \"a/b/c\"\n\t\t\tvar old = $window.location.href\n\t\t\t$window.location.href = \"..\"\n\n\t\t\to(old).equals(\"http://localhost/a/b/c\")\n\t\t\to($window.location.href).equals(\"http://localhost/a\")\n\t\t\to($window.location.pathname).equals(\"/a\")\n\t\t})\n\t\to(\"does not change url on dotdot location.href change from root\", function() {\n\t\t\tvar old = $window.location.href\n\t\t\t$window.location.href = \"..\"\n\n\t\t\to(old).equals(\"http://localhost/\")\n\t\t\to($window.location.href).equals(\"http://localhost/\")\n\t\t\to($window.location.pathname).equals(\"/\")\n\t\t})\n\t\to(\"changes url on dot relative location.href change\", function() {\n\t\t\tvar old = $window.location.href\n\t\t\t$window.location.href = \"a\"\n\t\t\t$window.location.href = \"./b\"\n\n\t\t\to(old).equals(\"http://localhost/\")\n\t\t\to($window.location.href).equals(\"http://localhost/b\")\n\t\t\to($window.location.pathname).equals(\"/b\")\n\t\t})\n\t\to(\"does not change url on dot location.href change\", function() {\n\t\t\tvar old = $window.location.href\n\t\t\t$window.location.href = \"a\"\n\t\t\t$window.location.href = \".\"\n\n\t\t\to(old).equals(\"http://localhost/\")\n\t\t\to($window.location.href).equals(\"http://localhost/a\")\n\t\t\to($window.location.pathname).equals(\"/a\")\n\t\t})\n\t\to(\"changes url on hash-only location.href change\", function() {\n\t\t\tvar old = $window.location.href\n\t\t\t$window.location.href = \"#a\"\n\n\t\t\to(old).equals(\"http://localhost/\")\n\t\t\to($window.location.href).equals(\"http://localhost/#a\")\n\t\t\to($window.location.hash).equals(\"#a\")\n\t\t})\n\t\to(\"changes url on search-only location.href change\", function() {\n\t\t\tvar old = $window.location.href\n\t\t\t$window.location.href = \"?a\"\n\n\t\t\to(old).equals(\"http://localhost/\")\n\t\t\to($window.location.href).equals(\"http://localhost/?a\")\n\t\t\to($window.location.search).equals(\"?a\")\n\t\t})\n\t\to(\"changes hash on location.href change\", function() {\n\t\t\tvar old = $window.location.href\n\t\t\t$window.location.href = \"http://localhost/a#b\"\n\n\t\t\to(old).equals(\"http://localhost/\")\n\t\t\to($window.location.href).equals(\"http://localhost/a#b\")\n\t\t\to($window.location.hash).equals(\"#b\")\n\t\t})\n\t\to(\"changes search on location.href change\", function() {\n\t\t\tvar old = $window.location.href\n\t\t\t$window.location.href = \"http://localhost/a?b\"\n\n\t\t\to(old).equals(\"http://localhost/\")\n\t\t\to($window.location.href).equals(\"http://localhost/a?b\")\n\t\t\to($window.location.search).equals(\"?b\")\n\t\t})\n\t\to(\"changes search and hash on location.href change\", function() {\n\t\t\tvar old = $window.location.href\n\t\t\t$window.location.href = \"http://localhost/a?b#c\"\n\n\t\t\to(old).equals(\"http://localhost/\")\n\t\t\to($window.location.href).equals(\"http://localhost/a?b#c\")\n\t\t\to($window.location.search).equals(\"?b\")\n\t\t\to($window.location.hash).equals(\"#c\")\n\t\t})\n\t\to(\"handles search with search and hash\", function() {\n\t\t\tvar old = $window.location.href\n\t\t\t$window.location.href = \"http://localhost/a?b?c#d\"\n\n\t\t\to(old).equals(\"http://localhost/\")\n\t\t\to($window.location.href).equals(\"http://localhost/a?b?c#d\")\n\t\t\to($window.location.search).equals(\"?b?c\")\n\t\t\to($window.location.hash).equals(\"#d\")\n\t\t})\n\t\to(\"handles hash with search and hash\", function() {\n\t\t\tvar old = $window.location.href\n\t\t\t$window.location.href = \"http://localhost/a#b?c#d\"\n\n\t\t\to(old).equals(\"http://localhost/\")\n\t\t\to($window.location.href).equals(\"http://localhost/a#b?c#d\")\n\t\t\to($window.location.search).equals(\"\")\n\t\t\to($window.location.hash).equals(\"#b?c#d\")\n\t\t})\n\t})\n\to.spec(\"set search\", function() {\n\t\to(\"changes url on location.search change\", function() {\n\t\t\tvar old = $window.location.href\n\t\t\t$window.location.search = \"?b\"\n\n\t\t\to(old).equals(\"http://localhost/\")\n\t\t\to($window.location.href).equals(\"http://localhost/?b\")\n\t\t\to($window.location.search).equals(\"?b\")\n\t\t})\n\t})\n\to.spec(\"set hash\", function() {\n\t\to(\"changes url on location.hash change\", function() {\n\t\t\tvar old = $window.location.href\n\t\t\t$window.location.hash = \"#b\"\n\n\t\t\to(old).equals(\"http://localhost/\")\n\t\t\to($window.location.href).equals(\"http://localhost/#b\")\n\t\t\to($window.location.hash).equals(\"#b\")\n\t\t})\n\t})\n\to.spec(\"set pathname\", function() {\n\t\to(\"changes url on location.pathname change\", function() {\n\t\t\tvar old = $window.location.href\n\t\t\t$window.location.pathname = \"/a\"\n\n\t\t\to(old).equals(\"http://localhost/\")\n\t\t\to($window.location.href).equals(\"http://localhost/a\")\n\t\t\to($window.location.pathname).equals(\"/a\")\n\t\t})\n\t})\n\to.spec(\"set protocol\", function() {\n\t\to(\"setting protocol throws\", function(done) {\n\t\t\ttry {\n\t\t\t\t$window.location.protocol = \"https://\"\n\t\t\t}\n\t\t\tcatch (e) {\n\t\t\t\treturn done()\n\t\t\t}\n\t\t\tthrow new Error(\"Expected an error\")\n\t\t})\n\t})\n\to.spec(\"set port\", function() {\n\t\to(\"setting origin changes href\", function() {\n\t\t\tvar old = $window.location.href\n\t\t\t$window.location.port = \"81\"\n\n\t\t\to(old).equals(\"http://localhost/\")\n\t\t\to($window.location.port).equals(\"81\")\n\t\t\to($window.location.href).equals(\"http://localhost:81/\")\n\t\t})\n\t})\n\to.spec(\"set hostname\", function() {\n\t\to(\"setting hostname changes href\", function() {\n\t\t\tvar old = $window.location.href\n\t\t\t$window.location.hostname = \"127.0.0.1\"\n\n\t\t\to(old).equals(\"http://localhost/\")\n\t\t\to($window.location.hostname).equals(\"127.0.0.1\")\n\t\t\to($window.location.href).equals(\"http://127.0.0.1/\")\n\t\t})\n\t})\n\to.spec(\"set origin\", function() {\n\t\to(\"setting origin is ignored\", function() {\n\t\t\tvar old = $window.location.href\n\t\t\t$window.location.origin = \"http://127.0.0.1\"\n\n\t\t\to(old).equals(\"http://localhost/\")\n\t\t\to($window.location.origin).equals(\"http://localhost\")\n\t\t})\n\t})\n\to.spec(\"set host\", function() {\n\t\to(\"setting host is ignored\", function() {\n\t\t\tvar old = $window.location.href\n\t\t\t$window.location.host = \"http://127.0.0.1\"\n\n\t\t\to(old).equals(\"http://localhost/\")\n\t\t\to($window.location.host).equals(\"localhost\")\n\t\t})\n\t})\n\to.spec(\"pushState\", function() {\n\t\to(\"changes url on pushstate\", function() {\n\t\t\tvar old = $window.location.href\n\t\t\t$window.history.pushState(null, null, \"http://localhost/a\")\n\n\t\t\to(old).equals(\"http://localhost/\")\n\t\t\to($window.location.href).equals(\"http://localhost/a\")\n\t\t})\n\t\to(\"changes search on pushstate\", function() {\n\t\t\tvar old = $window.location.href\n\t\t\t$window.history.pushState(null, null, \"http://localhost/?a\")\n\n\t\t\to(old).equals(\"http://localhost/\")\n\t\t\to($window.location.href).equals(\"http://localhost/?a\")\n\t\t\to($window.location.search).equals(\"?a\")\n\t\t})\n\t\to(\"changes search on relative pushstate\", function() {\n\t\t\tvar old = $window.location.href\n\t\t\t$window.history.pushState(null, null, \"?a\")\n\n\t\t\to(old).equals(\"http://localhost/\")\n\t\t\to($window.location.href).equals(\"http://localhost/?a\")\n\t\t\to($window.location.search).equals(\"?a\")\n\t\t})\n\t\to(\"changes hash on pushstate\", function() {\n\t\t\tvar old = $window.location.href\n\t\t\t$window.history.pushState(null, null, \"http://localhost/#a\")\n\n\t\t\to(old).equals(\"http://localhost/\")\n\t\t\to($window.location.href).equals(\"http://localhost/#a\")\n\t\t\to($window.location.hash).equals(\"#a\")\n\t\t})\n\t\to(\"changes hash on relative pushstate\", function() {\n\t\t\tvar old = $window.location.href\n\t\t\t$window.history.pushState(null, null, \"#a\")\n\n\t\t\to(old).equals(\"http://localhost/\")\n\t\t\to($window.location.href).equals(\"http://localhost/#a\")\n\t\t\to($window.location.hash).equals(\"#a\")\n\t\t})\n\t})\n\to.spec(\"onpopstate\", function() {\n\t\to(\"history.back() without history does not trigger onpopstate\", function() {\n\t\t\t$window.onpopstate = o.spy()\n\t\t\t$window.history.back()\n\n\t\t\to($window.onpopstate.callCount).equals(0)\n\t\t})\n\t\to(\"history.back() after pushstate triggers onpopstate\", function() {\n\t\t\t$window.onpopstate = o.spy()\n\t\t\t$window.history.pushState(null, null, \"http://localhost/a\")\n\t\t\t$window.history.back()\n\n\t\t\to($window.onpopstate.callCount).equals(1)\n\t\t\to($window.onpopstate.args[0].type).equals(\"popstate\")\n\t\t})\n\t\to(\"history.back() after relative pushstate triggers onpopstate\", function() {\n\t\t\t$window.onpopstate = o.spy()\n\t\t\t$window.history.pushState(null, null, \"a\")\n\t\t\t$window.history.back()\n\n\t\t\to($window.onpopstate.callCount).equals(1)\n\t\t})\n\t\to(\"history.back() after search pushstate triggers onpopstate\", function() {\n\t\t\t$window.onpopstate = o.spy()\n\t\t\t$window.history.pushState(null, null, \"http://localhost/?a\")\n\t\t\t$window.history.back()\n\n\t\t\to($window.onpopstate.callCount).equals(1)\n\t\t})\n\t\to(\"history.back() after relative search pushstate triggers onpopstate\", function() {\n\t\t\t$window.onpopstate = o.spy()\n\t\t\t$window.history.pushState(null, null, \"?a\")\n\t\t\t$window.history.back()\n\n\t\t\to($window.onpopstate.callCount).equals(1)\n\t\t})\n\t\to(\"history.back() after hash pushstate triggers onpopstate\", function() {\n\t\t\t$window.onpopstate = o.spy()\n\t\t\t$window.history.pushState(null, null, \"http://localhost/#a\")\n\t\t\t$window.history.back()\n\n\t\t\to($window.onpopstate.callCount).equals(1)\n\t\t})\n\t\to(\"history.back() after relative hash pushstate triggers onpopstate\", function() {\n\t\t\t$window.onpopstate = o.spy()\n\t\t\t$window.history.pushState(null, null, \"#a\")\n\t\t\t$window.history.back()\n\n\t\t\to($window.onpopstate.callCount).equals(1)\n\t\t})\n\t\to(\"history.back() after replacestate does not trigger onpopstate\", function() {\n\t\t\t$window.onpopstate = o.spy()\n\t\t\t$window.history.replaceState(null, null, \"http://localhost/a\")\n\t\t\t$window.history.back()\n\n\t\t\to($window.onpopstate.callCount).equals(0)\n\t\t})\n\t\to(\"history.back() after relative replacestate does not trigger onpopstate\", function() {\n\t\t\t$window.onpopstate = o.spy()\n\t\t\t$window.history.replaceState(null, null, \"a\")\n\t\t\t$window.history.back()\n\n\t\t\to($window.onpopstate.callCount).equals(0)\n\t\t})\n\t\to(\"history.back() after relative search replacestate does not trigger onpopstate\", function() {\n\t\t\t$window.onpopstate = o.spy()\n\t\t\t$window.history.replaceState(null, null, \"?a\")\n\t\t\t$window.history.back()\n\n\t\t\to($window.onpopstate.callCount).equals(0)\n\t\t})\n\t\to(\"history.back() after relative hash replacestate does not trigger onpopstate\", function() {\n\t\t\t$window.onpopstate = o.spy()\n\t\t\t$window.history.replaceState(null, null, \"#a\")\n\t\t\t$window.history.back()\n\n\t\t\to($window.onpopstate.callCount).equals(0)\n\t\t})\n\t\to(\"history.forward() after pushstate triggers onpopstate\", function() {\n\t\t\t$window.onpopstate = o.spy()\n\t\t\t$window.history.pushState(null, null, \"http://localhost/a\")\n\t\t\t$window.history.back()\n\t\t\t$window.history.forward()\n\n\t\t\to($window.onpopstate.callCount).equals(2)\n\t\t})\n\t\to(\"history.forward() after relative pushstate triggers onpopstate\", function() {\n\t\t\t$window.onpopstate = o.spy()\n\t\t\t$window.history.pushState(null, null, \"a\")\n\t\t\t$window.history.back()\n\t\t\t$window.history.forward()\n\n\t\t\to($window.onpopstate.callCount).equals(2)\n\t\t})\n\t\to(\"history.forward() after search pushstate triggers onpopstate\", function() {\n\t\t\t$window.onpopstate = o.spy()\n\t\t\t$window.history.pushState(null, null, \"http://localhost/?a\")\n\t\t\t$window.history.back()\n\t\t\t$window.history.forward()\n\n\t\t\to($window.onpopstate.callCount).equals(2)\n\t\t})\n\t\to(\"history.forward() after relative search pushstate triggers onpopstate\", function() {\n\t\t\t$window.onpopstate = o.spy()\n\t\t\t$window.history.pushState(null, null, \"?a\")\n\t\t\t$window.history.back()\n\t\t\t$window.history.forward()\n\n\t\t\to($window.onpopstate.callCount).equals(2)\n\t\t})\n\t\to(\"history.forward() after hash pushstate triggers onpopstate\", function() {\n\t\t\t$window.onpopstate = o.spy()\n\t\t\t$window.history.pushState(null, null, \"http://localhost/#a\")\n\t\t\t$window.history.back()\n\t\t\t$window.history.forward()\n\n\t\t\to($window.onpopstate.callCount).equals(2)\n\t\t})\n\t\to(\"history.forward() after relative hash pushstate triggers onpopstate\", function() {\n\t\t\t$window.onpopstate = o.spy()\n\t\t\t$window.history.pushState(null, null, \"#a\")\n\t\t\t$window.history.back()\n\t\t\t$window.history.forward()\n\n\t\t\to($window.onpopstate.callCount).equals(2)\n\t\t})\n\t\to(\"history.forward() without history does not trigger onpopstate\", function() {\n\t\t\t$window.onpopstate = o.spy()\n\t\t\t$window.history.forward()\n\n\t\t\to($window.onpopstate.callCount).equals(0)\n\t\t})\n\t\to(\"history navigation without history does not trigger onpopstate\", function() {\n\t\t\t$window.onpopstate = o.spy()\n\t\t\t$window.history.back()\n\t\t\t$window.history.forward()\n\n\t\t\to($window.onpopstate.callCount).equals(0)\n\t\t})\n\t\to(\"reverse history navigation without history does not trigger onpopstate\", function() {\n\t\t\t$window.onpopstate = o.spy()\n\t\t\t$window.history.forward()\n\t\t\t$window.history.back()\n\n\t\t\to($window.onpopstate.callCount).equals(0)\n\t\t})\n\t\to(\"onpopstate has correct url during call\", function(done) {\n\t\t\t$window.location.href = \"a\"\n\t\t\t$window.onpopstate = function() {\n\t\t\t\to($window.location.href).equals(\"http://localhost/a\")\n\t\t\t\tdone()\n\t\t\t}\n\t\t\t$window.history.pushState(null, null, \"b\")\n\t\t\t$window.history.back()\n\t\t})\n\t\to(\"replaceState does not break forward history\", function() {\n\t\t\t$window.onpopstate = o.spy()\n\n\t\t\t$window.history.pushState(null, null, \"b\")\n\t\t\t$window.history.back()\n\n\t\t\to($window.onpopstate.callCount).equals(1)\n\t\t\to($window.location.href).equals(\"http://localhost/\")\n\n\t\t\t$window.history.replaceState(null, null, \"a\")\n\n\t\t\to($window.location.href).equals(\"http://localhost/a\")\n\n\t\t\t$window.history.forward()\n\n\t\t\to($window.onpopstate.callCount).equals(2)\n\t\t\to($window.location.href).equals(\"http://localhost/b\")\n\t\t})\n\t\to(\"pushstate retains state\", function() {\n\t\t\t$window.onpopstate = o.spy()\n\n\t\t\t$window.history.pushState({a: 1}, null, \"#a\")\n\t\t\t$window.history.pushState({b: 2}, null, \"#b\")\n\n\t\t\to($window.onpopstate.callCount).equals(0)\n\n\t\t\t$window.history.back()\n\n\t\t\to($window.onpopstate.callCount).equals(1)\n\t\t\to($window.onpopstate.args[0].type).equals(\"popstate\")\n\t\t\to($window.onpopstate.args[0].state).deepEquals({a: 1})\n\n\t\t\t$window.history.back()\n\n\t\t\to($window.onpopstate.callCount).equals(2)\n\t\t\to($window.onpopstate.args[0].type).equals(\"popstate\")\n\t\t\to($window.onpopstate.args[0].state).equals(null)\n\n\t\t\t$window.history.forward()\n\n\t\t\to($window.onpopstate.callCount).equals(3)\n\t\t\to($window.onpopstate.args[0].type).equals(\"popstate\")\n\t\t\to($window.onpopstate.args[0].state).deepEquals({a: 1})\n\n\t\t\t$window.history.forward()\n\n\t\t\to($window.onpopstate.callCount).equals(4)\n\t\t\to($window.onpopstate.args[0].type).equals(\"popstate\")\n\t\t\to($window.onpopstate.args[0].state).deepEquals({b: 2})\n\t\t})\n\t\to(\"replacestate replaces state\", function() {\n\t\t\t$window.onpopstate = o.spy(pop)\n\n\t\t\t$window.history.replaceState({a: 1}, null, \"a\")\n\n\t\t\to($window.history.state).deepEquals({a: 1})\n\n\t\t\t$window.history.pushState(null, null, \"a\")\n\t\t\t$window.history.back()\n\n\t\t\tfunction pop(e) {\n\t\t\t\to(e.state).deepEquals({a: 1})\n\t\t\t\to($window.history.state).deepEquals({a: 1})\n\t\t\t}\n\t\t})\n\t})\n\to.spec(\"onhashchance\", function() {\n\t\to(\"onhashchange triggers on location.href change\", function(done) {\n\t\t\t$window.onhashchange = o.spy()\n\t\t\t$window.location.href = \"http://localhost/#a\"\n\n\t\t\tcallAsync(function(){\n\t\t\t\to($window.onhashchange.callCount).equals(1)\n\t\t\t\to($window.onhashchange.args[0].type).equals(\"hashchange\")\n\t\t\t\tdone()\n\t\t\t})\n\t\t})\n\t\to(\"onhashchange triggers on relative location.href change\", function(done) {\n\t\t\t$window.onhashchange = o.spy()\n\t\t\t$window.location.href = \"#a\"\n\n\t\t\tcallAsync(function(){\n\t\t\t\to($window.onhashchange.callCount).equals(1)\n\t\t\t\tdone()\n\t\t\t})\n\t\t})\n\t\to(\"onhashchange triggers on location.hash change\", function(done) {\n\t\t\t$window.onhashchange = o.spy()\n\t\t\t$window.location.hash = \"#a\"\n\n\t\t\tcallAsync(function(){\n\t\t\t\to($window.onhashchange.callCount).equals(1)\n\t\t\t\tdone()\n\t\t\t})\n\t\t})\n\t\to(\"onhashchange does not trigger on page change\", function(done) {\n\t\t\t$window.onhashchange = o.spy()\n\t\t\t$window.location.href = \"http://localhost/a\"\n\n\t\t\tcallAsync(function(){\n\t\t\t\to($window.onhashchange.callCount).equals(0)\n\t\t\t\tdone()\n\t\t\t})\n\t\t})\n\t\to(\"onhashchange does not trigger on page change with different hash\", function(done) {\n\t\t\t$window.location.href = \"http://localhost/#a\"\n\t\t\tcallAsync(function(){\n\t\t\t\t$window.onhashchange = o.spy()\n\t\t\t\t$window.location.href = \"http://localhost/a#b\"\n\n\t\t\t\tcallAsync(function(){\n\t\t\t\t\to($window.onhashchange.callCount).equals(0)\n\t\t\t\t\tdone()\n\t\t\t\t})\n\t\t\t})\n\t\t})\n\t\to(\"onhashchange does not trigger on page change with same hash\", function(done) {\n\t\t\t$window.location.href = \"http://localhost/#b\"\n\t\t\tcallAsync(function(){\n\t\t\t\t$window.onhashchange = o.spy()\n\t\t\t\t$window.location.href = \"http://localhost/a#b\"\n\n\t\t\t\tcallAsync(function(){\n\t\t\t\t\to($window.onhashchange.callCount).equals(0)\n\t\t\t\t\tdone()\n\t\t\t\t})\n\t\t\t})\n\t\t})\n\t\to(\"onhashchange triggers on history.back()\", function(done) {\n\t\t\t$window.location.href = \"#a\"\n\t\t\tcallAsync(function(){\n\t\t\t\t$window.onhashchange = o.spy()\n\t\t\t\t$window.history.back()\n\n\t\t\t\tcallAsync(function(){\n\t\t\t\t\to($window.onhashchange.callCount).equals(1)\n\t\t\t\t\tdone()\n\t\t\t\t})\n\t\t\t})\n\t\t})\n\t\to(\"onhashchange triggers on history.forward()\", function(done) {\n\t\t\t$window.location.href = \"#a\"\n\t\t\tcallAsync(function(){\n\t\t\t\t$window.onhashchange = o.spy()\n\t\t\t\t$window.history.back()\n\t\t\t\tcallAsync(function(){\n\t\t\t\t\t$window.history.forward()\n\n\t\t\t\t\tcallAsync(function(){\n\t\t\t\t\t\to($window.onhashchange.callCount).equals(2)\n\t\t\t\t\t\tdone()\n\t\t\t\t\t})\n\t\t\t\t})\n\t\t\t})\n\t\t})\n\t\to(\"onhashchange triggers once when the hash changes twice in a single tick\", function(done) {\n\t\t\t$window.location.href = \"#a\"\n\t\t\tcallAsync(function(){\n\t\t\t\t$window.onhashchange = o.spy()\n\t\t\t\t$window.history.back()\n\t\t\t\t$window.history.forward()\n\n\t\t\t\tcallAsync(function(){\n\t\t\t\t\to($window.onhashchange.callCount).equals(1)\n\t\t\t\t\tdone()\n\t\t\t\t})\n\t\t\t})\n\t\t})\n\t\to(\"onhashchange does not trigger on history.back() that causes page change with different hash\", function(done) {\n\t\t\t$window.location.href = \"#a\"\n\t\t\t$window.location.href = \"a#b\"\n\t\t\tcallAsync(function(){\n\t\t\t\t$window.onhashchange = o.spy()\n\t\t\t\t$window.history.back()\n\n\t\t\t\tcallAsync(function(){\n\t\t\t\t\to($window.onhashchange.callCount).equals(0)\n\t\t\t\t\tdone()\n\t\t\t\t})\n\t\t\t})\n\t\t})\n\t\to(\"onhashchange does not trigger on history.back() that causes page change with same hash\", function(done) {\n\t\t\t$window.location.href = \"#a\"\n\t\t\t$window.location.href = \"a#a\"\n\t\t\tcallAsync(function(){\n\t\t\t\t$window.onhashchange = o.spy()\n\t\t\t\t$window.history.back()\n\n\t\t\t\tcallAsync(function(){\n\t\t\t\t\to($window.onhashchange.callCount).equals(0)\n\t\t\t\t\tdone()\n\t\t\t\t})\n\t\t\t})\n\t\t})\n\t\to(\"onhashchange does not trigger on history.forward() that causes page change with different hash\", function(done) {\n\t\t\t$window.location.href = \"#a\"\n\t\t\t$window.location.href = \"a#b\"\n\t\t\tcallAsync(function(){\n\t\t\t\t$window.onhashchange = o.spy()\n\t\t\t\t$window.history.back()\n\t\t\t\t$window.history.forward()\n\n\t\t\t\tcallAsync(function(){\n\t\t\t\t\to($window.onhashchange.callCount).equals(0)\n\t\t\t\t\tdone()\n\t\t\t\t})\n\t\t\t})\n\t\t})\n\t\to(\"onhashchange does not trigger on history.forward() that causes page change with same hash\", function(done) {\n\t\t\t$window.location.href = \"#a\"\n\t\t\t$window.location.href = \"a#b\"\n\t\t\tcallAsync(function(){\n\t\t\t\t$window.onhashchange = o.spy()\n\t\t\t\t$window.history.back()\n\t\t\t\t$window.history.forward()\n\n\t\t\t\tcallAsync(function(){\n\t\t\t\t\to($window.onhashchange.callCount).equals(0)\n\t\t\t\t\tdone()\n\t\t\t\t})\n\t\t\t})\n\t\t})\n\t})\n\to.spec(\"onunload\", function() {\n\t\to(\"onunload triggers on location.href change\", function() {\n\t\t\t$window.onunload = o.spy()\n\t\t\t$window.location.href = \"http://localhost/a\"\n\n\t\t\to($window.onunload.callCount).equals(1)\n\t\t\to($window.onunload.args[0].type).equals(\"unload\")\n\t\t})\n\t\to(\"onunload triggers on relative location.href change\", function() {\n\t\t\t$window.onunload = o.spy()\n\t\t\t$window.location.href = \"a\"\n\n\t\t\to($window.onunload.callCount).equals(1)\n\t\t})\n\t\to(\"onunload triggers on search change via location.href\", function() {\n\t\t\t$window.onunload = o.spy()\n\t\t\t$window.location.href = \"http://localhost/?a\"\n\n\t\t\to($window.onunload.callCount).equals(1)\n\t\t})\n\t\to(\"onunload triggers on relative search change via location.href\", function() {\n\t\t\t$window.onunload = o.spy()\n\t\t\t$window.location.href = \"?a\"\n\n\t\t\to($window.onunload.callCount).equals(1)\n\t\t})\n\t\to(\"onunload does not trigger on hash change via location.href\", function() {\n\t\t\t$window.onunload = o.spy()\n\t\t\t$window.location.href = \"http://localhost/#a\"\n\n\t\t\to($window.onunload.callCount).equals(0)\n\t\t})\n\t\to(\"onunload does not trigger on relative hash change via location.href\", function() {\n\t\t\t$window.onunload = o.spy()\n\t\t\t$window.location.href = \"#a\"\n\n\t\t\to($window.onunload.callCount).equals(0)\n\t\t})\n\t\to(\"onunload does not trigger on hash-only history.back()\", function() {\n\t\t\t$window.location.href = \"#a\"\n\t\t\t$window.onunload = o.spy()\n\t\t\t$window.history.back()\n\n\t\t\to($window.onunload.callCount).equals(0)\n\t\t})\n\t\to(\"onunload does not trigger on hash-only history.forward()\", function() {\n\t\t\t$window.location.href = \"#a\"\n\t\t\t$window.history.back()\n\t\t\t$window.onunload = o.spy()\n\t\t\t$window.history.forward()\n\n\t\t\to($window.onunload.callCount).equals(0)\n\t\t})\n\t\to(\"onunload has correct url during call via location.href change\", function(done) {\n\t\t\t$window.onunload = function() {\n\t\t\t\to($window.location.href).equals(\"http://localhost/\")\n\t\t\t\tdone()\n\t\t\t}\n\t\t\t$window.location.href = \"a\"\n\t\t})\n\t\to(\"onunload has correct url during call via location.search change\", function(done) {\n\t\t\t$window.onunload = function() {\n\t\t\t\to($window.location.href).equals(\"http://localhost/\")\n\t\t\t\tdone()\n\t\t\t}\n\t\t\t$window.location.search = \"?a\"\n\t\t})\n\t})\n})\n"
  },
  {
    "path": "test-utils/tests/test-throttleMock.js",
    "content": "\"use strict\"\n\nvar o = require(\"ospec\")\nvar throttleMocker = require(\"../../test-utils/throttleMock\")\n\no.spec(\"throttleMock\", function() {\n\to(\"schedules one callback\", function() {\n\t\tvar throttleMock = throttleMocker()\n\t\tvar spy = o.spy()\n\n\t\to(throttleMock.queueLength()).equals(0)\n\t\tthrottleMock.schedule(spy)\n\t\to(throttleMock.queueLength()).equals(1)\n\t\to(spy.callCount).equals(0)\n\t\tthrottleMock.fire()\n\t\to(throttleMock.queueLength()).equals(0)\n\t\to(spy.callCount).equals(1)\n\t})\n\to(\"schedules two callbacks\", function() {\n\t\tvar throttleMock = throttleMocker()\n\t\tvar spy1 = o.spy()\n\t\tvar spy2 = o.spy()\n\n\t\to(throttleMock.queueLength()).equals(0)\n\t\tthrottleMock.schedule(spy1)\n\t\to(throttleMock.queueLength()).equals(1)\n\t\to(spy1.callCount).equals(0)\n\t\to(spy2.callCount).equals(0)\n\t\tthrottleMock.schedule(spy2)\n\t\to(throttleMock.queueLength()).equals(2)\n\t\to(spy1.callCount).equals(0)\n\t\to(spy2.callCount).equals(0)\n\t\tthrottleMock.fire()\n\t\to(throttleMock.queueLength()).equals(0)\n\t\to(spy1.callCount).equals(1)\n\t\to(spy2.callCount).equals(1)\n\t})\n})\n"
  },
  {
    "path": "test-utils/tests/test-xhrMock.js",
    "content": "\"use strict\"\n\nvar o = require(\"ospec\")\nvar xhrMock = require(\"../../test-utils/xhrMock\")\n\no.spec(\"xhrMock\", function() {\n\tvar $window\n\to.beforeEach(function() {\n\t\t$window = xhrMock()\n\t})\n\n\to.spec(\"xhr\", function() {\n\t\to(\"works\", function(done) {\n\t\t\t$window.$defineRoutes({\n\t\t\t\t\"GET /item\": function(request) {\n\t\t\t\t\to(request.url).equals(\"/item\")\n\t\t\t\t\treturn {status: 200, responseText: \"test\"}\n\t\t\t\t}\n\t\t\t})\n\t\t\tvar xhr = new $window.XMLHttpRequest()\n\t\t\txhr.open(\"GET\", \"/item\")\n\t\t\txhr.onreadystatechange = function() {\n\t\t\t\tif (xhr.readyState === 4) {\n\t\t\t\t\to(xhr.status).equals(200)\n\t\t\t\t\to(xhr.responseText).equals(\"test\")\n\t\t\t\t\tdone()\n\t\t\t\t}\n\t\t\t}\n\t\t\txhr.send()\n\t\t})\n\t\to(\"works w/ search\", function(done) {\n\t\t\t$window.$defineRoutes({\n\t\t\t\t\"GET /item\": function(request) {\n\t\t\t\t\to(request.query).equals(\"?a=b\")\n\t\t\t\t\treturn {status: 200, responseText: \"test\"}\n\t\t\t\t}\n\t\t\t})\n\t\t\tvar xhr = new $window.XMLHttpRequest()\n\t\t\txhr.open(\"GET\", \"/item?a=b\")\n\t\t\txhr.onreadystatechange = function() {\n\t\t\t\tif (xhr.readyState === 4) {\n\t\t\t\t\tdone()\n\t\t\t\t}\n\t\t\t}\n\t\t\txhr.send()\n\t\t})\n\t\to(\"works w/ body\", function(done) {\n\t\t\t$window.$defineRoutes({\n\t\t\t\t\"POST /item\": function(request) {\n\t\t\t\t\to(request.body).equals(\"a=b\")\n\t\t\t\t\treturn {status: 200, responseText: \"test\"}\n\t\t\t\t}\n\t\t\t})\n\t\t\tvar xhr = new $window.XMLHttpRequest()\n\t\t\txhr.open(\"POST\", \"/item\")\n\t\t\txhr.onreadystatechange = function() {\n\t\t\t\tif (xhr.readyState === 4) {\n\t\t\t\t\tdone()\n\t\t\t\t}\n\t\t\t}\n\t\t\txhr.send(\"a=b\")\n\t\t})\n\t\to(\"passes event to onreadystatechange\", function(done) {\n\t\t\t$window.$defineRoutes({\n\t\t\t\t\"GET /item\": function(request) {\n\t\t\t\t\to(request.url).equals(\"/item\")\n\t\t\t\t\treturn {status: 200, responseText: \"test\"}\n\t\t\t\t}\n\t\t\t})\n\t\t\tvar xhr = new $window.XMLHttpRequest()\n\t\t\txhr.open(\"GET\", \"/item\")\n\t\t\txhr.onreadystatechange = function(ev) {\n\t\t\t\to(ev.target).equals(xhr)\n\t\t\t\tif (xhr.readyState === 4) {\n\t\t\t\t\tdone()\n\t\t\t\t}\n\t\t\t}\n\t\t\txhr.send()\n\t\t})\n\t\to(\"handles routing error\", function(done) {\n\t\t\tvar xhr = new $window.XMLHttpRequest()\n\t\t\txhr.open(\"GET\", \"/nonexistent\")\n\t\t\txhr.onreadystatechange = function() {\n\t\t\t\tif (xhr.readyState === 4) {\n\t\t\t\t\to(xhr.status).equals(500)\n\t\t\t\t\tdone()\n\t\t\t\t}\n\t\t\t}\n\t\t\txhr.send(\"a=b\")\n\t\t})\n\t\to(\"Setting a header twice merges the header\", function() {\n\t\t\t// Source: https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/setRequestHeader\n\t\t\tvar xhr = new $window.XMLHttpRequest()\n\t\t\txhr.open(\"POST\", \"/test\")\n\t\t\txhr.setRequestHeader(\"Content-Type\", \"foo\")\n\t\t\txhr.setRequestHeader(\"Content-Type\", \"bar\")\n\t\t\to(xhr.getRequestHeader(\"Content-Type\")).equals(\"foo, bar\")\n\t\t})\n\t})\n})\n"
  },
  {
    "path": "test-utils/throttleMock.js",
    "content": "\"use strict\"\n\nmodule.exports = function() {\n\tvar queue = []\n\treturn {\n\t\tschedule: function(fn) {\n\t\t\tqueue.push(fn)\n\t\t},\n\t\tfire: function() {\n\t\t\tvar tasks = queue\n\t\t\tqueue = []\n\t\t\ttasks.forEach(function(fn) {fn()})\n\t\t},\n\t\tqueueLength: function(){\n\t\t\treturn queue.length\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "test-utils/xhrMock.js",
    "content": "\"use strict\"\n\nvar callAsync = require(\"../test-utils/callAsync\")\nvar parseURL = require(\"../test-utils/parseURL\")\nvar parseQueryString = require(\"../querystring/parse\")\n\nmodule.exports = function() {\n\tvar routes = {}\n\t// var callback = \"callback\"\n\tvar serverErrorHandler = function(url) {\n\t\treturn {status: 500, responseText: \"server error, most likely the URL was not defined \" + url}\n\t}\n\n\tfunction FormData() {}\n\tvar $window = {\n\t\tFormData: FormData,\n\t\tURLSearchParams: URLSearchParams,\n\t\tXMLHttpRequest: function XMLHttpRequest() {\n\t\t\tvar args = {}\n\t\t\tvar headers = {}\n\t\t\tvar aborted = false\n\t\t\tthis.setRequestHeader = function(header, value) {\n\t\t\t\t/*\n\t\t\t\t the behavior of setHeader is not your expected setX API.\n\t\t\t\t If the header is already set, it'll merge with whatever you add\n\t\t\t\t rather than overwrite\n\t\t\t\t Source: https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/setRequestHeader\n\t\t\t\t */\n\t\t\t\tif (headers[header]) {\n\t\t\t\t\theaders[header] += \", \" + value;\n\t\t\t\t} else {\n\t\t\t\t\theaders[header] = value\n\t\t\t\t}\n\t\t\t}\n\t\t\tthis.getRequestHeader = function(header) {\n\t\t\t\treturn headers[header]\n\t\t\t}\n\t\t\tthis.open = function(method, url, async, user, password) {\n\t\t\t\tvar urlData = parseURL(url, {protocol: \"http:\", hostname: \"localhost\", port: \"\", pathname: \"/\"})\n\t\t\t\targs.rawUrl = url\n\t\t\t\targs.method = method\n\t\t\t\targs.pathname = urlData.pathname\n\t\t\t\targs.search = urlData.search\n\t\t\t\targs.async = async != null ? async : true\n\t\t\t\targs.user = user\n\t\t\t\targs.password = password\n\t\t\t}\n\t\t\tthis.responseType = \"\"\n\t\t\tthis.response = null\n\t\t\tthis.timeout = 0\n\t\t\tObject.defineProperty(this, \"responseText\", {get: function() {\n\t\t\t\tif (this.responseType === \"\" || this.responseType === \"text\") {\n\t\t\t\t\treturn this.response\n\t\t\t\t} else {\n\t\t\t\t\tthrow new Error(\"Failed to read the 'responseText' property from 'XMLHttpRequest': The value is only accessible if the object's 'responseType' is '' or 'text' (was '\" + this.responseType + \"').\")\n\t\t\t\t}\n\t\t\t}})\n\t\t\tthis.send = function(body) {\n\t\t\t\tvar self = this\n\t\t\t\t\n\t\t\t\tvar completeResponse = function (data) {\n\t\t\t\t\tself._responseCompleted = true\n\t\t\t\t\tif(!aborted) {\n\t\t\t\t\t\tself.status = data.status\n\t\t\t\t\t\t// Match spec\n\t\t\t\t\t\tif (self.responseType === \"json\") {\n\t\t\t\t\t\t\ttry { self.response = JSON.parse(data.responseText) }\n\t\t\t\t\t\t\tcatch (e) { /* ignore */ }\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tself.response = data.responseText\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tself.status = 0\n\t\t\t\t\t}\n\t\t\t\t\tself.readyState = 4\n\t\t\t\t\tif (args.async === true) {\n\t\t\t\t\t\tcallAsync(function() {\n\t\t\t\t\t\t\tif (typeof self.onreadystatechange === \"function\") self.onreadystatechange({target: self})\n\t\t\t\t\t\t})\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tvar data\n\t\t\t\tif (!aborted) {\n\t\t\t\t\tvar handler = routes[args.method + \" \" + args.pathname] || serverErrorHandler.bind(null, args.pathname)\n\t\t\t\t\tdata = handler({rawUrl: args.rawUrl, url: args.pathname, query: args.search || {}, body: body || null})\n\t\t\t\t}\n\n\t\t\t\tif (typeof self.timeout === \"number\" && self.timeout > 0) {\n\t\t\t\t\tsetTimeout(function () {\n\t\t\t\t\t\tif (self._responseCompleted) {\n\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tself.status = 0;\n\t\t\t\t\t\tif (typeof self.ontimeout === \"function\") self.ontimeout({target: self, type:\"timeout\"})\n\t\t\t\t\t}, self.timeout)\n\t\t\t\t}\n\n\t\t\t\tif (data instanceof Promise) {\n\t\t\t\t\tdata.then(completeResponse)\n\t\t\t\t} else {\n\t\t\t\t\tcompleteResponse(data)\n\t\t\t\t}\n\t\t\t}\n\t\t\tthis.abort = function() {\n\t\t\t\taborted = true\n\t\t\t}\n\t\t},\n\t\tdocument: {\n\t\t\tcreateElement: function(tag) {\n\t\t\t\treturn {nodeName: tag.toUpperCase(), parentNode: null}\n\t\t\t},\n\t\t\tdocumentElement: {\n\t\t\t\tappendChild: function(element) {\n\t\t\t\t\telement.parentNode = this\n\t\t\t\t\tif (element.nodeName === \"SCRIPT\") {\n\t\t\t\t\t\tvar urlData = parseURL(element.src, {protocol: \"http:\", hostname: \"localhost\", port: \"\", pathname: \"/\"})\n\t\t\t\t\t\tvar handler = routes[\"GET \" + urlData.pathname] || serverErrorHandler.bind(null, element.src)\n\t\t\t\t\t\tvar data = handler({url: urlData.pathname, query: urlData.search, body: null})\n\t\t\t\t\t\tparseQueryString(urlData.search)\n\t\t\t\t\t\tcallAsync(function() {\n\t\t\t\t\t\t\tif (data.status === 200) {\n\t\t\t\t\t\t\t\tnew Function(\"$window\", \"with ($window) return \" + data.responseText).call($window, $window)\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse if (typeof element.onerror === \"function\") {\n\t\t\t\t\t\t\t\telement.onerror({type: \"error\"})\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t})\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\tremoveChild: function(element) {\n\t\t\t\t\telement.parentNode = null\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t$defineRoutes: function(rules) {\n\t\t\troutes = rules\n\t\t},\n\t}\n\treturn $window\n}\n"
  },
  {
    "path": "tests/test-api.js",
    "content": "\"use strict\"\n\nvar o = require(\"ospec\")\nvar browserMock = require(\"../test-utils/browserMock\")\nvar components = require(\"../test-utils/components\")\n\no.spec(\"api\", function() {\n\tvar FRAME_BUDGET = Math.floor(1000 / 60)\n\tvar mock = browserMock(), root\n\tmock.setTimeout = setTimeout\n\tif (typeof global !== \"undefined\") {\n\t\tglobal.window = mock\n\t\tglobal.requestAnimationFrame = mock.requestAnimationFrame\n\t}\n\tvar m = require(\"..\") // eslint-disable-line global-require\n\n\to.afterEach(function() {\n\t\tif (root) m.mount(root, null)\n\t})\n\n\to.spec(\"m\", function() {\n\t\to(\"works\", function() {\n\t\t\tvar vnode = m(\"div\")\n\n\t\t\to(vnode.tag).equals(\"div\")\n\t\t})\n\t})\n\to.spec(\"m.trust\", function() {\n\t\to(\"works\", function() {\n\t\t\tvar vnode = m.trust(\"<br>\")\n\n\t\t\to(vnode.tag).equals(\"<\")\n\t\t\to(vnode.children).equals(\"<br>\")\n\t\t})\n\t})\n\to.spec(\"m.fragment\", function() {\n\t\to(\"works\", function() {\n\t\t\tvar vnode = m.fragment({key: 123}, [m(\"div\")])\n\n\t\t\to(vnode.tag).equals(\"[\")\n\t\t\to(vnode.key).equals(123)\n\t\t\to(vnode.children.length).equals(1)\n\t\t\to(vnode.children[0].tag).equals(\"div\")\n\t\t})\n\t})\n\to.spec(\"m.parseQueryString\", function() {\n\t\to(\"works\", function() {\n\t\t\tvar query = m.parseQueryString(\"?a=1&b=2\")\n\n\t\t\to(query).deepEquals({a: \"1\", b: \"2\"})\n\t\t})\n\t})\n\to.spec(\"m.buildQueryString\", function() {\n\t\to(\"works\", function() {\n\t\t\tvar query = m.buildQueryString({a: 1, b: 2})\n\n\t\t\to(query).equals(\"a=1&b=2\")\n\t\t})\n\t})\n\to.spec(\"m.request\", function() {\n\t\to(\"works\", function() {\n\t\t\to(typeof m.request).equals(\"function\") // TODO improve\n\t\t})\n\t})\n\to.spec(\"m.render\", function() {\n\t\to(\"works\", function() {\n\t\t\troot = window.document.createElement(\"div\")\n\t\t\tm.render(root, m(\"div\"))\n\n\t\t\to(root.childNodes.length).equals(1)\n\t\t\to(root.firstChild.nodeName).equals(\"DIV\")\n\t\t})\n\t})\n\tcomponents.forEach(function(cmp){\n\t\to.spec(cmp.kind, function(){\n\t\t\tvar createComponent = cmp.create\n\n\t\t\to.spec(\"m.mount\", function() {\n\t\t\t\to(\"works\", function() {\n\t\t\t\t\troot = window.document.createElement(\"div\")\n\t\t\t\t\tm.mount(root, createComponent({view: function() {return m(\"div\")}}))\n\n\t\t\t\t\to(root.childNodes.length).equals(1)\n\t\t\t\t\to(root.firstChild.nodeName).equals(\"DIV\")\n\t\t\t\t})\n\t\t\t})\n\t\t\to.spec(\"m.route\", function() {\n\t\t\t\to(\"works\", function(done) {\n\t\t\t\t\troot = window.document.createElement(\"div\")\n\t\t\t\t\tm.route(root, \"/a\", {\n\t\t\t\t\t\t\"/a\": createComponent({view: function() {return m(\"div\")}})\n\t\t\t\t\t})\n\n\t\t\t\t\tsetTimeout(function() {\n\t\t\t\t\t\to(root.childNodes.length).equals(1)\n\t\t\t\t\t\to(root.firstChild.nodeName).equals(\"DIV\")\n\n\t\t\t\t\t\tdone()\n\t\t\t\t\t}, FRAME_BUDGET)\n\t\t\t\t})\n\t\t\t\to(\"m.route.prefix\", function(done) {\n\t\t\t\t\troot = window.document.createElement(\"div\")\n\t\t\t\t\tm.route.prefix = \"#\"\n\t\t\t\t\tm.route(root, \"/a\", {\n\t\t\t\t\t\t\"/a\": createComponent({view: function() {return m(\"div\")}})\n\t\t\t\t\t})\n\n\t\t\t\t\tsetTimeout(function() {\n\t\t\t\t\t\to(root.childNodes.length).equals(1)\n\t\t\t\t\t\to(root.firstChild.nodeName).equals(\"DIV\")\n\n\t\t\t\t\t\tdone()\n\t\t\t\t\t}, FRAME_BUDGET)\n\t\t\t\t})\n\t\t\t\to(\"m.route.get\", function(done) {\n\t\t\t\t\troot = window.document.createElement(\"div\")\n\t\t\t\t\tm.route(root, \"/a\", {\n\t\t\t\t\t\t\"/a\": createComponent({view: function() {return m(\"div\")}})\n\t\t\t\t\t})\n\n\t\t\t\t\tsetTimeout(function() {\n\t\t\t\t\t\to(m.route.get()).equals(\"/a\")\n\n\t\t\t\t\t\tdone()\n\t\t\t\t\t}, FRAME_BUDGET)\n\t\t\t\t})\n\t\t\t\to(\"m.route.set\", function(done) {\n\t\t\t\t\to.timeout(100)\n\t\t\t\t\troot = window.document.createElement(\"div\")\n\t\t\t\t\tm.route(root, \"/a\", {\n\t\t\t\t\t\t\"/:id\": createComponent({view: function() {return m(\"div\")}})\n\t\t\t\t\t})\n\n\t\t\t\t\tsetTimeout(function() {\n\t\t\t\t\t\tm.route.set(\"/b\")\n\t\t\t\t\t\tsetTimeout(function() {\n\t\t\t\t\t\t\to(m.route.get()).equals(\"/b\")\n\n\t\t\t\t\t\t\tdone()\n\t\t\t\t\t\t}, FRAME_BUDGET)\n\t\t\t\t\t}, FRAME_BUDGET)\n\t\t\t\t})\n\t\t\t})\n\t\t\to.spec(\"m.redraw\", function() {\n\t\t\t\to(\"works\", function(done) {\n\t\t\t\t\tvar count = 0\n\t\t\t\t\troot = window.document.createElement(\"div\")\n\t\t\t\t\tm.mount(root, createComponent({view: function() {count++}}))\n\t\t\t\t\to(count).equals(1)\n\t\t\t\t\tm.redraw()\n\t\t\t\t\to(count).equals(1)\n\t\t\t\t\tsetTimeout(function() {\n\n\t\t\t\t\t\to(count).equals(2)\n\n\t\t\t\t\t\tdone()\n\t\t\t\t\t}, FRAME_BUDGET)\n\t\t\t\t})\n\t\t\t\to(\"sync\", function() {\n\t\t\t\t\troot = window.document.createElement(\"div\")\n\t\t\t\t\tvar view = o.spy()\n\t\t\t\t\tm.mount(root, createComponent({view: view}))\n\t\t\t\t\to(view.callCount).equals(1)\n\t\t\t\t\tm.redraw.sync()\n\t\t\t\t\to(view.callCount).equals(2)\n\t\t\t\t})\n\t\t\t})\n\t\t})\n\t})\n})\n"
  },
  {
    "path": "util/censor.js",
    "content": "\"use strict\"\n\n// Note: this is mildly perf-sensitive.\n//\n// It does *not* use `delete` - dynamic `delete`s usually cause objects to bail\n// out into dictionary mode and just generally cause a bunch of optimization\n// issues within engines.\n//\n// Ideally, I would've preferred to do this, if it weren't for the optimization\n// issues:\n//\n// ```js\n// const hasOwn = require(\"./hasOwn\")\n// const magic = [\n//     \"key\", \"oninit\", \"oncreate\", \"onbeforeupdate\", \"onupdate\",\n//     \"onbeforeremove\", \"onremove\",\n// ]\n// module.exports = (attrs, extras) => {\n//     const result = Object.assign(Object.create(null), attrs)\n//     for (const key of magic) delete result[key]\n//     if (extras != null) for (const key of extras) delete result[key]\n//     return result\n// }\n// ```\n\nvar hasOwn = require(\"./hasOwn\")\nvar magic = /^(?:key|oninit|oncreate|onbeforeupdate|onupdate|onbeforeremove|onremove)$/\n\nmodule.exports = function(attrs, extras) {\n\tvar result = {}\n\n\tif (extras != null) {\n\t\tfor (var key in attrs) {\n\t\t\tif (hasOwn.call(attrs, key) && !magic.test(key) && extras.indexOf(key) < 0) {\n\t\t\t\tresult[key] = attrs[key]\n\t\t\t}\n\t\t}\n\t} else {\n\t\tfor (var key in attrs) {\n\t\t\tif (hasOwn.call(attrs, key) && !magic.test(key)) {\n\t\t\t\tresult[key] = attrs[key]\n\t\t\t}\n\t\t}\n\t}\n\n\treturn result\n}\n"
  },
  {
    "path": "util/decodeURIComponentSafe.js",
    "content": "\"use strict\"\n\n/*\nPercent encodings encode UTF-8 bytes, so this regexp needs to match that.\nHere's how UTF-8 encodes stuff:\n- `00-7F`: 1-byte, for U+0000-U+007F\n- `C2-DF 80-BF`: 2-byte, for U+0080-U+07FF\n- `E0-EF 80-BF 80-BF`: 3-byte, encodes U+0800-U+FFFF\n- `F0-F4 80-BF 80-BF 80-BF`: 4-byte, encodes U+10000-U+10FFFF\nIn this, there's a number of invalid byte sequences:\n- `80-BF`: Continuation byte, invalid as start\n- `C0-C1 80-BF`: Overlong encoding for U+0000-U+007F\n- `E0 80-9F 80-BF`: Overlong encoding for U+0080-U+07FF\n- `ED A0-BF 80-BF`: Encoding for UTF-16 surrogate U+D800-U+DFFF\n- `F0 80-8F 80-BF 80-BF`: Overlong encoding for U+0800-U+FFFF\n- `F4 90-BF`: RFC 3629 restricted UTF-8 to only code points UTF-16 could encode.\n- `F5-FF`: RFC 3629 restricted UTF-8 to only code points UTF-16 could encode.\nSo in reality, only the following sequences can encode are valid characters:\n- 00-7F\n- C2-DF 80-BF\n- E0    A0-BF 80-BF\n- E1-EC 80-BF 80-BF\n- ED    80-9F 80-BF\n- EE-EF 80-BF 80-BF\n- F0    90-BF 80-BF 80-BF\n- F1-F3 80-BF 80-BF 80-BF\n- F4    80-8F 80-BF 80-BF\n\nThe regexp just tries to match this as compactly as possible.\n*/\nvar validUtf8Encodings = /%(?:[0-7]|(?!c[01]|e0%[89]|ed%[ab]|f0%8|f4%[9ab])(?:c|d|(?:e|f[0-4]%[89ab])[\\da-f]%[89ab])[\\da-f]%[89ab])[\\da-f]/gi\n\nmodule.exports = function(str) {\n\treturn String(str).replace(validUtf8Encodings, decodeURIComponent)\n}\n"
  },
  {
    "path": "util/hasOwn.js",
    "content": "// This exists so I'm only saving it once.\n\"use strict\"\n\nmodule.exports = {}.hasOwnProperty\n"
  },
  {
    "path": "util/tests/test-censor.js",
    "content": "\"use strict\"\n\nvar o = require(\"ospec\")\nvar censor = require(\"../../util/censor\")\n\no.spec(\"censor\", function() {\n\to.spec(\"magic missing, no extras\", function() {\n\t\to(\"returns new object\", function() {\n\t\t\tvar original = {one: \"two\"}\n\t\t\tvar censored = censor(original)\n\t\t\to(censored).notEquals(original)\n\t\t\to(censored).deepEquals({one: \"two\"})\n\t\t})\n\t\to(\"does not modify original object\", function() {\n\t\t\tvar original = {one: \"two\"}\n\t\t\tcensor(original)\n\t\t\to(original).deepEquals({one: \"two\"})\n\t\t})\n\t})\n\n\to.spec(\"magic present, no extras\", function() {\n\t\to(\"returns new object\", function() {\n\t\t\tvar original = {\n\t\t\t\tone: \"two\",\n\t\t\t\tkey: \"test\",\n\t\t\t\toninit: \"test\",\n\t\t\t\toncreate: \"test\",\n\t\t\t\tonbeforeupdate: \"test\",\n\t\t\t\tonupdate: \"test\",\n\t\t\t\tonbeforeremove: \"test\",\n\t\t\t\tonremove: \"test\",\n\t\t\t}\n\t\t\tvar censored = censor(original)\n\t\t\to(censored).notEquals(original)\n\t\t\to(censored).deepEquals({one: \"two\"})\n\t\t})\n\t\to(\"does not modify original object\", function() {\n\t\t\tvar original = {\n\t\t\t\tone: \"two\",\n\t\t\t\tkey: \"test\",\n\t\t\t\toninit: \"test\",\n\t\t\t\toncreate: \"test\",\n\t\t\t\tonbeforeupdate: \"test\",\n\t\t\t\tonupdate: \"test\",\n\t\t\t\tonbeforeremove: \"test\",\n\t\t\t\tonremove: \"test\",\n\t\t\t}\n\t\t\tcensor(original)\n\t\t\to(original).deepEquals({\n\t\t\t\tone: \"two\",\n\t\t\t\tkey: \"test\",\n\t\t\t\toninit: \"test\",\n\t\t\t\toncreate: \"test\",\n\t\t\t\tonbeforeupdate: \"test\",\n\t\t\t\tonupdate: \"test\",\n\t\t\t\tonbeforeremove: \"test\",\n\t\t\t\tonremove: \"test\",\n\t\t\t})\n\t\t})\n\t})\n\n\to.spec(\"magic missing, null extras\", function() {\n\t\to(\"returns new object\", function() {\n\t\t\tvar original = {one: \"two\"}\n\t\t\tvar censored = censor(original, null)\n\t\t\to(censored).notEquals(original)\n\t\t\to(censored).deepEquals({one: \"two\"})\n\t\t})\n\t\to(\"does not modify original object\", function() {\n\t\t\tvar original = {one: \"two\"}\n\t\t\tcensor(original, null)\n\t\t\to(original).deepEquals({one: \"two\"})\n\t\t})\n\t})\n\n\to.spec(\"magic present, null extras\", function() {\n\t\to(\"returns new object\", function() {\n\t\t\tvar original = {\n\t\t\t\tone: \"two\",\n\t\t\t\tkey: \"test\",\n\t\t\t\toninit: \"test\",\n\t\t\t\toncreate: \"test\",\n\t\t\t\tonbeforeupdate: \"test\",\n\t\t\t\tonupdate: \"test\",\n\t\t\t\tonbeforeremove: \"test\",\n\t\t\t\tonremove: \"test\",\n\t\t\t}\n\t\t\tvar censored = censor(original, null)\n\t\t\to(censored).notEquals(original)\n\t\t\to(censored).deepEquals({one: \"two\"})\n\t\t})\n\t\to(\"does not modify original object\", function() {\n\t\t\tvar original = {\n\t\t\t\tone: \"two\",\n\t\t\t\tkey: \"test\",\n\t\t\t\toninit: \"test\",\n\t\t\t\toncreate: \"test\",\n\t\t\t\tonbeforeupdate: \"test\",\n\t\t\t\tonupdate: \"test\",\n\t\t\t\tonbeforeremove: \"test\",\n\t\t\t\tonremove: \"test\",\n\t\t\t}\n\t\t\tcensor(original, null)\n\t\t\to(original).deepEquals({\n\t\t\t\tone: \"two\",\n\t\t\t\tkey: \"test\",\n\t\t\t\toninit: \"test\",\n\t\t\t\toncreate: \"test\",\n\t\t\t\tonbeforeupdate: \"test\",\n\t\t\t\tonupdate: \"test\",\n\t\t\t\tonbeforeremove: \"test\",\n\t\t\t\tonremove: \"test\",\n\t\t\t})\n\t\t})\n\t})\n\n\to.spec(\"magic missing, extras missing\", function() {\n\t\to(\"returns new object\", function() {\n\t\t\tvar original = {one: \"two\"}\n\t\t\tvar censored = censor(original, [\"extra\"])\n\t\t\to(censored).notEquals(original)\n\t\t\to(censored).deepEquals({one: \"two\"})\n\t\t})\n\t\to(\"does not modify original object\", function() {\n\t\t\tvar original = {one: \"two\"}\n\t\t\tcensor(original, [\"extra\"])\n\t\t\to(original).deepEquals({one: \"two\"})\n\t\t})\n\t})\n\n\to.spec(\"magic present, extras missing\", function() {\n\t\to(\"returns new object\", function() {\n\t\t\tvar original = {\n\t\t\t\tone: \"two\",\n\t\t\t\tkey: \"test\",\n\t\t\t\toninit: \"test\",\n\t\t\t\toncreate: \"test\",\n\t\t\t\tonbeforeupdate: \"test\",\n\t\t\t\tonupdate: \"test\",\n\t\t\t\tonbeforeremove: \"test\",\n\t\t\t\tonremove: \"test\",\n\t\t\t}\n\t\t\tvar censored = censor(original, [\"extra\"])\n\t\t\to(censored).notEquals(original)\n\t\t\to(censored).deepEquals({one: \"two\"})\n\t\t})\n\t\to(\"does not modify original object\", function() {\n\t\t\tvar original = {\n\t\t\t\tone: \"two\",\n\t\t\t\tkey: \"test\",\n\t\t\t\toninit: \"test\",\n\t\t\t\toncreate: \"test\",\n\t\t\t\tonbeforeupdate: \"test\",\n\t\t\t\tonupdate: \"test\",\n\t\t\t\tonbeforeremove: \"test\",\n\t\t\t\tonremove: \"test\",\n\t\t\t}\n\t\t\tcensor(original, [\"extra\"])\n\t\t\to(original).deepEquals({\n\t\t\t\tone: \"two\",\n\t\t\t\tkey: \"test\",\n\t\t\t\toninit: \"test\",\n\t\t\t\toncreate: \"test\",\n\t\t\t\tonbeforeupdate: \"test\",\n\t\t\t\tonupdate: \"test\",\n\t\t\t\tonbeforeremove: \"test\",\n\t\t\t\tonremove: \"test\",\n\t\t\t})\n\t\t})\n\t})\n\n\to.spec(\"magic missing, extras present\", function() {\n\t\to(\"returns new object\", function() {\n\t\t\tvar original = {\n\t\t\t\tone: \"two\",\n\t\t\t\textra: \"test\",\n\t\t\t}\n\t\t\tvar censored = censor(original, [\"extra\"])\n\t\t\to(censored).notEquals(original)\n\t\t\to(censored).deepEquals({one: \"two\"})\n\t\t})\n\t\to(\"does not modify original object\", function() {\n\t\t\tvar original = {\n\t\t\t\tone: \"two\",\n\t\t\t\textra: \"test\",\n\t\t\t}\n\t\t\tcensor(original, [\"extra\"])\n\t\t\to(original).deepEquals({\n\t\t\t\tone: \"two\",\n\t\t\t\textra: \"test\",\n\t\t\t})\n\t\t})\n\t})\n\n\to.spec(\"magic present, extras present\", function() {\n\t\to(\"returns new object\", function() {\n\t\t\tvar original = {\n\t\t\t\tone: \"two\",\n\t\t\t\textra: \"test\",\n\t\t\t\tkey: \"test\",\n\t\t\t\toninit: \"test\",\n\t\t\t\toncreate: \"test\",\n\t\t\t\tonbeforeupdate: \"test\",\n\t\t\t\tonupdate: \"test\",\n\t\t\t\tonbeforeremove: \"test\",\n\t\t\t\tonremove: \"test\",\n\t\t\t}\n\t\t\tvar censored = censor(original, [\"extra\"])\n\t\t\to(censored).notEquals(original)\n\t\t\to(censored).deepEquals({one: \"two\"})\n\t\t})\n\t\to(\"does not modify original object\", function() {\n\t\t\tvar original = {\n\t\t\t\tone: \"two\",\n\t\t\t\textra: \"test\",\n\t\t\t\tkey: \"test\",\n\t\t\t\toninit: \"test\",\n\t\t\t\toncreate: \"test\",\n\t\t\t\tonbeforeupdate: \"test\",\n\t\t\t\tonupdate: \"test\",\n\t\t\t\tonbeforeremove: \"test\",\n\t\t\t\tonremove: \"test\",\n\t\t\t}\n\t\t\tcensor(original, [\"extra\"])\n\t\t\to(original).deepEquals({\n\t\t\t\tone: \"two\",\n\t\t\t\textra: \"test\",\n\t\t\t\tkey: \"test\",\n\t\t\t\toninit: \"test\",\n\t\t\t\toncreate: \"test\",\n\t\t\t\tonbeforeupdate: \"test\",\n\t\t\t\tonupdate: \"test\",\n\t\t\t\tonbeforeremove: \"test\",\n\t\t\t\tonremove: \"test\",\n\t\t\t})\n\t\t})\n\t})\n})\n"
  },
  {
    "path": "util/tests/test-decodeURIComponentSafe.js",
    "content": "\"use strict\"\n\nvar o = require(\"ospec\")\nvar decodeURIComponentSafe = require(\"../../util/decodeURIComponentSafe\")\n\no.spec(\"decodeURIComponentSafe\", function() {\n\to(\"non-string type (compared to decodeURIComponent)\", function() {\n\t\to(decodeURIComponentSafe()).equals(decodeURIComponent())\n\t\to(decodeURIComponentSafe(null)).equals(decodeURIComponent(null))\n\t\to(decodeURIComponentSafe(0)).equals(decodeURIComponent(0))\n\t\to(decodeURIComponentSafe(true)).equals(decodeURIComponent(true))\n\t\to(decodeURIComponentSafe(false)).equals(decodeURIComponent(false))\n\t\to(decodeURIComponentSafe({})).equals(decodeURIComponent({}))\n\t\to(decodeURIComponentSafe([])).equals(decodeURIComponent([]))\n\t\to(decodeURIComponentSafe(function(){})).equals(decodeURIComponent(function(){}))\n\t})\n\n\to(\"non-percent-encoded string\", function() {\n\t\to(decodeURIComponentSafe(\"\")).equals(\"\")\n\t\to(decodeURIComponentSafe(\"1\")).equals(\"1\")\n\t\to(decodeURIComponentSafe(\"abc\")).equals(\"abc\")\n\t\to(decodeURIComponentSafe(\"😃\")).equals(\"😃\")\n\t})\n\n\to(\"percent-encoded ASCII\", function() {\n\t\tfor (var i = 0; i < 128; i++) {\n\t\t\tvar char = String.fromCharCode(i)\n\t\t\tvar uenc = \"%\" + Number(i).toString(16).padStart(2, \"0\").toUpperCase()\n\t\t\tvar lenc = \"%\" + Number(i).toString(16).padStart(2, \"0\").toLowerCase()\n\t\t\tvar uout = decodeURIComponentSafe(uenc)\n\t\t\tvar lout = decodeURIComponentSafe(lenc)\n\t\t\to(char).equals(uout)\n\t\t\to(char).equals(lout)\n\t\t}\n\t})\n\n\to(\"all code points (without surrogates)\", function() {\n\t\tvar ranges = [\n\t\t\t[0x0000, 0xD7FF],\n\t\t\t/* [0xD800, 0xDFFF], */\n\t\t\t[0xE000, 0x10FFFF]\n\t\t]\n\t\tfor (var [lo, hi] of ranges) {\n\t\t\tfor (var cp = lo; cp <= hi; cp++) {\n\t\t\t\tvar char = String.fromCodePoint(cp)\n\t\t\t\t// including ASCII characters not encoded by encodeURIComponent\n\t\t\t\tvar enc = encodeURIComponent(char)\n\t\t\t\tvar out = decodeURIComponentSafe(enc)\n\t\t\t\to(char).equals(out)\n\t\t\t}\n\t\t}\n\t})\n\n\to(\"invalid byte sequences\", function() {\n\t\t// `80-BF`: Continuation byte, invalid as start\n\t\to(decodeURIComponentSafe(\"%7F\")).notEquals(\"%7F\")\n\t\to(decodeURIComponentSafe(\"%80\")).equals(\"%80\")\n\t\to(decodeURIComponentSafe(\"%BF\")).equals(\"%BF\")\n\n\t\t// `C0-C1 80-BF`: Overlong encoding for U+0000-U+007F\n\t\to(decodeURIComponentSafe(\"%C0%80\")).equals(\"%C0%80\") // U+0000\n\t\to(decodeURIComponentSafe(\"%C1%BF\")).equals(\"%C1%BF\") // U+007F\n\t\to(decodeURIComponentSafe(\"%C2%80\")).notEquals(\"%C2%80\") // U+0080\n\n\t\t// `E0 80-9F 80-BF`: Overlong encoding for U+0080-U+07FF\n\t\to(decodeURIComponentSafe(\"%DF%BF\")).notEquals(\"%DF%BF\") // U+07FF\n\t\to(decodeURIComponentSafe(\"%E0%80%80\")).equals(\"%E0%80%80\") // U+0000\n\t\to(decodeURIComponentSafe(\"%E0%9F%BF\")).equals(\"%E0%9F%BF\") // U+07FF\n\t\to(decodeURIComponentSafe(\"%E0%A0%80\")).notEquals(\"%E0%A0%80\") // U+0800\n\n\t\t// `ED A0-BF 80-BF`: Encoding for UTF-16 surrogate U+D800-U+DFFF\n\t\to(decodeURIComponentSafe(\"%ED%9F%BF\")).notEquals(\"%ED%9F%BF\") // U+D7FF\n\t\to(decodeURIComponentSafe(\"%ED%A0%80\")).equals(\"%ED%A0%80\") // U+D800\n\t\to(decodeURIComponentSafe(\"%ED%AF%BF\")).equals(\"%ED%AF%BF\") // U+DBFF\n\t\to(decodeURIComponentSafe(\"%ED%B0%80\")).equals(\"%ED%B0%80\") // U+DC00\n\t\to(decodeURIComponentSafe(\"%ED%BF%BF\")).equals(\"%ED%BF%BF\") // U+DFFF\n\t\to(decodeURIComponentSafe(\"%EE%80%80\")).notEquals(\"%EE%80%80\") // U+E000\n\n\t\t// `F0 80-8F 80-BF 80-BF`: Overlong encoding for U+0800-U+FFFF\n\t\to(decodeURIComponentSafe(\"%EF%BF%BF\")).notEquals(\"%EF%BF%BF\") // U+FFFF\n\t\to(decodeURIComponentSafe(\"%F0%80%80%80\")).equals(\"%F0%80%80%80\") // U+0000\n\t\to(decodeURIComponentSafe(\"%E0%80%9F%BF\")).equals(\"%E0%80%9F%BF\") // U+07FF\n\t\to(decodeURIComponentSafe(\"%E0%80%A0%80\")).equals(\"%E0%80%A0%80\") // U+0800\n\t\to(decodeURIComponentSafe(\"%F0%8F%BF%BF\")).equals(\"%F0%8F%BF%BF\") // U+FFFF\n\t\to(decodeURIComponentSafe(\"%F0%90%80%80\")).notEquals(\"%F0%90%80%80\") // U+10000\n\n\t\t// `F4 90-BF`: RFC 3629 restricted UTF-8 to only code points UTF-16 could encode.\n\t\to(decodeURIComponentSafe(\"%F4%8F%BF%BF\")).notEquals(\"%F4%8F%BF%BF\") // U+10FFFF\n\t\to(decodeURIComponentSafe(\"%F4%90%80%80\")).equals(\"%F4%90%80%80\") // U+110000\n\t\to(decodeURIComponentSafe(\"%F4%BF%BF%BF\")).equals(\"%F4%BF%BF%BF\") // U+13FFFF\n\n\t\t// `F5-FF`: RFC 3629 restricted UTF-8 to only code points UTF-16 could encode.\n\t\to(decodeURIComponentSafe(\"%F5\")).equals(\"%F5\")\n\t\to(decodeURIComponentSafe(\"%FF\")).equals(\"%FF\")\n\t\to(decodeURIComponentSafe(\"%F5%80%80%80\")).equals(\"%F5%80%80%80\") // U+140000\n\t\to(decodeURIComponentSafe(\"%FF%8F%BF%BF\")).equals(\"%FF%8F%BF%BF\")\n\t})\n\n\to(\"malformed URI sequence\", function() {\n\t\t// \"%\" only\n\t\to(() => decodeURIComponent(\"%\")).throws(URIError)\n\t\to(decodeURIComponentSafe(\"%\")).equals(\"%\")\n\t\t// \"%\" with one digit\n\t\to(() => decodeURIComponent(\"%1\")).throws(URIError)\n\t\to(decodeURIComponentSafe(\"%1\")).equals(\"%1\")\n\t\t// \"%\" with non-hexadecimal\n\t\to(() => decodeURIComponent(\"%G0\")).throws(URIError)\n\t\to(decodeURIComponentSafe(\"%G0\")).equals(\"%G0\")\n\t\t// \"%\" in string\n\t\to(() => decodeURIComponent(\"x%y\")).throws(URIError)\n\t\to(decodeURIComponentSafe(\"x%y\")).equals(\"x%y\")\n\t\t// Overlong encoding\n\t\to(() => decodeURIComponent(\"%E0%80%AF\")).throws(URIError)\n\t\to(decodeURIComponentSafe(\"%E0%80%AF\")).equals(\"%E0%80%AF\")\n\t\t// surrogate\n\t\to(() => decodeURIComponent(\"%ED%A0%80\")).throws(URIError)\n\t\to(decodeURIComponentSafe(\"%ED%A0%80\")).equals(\"%ED%A0%80\")\n\t})\n})\n"
  }
]