[
  {
    "path": ".eustia.js",
    "content": "module.exports = {\n  test: {\n    library: ['node_modules/eustia-module'],\n    files: ['test/*.js', 'test/*.html'],\n    exclude: ['js'],\n    namespace: 'util',\n    output: 'test/util.js',\n  },\n}\n"
  },
  {
    "path": ".gitattributes",
    "content": "* text=auto eol=lf"
  },
  {
    "path": ".github/FUNDING.yml",
    "content": "open_collective: eruda\nko_fi: surunzi\ncustom: [surunzi.com/wechatpay.html]"
  },
  {
    "path": ".github/workflows/main.yml",
    "content": "name: CI\n\non:\n  workflow_dispatch:\n  push:\n    branches:\n      - 'master'\n    paths:\n      - 'src/**/*'\n      - 'test/**/*'\n\njobs:\n  ci:\n\n    runs-on: ubuntu-latest\n\n    steps:\n    - uses: actions/checkout@v4\n    - uses: actions/setup-node@v4\n      with:\n        node-version: '18.x' \n    - run: |\n        npm install -g @liriliri/lsla\n        npm i\n        npm run ci\n    - uses: codecov/codecov-action@v4\n      with:\n        token: ${{ secrets.CODECOV_TOKEN }} "
  },
  {
    "path": ".github/workflows/publish.yml",
    "content": "name: Publish to NPM\n\non:\n  workflow_dispatch:\n  release:\n    types: [created]\n\njobs:\n  publish:\n\n    runs-on: ubuntu-latest\n\n    steps:\n      - uses: actions/checkout@v4\n      - uses: actions/setup-node@v4\n        with:\n          node-version: '18.x'\n          registry-url: 'https://registry.npmjs.org'\n      - run: |\n          npm i -g @liriliri/lsla\n          npm i\n          npm run build\n      - working-directory: dist\n        run: npm publish\n        env:\n          NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}"
  },
  {
    "path": ".gitignore",
    "content": ".idea/\ndist/\nnode_modules/\ntest/lib/\ncoverage/\ntest/playground.html\nnpm-debug.log\npackage-lock.json"
  },
  {
    "path": ".gitmodules",
    "content": "[submodule \"src/style/icon\"]\n\tpath = src/style/icon\n\turl = https://github.com/liriliri/icon-share.git\n"
  },
  {
    "path": ".prettierignore",
    "content": "test/util.js"
  },
  {
    "path": ".prettierrc.json",
    "content": "{\n  \"singleQuote\": true,\n  \"tabWidth\": 2,\n  \"semi\": false\n}\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "## 3.4.3 (15 Jun 2025)\n\n* fix: redundant code imported\n\n## 3.4.2 (15 Jun 2025)\n\n* fix: elements horizontal scrollbar [#504](https://github.com/liriliri/eruda/issues/504)\n\n## 3.4.1 (10 Nov 2024)\n\n* fix: no copy and delete for shadow root\n* fix: fetch remains pending when error occurs\n* fix: theme not updated if system theme changed\n\n## 3.4.0 (27 Sep 2024)\n\n* feat: support shadow dom [#158](https://github.com/liriliri/eruda/issues/158)\n* fix: quirks mode table rendering [#459](https://github.com/liriliri/eruda/issues/459)\n\n## 3.3.0 (9 Sep 2024)\n\n* feat: add vue devtools plugin\n\n## 3.2.3 (10 AUG 2024)\n\n* fix: WebSocket message base64 encoded [#447](https://github.com/liriliri/eruda/issues/447)\n\n## 3.2.2 (8 AUG 2024)\n\n* chore: update plugin versions\n\n## 3.2.1 (20 JUL 2024)\n\n* fix: touches plugin [#344](https://github.com/liriliri/eruda/issues/344)\n\n## 3.2.0 (16 JUL 2024)\n\n* feat: support inline mode\n* feat: allow spaces in plugin name\n* fix: some typescript d.ts mistakes\n* chore: remove elements set api\n* chore: update monitor plugin version\n\n## 3.1.0 (9 JUL 2024)\n\n* feat: add AMOLED theme [#414](https://github.com/liriliri/eruda/pull/414)\n* feat: support system preference theme config\n* feat: add isDarkTheme, getTheme util\n* fix: backers.svg lazy loading [#407](https://github.com/liriliri/eruda/issues/407)\n\n## 3.0.1 (18 JUL 2023)\n\n* fix: can not print string with %o [#336](https://github.com/liriliri/eruda/issues/336)\n* fix: mouse event on touch device [#302](https://github.com/liriliri/eruda/issues/302)\n* fix: unable to remove snippets [#349](https://github.com/liriliri/eruda/issues/349)\n\n## 3.0.0 (2 Apr 2023)\n\n* feat: replace fps and memory with monitor plugin\n* fix: resource stylesheet show failed\n* chore: remove licia utils\n* chore: separate polyfill\n\n## 2.11.3 (3 Mar 2023)\n\n* fix: scale [#307](https://github.com/liriliri/eruda/issues/307)\n\n## 2.11.2 (28 Jan 2023)\n\n* fix: check safe area error\n\n## 2.11.1 (28 Jan 2023)\n\n* fix: bottom safe area\n* fix(console): filter function support\n* fix: click event stop propagation [#155](https://github.com/liriliri/eruda/issues/155)\n* fix: worker null error [#152](https://github.com/liriliri/eruda/issues/152)\n\n## 2.11.0 (19 Jan 2023)\n\n* feat(network): filter\n* feat(info): add backers\n* feat(settings): use luna setting\n* feat(resources): use luna data grid\n* feat(resources): copy storage, cookie\n* fix(sources): code not selectable\n* fix(console): filter api\n\n## 2.10.0 (24 Dec 2022)\n\n* feat(sources): use luna text viewer\n* feat(elements): split mode\n* feat(network): split mode\n* fix(resources): delete cookie\n\n## 2.9.1 (20 Dec 2022)\n\n* fix(elements): select element using touch events\n\n## 2.9.0 (20 Dec 2022)\n\n* feat(elements): integrate dom viewer\n* feat(elements): element crumbs\n* feat(elements): copy node and delete node\n* feat(network): copy response\n* feat(network): toggle recording\n* chore: remove dom plugin snippet\n\n## 2.8.3 (13 Dec 2022)\n\n* fix(network): remove data grid ios outline\n* chore: update luna console and luna object viewer \n\n## 2.8.2 (12 Dec 2022)\n\n* fix: some variables not reset when destroy\n\n## 2.8.1 (12 Dec 2022)\n\n* fix: remove luna syntax highlighter\n\n## 2.8.0 (11 Dec 2022)\n\n* feat(info): copy\n* feat(sources): use luna syntax highlighter\n* feat(network): use luna data grid\n* feat(network): copy as curl [#220](https://github.com/liriliri/eruda/issues/220)\n* fix(network): recognize JSON [#201](https://github.com/liriliri/eruda/issues/201)\n* fix: init with shadow dom style error [#195](https://github.com/liriliri/eruda/issues/195)\n\n## 2.7.4 (10 Dec 2022)\n\n* fix: firefox document.body is null error [#293](https://github.com/liriliri/eruda/issues/293)\n\n## 2.7.3 (8 Dec 2022)\n\n* fix: remove tabs horizontal scrollbar [#236](https://github.com/liriliri/eruda/issues/236)\n\n## 2.7.2 (7 Dec 2022)\n\n* fix: luna modal style\n\n## 2.7.1 (7 Dec 2022)\n\n* fix: remove debug log\n\n## 2.7.0 (7 Dec 2022)\n\n* feat: drag to resize\n* feat: update icons\n* feat: use luna modal to replace browser prompt\n\n## 2.6.2 (3 Dec 2022)\n\n* feat: support android 5.0\n* feat(sources): remove code beautify\n* fix: code plugin theme\n\n## 2.6.1 (26 Nov 2022)\n\n* fix: dark mode scrollbar style\n* fix: unable to load timing plugin\n\n## 2.6.0 (25 Nov 2022)\n\n* feat(console): select and copy\n* chore: update luna console\n* chore: update chobitsu\n\n## 2.5.0 (9 Jul 2022)\n\n* feat: add ts declaration [#187](https://github.com/liriliri/eruda/pull/187)\n* refactor: use luna console\n* refactor: use chobitsu for highlighting element\n\n## 2.4.1 (28 Sep 2020)\n\n* fix: remove arrow function [#160](https://github.com/liriliri/eruda/issues/160)\n\n## 2.4.0 (14 Sep 2020)\n\n* feat: default settings [#141](https://github.com/liriliri/eruda/issues/141)\n* fix(elements): highlight\n* fix(console): blinks frequently as it scroll to the border\n* refactor: use chobitsu\n\n## 2.3.3 (3 May 2020)\n\n* fix: unsafe-eval CSP violation [#140](https://github.com/liriliri/eruda/issues/140)\n\n## v2.3.2 (29 Apr 2020)\n\n* fix(console): scroll performance\n\n## v2.3.1 (28 Apr 2020)\n\n* fix(elements): content highlight\n\n## v2.3.0 (28 Apr 2020)\n\n* feat: refresh notification\n* fix(console): safari bounce effect\n* fix(elements): highlight\n\n## v2.2.2 (17 Apr 2020)\n\n* fix(console): extra info from\n* chore: update icons\n\n## v2.2.1 (20 Mar 2020)\n\n* fix: redundant evaluated style\n* chore: use [luna-object-viewer](https://github.com/liriliri/luna) for viewing object\n\n## v2.2.0 (9 Feb 2020)\n\n* feat: use dark theme for dark mode\n* feat(elements): computed style filter\n* feat(resources): storage and cookie filter\n* fix(snippet): error loading plugin for local page\n* fix(console): unable to clear filter\n\n## v2.1.0 (2 Feb 2020)\n\n* feat: change navigation bar height\n* feat: change default transparency to 1\n* feat: change loaded plugin position\n* feat(console): remove debug filter\n* feat(console): improve input style\n* feat(console): show filter text\n* feat(network): add requests api [#132](https://github.com/liriliri/eruda/issues/132)\n\n## v2.0.2 (9 Jan 2020)\n\n* chore: reduce file size (452kb -> 418kb)\n\n## v2.0.1 (6 Jan 2020)\n\n* chore: update plugins\n\n## v2.0.0 (3 Jan 2020)\n\n* feat: theme support\n* feat(console): $x utility\n* feat(console): remove useWorker\n* feat(sources): indent size configuration\n* fix(console): url recognition\n* fix(console): log style\n* fix(sources): scrolling\n* perf(console): large object expansion\n* chore: reduce file size (472kb -> 452kb)\n\n## v1.10.3 (8 Nov 2019)\n\n* fix(info): escape location [#127](https://github.com/liriliri/eruda/issues/127)\n* chore: update refresh icon\n* chore: update timing plugin version\n\n## v1.10.2 (5 Nov 2019)\n\n* fix: must add .default if using require \n\n## v1.10.1 (4 Nov 2019)\n\n* fix(console): error display when js execution disabled\n\n## v1.10.0 (4 Nov 2019)\n\n* chore: updated to babel7, must add .default if using require \n* feat(console): multiple console instance\n* perf(console): rendering for a large number of logs\n\n## v1.9.2 (1 Nov 2019)\n\n* perf(console): rendering\n\n## v1.9.1 (27 Oct 2019)\n\n* perf(console): asynchronous log render\n* perf(console): reduce memory usage, 50% drop\n\n## v1.9.0 (20 Oct 2019)\n\n* feat: add snippet for loading touches plugin\n* feat: add fit screen snippet\n* fix(console): filter shouldn't affect group\n\n## v1.8.1 (14 Oct 2019)\n\n* fix(network): style [#121](https://github.com/liriliri/eruda/issues/121)\n\n## v1.8.0 (13 Oct 2019)\n\n* feat(network): display optimization\n* feat: move http view from sources to network\n* fix(console): group object expansion\n\n## v1.7.2 (11 Oct 2019)\n\n* fix(console): blank bottom if js input is disabled\n* chore: update eruda-dom version\n\n## v1.7.1 (10 Oct 2019)\n\n* fix: resize\n\n## v1.7.0 (8 Oct 2019)\n\n* feat: resize [#89](https://github.com/liriliri/eruda/issues/89)\n* feat(console): replace help button with filter\n* feat(console): disable js execution\n* feat(console): [utilities api](https://developers.google.cn/web/tools/chrome-devtools/console/utilities)\n* fix(console): disable log collapsing for group\n* fix(elements): select not working for desktop\n\n## v1.6.3 (1 Oct 2019)\n\n* fix(console): log border style\n\n## v1.6.2 (29 Sep 2019)\n\n* fix: container style affected [#119](https://github.com/liriliri/eruda/issues/119)\n* fix(console): log style, line-height should be normal\n\n## v1.6.1 (27 Sep 2019)\n\n* feat(network): catch fetch request headers\n* feat(console): timeLog, countReset\n* fix(console): clear not working\n* fix(console): table\n\n## v1.6.0 (26 Sep 2019)\n\n* feat: console group\n* fix: console style, width and height is forbidden\n* fix: regexp json view\n* chore: update fps and memory plugin version\n\n## v1.5.8 (2 Aug 2019)\n\n* fix: safeStorage undefined [#108](https://github.com/liriliri/eruda/issues/108)\n\n## v1.5.7 (15 Jul 2019)\n\n* Fix iOS max log number\n* Disable calling init if already initialized\n* Disable worker by default\n* Support xhr blob response type [#104](https://github.com/liriliri/eruda/issues/100)\n\n## v1.5.6 (17 Jun 2019)\n\n* Disable log collapse for objects\n\n## v1.5.5 (25 May 2019)\n\n* Fix resources error when cookie has % [#100](https://github.com/liriliri/eruda/issues/100)\n* Update dom plugin version\n\n## v1.5.4 (23 Sep 2018)\n\n* Fix network url start with //\n* Smaller padding for logs\n\n## v1.5.3 (2 Sep 2018)\n\n* Add load dom plugin snippet\n* Disable highlight for invisible elements\n* Fix unexpected token \\t in JSON\n* Add load orientation plugin snippet\n\n## v1.5.2 (23 Aug 2018)\n\n* Fix console show in sources panel\n* Fix log merge\n* Support getting entryBtn instance\n* Update timing plugin version\n* Add remove setting api\n* Fix safari merge log exception\n\n## v1.5.1 (18 Aug 2018)\n\n* Fix uglifyjs unicode escape [#69](https://github.com/liriliri/eruda/issues/69)\n* Update icons, use [iconfont](http://www.iconfont.cn) instead of [icomoon](https://icomoon.io/)\n* Show custom request headers [#78](https://github.com/liriliri/eruda/pull/78)\n* Add get api to info panel [#83](https://github.com/liriliri/eruda/issues/83)\n* Fix responseType json error [#82](https://github.com/liriliri/eruda/issues/82)\n* Support console lazy evaluation\n\n## v1.5.0 (19 Jun 2018)\n\n* Use shadow dom to encapsulate css\n* Enable sources copy [#71](https://github.com/liriliri/eruda/issues/71)\n* Improve **borderAll** style\n* Add **position** api [#74](https://github.com/liriliri/eruda/issues/74)\n* Fix nav bottom bar wrong position when removed\n\n## v1.4.4 (27 May 2018)\n\n* Improve console line break display\n* Add **rmCookie** util\n* Add **Load Geolocation Plugin** snippet\n* Fix Elements cssRules [#63](https://github.com/liriliri/eruda/issues/63)\n* Support console events [#66](https://github.com/liriliri/eruda/issues/66)\n* Fix Uc browser console worker [#62](https://github.com/liriliri/eruda/issues/62)\n\n## v1.4.3 (7 Feb 2018)\n\n* Dynamic info content support [#51](https://github.com/liriliri/eruda/issues/51)\n* Fix console input covered by error log\n* Add elements box model chart\n* Fix source code white-space style [#53](https://github.com/liriliri/eruda/issues/53)\n* Resources support iframe\n* Add **Load Benchmark Plugin** snippet\n\n## v1.4.2 (28 Jan 2018)\n\n* Extract viewportScale util into [eris](https://github.com/liriliri/eris)\n* Improve image list view using flex\n* Add DevTools display event hooks [#50](https://github.com/liriliri/eruda/issues/50)\n\n## v1.4.1 (13 Jan 2018)\n\n* Update timing plugin version\n* Fix viewportScale\n* Optimize console performance for big data\n* Expose snippets run api\n* Delete desktop scrollbar style\n* Add code plugin to snippets\n\n## v1.4.0 (7 Jan 2018)\n\n* Remove network timing into external plugin\n* Add system info\n* Add memory plugin snippet\n* Monitor fetch requests [#24](https://github.com/liriliri/eruda/issues/24)\n* Reduce json viewer click area\n* Use resource timing for image capture\n\n## v1.3.2 (14 Dec 2017)\n\n* Fix restore settings snippet\n* Extract *features* into an external plugin\n\n## v1.3.1 (19 Nov 2017)\n\n* Observe elements in resources panel\n* Fix performance timing not supported [#40](https://github.com/liriliri/eruda/issues/40)\n\n## v1.3.0 (5 Nov 2017)\n\n* Remove log margin\n* Fix css custom properties [#33](https://github.com/liriliri/eruda/issues/33)\n* Add version info\n* Change icomoon generated font name\n* Improve snippets style\n* Add *Load Fps Plugin* and *Restore Settings* snippets\n* Support navbar color customization\n* Support range in settings panel\n* Support auto scale [#32](https://github.com/liriliri/eruda/issues/32)\n* Improve *Border All* snippet\n* Use high resolution time for console time\n\n## v1.2.6 (31 Aug 2017)\n\n* Fix catch global errors\n\n## v1.2.5 (20 Aug 2017)\n\n* Fix cookie URI malformed\n* Fix single string argument unescaped\n* Update util library and dependencies\n* Fix catch event listeners [#31](https://github.com/liriliri/eruda/issues/31)\n* Console log scroll automatically only at bottom\n* Fix unformatted html tag\n\n## v1.2.4 (1 Jul 2017)\n\n* Fix uncaught promise error [#29](https://github.com/liriliri/eruda/issues/23)\n* Fix bad classes [#28](https://github.com/liriliri/eruda/issues/23)\n\n## v1.2.3 (15 May 2017)\n\n* Disable modernizr classes\n* Update eustia util\n* Fix console resize [#23](https://github.com/liriliri/eruda/issues/23)\n* Improve object log\n* Use outline for borderAll snippet\n\n## v1.2.2 (11 Mar 2017)\n\n* Fix log url recognition\n* Fix error log stack url and style\n* Fix table log ouput\n* Fix storage initialization [#20](https://github.com/liriliri/eruda/issues/20)\n* Update eustia lib\n* Elements auto refresh\n* Add pc scrollbar style"
  },
  {
    "path": "LICENSE",
    "content": "The MIT License (MIT)\n\nCopyright (c) 2016-present liriliri\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": "<div align=\"center\">\n  <a href=\"https://eruda.liriliri.io/\" target=\"_blank\">\n    <img src=\"https://eruda.liriliri.io/icon.png\" width=\"400\">\n  </a>\n</div>\n\n<h1 align=\"center\">Eruda</h1>\n\n<div align=\"center\">\n\nConsole for Mobile Browsers.\n\n[![NPM version][npm-image]][npm-url]\n[![Build status][ci-image]][ci-url]\n[![Test coverage][codecov-image]][codecov-url]\n[![Downloads][jsdelivr-image]][jsdelivr-url]\n[![License][license-image]][npm-url]\n\n</div>\n\n[npm-image]: https://img.shields.io/npm/v/eruda?style=flat-square\n[npm-url]: https://npmjs.org/package/eruda\n[jsdelivr-image]: https://img.shields.io/jsdelivr/npm/hm/eruda?style=flat-square\n[jsdelivr-url]: https://www.jsdelivr.com/package/npm/eruda\n[ci-image]: https://img.shields.io/github/actions/workflow/status/liriliri/eruda/main.yml?branch=master&style=flat-square\n[ci-url]: https://github.com/liriliri/eruda/actions/workflows/main.yml \n[codecov-image]: https://img.shields.io/codecov/c/github/liriliri/eruda?style=flat-square\n[codecov-url]: https://codecov.io/github/liriliri/eruda?branch=master\n[license-image]: https://img.shields.io/npm/l/eruda?style=flat-square\n[donate-image]: https://img.shields.io/badge/$-donate-0070ba.svg?style=flat-square\n\n<img src=\"https://eruda.liriliri.io/screenshot.jpg\" style=\"width:100%\">\n\n## Demo\n\n![Demo](https://eruda.liriliri.io/qrcode.png)\n\nBrowse it on your phone: [eruda.liriliri.io](https://eruda.liriliri.io/)\n\n## Install\n\nYou can get it on npm.\n\n```bash\nnpm install eruda --save-dev\n```\n\nAdd this script to your page.\n\n```html\n<script src=\"node_modules/eruda/eruda.js\"></script>\n<script>eruda.init();</script>\n```\n\nIt's also available on [jsDelivr](http://www.jsdelivr.com/projects/eruda) and [cdnjs](https://cdnjs.com/libraries/eruda).\n\n```html\n<script src=\"https://cdn.jsdelivr.net/npm/eruda\"></script>\n<script>eruda.init();</script>\n```\n\nFor more detailed usage instructions, please read the documentation at [eruda.liriliri.io](https://eruda.liriliri.io/docs/)!\n\n## Related Projects\n\n* [eruda-android](https://github.com/liriliri/eruda-android): Simple webview with eruda loaded automatically.\n* [chii](https://github.com/liriliri/chii): Remote debugging tool.\n* [chobitsu](https://github.com/liriliri/chobitsu): Chrome devtools protocol JavaScript implementation.\n* [licia](https://github.com/liriliri/licia): Utility library used by eruda.\n* [luna](https://github.com/liriliri/luna): UI components used by eruda.\n* [vivy](https://github.com/liriliri/vivy-docs): Icon image generation.\n\n## Third Party\n\n* [eruda-pixel](https://github.com/Faithree/eruda-pixel): UI pixel restoration tool.\n* [eruda-webpack-plugin](https://github.com/huruji/eruda-webpack-plugin): Eruda webpack plugin.\n* [eruda-vue-devtools](https://github.com/Zippowxk/vue-devtools-plugin): Eruda Vue-devtools plugin.\n\n## Backers\n\n<a rel=\"noreferrer noopener\" href=\"https://opencollective.com/eruda\" target=\"_blank\"><img src=\"https://opencollective.com/eruda/backers.svg?width=890\"></a>\n\n## Contribution\n\nRead [Contributing Guide](https://eruda.liriliri.io/docs/contributing.html) for development setup instructions.\n"
  },
  {
    "path": "build/build.js",
    "content": "const path = require('path')\nconst fs = require('licia/fs')\n\nconst pkg = require('../package.json')\n\ndelete pkg.scripts\ndelete pkg.devDependencies\n\nfs.writeFile(\n  path.resolve(__dirname, '../dist/package.json'),\n  JSON.stringify(pkg, null, 2),\n  'utf8'\n)\n"
  },
  {
    "path": "build/loaders/handlebars-minifier-loader.js",
    "content": "module.exports = function (src) {\n    return src.replace(/\"loc\":\\{\"start\":\\{\"line\":\\d+,\"column\":\\d+},\"end\":\\{\"line\":\\d+,\"column\":\\d+\\}\\}/g, '')\n}"
  },
  {
    "path": "build/webpack.analyser.js",
    "content": "const BundleAnalyzerPlugin =\n  require('webpack-bundle-analyzer').BundleAnalyzerPlugin\n\nexports = require('./webpack.prod')\n\nexports.plugins.push(new BundleAnalyzerPlugin())\n\nmodule.exports = exports\n"
  },
  {
    "path": "build/webpack.base.js",
    "content": "const autoprefixer = require('autoprefixer')\nconst prefixer = require('postcss-prefixer')\nconst clean = require('postcss-clean')\nconst webpack = require('webpack')\nconst pkg = require('../package.json')\nconst path = require('path')\nconst ESLintPlugin = require('eslint-webpack-plugin')\n\nprocess.traceDeprecation = true\n\nconst banner = pkg.name + ' v' + pkg.version + ' ' + pkg.homepage\n\nconst postcssLoader = {\n  loader: 'postcss-loader',\n  options: {\n    plugins: [\n      prefixer({\n        prefix: '_',\n        ignore: [/luna-*/],\n      }),\n      autoprefixer,\n      clean(),\n    ],\n  },\n}\n\nconst rawLoader = {\n  loader: 'raw-loader',\n  options: {\n    esModule: false,\n  },\n}\n\nmodule.exports = {\n  entry: './src/index',\n  resolve: {\n    symlinks: false,\n    alias: {\n      axios: path.resolve(__dirname, '../src/lib/empty.js'),\n      micromark: path.resolve(__dirname, '../src/lib/micromark.js'),\n    },\n  },\n  devServer: {\n    static: {\n      directory: path.join(__dirname, '../test'),\n    },\n    port: 8080,\n  },\n  output: {\n    path: path.resolve(__dirname, '../dist'),\n    publicPath: '/assets/',\n    library: 'eruda',\n    libraryTarget: 'umd',\n  },\n  module: {\n    rules: [\n      {\n        test: /\\.js$/,\n        include: [\n          path.resolve(__dirname, '../src'),\n          path.resolve(__dirname, '../node_modules/luna-console'),\n          path.resolve(__dirname, '../node_modules/luna-modal'),\n          path.resolve(__dirname, '../node_modules/luna-tab'),\n          path.resolve(__dirname, '../node_modules/luna-data-grid'),\n          path.resolve(__dirname, '../node_modules/luna-object-viewer'),\n          path.resolve(__dirname, '../node_modules/luna-dom-viewer'),\n          path.resolve(__dirname, '../node_modules/luna-text-viewer'),\n          path.resolve(__dirname, '../node_modules/luna-setting'),\n          path.resolve(__dirname, '../node_modules/luna-box-model'),\n          path.resolve(__dirname, '../node_modules/luna-notification'),\n        ],\n        use: [\n          {\n            loader: 'babel-loader',\n            options: {\n              sourceType: 'unambiguous',\n              presets: ['@babel/preset-env'],\n              plugins: [\n                '@babel/plugin-transform-runtime',\n                '@babel/plugin-proposal-class-properties',\n              ],\n            },\n          },\n        ],\n      },\n      {\n        test: /\\.scss$/,\n        use: [\n          'css-loader',\n          postcssLoader,\n          { loader: 'sass-loader', options: { api: 'modern' } },\n        ],\n      },\n      {\n        test: /\\.css$/,\n        exclude: /luna-dom-highlighter/,\n        use: ['css-loader', postcssLoader],\n      },\n      {\n        test: /luna-dom-highlighter\\.css$/,\n        use: [rawLoader],\n      },\n    ],\n  },\n  plugins: [\n    new webpack.BannerPlugin(banner),\n    new webpack.DefinePlugin({\n      VERSION: '\"' + pkg.version + '\"',\n    }),\n    new ESLintPlugin(),\n  ],\n}\n"
  },
  {
    "path": "build/webpack.dev.js",
    "content": "const webpack = require('webpack')\n\nexports = require('./webpack.base')\n\nexports.mode = 'development'\nexports.output.filename = 'eruda.js'\nexports.devtool = 'source-map'\nexports.plugins = exports.plugins.concat([\n  new webpack.DefinePlugin({\n    ENV: '\"development\"',\n  }),\n])\n\nmodule.exports = exports\n"
  },
  {
    "path": "build/webpack.polyfill.js",
    "content": "const path = require('path')\n\nmodule.exports = {\n  mode: 'production',\n  entry: './src/polyfill',\n  output: {\n    path: path.resolve(__dirname, '../dist'),\n    filename: 'eruda-polyfill.js',\n  },\n}\n"
  },
  {
    "path": "build/webpack.prod.js",
    "content": "const webpack = require('webpack')\nconst TerserPlugin = require('terser-webpack-plugin')\n\nexports = require('./webpack.base')\n\nexports.mode = 'production'\nexports.output.filename = 'eruda.js'\nexports.devtool = 'source-map'\nexports.plugins = exports.plugins.concat([\n  new webpack.DefinePlugin({\n    ENV: '\"production\"',\n  }),\n])\nexports.optimization = {\n  minimize: true,\n  minimizer: [\n    new TerserPlugin({\n      extractComments: false,\n    }),\n  ],\n}\n\nmodule.exports = exports\n"
  },
  {
    "path": "eruda.d.ts",
    "content": "/**\n * Type definitions for Eruda\n * @see https://github.com/liriliri/eruda\n */\ndeclare module 'eruda' {\n  export interface InitDefaults {\n    /**\n     * Transparency, 0 to 1\n     */\n    transparency?: number\n    /**\n     * Display size, 0 to 100\n     */\n    displaySize?: number\n    /**\n     * Theme, defaults to Light or Dark in dark mode\n     */\n    theme?: string\n  }\n\n  export interface InitOptions {\n    /**\n     * Container element. If not set, it will append an element directly under html root element\n     */\n    container?: HTMLElement\n    /**\n     * Choose which default tools you want, by default all will be added\n     */\n    tool?: string[]\n    /**\n     * Auto scale eruda for different viewport settings\n     */\n    autoScale?: boolean\n    /**\n     * Use shadow dom for css encapsulation\n     */\n    useShadowDom?: boolean\n    /**\n     * Enable inline mode\n     */\n    inline?: boolean\n    /**\n     * Default settings\n     */\n    defaults?: InitDefaults\n  }\n\n  export interface Position {\n    x: number\n    y: number\n  }\n\n  type AnyFn = (...args: any[]) => any\n\n  export interface Emitter {\n    on(event: string, listener: AnyFn): Emitter\n    off(event: string, listener: AnyFn): Emitter\n    once(event: string, listener: AnyFn): Emitter\n    emit(event: string, ...args: any[]): Emitter\n    removeAllListeners(event?: string): Emitter\n  }\n\n  /**\n   * Eruda Plugin\n   * @see https://eruda.liriliri.io/docs/plugin.html\n   */\n  export interface Tool {\n    /**\n     * Every plugin must have a unique name, which will be shown as the tab name on the top.\n     */\n    name: string\n    /**\n     * Called when plugin is added, and a document element used to display content is passed in.\n     * The element is wrapped as a jQuery like object, provided by the licia utility library.\n     */\n    init(el: unknown): void\n    /**\n     * Called when switch to the panel. Usually all you need to do is to show the container element.\n     */\n    show(): Tool | undefined\n    /**\n     * Called when switch to other panel. You should at least hide the container element here.\n     */\n    hide(): Tool | undefined\n    /**\n     * Called when plugin is removed using `eruda.remove('plugin name')`.\n     */\n    destroy(): void\n  }\n\n  export interface ToolConstructor {\n    new (): Tool\n    readonly prototype: Tool\n\n    extend(tool: Tool): ToolConstructor\n  }\n\n  export interface ConsoleConfig {\n    /**\n     * Asynchronous rendering\n     */\n    asyncRender?: boolean\n    /**\n     * Enable JavaScript execution\n     */\n    jsExecution?: boolean\n    /**\n     * Catch global errors\n     */\n    catchGlobalErr?: boolean\n    /**\n     * Override console\n     */\n    overrideConsole?: boolean\n    /**\n     * Display extra information\n     */\n    displayExtraInfo?: boolean\n    /**\n     * Display unenumerable properties\n     */\n    displayUnenumerable?: boolean\n    /**\n     * Access getter value\n     */\n    displayGetterVal?: boolean\n    /**\n     * Stringify object when clicked\n     */\n    lazyEvaluation?: boolean\n    /**\n     * Auto display if error occurs\n     */\n    displayIfErr?: boolean\n    /**\n     * Max log number\n     */\n    maxLogNum?: string\n  }\n\n  export interface Log {\n    type: string\n  }\n\n  export interface ErudaConsole extends Tool, Console {\n    config: {\n      set<K extends keyof ConsoleConfig>(name: K, value: ConsoleConfig[K]): void\n    }\n    /**\n     * Custom filter\n     */\n    filter(pattern: string | RegExp | ((log: Log) => boolean)): void\n    /**\n     * Html string\n     */\n    html(htmlStr: string): void\n  }\n\n  export interface ErudaConsoleConstructor {\n    new (): ErudaConsole\n    readonly prototype: ErudaConsole\n  }\n\n  export interface ElementsConfig {\n    /**\n     * Catch Event Listeners\n     */\n    overrideEventTarget?: boolean\n    /**\n     * Auto Refresh\n     */\n    observeElement?: boolean\n  }\n\n  export interface Elements extends Tool {\n    config: {\n      set<K extends keyof ElementsConfig>(\n        name: K,\n        value: ElementsConfig[K]\n      ): void\n    }\n    /**\n     * Element to display\n     */\n    select(el: HTMLElement): void\n  }\n\n  export interface ElementsConstructor {\n    new (): Elements\n    readonly prototype: Elements\n  }\n\n  export interface Network extends Tool {\n    /**\n     * Clear requests\n     */\n    clear(): void\n    /**\n     * Get request data\n     */\n    requests(): object[]\n  }\n\n  export interface NetworkConstructor {\n    new (): Network\n    readonly prototype: Network\n  }\n\n  export interface ResourcesConfig {\n    /**\n     * Hide Eruda Setting\n     */\n    hideErudaSetting?: boolean\n    /**\n     * Auto Refresh Elements\n     */\n    observeElement?: boolean\n  }\n\n  export interface Resources extends Tool {\n    config: {\n      set<K extends keyof ResourcesConfig>(\n        name: K,\n        value: ResourcesConfig[K]\n      ): void\n    }\n  }\n\n  export interface ResourcesConstructor {\n    new (): Resources\n    readonly prototype: Resources\n  }\n\n  export interface SourcesConfig {\n    /**\n     * Show Line Numbers\n     */\n    showLineNum?: boolean\n    /**\n     * Beautify Code\n     */\n    formatCode?: boolean\n    /**\n     * Indent Size\n     */\n    indentSize?: string\n  }\n\n  export interface Sources extends Tool {\n    config: {\n      set<K extends keyof SourcesConfig>(name: K, value: SourcesConfig[K]): void\n    }\n  }\n\n  export interface SourcesConstructor {\n    new (): Sources\n    readonly prototype: Sources\n  }\n\n  export interface InfoItem {\n    name: string\n    val: string\n  }\n\n  export interface Info extends Tool {\n    /**\n     * Clear infos\n     */\n    clear(): void\n    /**\n     * Add info\n     */\n    add(name: string, content: string | (() => void)): void\n    /**\n     * Get info or infos\n     */\n    get(): InfoItem[]\n    get(name: string): string\n    /**\n     * Remove specified info\n     */\n    remove(name: string): void\n  }\n\n  export interface InfoConstructor {\n    new (): Info\n    readonly prototype: Info\n  }\n\n  export interface Snippets extends Tool {\n    /**\n     * Clear snippets\n     */\n    clear(): void\n    /**\n     * Add snippet\n     * @param name Snippet name\n     * @param fn Function to be triggered\n     * @param desc Snippet description\n     */\n    add(name: string, fn: Function, desc: string): void\n    /**\n     * Remove specified snippet\n     * @param name Snippet name\n     */\n    remove(name: string): void\n    /**\n     * Run specified snippet\n     * @param name Snippet name\n     */\n    run(name: string): void\n  }\n\n  export interface SnippetsConstructor {\n    new (): Snippets\n    readonly prototype: Snippets\n  }\n\n  export interface SettingsRangeOptions {\n    min?: number\n    max?: number\n    step?: number\n  }\n\n  export interface Settings extends Tool {\n    /**\n     * Clear settings\n     */\n    clear(): void\n    /**\n     * Remove setting\n     * @param cfg Config object\n     * @param name Option name\n     */\n    remove(cfj: object, name: string): void\n    /**\n     * Add text\n     */\n    text(str: string): void\n    /**\n     * Add switch to toggle a boolean value\n     * @param cfg Config object created by util.createCfg\n     * @param name Option name\n     * @param desc Option description\n     */\n    switch(cfg: object, name: string, desc: string): void\n    /**\n     * Add select to select a number of string values\n     * @param cfg Config object\n     * @param name Option name\n     * @param desc Option description\n     * @param values Array of strings to select\n     */\n    select(cfg: object, name: string, desc: string, values: string[]): void\n    /**\n     * Add range to input a number\n     * @param cfg Config object\n     * @param name Option name\n     * @param desc Option description\n     * @param options Min, max, step\n     */\n    range(\n      cfg: object,\n      name: string,\n      desc: string,\n      options?: SettingsRangeOptions\n    ): void\n    /**\n     * Add a separator\n     */\n    separator(): void\n  }\n\n  export interface SettingsConstructor {\n    new (): Settings\n    readonly prototype: Settings\n  }\n\n  export interface EntryBtn extends Emitter {\n    show(): void\n    hide(): void\n    getPos(): Position\n    setPos(pos: Position): void\n    destroy(): void\n  }\n\n  export interface EntryBtnConstructor {\n    new (): EntryBtn\n    readonly prototype: EntryBtn\n  }\n\n  export interface DevTools extends Emitter {\n    show(): DevTools\n    hide(): DevTools\n    toggle(): void\n    add(tool: Tool | object): DevTools\n    remove(name: string): DevTools\n    removeAll(): DevTools\n    get<T extends ToolConstructor>(name: string): InstanceType<T> | undefined\n    showTool(name: string): DevTools\n    initCfg(settings: Settings): void\n    notify(content: string, options: object): void\n    destroy(): void\n  }\n\n  export interface DevToolsConstructor {\n    new (): DevTools\n    readonly prototype: DevTools\n  }\n\n  /**\n   * Eruda Util\n   * @see https://eruda.liriliri.io/docs/plugin.html#utility\n   */\n  export interface Util {\n    evalCss(css: string): HTMLStyleElement\n    isErudaEl(val: any): boolean\n    isDarkTheme(theme?: string): boolean\n    getTheme(): string\n  }\n\n  interface IToolNameMap {\n    console: InstanceType<ErudaConsoleConstructor>\n    elements: InstanceType<ElementsConstructor>\n    info: InstanceType<InfoConstructor>\n    network: InstanceType<NetworkConstructor>\n    resources: InstanceType<ResourcesConstructor>\n    settings: InstanceType<SettingsConstructor>\n    snippets: InstanceType<SnippetsConstructor>\n    sources: InstanceType<SourcesConstructor>\n    entryBtn: InstanceType<EntryBtnConstructor>\n  }\n\n  /**\n   * Eruda APIs\n   * @see https://eruda.liriliri.io/docs/api.html\n   */\n  export interface ErudaApis {\n    /**\n     * Initialize eruda.\n     */\n    init(options?: InitOptions): void\n    /**\n     * Destory eruda.\n     * Note: You can call `init` method again after destruction.\n     */\n    destroy(): void\n    /**\n     * Set or get scale.\n     */\n    scale(): number\n    scale(s: number): Eruda\n    /**\n     * Set or get entry button position.\n     * It will not take effect if given pos is out of range.\n     */\n    position(): Position\n    position(p: Position): Eruda\n    /**\n     * Get tool, eg. console, elements panels.\n     */\n    get<K extends keyof IToolNameMap>(name: K): IToolNameMap[K]\n    get<T extends ToolConstructor>(name: string): InstanceType<T> | undefined\n    get(): InstanceType<DevToolsConstructor>\n    /**\n     * Add tool.\n     */\n    add<T extends ToolConstructor>(\n      tool: InstanceType<T> | ((eruda: Eruda) => InstanceType<T>)\n    ): Eruda | undefined\n    /**\n     * Remove tool.\n     */\n    remove(name: string): Eruda | undefined\n    /**\n     * Show eruda panel.\n     */\n    show(name?: string): Eruda | undefined\n    /**\n     * Hide eruda panel.\n     */\n    hide(): Eruda | undefined\n  }\n\n  export interface Eruda extends ErudaApis {\n    /**\n     * Display console logs. Implementation detail follows the console api spec.\n     */\n    Console: ErudaConsoleConstructor\n    /**\n     * Check dom element status.\n     */\n    Elements: ElementsConstructor\n    /**\n     * Display special information, could be used for displaying user info to track user logs.\n     * By default, page url and browser user agent is shown.\n     */\n    Info: InfoConstructor\n    /**\n     * Display requests.\n     */\n    Network: NetworkConstructor\n    /**\n     * LocalStorage, sessionStorage, cookies, scripts, styleSheets and images.\n     */\n    Resources: ResourcesConstructor\n    /**\n     * Customization for all tools.\n     */\n    Settings: SettingsConstructor\n    /**\n     * Allow you to register small functions that can be triggered multiple times.\n     */\n    Snippets: SnippetsConstructor\n    /**\n     * View object, html, js, and css.\n     */\n    Sources: SourcesConstructor\n    /**\n     * Eruda Tool\n     */\n    Tool: ToolConstructor\n    /**\n     * Eruda Util\n     */\n    util: Util\n    /**\n     * Eruda version\n     */\n    readonly version: string\n  }\n\n  const eruda: Eruda\n\n  export default eruda\n}\n"
  },
  {
    "path": "eslint.config.mjs",
    "content": "import babelEslintParser from '@babel/eslint-parser'\nimport eslintJs from '@eslint/js'\nimport globals from 'globals'\n\nexport default [\n  eslintJs.configs.recommended,\n  {\n    languageOptions: {\n      parser: babelEslintParser,\n      parserOptions: {\n        requireConfigFile: false,\n        babelOptions: {\n          babelrc: false,\n          configFile: false,\n        },\n      },\n      globals: {\n        ...globals.builtin,\n        ...globals.browser,\n        ...globals.commonjs,\n        VERSION: true,\n        ENV: true,\n      },\n    },\n    rules: {\n      quotes: ['error', 'single', { 'avoidEscape': true, 'allowTemplateLiterals': false }],\n      'prefer-const': 2,\n    },\n  },\n  {files: ['build/**/*.js'], languageOptions:{globals: {...globals.node}}},\n  {\n    ignores: ['test','dist','coverage'],\n  }\n]\n"
  },
  {
    "path": "karma.conf.js",
    "content": "const webpackCfg = require('./build/webpack.dev')\nwebpackCfg.devtool = 'inline-source-map'\nwebpackCfg.module.rules.push({\n  test: /\\.js$/,\n  exclude: /node_modules|lib\\/util\\.js/,\n  loader: '@jsdevtools/coverage-istanbul-loader',\n  enforce: 'post',\n  options: {\n    esModules: true,\n  },\n})\n\nmodule.exports = function (config) {\n  config.set({\n    basePath: '',\n    frameworks: ['jquery-1.8.3'],\n    files: [\n      'src/index.js',\n      'test/init.js',\n      'node_modules/jasmine-core/lib/jasmine-core/jasmine.js',\n      'node_modules/karma-jasmine/lib/boot.js',\n      'node_modules/karma-jasmine/lib/adapter.js',\n      'node_modules/jasmine-jquery/lib/jasmine-jquery.js',\n      'test/util.js',\n      'test/console.js',\n      'test/elements.js',\n      'test/info.js',\n      'test/network.js',\n      'test/resources.js',\n      'test/snippets.js',\n      'test/sources.js',\n      'test/settings.js',\n      'test/eruda.js',\n    ],\n    plugins: [\n      'karma-jasmine',\n      'karma-jquery',\n      'karma-chrome-launcher',\n      'karma-webpack',\n      'karma-sourcemap-loader',\n      'karma-coverage-istanbul-reporter',\n    ],\n    webpackServer: {\n      noInfo: true,\n    },\n    preprocessors: {\n      'src/index.js': ['webpack', 'sourcemap'],\n    },\n    webpack: webpackCfg,\n    coverageIstanbulReporter: {\n      reports: ['html', 'lcovonly', 'text', 'text-summary'],\n    },\n    reporters: ['progress', 'coverage-istanbul'],\n    port: 9876,\n    colors: true,\n    logLevel: config.LOG_INFO,\n    browsers: ['ChromeHeadless'],\n    singleRun: true,\n    concurrency: Infinity,\n  })\n}\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"eruda\",\n  \"version\": \"3.4.3\",\n  \"description\": \"Console for Mobile Browsers\",\n  \"main\": \"eruda.js\",\n  \"browserslist\": [\n    \"since 2015\",\n    \"not dead\"\n  ],\n  \"scripts\": {\n    \"ci\": \"npm run lint && npm run test && npm run build && npm run es5\",\n    \"build\": \"lsla shx rm -rf dist && webpack --config build/webpack.prod.js && webpack --config build/webpack.polyfill.js && node build/build && lsla shx cp README.md eruda.d.ts dist\",\n    \"build:analyser\": \"webpack --config build/webpack.analyser.js\",\n    \"dev\": \"webpack-dev-server --config build/webpack.dev.js --host 0.0.0.0\",\n    \"test\": \"karma start\",\n    \"format\": \"lsla prettier \\\"*.{js,ts}\\\" \\\"src/**/*.{js,scss,css,json}\\\" \\\"build/*.js\\\" \\\"test/*.{js,html}\\\" --write\",\n    \"lint\": \"eslint .\",\n    \"lint:fix\": \"npm run lint -- --fix\",\n    \"es5\": \"es-check es5 dist/eruda.js dist/eruda-polyfill.js\",\n    \"setup\": \"lsla shx mkdir -p test/lib && lsla shx cp node_modules/jasmine-core/lib/jasmine-core/{jasmine.css,jasmine.js,jasmine-html.js,boot.js} test/lib && lsla shx cp node_modules/jasmine-jquery/lib/jasmine-jquery.js test/lib && lsla shx cp node_modules/jquery/dist/jquery.js test/lib\",\n    \"genIcon\": \"lsla genIcon --input src/style/ --output src/style/icon.css --name eruda-icon --source src/style/icon/ && lsla prettier src/**/*.css --write\"\n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git+https://github.com/liriliri/eruda.git\"\n  },\n  \"keywords\": [\n    \"console\",\n    \"mobile\",\n    \"debug\"\n  ],\n  \"author\": \"redhoodsu\",\n  \"license\": \"MIT\",\n  \"bugs\": {\n    \"url\": \"https://github.com/liriliri/eruda/issues\"\n  },\n  \"homepage\": \"https://eruda.liriliri.io/\",\n  \"devDependencies\": {\n    \"@babel/core\": \"^7.18.6\",\n    \"@babel/eslint-parser\": \"^7.26.10\",\n    \"@babel/plugin-proposal-class-properties\": \"^7.18.6\",\n    \"@babel/plugin-transform-runtime\": \"^7.18.6\",\n    \"@babel/preset-env\": \"^7.18.6\",\n    \"@babel/runtime\": \"^7.18.6\",\n    \"@eslint/js\": \"^9.22.0\",\n    \"@jsdevtools/coverage-istanbul-loader\": \"^3.0.5\",\n    \"autoprefixer\": \"^9.7.4\",\n    \"babel-loader\": \"^8.2.5\",\n    \"chobitsu\": \"^1.8.4\",\n    \"core-js\": \"^3.37.1\",\n    \"css-loader\": \"^3.4.2\",\n    \"es-check\": \"^6.2.1\",\n    \"eslint\": \"^9.22.0\",\n    \"eslint-webpack-plugin\": \"^5.0.0\",\n    \"globals\": \"^16.0.0\",\n    \"jasmine-core\": \"^2.99.1\",\n    \"jasmine-jquery\": \"^2.1.1\",\n    \"jquery\": \"^3.4.1\",\n    \"karma\": \"^6.4.0\",\n    \"karma-chrome-launcher\": \"^3.1.0\",\n    \"karma-coverage-istanbul-reporter\": \"^2.1.1\",\n    \"karma-jasmine\": \"^1.1.2\",\n    \"karma-jquery\": \"^0.2.4\",\n    \"karma-sourcemap-loader\": \"^0.3.7\",\n    \"karma-webpack\": \"^5.0.0\",\n    \"licia\": \"^1.44.0\",\n    \"luna-box-model\": \"^1.0.1\",\n    \"luna-console\": \"^1.3.6\",\n    \"luna-data-grid\": \"^1.6.4\",\n    \"luna-dom-viewer\": \"^1.8.3\",\n    \"luna-modal\": \"^1.3.1\",\n    \"luna-notification\": \"^0.3.3\",\n    \"luna-object-viewer\": \"^0.3.2\",\n    \"luna-setting\": \"^2.0.2\",\n    \"luna-tab\": \"^0.4.3\",\n    \"luna-text-viewer\": \"^0.2.1\",\n    \"postcss-clean\": \"^1.2.2\",\n    \"postcss-loader\": \"^3.0.0\",\n    \"postcss-prefixer\": \"^2.1.3\",\n    \"raw-loader\": \"^4.0.2\",\n    \"sass\": \"^1.77.6\",\n    \"sass-loader\": \"^14.2.1\",\n    \"webpack\": \"^5.92.1\",\n    \"webpack-bundle-analyzer\": \"^4.7.0\",\n    \"webpack-cli\": \"^5.1.4\",\n    \"webpack-dev-server\": \"^5.0.4\"\n  }\n}\n"
  },
  {
    "path": "src/Console/Console.js",
    "content": "import Tool from '../DevTools/Tool'\nimport noop from 'licia/noop'\nimport $ from 'licia/$'\nimport toStr from 'licia/toStr'\nimport isFn from 'licia/isFn'\nimport Emitter from 'licia/Emitter'\nimport isStr from 'licia/isStr'\nimport isRegExp from 'licia/isRegExp'\nimport uncaught from 'licia/uncaught'\nimport trim from 'licia/trim'\nimport upperFirst from 'licia/upperFirst'\nimport isHidden from 'licia/isHidden'\nimport isNull from 'licia/isNull'\nimport isArr from 'licia/isArr'\nimport extend from 'licia/extend'\nimport evalCss from '../lib/evalCss'\nimport Settings from '../Settings/Settings'\nimport LunaConsole from 'luna-console'\nimport LunaModal from 'luna-modal'\nimport { classPrefix as c } from '../lib/util'\n\nuncaught.start()\n\nexport default class Console extends Tool {\n  constructor({ name = 'console' } = {}) {\n    super()\n\n    Emitter.mixin(this)\n\n    this.name = name\n    this._selectedLog = null\n  }\n  init($el, container) {\n    super.init($el)\n    this._container = container\n\n    this._appendTpl()\n\n    this._initCfg()\n\n    this._initLogger()\n    this._exposeLogger()\n    this._bindEvent()\n  }\n  show() {\n    super.show()\n    this._handleShow()\n  }\n  overrideConsole() {\n    const origConsole = (this._origConsole = {})\n    const winConsole = window.console\n\n    CONSOLE_METHOD.forEach((name) => {\n      let origin = (origConsole[name] = noop)\n      if (winConsole[name]) {\n        origin = origConsole[name] = winConsole[name].bind(winConsole)\n      }\n\n      winConsole[name] = (...args) => {\n        this[name](...args)\n        origin(...args)\n      }\n    })\n\n    return this\n  }\n  setGlobal(name, val) {\n    this._logger.setGlobal(name, val)\n  }\n  restoreConsole() {\n    if (!this._origConsole) return this\n\n    CONSOLE_METHOD.forEach(\n      (name) => (window.console[name] = this._origConsole[name])\n    )\n    delete this._origConsole\n\n    return this\n  }\n  catchGlobalErr() {\n    uncaught.addListener(this._handleErr)\n\n    return this\n  }\n  ignoreGlobalErr() {\n    uncaught.rmListener(this._handleErr)\n\n    return this\n  }\n  filter(filter) {\n    const $filterText = this._$filterText\n    const logger = this._logger\n\n    if (isStr(filter)) {\n      $filterText.text(filter)\n      logger.setOption('filter', trim(filter))\n    } else if (isRegExp(filter)) {\n      $filterText.text(toStr(filter))\n      logger.setOption('filter', filter)\n    } else if (isFn(filter)) {\n      $filterText.text('ƒ')\n      logger.setOption('filter', filter)\n    }\n  }\n  destroy() {\n    this._logger.destroy()\n    super.destroy()\n\n    this._container.off('show', this._handleShow)\n\n    if (this._style) {\n      evalCss.remove(this._style)\n    }\n    this.ignoreGlobalErr()\n    this.restoreConsole()\n    this._rmCfg()\n  }\n  _handleShow = () => {\n    if (isHidden(this._$el.get(0))) return\n    this._logger.renderViewport()\n  }\n  _handleErr = (err) => {\n    this._logger.error(err)\n  }\n  _enableJsExecution(enabled) {\n    const $el = this._$el\n    const $jsInput = $el.find(c('.js-input'))\n\n    if (enabled) {\n      $jsInput.show()\n      $el.rmClass(c('js-input-hidden'))\n    } else {\n      $jsInput.hide()\n      $el.addClass(c('js-input-hidden'))\n    }\n  }\n  _appendTpl() {\n    const $el = this._$el\n\n    this._style = evalCss(require('./Console.scss'))\n    $el.append(\n      c(`\n      <div class=\"control\">\n        <span class=\"icon-clear clear-console\"></span>\n        <span class=\"level active\" data-level=\"all\">All</span>\n        <span class=\"level\" data-level=\"info\">Info</span>\n        <span class=\"level\" data-level=\"warning\">Warning</span>\n        <span class=\"level\" data-level=\"error\">Error</span>\n        <span class=\"filter-text\"></span>\n        <span class=\"icon-filter filter\"></span>\n        <span class=\"icon-copy icon-disabled copy\"></span>\n      </div>\n      <div class=\"logs-container\"></div>\n      <div class=\"js-input\">\n        <div class=\"buttons\">\n          <div class=\"button cancel\">Cancel</div>\n          <div class=\"button execute\">Execute</div>\n        </div>\n        <span class=\"icon-right\"></span>\n        <textarea></textarea>\n      </div>\n    `)\n    )\n\n    const _$inputContainer = $el.find(c('.js-input'))\n    const _$input = _$inputContainer.find('textarea')\n    const _$inputBtns = _$inputContainer.find(c('.buttons'))\n\n    extend(this, {\n      _$control: $el.find(c('.control')),\n      _$logs: $el.find(c('.logs-container')),\n      _$inputContainer,\n      _$input,\n      _$inputBtns,\n      _$filterText: $el.find(c('.filter-text')),\n    })\n  }\n  _initLogger() {\n    const cfg = this.config\n    let maxLogNum = cfg.get('maxLogNum')\n    maxLogNum = maxLogNum === 'infinite' ? 0 : +maxLogNum\n\n    const $level = this._$control.find(c('.level'))\n    const logger = new LunaConsole(this._$logs.get(0), {\n      asyncRender: cfg.get('asyncRender'),\n      maxNum: maxLogNum,\n      showHeader: cfg.get('displayExtraInfo'),\n      unenumerable: cfg.get('displayUnenumerable'),\n      accessGetter: cfg.get('displayGetterVal'),\n      lazyEvaluation: cfg.get('lazyEvaluation'),\n    })\n\n    logger.on('optionChange', (name, val) => {\n      switch (name) {\n        case 'level':\n          $level.each(function () {\n            const $this = $(this)\n            const level = $this.data('level')\n            const isMatch = level === val || (level === 'all' && isArr(val))\n\n            $this[isMatch ? 'addClass' : 'rmClass'](c('active'))\n          })\n          break\n      }\n    })\n\n    if (cfg.get('overrideConsole')) this.overrideConsole()\n\n    this._logger = logger\n  }\n  _exposeLogger() {\n    const logger = this._logger\n    const methods = ['html'].concat(CONSOLE_METHOD)\n\n    methods.forEach(\n      (name) =>\n        (this[name] = (...args) => {\n          logger[name](...args)\n          this.emit(name, ...args)\n\n          return this\n        })\n    )\n  }\n  _bindEvent() {\n    const container = this._container\n    const $input = this._$input\n    const $inputBtns = this._$inputBtns\n    const $control = this._$control\n\n    const logger = this._logger\n    const config = this.config\n\n    $control\n      .on('click', c('.clear-console'), () => logger.clear(true))\n      .on('click', c('.level'), function () {\n        let level = $(this).data('level')\n        if (level === 'all') {\n          level = ['verbose', 'info', 'warning', 'error']\n        }\n        logger.setOption('level', level)\n      })\n      .on('click', c('.filter'), () => {\n        LunaModal.prompt('Filter').then((filter) => {\n          if (isNull(filter)) return\n          this.filter(filter)\n        })\n      })\n      .on('click', c('.copy'), () => {\n        this._selectedLog.copy()\n        container.notify('Copied', { icon: 'success' })\n      })\n\n    $inputBtns\n      .on('click', c('.cancel'), () => this._hideInput())\n      .on('click', c('.execute'), () => {\n        const jsInput = $input.val().trim()\n        if (jsInput === '') return\n\n        logger.evaluate(jsInput)\n        $input.val('').get(0).blur()\n        this._hideInput()\n      })\n\n    $input.on('focusin', () => this._showInput())\n\n    logger.on('insert', (log) => {\n      const autoShow = log.type === 'error' && config.get('displayIfErr')\n\n      if (autoShow) container.showTool('console').show()\n    })\n\n    logger.on('select', (log) => {\n      this._selectedLog = log\n      $control.find(c('.icon-copy')).rmClass(c('icon-disabled'))\n    })\n\n    logger.on('deselect', () => {\n      this._selectedLog = null\n      $control.find(c('.icon-copy')).addClass(c('icon-disabled'))\n    })\n\n    container.on('show', this._handleShow)\n  }\n  _hideInput() {\n    this._$inputContainer.rmClass(c('active'))\n    this._$inputBtns.css('display', 'none')\n  }\n  _showInput() {\n    this._$inputContainer.addClass(c('active'))\n    this._$inputBtns.css('display', 'flex')\n  }\n  _rmCfg() {\n    const cfg = this.config\n\n    const settings = this._container.get('settings')\n    if (!settings) return\n\n    settings\n      .remove(cfg, 'asyncRender')\n      .remove(cfg, 'jsExecution')\n      .remove(cfg, 'catchGlobalErr')\n      .remove(cfg, 'overrideConsole')\n      .remove(cfg, 'displayExtraInfo')\n      .remove(cfg, 'displayUnenumerable')\n      .remove(cfg, 'displayGetterVal')\n      .remove(cfg, 'lazyEvaluation')\n      .remove(cfg, 'displayIfErr')\n      .remove(cfg, 'maxLogNum')\n      .remove(upperFirst(this.name))\n  }\n  _initCfg() {\n    const container = this._container\n\n    const cfg = (this.config = Settings.createCfg(this.name, {\n      asyncRender: true,\n      catchGlobalErr: true,\n      jsExecution: true,\n      overrideConsole: true,\n      displayExtraInfo: false,\n      displayUnenumerable: true,\n      displayGetterVal: true,\n      lazyEvaluation: true,\n      displayIfErr: false,\n      maxLogNum: 'infinite',\n    }))\n\n    this._enableJsExecution(cfg.get('jsExecution'))\n    if (cfg.get('catchGlobalErr')) this.catchGlobalErr()\n\n    cfg.on('change', (key, val) => {\n      const logger = this._logger\n      switch (key) {\n        case 'asyncRender':\n          return logger.setOption('asyncRender', val)\n        case 'jsExecution':\n          return this._enableJsExecution(val)\n        case 'catchGlobalErr':\n          return val ? this.catchGlobalErr() : this.ignoreGlobalErr()\n        case 'overrideConsole':\n          return val ? this.overrideConsole() : this.restoreConsole()\n        case 'maxLogNum':\n          return logger.setOption('maxNum', val === 'infinite' ? 0 : +val)\n        case 'displayExtraInfo':\n          return logger.setOption('showHeader', val)\n        case 'displayUnenumerable':\n          return logger.setOption('unenumerable', val)\n        case 'displayGetterVal':\n          return logger.setOption('accessGetter', val)\n        case 'lazyEvaluation':\n          return logger.setOption('lazyEvaluation', val)\n      }\n    })\n\n    const settings = container.get('settings')\n    if (!settings) return\n\n    settings\n      .text(upperFirst(this.name))\n      .switch(cfg, 'asyncRender', 'Asynchronous Rendering')\n      .switch(cfg, 'jsExecution', 'Enable JavaScript Execution')\n      .switch(cfg, 'catchGlobalErr', 'Catch Global Errors')\n      .switch(cfg, 'overrideConsole', 'Override Console')\n      .switch(cfg, 'displayIfErr', 'Auto Display If Error Occurs')\n      .switch(cfg, 'displayExtraInfo', 'Display Extra Information')\n      .switch(cfg, 'displayUnenumerable', 'Display Unenumerable Properties')\n      .switch(cfg, 'displayGetterVal', 'Access Getter Value')\n      .switch(cfg, 'lazyEvaluation', 'Lazy Evaluation')\n      .select(cfg, 'maxLogNum', 'Max Log Number', [\n        'infinite',\n        '250',\n        '125',\n        '100',\n        '50',\n        '10',\n      ])\n      .separator()\n  }\n}\n\nconst CONSOLE_METHOD = [\n  'log',\n  'error',\n  'info',\n  'warn',\n  'dir',\n  'time',\n  'timeLog',\n  'timeEnd',\n  'clear',\n  'table',\n  'assert',\n  'count',\n  'countReset',\n  'debug',\n  'group',\n  'groupCollapsed',\n  'groupEnd',\n]\n"
  },
  {
    "path": "src/Console/Console.scss",
    "content": "@use '../style/variable' as *;\n@use '../style/mixin' as *;\n\n#console {\n  padding-top: 40px;\n  padding-bottom: 24px;\n  width: 100%;\n  height: 100%;\n  &.js-input-hidden {\n    padding-bottom: 0;\n  }\n  .control {\n    padding: 10px 10px 10px 35px;\n    @include control();\n    .icon-clear {\n      padding-right: 0px;\n      left: 0;\n    }\n    .icon-copy {\n      right: 0;\n    }\n    .icon-filter {\n      right: 23px;\n    }\n    .level {\n      cursor: pointer;\n      font-size: $font-size-s;\n      height: 20px;\n      display: inline-block;\n      margin: 0 2px;\n      padding: 0 4px;\n      line-height: 20px;\n      transition: background-color $anim-duration, color $anim-duration;\n      &.active {\n        background: var(--highlight);\n        color: var(--select-foreground);\n      }\n    }\n    .filter-text {\n      white-space: nowrap;\n      position: absolute;\n      line-height: 20px;\n      max-width: 80px;\n      overflow: hidden;\n      right: 55px;\n      font-size: $font-size;\n      text-overflow: ellipsis;\n    }\n  }\n  .js-input {\n    pointer-events: none;\n    position: absolute;\n    z-index: 100;\n    left: 0;\n    bottom: 0;\n    width: 100%;\n    border-top: 1px solid var(--border);\n    height: 24px;\n    .icon-right {\n      line-height: 23px;\n      color: var(--accent);\n      position: absolute;\n      left: 10px;\n      top: 0;\n      z-index: 10;\n    }\n    &.active {\n      height: 100%;\n      padding-top: 40px;\n      padding-bottom: 40px;\n      border-top: none;\n      .icon-right {\n        display: none;\n      }\n      textarea {\n        overflow: auto;\n        padding-left: 10px;\n      }\n    }\n    .buttons {\n      display: none;\n      position: absolute;\n      left: 0;\n      bottom: 0;\n      width: 100%;\n      height: 40px;\n      color: var(--primary);\n      background: var(--darker-background);\n      font-size: $font-size-s;\n      border-top: 1px solid var(--border);\n      .button {\n        pointer-events: all;\n        cursor: pointer;\n        flex: 1;\n        text-align: center;\n        border-right: 1px solid var(--border);\n        height: 40px;\n        line-height: 40px;\n        transition: background-color $anim-duration, color $anim-duration;\n        &:last-child {\n          border-right: none;\n        }\n        &:active {\n          color: var(--select-foreground);\n          background: var(--highlight);\n        }\n      }\n    }\n    textarea {\n      overflow: hidden;\n      pointer-events: all;\n      padding: 3px 10px;\n      padding-left: 25px;\n      outline: none;\n      border: none;\n      font-size: $font-size;\n      width: 100%;\n      height: 100%;\n      user-select: text;\n      resize: none;\n      color: var(--primary);\n      background: var(--background);\n    }\n  }\n}\n\n.safe-area #console {\n  @include safe-area(padding-bottom, 24px);\n  &.js-input-hidden {\n    padding-bottom: 0;\n  }\n  .js-input {\n    @include safe-area(height, 24px);\n    &.active {\n      height: 100%;\n      @include safe-area(padding-bottom, 40px);\n    }\n    .buttons {\n      @include safe-area(height, 40px);\n      .button {\n        @include safe-area(height, 40px);\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "src/DevTools/DevTools.js",
    "content": "import logger from '../lib/logger'\nimport Tool from './Tool'\nimport Settings from '../Settings/Settings'\nimport Emitter from 'licia/Emitter'\nimport defaults from 'licia/defaults'\nimport keys from 'licia/keys'\nimport last from 'licia/last'\nimport each from 'licia/each'\nimport isNum from 'licia/isNum'\nimport nextTick from 'licia/nextTick'\nimport $ from 'licia/$'\nimport toNum from 'licia/toNum'\nimport extend from 'licia/extend'\nimport isStr from 'licia/isStr'\nimport theme from 'licia/theme'\nimport upperFirst from 'licia/upperFirst'\nimport startWith from 'licia/startWith'\nimport ready from 'licia/ready'\nimport pointerEvent from 'licia/pointerEvent'\nimport evalCss from '../lib/evalCss'\nimport emitter from '../lib/emitter'\nimport { isDarkTheme } from '../lib/themes'\nimport LunaNotification from 'luna-notification'\nimport LunaModal from 'luna-modal'\nimport LunaTab from 'luna-tab'\nimport {\n  classPrefix as c,\n  eventClient,\n  hasSafeArea,\n  safeStorage,\n} from '../lib/util'\n\nexport default class DevTools extends Emitter {\n  constructor($container, { defaults = {}, inline = false } = {}) {\n    super()\n\n    this._defCfg = extend(\n      {\n        transparency: 1,\n        displaySize: 80,\n        theme: 'System preference',\n      },\n      defaults\n    )\n\n    this._style = evalCss(require('./DevTools.scss'))\n\n    this.$container = $container\n    this._isShow = false\n    this._opacity = 1\n    this._tools = {}\n    this._isResizing = false\n    this._resizeTimer = null\n    this._resizeStartY = 0\n    this._resizeStartSize = 0\n    this._inline = inline\n\n    this._initTpl()\n    this._initTab()\n    this._initNotification()\n    this._initModal()\n\n    ready(() => this._checkSafeArea())\n    this._bindEvent()\n  }\n  show() {\n    this._isShow = true\n\n    this._$el.show()\n    this._tab.updateSlider()\n\n    // Need a delay after show to enable transition effect.\n    setTimeout(() => {\n      this._$el.css('opacity', this._opacity)\n    }, 50)\n\n    this.emit('show')\n\n    return this\n  }\n  hide() {\n    if (this._inline) {\n      return\n    }\n\n    this._isShow = false\n    this.emit('hide')\n\n    this._$el.css({ opacity: 0 })\n    setTimeout(() => this._$el.hide(), 300)\n\n    return this\n  }\n  toggle() {\n    return this._isShow ? this.hide() : this.show()\n  }\n  add(tool) {\n    const tab = this._tab\n\n    if (!(tool instanceof Tool)) {\n      const { init, show, hide, destroy } = new Tool()\n      defaults(tool, { init, show, hide, destroy })\n    }\n\n    const name = tool.name\n    if (!name) {\n      return logger.error('You must specify a name for a tool')\n    }\n\n    if (this._tools[name]) {\n      return logger.warn(`Tool ${name} already exists`)\n    }\n\n    const id = name.replace(/\\s+/g, '-')\n    this._$tools.prepend(`<div id=\"${c(id)}\" class=\"${c(id + ' tool')}\"></div>`)\n    tool.init(this._$tools.find(`.${c(id)}.${c('tool')}`), this)\n    tool.active = false\n    this._tools[name] = tool\n\n    if (name === 'settings') {\n      tab.append({\n        id: name,\n        title: name,\n      })\n    } else {\n      tab.insert(tab.length - 1, {\n        id: name,\n        title: name,\n      })\n    }\n\n    return this\n  }\n  remove(name) {\n    const tools = this._tools\n\n    if (!tools[name]) return logger.warn(`Tool ${name} doesn't exist`)\n\n    this._tab.remove(name)\n\n    const tool = tools[name]\n    delete tools[name]\n    if (tool.active) {\n      const toolKeys = keys(tools)\n      if (toolKeys.length > 0) this.showTool(tools[last(toolKeys)].name)\n    }\n    tool.destroy()\n\n    return this\n  }\n  removeAll() {\n    each(this._tools, (tool) => this.remove(tool.name))\n\n    return this\n  }\n  get(name) {\n    const tool = this._tools[name]\n\n    if (tool) return tool\n  }\n  showTool(name) {\n    if (this._curTool === name) {\n      return this\n    }\n    this._curTool = name\n\n    const tools = this._tools\n\n    const tool = tools[name]\n    if (!tool) return\n\n    let lastTool = {}\n\n    each(tools, (tool) => {\n      if (tool.active) {\n        lastTool = tool\n        tool.active = false\n        tool.hide()\n      }\n    })\n\n    tool.active = true\n    tool.show()\n\n    this._tab.select(name)\n\n    this.emit('showTool', name, lastTool)\n\n    return this\n  }\n  initCfg(settings) {\n    const cfg = (this.config = Settings.createCfg('dev-tools', this._defCfg))\n\n    this._setTransparency(cfg.get('transparency'))\n    this._setDisplaySize(cfg.get('displaySize'))\n    this._setTheme(cfg.get('theme'))\n\n    cfg.on('change', (key, val) => {\n      switch (key) {\n        case 'transparency':\n          return this._setTransparency(val)\n        case 'displaySize':\n          return this._setDisplaySize(val)\n        case 'theme':\n          return this._setTheme(val)\n      }\n    })\n\n    settings\n      .separator()\n      .select(cfg, 'theme', 'Theme', [\n        'System preference',\n        ...keys(evalCss.getThemes()),\n      ])\n\n    if (!this._inline) {\n      settings\n        .range(cfg, 'transparency', 'Transparency', {\n          min: 0.2,\n          max: 1,\n          step: 0.01,\n        })\n        .range(cfg, 'displaySize', 'Display Size', {\n          min: 40,\n          max: 100,\n          step: 1,\n        })\n    }\n\n    settings\n      .button('Restore defaults and reload', function () {\n        const store = safeStorage('local')\n\n        const data = JSON.parse(JSON.stringify(store))\n        each(data, (val, key) => {\n          if (!isStr(val)) {\n            return\n          }\n\n          if (startWith(key, 'eruda')) {\n            store.removeItem(key)\n          }\n        })\n\n        window.location.reload()\n      })\n      .separator()\n  }\n  notify(content, options) {\n    this._notification.notify(content, options)\n  }\n  destroy() {\n    evalCss.remove(this._style)\n    this.removeAll()\n    this._tab.destroy()\n    this._$el.remove()\n    window.removeEventListener('resize', this._checkSafeArea)\n    emitter.off(emitter.SCALE, this._updateTabHeight)\n  }\n  _checkSafeArea = () => {\n    const { $container } = this\n\n    if (hasSafeArea()) {\n      $container.addClass(c('safe-area'))\n    } else {\n      $container.rmClass(c('safe-area'))\n    }\n  }\n  _setTheme(t) {\n    const { $container } = this\n\n    if (t === 'System preference') {\n      t = upperFirst(theme.get())\n    }\n\n    if (isDarkTheme(t)) {\n      $container.addClass(c('dark'))\n    } else {\n      $container.rmClass(c('dark'))\n    }\n    evalCss.setTheme(t)\n  }\n  _setTransparency(opacity) {\n    if (!isNum(opacity)) return\n\n    this._opacity = opacity\n    if (this._isShow) this._$el.css({ opacity })\n  }\n  _setDisplaySize(height) {\n    if (this._inline) {\n      height = 100\n    }\n\n    if (!isNum(height)) return\n\n    this._$el.css({ height: height + '%' })\n  }\n  _initTpl() {\n    const $container = this.$container\n\n    $container.append(\n      c(`\n      <div class=\"dev-tools\">\n        <div class=\"resizer\"></div>\n        <div class=\"tab\"></div>\n        <div class=\"tools\"></div>\n        <div class=\"notification\"></div>\n        <div class=\"modal\"></div>\n      </div>\n      `)\n    )\n\n    this._$el = $container.find(c('.dev-tools'))\n    this._$tools = this._$el.find(c('.tools'))\n  }\n  _initTab() {\n    this._tab = new LunaTab(this._$el.find(c('.tab')).get(0), {\n      height: 40,\n    })\n    this._tab.on('select', (id) => this.showTool(id))\n  }\n  _updateTabHeight = (scale) => {\n    this._tab.setOption('height', 40 * scale)\n    nextTick(() => {\n      this._tab.updateSlider()\n    })\n  }\n  _initNotification() {\n    this._notification = new LunaNotification(\n      this._$el.find(c('.notification')).get(0),\n      {\n        position: {\n          x: 'center',\n          y: 'top',\n        },\n      }\n    )\n  }\n  _initModal() {\n    LunaModal.setContainer(this._$el.find(c('.modal')).get(0))\n  }\n  _bindEvent() {\n    const $resizer = this._$el.find(c('.resizer'))\n    const $navBar = this._$el.find(c('.nav-bar'))\n    const $document = $(document)\n\n    if (this._inline) {\n      $resizer.hide()\n    }\n\n    const startListener = (e) => {\n      e.preventDefault()\n      e.stopPropagation()\n\n      e = e.origEvent\n      this._isResizing = true\n      this._resizeStartSize = this.config.get('displaySize')\n      this._resizeStartY = eventClient('y', e)\n\n      $resizer.css('height', '100%')\n\n      $document.on(pointerEvent('move'), moveListener)\n      $document.on(pointerEvent('up'), endListener)\n    }\n    const moveListener = (e) => {\n      if (!this._isResizing) {\n        return\n      }\n      e.preventDefault()\n      e.stopPropagation()\n\n      e = e.origEvent\n      const deltaY =\n        ((this._resizeStartY - eventClient('y', e)) / window.innerHeight) * 100\n      let displaySize = this._resizeStartSize + deltaY\n      if (displaySize < 40) {\n        displaySize = 40\n      } else if (displaySize > 100) {\n        displaySize = 100\n      }\n      this.config.set('displaySize', toNum(displaySize.toFixed(2)))\n    }\n    const endListener = () => {\n      clearTimeout(this._resizeTimer)\n      this._isResizing = false\n\n      $resizer.css('height', 10)\n\n      $document.off(pointerEvent('move'), moveListener)\n      $document.off(pointerEvent('up'), endListener)\n    }\n    $resizer.css('height', 10)\n    $resizer.on(pointerEvent('down'), startListener)\n\n    $navBar.on('contextmenu', (e) => e.preventDefault())\n    this.$container.on('click', (e) => e.stopPropagation())\n    window.addEventListener('resize', this._checkSafeArea)\n\n    emitter.on(emitter.SCALE, this._updateTabHeight)\n\n    theme.on('change', () => {\n      const t = this.config.get('theme')\n      if (t === 'System preference') {\n        this._setTheme(t)\n      }\n    })\n  }\n}\n"
  },
  {
    "path": "src/DevTools/DevTools.scss",
    "content": "@use '../style/variable' as *;\n@use '../style/mixin' as *;\n\n.dev-tools {\n  position: absolute;\n  width: 100%;\n  height: 100%;\n  left: 0;\n  bottom: 0;\n  background: var(--background);\n  z-index: 500;\n  display: none;\n  padding-top: 40px !important;\n  opacity: 0;\n  transition: opacity $anim-duration;\n  border-top: 1px solid var(--border);\n  .resizer {\n    position: absolute;\n    width: 100%;\n    touch-action: none;\n    left: 0;\n    top: -8px;\n    cursor: row-resize;\n    z-index: 120;\n  }\n  .tools {\n    @include overflow-auto();\n    height: 100%;\n    width: 100%;\n    position: relative;\n    .tool {\n      @include absolute();\n      overflow: hidden;\n      display: none;\n    }\n  }\n}\n"
  },
  {
    "path": "src/DevTools/Tool.js",
    "content": "import Class from 'licia/Class'\n\nexport default Class({\n  init($el) {\n    this._$el = $el\n  },\n  show() {\n    this._$el.show()\n\n    return this\n  },\n  hide() {\n    this._$el.hide()\n\n    return this\n  },\n  destroy() {\n    this._$el.remove()\n  },\n})\n"
  },
  {
    "path": "src/Elements/CssStore.js",
    "content": "import each from 'licia/each'\nimport sortKeys from 'licia/sortKeys'\n\nfunction formatStyle(style) {\n  const ret = {}\n\n  for (let i = 0, len = style.length; i < len; i++) {\n    const name = style[i]\n\n    if (style[name] === 'initial') continue\n\n    ret[name] = style[name]\n  }\n\n  return sortStyleKeys(ret)\n}\n\nconst elProto = Element.prototype\n\nlet matchesSel = function () {\n  return false\n}\n\nif (elProto.webkitMatchesSelector) {\n  matchesSel = (el, selText) => el.webkitMatchesSelector(selText)\n} else if (elProto.mozMatchesSelector) {\n  matchesSel = (el, selText) => el.mozMatchesSelector(selText)\n}\n\nexport default class CssStore {\n  constructor(el) {\n    this._el = el\n  }\n  getComputedStyle() {\n    const computedStyle = window.getComputedStyle(this._el)\n\n    return formatStyle(computedStyle)\n  }\n  getMatchedCSSRules() {\n    const ret = []\n\n    each(document.styleSheets, (styleSheet) => {\n      try {\n        // Started with version 64, Chrome does not allow cross origin script to access this property.\n        if (!styleSheet.cssRules) return\n      } catch {\n        return\n      }\n\n      each(styleSheet.cssRules, (cssRule) => {\n        let matchesEl = false\n\n        // Mobile safari will throw DOM Exception 12 error, need to try catch it.\n        try {\n          matchesEl = this._elMatchesSel(cssRule.selectorText)\n        } catch {\n          // No op\n        }\n\n        if (!matchesEl) return\n\n        ret.push({\n          selectorText: cssRule.selectorText,\n          style: formatStyle(cssRule.style),\n        })\n      })\n    })\n\n    return ret\n  }\n  _elMatchesSel(selText) {\n    return matchesSel(this._el, selText)\n  }\n}\n\nfunction sortStyleKeys(style) {\n  return sortKeys(style, {\n    comparator: (a, b) => {\n      const lenA = a.length\n      const lenB = b.length\n      const len = lenA > lenB ? lenB : lenA\n\n      for (let i = 0; i < len; i++) {\n        const codeA = a.charCodeAt(i)\n        const codeB = b.charCodeAt(i)\n        const cmpResult = cmpCode(codeA, codeB)\n\n        if (cmpResult !== 0) return cmpResult\n      }\n\n      if (lenA > lenB) return 1\n      if (lenA < lenB) return -1\n\n      return 0\n    },\n  })\n}\n\nfunction cmpCode(a, b) {\n  a = transCode(a)\n  b = transCode(b)\n\n  if (a > b) return 1\n  if (a < b) return -1\n  return 0\n}\n\nfunction transCode(code) {\n  // - should be placed after lowercase chars.\n  if (code === 45) return 123\n  return code\n}\n"
  },
  {
    "path": "src/Elements/Detail.js",
    "content": "import isEmpty from 'licia/isEmpty'\nimport lowerCase from 'licia/lowerCase'\nimport pick from 'licia/pick'\nimport toStr from 'licia/toStr'\nimport map from 'licia/map'\nimport isEl from 'licia/isEl'\nimport escape from 'licia/escape'\nimport startWith from 'licia/startWith'\nimport contain from 'licia/contain'\nimport unique from 'licia/unique'\nimport each from 'licia/each'\nimport keys from 'licia/keys'\nimport isNull from 'licia/isNull'\nimport trim from 'licia/trim'\nimport isFn from 'licia/isFn'\nimport isBool from 'licia/isBool'\nimport safeGet from 'licia/safeGet'\nimport $ from 'licia/$'\nimport h from 'licia/h'\nimport extend from 'licia/extend'\nimport MutationObserver from 'licia/MutationObserver'\nimport CssStore from './CssStore'\nimport Settings from '../Settings/Settings'\nimport LunaModal from 'luna-modal'\nimport LunaBoxModel from 'luna-box-model'\nimport chobitsu from '../lib/chobitsu'\nimport { formatNodeName } from './util'\nimport { isErudaEl, classPrefix as c } from '../lib/util'\n\nexport default class Detail {\n  constructor($container, devtools) {\n    this._$container = $container\n    this._devtools = devtools\n    this._curEl = document.documentElement\n    this._initObserver()\n    this._initCfg()\n    this._initTpl()\n    this._bindEvent()\n  }\n  show(el) {\n    this._curEl = el\n    this._rmDefComputedStyle = true\n    this._computedStyleSearchKeyword = ''\n    this._enableObserver()\n    this._render()\n    this._highlight()\n  }\n  hide = () => {\n    this._$container.hide()\n    this._disableObserver()\n    chobitsu.domain('Overlay').hideHighlight()\n  }\n  destroy() {\n    this._disableObserver()\n    this.restoreEventTarget()\n    this._rmCfg()\n  }\n  overrideEventTarget() {\n    const winEventProto = getWinEventProto()\n\n    const origAddEvent = (this._origAddEvent = winEventProto.addEventListener)\n    const origRmEvent = (this._origRmEvent = winEventProto.removeEventListener)\n\n    winEventProto.addEventListener = function (type, listener, useCapture) {\n      addEvent(this, type, listener, useCapture)\n      origAddEvent.apply(this, arguments)\n    }\n\n    winEventProto.removeEventListener = function (type, listener, useCapture) {\n      rmEvent(this, type, listener, useCapture)\n      origRmEvent.apply(this, arguments)\n    }\n  }\n  restoreEventTarget() {\n    const winEventProto = getWinEventProto()\n\n    if (this._origAddEvent) winEventProto.addEventListener = this._origAddEvent\n    if (this._origRmEvent) winEventProto.removeEventListener = this._origRmEvent\n  }\n  _highlight = (type) => {\n    const el = this._curEl\n\n    const highlightConfig = {\n      showInfo: false,\n    }\n    if (!type || type === 'all') {\n      extend(highlightConfig, {\n        showInfo: true,\n        contentColor: 'rgba(111, 168, 220, .66)',\n        paddingColor: 'rgba(147, 196, 125, .55)',\n        borderColor: 'rgba(255, 229, 153, .66)',\n        marginColor: 'rgba(246, 178, 107, .66)',\n      })\n    } else if (type === 'margin') {\n      highlightConfig.marginColor = 'rgba(246, 178, 107, .66)'\n    } else if (type === 'border') {\n      highlightConfig.borderColor = 'rgba(255, 229, 153, .66)'\n    } else if (type === 'padding') {\n      highlightConfig.paddingColor = 'rgba(147, 196, 125, .55)'\n    } else if (type === 'content') {\n      highlightConfig.contentColor = 'rgba(111, 168, 220, .66)'\n    }\n\n    const { nodeId } = chobitsu.domain('DOM').getNodeId({ node: el })\n    chobitsu.domain('Overlay').highlightNode({\n      nodeId,\n      highlightConfig,\n    })\n  }\n  _initTpl() {\n    const $container = this._$container\n\n    const html = `<div class=\"${c('control')}\">\n      <span class=\"${c('icon-left back')}\"></span>\n      <span class=\"${c('element-name')}\"></span>\n      <span class=\"${c('icon-refresh refresh')}\"></span>\n    </div>\n    <div class=\"${c('element')}\">\n      <div class=\"${c('attributes section')}\"></div>\n      <div class=\"${c('styles section')}\"></div>\n      <div class=\"${c('computed-style section')}\"></div>\n      <div class=\"${c('listeners section')}\"></div>\n    </div>`\n\n    $container.html(html)\n\n    this._$elementName = $container.find(c('.element-name'))\n    this._$attributes = $container.find(c('.attributes'))\n    this._$styles = $container.find(c('.styles'))\n    this._$listeners = $container.find(c('.listeners'))\n    this._$computedStyle = $container.find(c('.computed-style'))\n\n    const boxModelContainer = h('div')\n    this._$boxModel = $(boxModelContainer)\n    this._boxModel = new LunaBoxModel(boxModelContainer)\n  }\n  _toggleAllComputedStyle() {\n    this._rmDefComputedStyle = !this._rmDefComputedStyle\n\n    this._render()\n  }\n  _render() {\n    const data = this._getData(this._curEl)\n    const $attributes = this._$attributes\n    const $elementName = this._$elementName\n    const $styles = this._$styles\n    const $computedStyle = this._$computedStyle\n    const $listeners = this._$listeners\n\n    $elementName.html(data.name)\n\n    let attributes = '<tr><td>Empty</td></tr>'\n    if (!isEmpty(data.attributes)) {\n      attributes = map(data.attributes, ({ name, value }) => {\n        return `<tr>\n          <td class=\"${c('attribute-name-color')}\">${escape(name)}</td>\n          <td class=\"${c('string-color')}\">${value}</td>\n        </tr>`\n      }).join('')\n    }\n    attributes = `<h2>Attributes</h2>\n    <div class=\"${c('table-wrapper')}\">\n      <table>\n        <tbody>\n          ${attributes} \n        </tbody>\n      </table>\n    </div>`\n    $attributes.html(attributes)\n\n    let styles = ''\n    if (!isEmpty(data.styles)) {\n      const style = map(data.styles, ({ selectorText, style }) => {\n        style = map(style, (val, key) => {\n          return `<div class=\"${c('rule')}\"><span>${escape(\n            key\n          )}</span>: ${val};</div>`\n        }).join('')\n        return `<div class=\"${c('style-rules')}\">\n          <div>${escape(selectorText)} {</div>\n            ${style}\n          <div>}</div>\n        </div>`\n      }).join('')\n      styles = `<h2>Styles</h2>\n      <div class=\"${c('style-wrapper')}\">\n        ${style}\n      </div>`\n      $styles.html(styles).show()\n    } else {\n      $styles.hide()\n    }\n\n    let computedStyle = ''\n    if (data.computedStyle) {\n      let toggleButton = c(`<div class=\"btn toggle-all-computed-style\">\n        <span class=\"icon-expand\"></span>\n      </div>`)\n      if (data.rmDefComputedStyle) {\n        toggleButton = c(`<div class=\"btn toggle-all-computed-style\">\n          <span class=\"icon-compress\"></span>\n        </div>`)\n      }\n\n      computedStyle = `<h2>\n        Computed Style\n        ${toggleButton}\n        <div class=\"${c('btn computed-style-search')}\">\n          <span class=\"${c('icon-filter')}\"></span>\n        </div>\n        ${\n          data.computedStyleSearchKeyword\n            ? `<div class=\"${c('btn filter-text')}\">${escape(\n                data.computedStyleSearchKeyword\n              )}</div>`\n            : ''\n        }\n      </h2>\n      <div class=\"${c('box-model')}\"></div>\n      <div class=\"${c('table-wrapper')}\">\n        <table>\n          <tbody>\n          ${map(data.computedStyle, (val, key) => {\n            return `<tr>\n              <td class=\"${c('key')}\">${escape(key)}</td>\n              <td>${val}</td>\n            </tr>`\n          }).join('')}\n          </tbody>\n        </table>\n      </div>`\n\n      $computedStyle.html(computedStyle).show()\n      this._boxModel.setOption('element', this._curEl)\n      $computedStyle.find(c('.box-model')).append(this._$boxModel.get(0))\n    } else {\n      $computedStyle.text('').hide()\n    }\n\n    let listeners = ''\n    if (data.listeners) {\n      listeners = map(data.listeners, (listeners, key) => {\n        listeners = map(listeners, ({ useCapture, listenerStr }) => {\n          return `<li ${useCapture ? `class=\"${c('capture')}\"` : ''}>${escape(\n            listenerStr\n          )}</li>`\n        }).join('')\n        return `<div class=\"${c('listener')}\">\n          <div class=\"${c('listener-type')}\">${escape(key)}</div>\n          <ul class=\"${c('listener-content')}\">\n            ${listeners}\n          </ul>\n        </div>`\n      }).join('')\n      listeners = `<h2>Event Listeners</h2>\n      <div class=\"${c('listener-wrapper')}\">\n        ${listeners} \n      </div>`\n      $listeners.html(listeners).show()\n    } else {\n      $listeners.hide()\n    }\n\n    this._$container.show()\n  }\n  _getData(el) {\n    const ret = {}\n\n    const cssStore = new CssStore(el)\n\n    const { className, id, attributes, tagName } = el\n\n    ret.computedStyleSearchKeyword = this._computedStyleSearchKeyword\n    ret.attributes = formatAttr(attributes)\n    ret.name = formatNodeName({ tagName, id, className, attributes })\n\n    const events = el.erudaEvents\n    if (events && keys(events).length !== 0) ret.listeners = events\n\n    if (needNoStyle(tagName)) {\n      return ret\n    }\n\n    let computedStyle = cssStore.getComputedStyle()\n\n    const styles = cssStore.getMatchedCSSRules()\n    styles.unshift(getInlineStyle(el.style))\n    styles.forEach((style) => processStyleRules(style.style))\n    ret.styles = styles\n\n    if (this._rmDefComputedStyle) {\n      computedStyle = rmDefComputedStyle(computedStyle, styles)\n    }\n    ret.rmDefComputedStyle = this._rmDefComputedStyle\n    const computedStyleSearchKeyword = lowerCase(ret.computedStyleSearchKeyword)\n    if (computedStyleSearchKeyword) {\n      computedStyle = pick(computedStyle, (val, property) => {\n        return (\n          contain(property, computedStyleSearchKeyword) ||\n          contain(val, computedStyleSearchKeyword)\n        )\n      })\n    }\n    processStyleRules(computedStyle)\n    ret.computedStyle = computedStyle\n\n    return ret\n  }\n  _bindEvent() {\n    const devtools = this._devtools\n\n    this._$container\n      .on('click', c('.toggle-all-computed-style'), () =>\n        this._toggleAllComputedStyle()\n      )\n      .on('click', c('.computed-style-search'), () => {\n        LunaModal.prompt('Filter').then((filter) => {\n          if (isNull(filter)) return\n          filter = trim(filter)\n          this._computedStyleSearchKeyword = filter\n          this._render()\n        })\n      })\n      .on('click', '.eruda-listener-content', function () {\n        const text = $(this).text()\n        const sources = devtools.get('sources')\n\n        if (sources) {\n          sources.set('js', text)\n          devtools.showTool('sources')\n        }\n      })\n      .on('click', c('.element-name'), () => {\n        const sources = devtools.get('sources')\n\n        if (sources) {\n          sources.set('object', this._curEl)\n          devtools.showTool('sources')\n        }\n      })\n      .on('click', c('.back'), this.hide)\n      .on('click', c('.refresh'), () => {\n        this._render()\n        devtools.notify('Refreshed', { icon: 'success' })\n      })\n\n    this._boxModel.on('highlight', this._highlight)\n  }\n  _initObserver() {\n    this._observer = new MutationObserver((mutations) => {\n      each(mutations, (mutation) => this._handleMutation(mutation))\n    })\n  }\n  _enableObserver() {\n    this._observer.observe(document.documentElement, {\n      attributes: true,\n      childList: true,\n      subtree: true,\n    })\n  }\n  _disableObserver() {\n    this._observer.disconnect()\n  }\n  _handleMutation(mutation) {\n    if (isErudaEl(mutation.target)) return\n\n    if (mutation.type === 'attributes') {\n      if (mutation.target !== this._curEl) return\n      this._render()\n    }\n  }\n  _rmCfg() {\n    const cfg = this.config\n\n    const settings = this._devtools.get('settings')\n\n    if (!settings) return\n\n    settings\n      .remove(cfg, 'overrideEventTarget')\n      .remove(cfg, 'observeElement')\n      .remove('Elements')\n  }\n  _initCfg() {\n    const cfg = (this.config = Settings.createCfg('elements', {\n      overrideEventTarget: true,\n    }))\n\n    if (cfg.get('overrideEventTarget')) this.overrideEventTarget()\n\n    cfg.on('change', (key, val) => {\n      switch (key) {\n        case 'overrideEventTarget':\n          return val ? this.overrideEventTarget() : this.restoreEventTarget()\n      }\n    })\n\n    const settings = this._devtools.get('settings')\n    if (!settings) return\n\n    settings\n      .text('Elements')\n      .switch(cfg, 'overrideEventTarget', 'Catch Event Listeners')\n\n    settings.separator()\n  }\n}\n\nfunction processStyleRules(style) {\n  each(style, (val, key) => (style[key] = processStyleRule(val)))\n}\n\nconst formatAttr = (attributes) =>\n  map(attributes, (attr) => {\n    let { value } = attr\n    const { name } = attr\n    value = escape(value)\n\n    const isLink =\n      (name === 'src' || name === 'href') && !startWith(value, 'data')\n    if (isLink) value = wrapLink(value)\n    if (name === 'style') value = processStyleRule(value)\n\n    return { name, value }\n  })\n\nconst regColor = /rgba?\\((.*?)\\)/g\nconst regCssUrl = /url\\(\"?(.*?)\"?\\)/g\n\nfunction processStyleRule(val) {\n  // For css custom properties, val is unable to retrieved.\n  val = toStr(val)\n\n  return val\n    .replace(\n      regColor,\n      '<span class=\"eruda-style-color\" style=\"background-color: $&\"></span>$&'\n    )\n    .replace(regCssUrl, (match, url) => `url(\"${wrapLink(url)}\")`)\n}\n\nfunction getInlineStyle(style) {\n  const ret = {\n    selectorText: 'element.style',\n    style: {},\n  }\n\n  for (let i = 0, len = style.length; i < len; i++) {\n    const s = style[i]\n\n    ret.style[s] = style[s]\n  }\n\n  return ret\n}\n\nfunction rmDefComputedStyle(computedStyle, styles) {\n  const ret = {}\n\n  let keepStyles = ['display', 'width', 'height']\n  each(styles, (style) => {\n    keepStyles = keepStyles.concat(keys(style.style))\n  })\n  keepStyles = unique(keepStyles)\n\n  each(computedStyle, (val, key) => {\n    if (!contain(keepStyles, key)) return\n\n    ret[key] = val\n  })\n\n  return ret\n}\n\nconst NO_STYLE_TAG = ['script', 'style', 'meta', 'title', 'link', 'head']\n\nconst needNoStyle = (tagName) => {\n  NO_STYLE_TAG.indexOf(tagName.toLowerCase()) > -1\n}\n\nconst wrapLink = (link) => `<a href=\"${link}\" target=\"_blank\">${link}</a>`\n\nfunction addEvent(el, type, listener, useCapture = false) {\n  if (!isEl(el) || !isFn(listener) || !isBool(useCapture)) return\n\n  const events = (el.erudaEvents = el.erudaEvents || {})\n\n  events[type] = events[type] || []\n  events[type].push({\n    listener: listener,\n    listenerStr: listener.toString(),\n    useCapture: useCapture,\n  })\n}\n\nfunction rmEvent(el, type, listener, useCapture = false) {\n  if (!isEl(el) || !isFn(listener) || !isBool(useCapture)) return\n\n  const events = el.erudaEvents\n\n  if (!(events && events[type])) return\n\n  const listeners = events[type]\n\n  for (let i = 0, len = listeners.length; i < len; i++) {\n    if (listeners[i].listener === listener) {\n      listeners.splice(i, 1)\n      break\n    }\n  }\n\n  if (listeners.length === 0) delete events[type]\n  if (keys(events).length === 0) delete el.erudaEvents\n}\n\nconst getWinEventProto = () => {\n  return safeGet(window, 'EventTarget.prototype') || window.Node.prototype\n}\n"
  },
  {
    "path": "src/Elements/Elements.js",
    "content": "import Tool from '../DevTools/Tool'\nimport $ from 'licia/$'\nimport isEl from 'licia/isEl'\nimport nextTick from 'licia/nextTick'\nimport Emitter from 'licia/Emitter'\nimport map from 'licia/map'\nimport MediaQuery from 'licia/MediaQuery'\nimport isEmpty from 'licia/isEmpty'\nimport toNum from 'licia/toNum'\nimport copy from 'licia/copy'\nimport isMobile from 'licia/isMobile'\nimport isShadowRoot from 'licia/isShadowRoot'\nimport LunaDomViewer from 'luna-dom-viewer'\nimport { isErudaEl, classPrefix as c, isChobitsuEl } from '../lib/util'\nimport evalCss from '../lib/evalCss'\nimport Detail from './Detail'\nimport chobitsu from '../lib/chobitsu'\nimport emitter from '../lib/emitter'\nimport { formatNodeName } from './util'\n\nexport default class Elements extends Tool {\n  constructor() {\n    super()\n\n    this._style = evalCss(require('./Elements.scss'))\n\n    this.name = 'elements'\n    this._selectElement = false\n    this._observeElement = true\n    this._history = []\n\n    Emitter.mixin(this)\n  }\n  init($el, container) {\n    super.init($el)\n\n    this._container = container\n\n    this._initTpl()\n    this._htmlEl = document.documentElement\n    this._detail = new Detail(this._$detail, container)\n    this.config = this._detail.config\n    this._splitMediaQuery = new MediaQuery('screen and (min-width: 680px)')\n    this._splitMode = this._splitMediaQuery.isMatch()\n    this._domViewer = new LunaDomViewer(this._$domViewer.get(0), {\n      node: this._htmlEl,\n      ignore: (node) => isErudaEl(node) || isChobitsuEl(node),\n    })\n    this._domViewer.expand()\n    this._bindEvent()\n    chobitsu.domain('Overlay').enable()\n\n    nextTick(() => this._updateHistory())\n  }\n  show() {\n    super.show()\n    this._isShow = true\n\n    if (!this._curNode) {\n      this.select(document.body)\n    } else if (this._splitMode) {\n      this._showDetail()\n    }\n  }\n  hide() {\n    super.hide()\n    this._isShow = false\n\n    chobitsu.domain('Overlay').hideHighlight()\n  }\n  select(node) {\n    this._domViewer.select(node)\n    this._setNode(node)\n    this.emit('change', node)\n    return this\n  }\n  destroy() {\n    super.destroy()\n\n    emitter.off(emitter.SCALE, this._updateScale)\n    evalCss.remove(this._style)\n    this._detail.destroy()\n    chobitsu\n      .domain('Overlay')\n      .off('inspectNodeRequested', this._inspectNodeRequested)\n    chobitsu.domain('Overlay').disable()\n    this._splitMediaQuery.removeAllListeners()\n  }\n  _updateButtons() {\n    const $control = this._$control\n    const $showDetail = $control.find(c('.show-detail'))\n    const $copyNode = $control.find(c('.copy-node'))\n    const $deleteNode = $control.find(c('.delete-node'))\n    const iconDisabled = c('icon-disabled')\n\n    $showDetail.addClass(iconDisabled)\n    $copyNode.addClass(iconDisabled)\n    $deleteNode.addClass(iconDisabled)\n\n    const node = this._curNode\n\n    if (!node || isShadowRoot(node)) {\n      return\n    }\n\n    if (node !== document.documentElement && node !== document.body) {\n      $deleteNode.rmClass(iconDisabled)\n    }\n    $copyNode.rmClass(iconDisabled)\n\n    if (node.nodeType === Node.ELEMENT_NODE) {\n      $showDetail.rmClass(iconDisabled)\n    }\n  }\n  _showDetail = () => {\n    if (!this._isShow || !this._curNode) {\n      return\n    }\n    if (this._curNode.nodeType === Node.ELEMENT_NODE) {\n      this._detail.show(this._curNode)\n    } else {\n      this._detail.show(this._curNode.parentNode || this._curNode.host)\n    }\n  }\n  _initTpl() {\n    const $el = this._$el\n\n    $el.html(\n      c(`<div class=\"elements\">\n        <div class=\"control\">\n          <span class=\"icon icon-select select\"></span>\n          <span class=\"icon icon-eye show-detail\"></span>\n          <span class=\"icon icon-copy copy-node\"></span>\n          <span class=\"icon icon-delete delete-node\"></span>\n        </div>\n        <div class=\"dom-viewer-container\">\n          <div class=\"dom-viewer\"></div>\n        </div>\n        <div class=\"crumbs\"></div>\n      </div>\n      <div class=\"detail\"></div>`)\n    )\n\n    this._$detail = $el.find(c('.detail'))\n    this._$domViewer = $el.find(c('.dom-viewer'))\n    this._$control = $el.find(c('.control'))\n    this._$crumbs = $el.find(c('.crumbs'))\n  }\n  _renderCrumbs() {\n    const crumbs = getCrumbs(this._curNode)\n    let html = ''\n    if (!isEmpty(crumbs)) {\n      html = map(crumbs, ({ text, idx }) => {\n        return `<li class=\"${c('crumb')}\" data-idx=\"${idx}\">${text}</div></li>`\n      }).join('')\n    }\n    this._$crumbs.html(html)\n  }\n  _back = () => {\n    if (this._curNode === this._htmlEl) return\n\n    const parentQueue = this._curParentQueue\n    let parent = parentQueue.shift()\n\n    while (!isElExist(parent)) {\n      parent = parentQueue.shift()\n    }\n\n    this.set(parent)\n  }\n  _bindEvent() {\n    const self = this\n\n    this._$el.on('click', c('.crumb'), function () {\n      let idx = toNum($(this).data('idx'))\n      let node = self._curNode\n\n      while (idx-- && node.parentElement) {\n        node = node.parentElement\n      }\n\n      if (isElExist(node)) {\n        self.select(node)\n      }\n    })\n\n    this._$control\n      .on('click', c('.select'), this._toggleSelect)\n      .on('click', c('.show-detail'), this._showDetail)\n      .on('click', c('.copy-node'), this._copyNode)\n      .on('click', c('.delete-node'), this._deleteNode)\n\n    this._domViewer.on('select', this._setNode).on('deselect', this._back)\n\n    chobitsu\n      .domain('Overlay')\n      .on('inspectNodeRequested', this._inspectNodeRequested)\n\n    this._splitMediaQuery.on('match', () => {\n      this._splitMode = true\n      this._showDetail()\n    })\n    this._splitMediaQuery.on('unmatch', () => {\n      this._splitMode = false\n      this._detail.hide()\n    })\n\n    emitter.on(emitter.SCALE, this._updateScale)\n  }\n  _updateScale = (scale) => {\n    this._splitMediaQuery.setQuery(`screen and (min-width: ${680 * scale}px)`)\n  }\n  _deleteNode = () => {\n    const node = this._curNode\n\n    if (node.parentNode) {\n      node.parentNode.removeChild(node)\n    }\n  }\n  _copyNode = () => {\n    const node = this._curNode\n\n    if (node.nodeType === Node.ELEMENT_NODE) {\n      copy(node.outerHTML)\n    } else {\n      copy(node.nodeValue)\n    }\n\n    this._container.notify('Copied', { icon: 'success' })\n  }\n  _toggleSelect = () => {\n    this._$el.find(c('.select')).toggleClass(c('active'))\n    this._selectElement = !this._selectElement\n\n    if (this._selectElement) {\n      chobitsu.domain('Overlay').setInspectMode({\n        mode: 'searchForNode',\n        highlightConfig: {\n          showInfo: !isMobile(),\n          showRulers: false,\n          showAccessibilityInfo: !isMobile(),\n          showExtensionLines: false,\n          contrastAlgorithm: 'aa',\n          contentColor: 'rgba(111, 168, 220, .66)',\n          paddingColor: 'rgba(147, 196, 125, .55)',\n          borderColor: 'rgba(255, 229, 153, .66)',\n          marginColor: 'rgba(246, 178, 107, .66)',\n        },\n      })\n      this._container.hide()\n    } else {\n      chobitsu.domain('Overlay').setInspectMode({\n        mode: 'none',\n      })\n      chobitsu.domain('Overlay').hideHighlight()\n    }\n  }\n  _inspectNodeRequested = ({ backendNodeId }) => {\n    this._container.show()\n    this._toggleSelect()\n    try {\n      const { node } = chobitsu.domain('DOM').getNode({ nodeId: backendNodeId })\n      this.select(node)\n    } catch {\n      // No op\n    }\n  }\n  _setNode = (node) => {\n    if (node === this._curNode) return\n\n    this._curNode = node\n    this._renderCrumbs()\n\n    const parentQueue = []\n\n    let parent = node.parentNode\n    while (parent) {\n      parentQueue.push(parent)\n      parent = parent.parentNode\n    }\n    this._curParentQueue = parentQueue\n\n    if (this._splitMode) {\n      this._showDetail()\n    }\n    this._updateButtons()\n    this._updateHistory()\n  }\n  _updateHistory() {\n    const console = this._container.get('console')\n    if (!console) return\n\n    const history = this._history\n    history.unshift(this._curNode)\n    if (history.length > 5) history.pop()\n    for (let i = 0; i < 5; i++) {\n      console.setGlobal(`$${i}`, history[i])\n    }\n  }\n}\n\nconst isElExist = (val) => isEl(val) && val.parentNode\n\nfunction getCrumbs(el) {\n  const ret = []\n  let i = 0\n\n  while (el) {\n    ret.push({\n      text: formatNodeName(el, { noAttr: true }),\n      idx: i++,\n    })\n\n    if (isShadowRoot(el)) {\n      el = el.host\n    }\n    if (!el.parentElement && isShadowRoot(el.parentNode)) {\n      el = el.parentNode\n    } else {\n      el = el.parentElement\n    }\n  }\n\n  return ret.reverse()\n}\n"
  },
  {
    "path": "src/Elements/Elements.scss",
    "content": "@use '../style/variable' as *;\n@use '../style/mixin' as *;\n\n#elements {\n  .elements {\n    @include absolute();\n    padding-top: 40px;\n    padding-bottom: 24px;\n    font-size: 14px;\n  }\n  .control {\n    padding: 10px 0;\n    @include control();\n    .icon-eye {\n      right: 0;\n    }\n    .icon-copy {\n      right: 23px;\n    }\n    .icon-delete {\n      right: 46px;\n    }\n  }\n  .dom-viewer-container {\n    @include overflow-auto();\n    height: 100%;\n    padding: 5px 0;\n  }\n  .crumbs {\n    @include absolute(100%, 24px);\n    top: initial;\n    line-height: 24px;\n    bottom: 0;\n    border-top: 1px solid var(--border);\n    background: var(--darker-background);\n    color: var(--primary);\n    font-size: $font-size-s;\n    white-space: nowrap;\n    overflow: hidden;\n    text-overflow: ellipsis;\n    li {\n      cursor: pointer;\n      padding: 0 7px;\n      display: inline-block;\n      &:hover,\n      &:last-child {\n        background: var(--highlight);\n      }\n    }\n  }\n  .detail {\n    @include absolute();\n    z-index: 10;\n    padding-top: 40px;\n    display: none;\n    background: var(--background);\n    .control {\n      padding: 10px 35px;\n      .element-name {\n        font-size: $font-size-s;\n        overflow: hidden;\n        white-space: nowrap;\n        text-overflow: ellipsis;\n        width: 100%;\n        display: inline-block;\n      }\n      .icon-left {\n        left: 0;\n      }\n      .icon-refresh {\n        right: 0;\n      }\n    }\n    .element {\n      @include overflow-auto(y);\n      height: 100%;\n    }\n  }\n  .section {\n    border-bottom: 1px solid var(--border);\n    color: var(--foreground);\n    margin: 10px 0;\n    h2 {\n      color: var(--primary);\n      background: var(--darker-background);\n      border-top: 1px solid var(--border);\n      padding: $padding;\n      line-height: 18px;\n      font-size: $font-size;\n      transition: background-color $anim-duration;\n      @include right-btn();\n      &.active-effect {\n        cursor: pointer;\n      }\n      &.active-effect:active {\n        background: var(--highlight);\n        color: var(--select-foreground);\n      }\n    }\n  }\n  .attributes {\n    font-size: $font-size-s;\n    a {\n      color: var(--link-color);\n    }\n    .table-wrapper {\n      @include overflow-auto(x);\n    }\n    table {\n      td {\n        padding: 5px 10px;\n      }\n    }\n  }\n  .text-content {\n    background: #fff;\n    .content {\n      @include overflow-auto(x);\n      padding: $padding;\n    }\n  }\n  .style-color {\n    position: relative;\n    top: 1px;\n    width: 10px;\n    height: 10px;\n    border-radius: 50%;\n    margin-right: 2px;\n    border: 1px solid var(--border);\n    display: inline-block;\n  }\n  .box-model {\n    @include overflow-auto(x);\n    padding: $padding;\n    text-align: center;\n    border-bottom: 1px solid var(--color);\n  }\n  .computed-style {\n    font-size: $font-size-s;\n    a {\n      color: var(--link-color);\n    }\n    .table-wrapper {\n      @include overflow-auto(y);\n      max-height: 200px;\n      border-top: 1px solid var(--border);\n    }\n    table {\n      td {\n        padding: 5px 10px;\n        &.key {\n          white-space: nowrap;\n          color: var(--var-color);\n        }\n      }\n    }\n  }\n  .styles {\n    font-size: $font-size-s;\n    .style-wrapper {\n      padding: $padding;\n      .style-rules {\n        border: 1px solid var(--border);\n        padding: $padding;\n        margin-bottom: 10px;\n        .rule {\n          padding-left: 2em;\n          word-break: break-all;\n          a {\n            color: var(--link-color);\n          }\n          span {\n            color: var(--var-color);\n          }\n        }\n        &:last-child {\n          margin-bottom: 0;\n        }\n      }\n    }\n  }\n  .listeners {\n    font-size: $font-size-s;\n    .listener-wrapper {\n      padding: $padding;\n      .listener {\n        margin-bottom: 10px;\n        overflow: hidden;\n        border: 1px solid var(--border);\n        .listener-type {\n          padding: $padding;\n          background: var(--darker-background);\n          color: var(--primary);\n        }\n        .listener-content {\n          li {\n            @include overflow-auto(x);\n            padding: $padding;\n            border-top: none;\n          }\n        }\n      }\n    }\n  }\n}\n\n.safe-area #elements {\n  .elements {\n    @include safe-area(padding-bottom, 24px);\n  }\n  .crumbs {\n    @include safe-area(height, 24px);\n  }\n  .element {\n    @include safe-area(padding-bottom, 0px);\n  }\n}\n\n@media screen and (min-width: 680px) {\n  #elements {\n    .elements {\n      width: 50%;\n      .control {\n        .icon-eye {\n          display: none;\n        }\n        .icon-copy {\n          right: 0;\n        }\n        .icon-delete {\n          right: 23px;\n        }\n      }\n    }\n    .detail {\n      width: 50%;\n      left: initial;\n      right: 0;\n      border-left: 1px solid var(--border);\n      .control {\n        padding-left: 10px;\n        .icon-left {\n          display: none;\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "src/Elements/util.js",
    "content": "import each from 'licia/each'\nimport isStr from 'licia/isStr'\nimport isShadowRoot from 'licia/isShadowRoot'\nimport { classPrefix as c } from '../lib/util'\n\nexport function formatNodeName(node, { noAttr = false } = {}) {\n  if (node.nodeType === Node.TEXT_NODE) {\n    return `<span class=\"${c('tag-name-color')}\">(text)</span>`\n  } else if (node.nodeType === Node.COMMENT_NODE) {\n    return `<span class=\"${c('tag-name-color')}\"><!--></span>`\n  } else if (isShadowRoot(node)) {\n    return `<span class=\"${c('tag-name-color')}\">#shadow-root</span>`\n  }\n\n  const { id, className, attributes } = node\n\n  let ret = `<span class=\"eruda-tag-name-color\">${node.tagName.toLowerCase()}</span>`\n\n  if (id !== '') ret += `<span class=\"eruda-function-color\">#${id}</span>`\n\n  if (isStr(className)) {\n    let classes = ''\n    each(className.split(/\\s+/g), (val) => {\n      if (val.trim() === '') return\n      classes += `.${val}`\n    })\n    ret += `<span class=\"eruda-attribute-name-color\">${classes}</span>`\n  }\n\n  if (!noAttr) {\n    each(attributes, (attr) => {\n      const name = attr.name\n      if (name === 'id' || name === 'class' || name === 'style') return\n      ret += ` <span class=\"eruda-attribute-name-color\">${name}</span><span class=\"eruda-operator-color\">=\"</span><span class=\"eruda-string-color\">${attr.value}</span><span class=\"eruda-operator-color\">\"</span>`\n    })\n  }\n\n  return ret\n}\n"
  },
  {
    "path": "src/EntryBtn/EntryBtn.js",
    "content": "import emitter from '../lib/emitter'\nimport Settings from '../Settings/Settings'\nimport Emitter from 'licia/Emitter'\nimport $ from 'licia/$'\nimport nextTick from 'licia/nextTick'\nimport orientation from 'licia/orientation'\nimport pointerEvent from 'licia/pointerEvent'\nimport { pxToNum, classPrefix as c, eventClient } from '../lib/util'\nimport evalCss from '../lib/evalCss'\n\nconst $document = $(document)\n\nexport default class EntryBtn extends Emitter {\n  constructor($container) {\n    super()\n\n    this._style = evalCss(require('./EntryBtn.scss'))\n\n    this._$container = $container\n    this._initTpl()\n    this._bindEvent()\n    this._registerListener()\n  }\n  hide() {\n    this._$el.hide()\n  }\n  show() {\n    this._$el.show()\n  }\n  setPos(pos) {\n    if (this._isOutOfRange(pos)) {\n      pos = this._getDefPos()\n    }\n\n    this._$el.css({\n      left: pos.x,\n      top: pos.y,\n    })\n\n    this.config.set('pos', pos)\n  }\n  getPos() {\n    return this.config.get('pos')\n  }\n  destroy() {\n    evalCss.remove(this._style)\n    this._unregisterListener()\n    this._$el.remove()\n  }\n  _isOutOfRange(pos) {\n    pos = pos || this.config.get('pos')\n    const defPos = this._getDefPos()\n\n    return (\n      pos.x > defPos.x + 10 || pos.x < 0 || pos.y < 0 || pos.y > defPos.y + 10\n    )\n  }\n  _registerListener() {\n    this._scaleListener = () =>\n      nextTick(() => {\n        if (this._isOutOfRange()) this._resetPos()\n      })\n    emitter.on(emitter.SCALE, this._scaleListener)\n  }\n  _unregisterListener() {\n    emitter.off(emitter.SCALE, this._scaleListener)\n  }\n  _initTpl() {\n    const $container = this._$container\n\n    $container.append(\n      c('<div class=\"entry-btn\"><span class=\"icon-tool\"></span></div>')\n    )\n    this._$el = $container.find('.eruda-entry-btn')\n  }\n  _resetPos(orientationChanged) {\n    const cfg = this.config\n    let pos = cfg.get('pos')\n    const defPos = this._getDefPos()\n\n    if (!cfg.get('rememberPos') || orientationChanged) {\n      pos = defPos\n    }\n\n    this.setPos(pos)\n  }\n  _onDragStart = (e) => {\n    const $el = this._$el\n    $el.addClass(c('active'))\n\n    this._isClick = true\n    e = e.origEvent\n    this._startX = eventClient('x', e)\n    this._oldX = pxToNum($el.css('left'))\n    this._oldY = pxToNum($el.css('top'))\n    this._startY = eventClient('y', e)\n    $document.on(pointerEvent('move'), this._onDragMove)\n    $document.on(pointerEvent('up'), this._onDragEnd)\n  }\n  _onDragMove = (e) => {\n    const btnSize = this._$el.get(0).offsetWidth\n    const maxWidth = this._$container.get(0).offsetWidth\n    const maxHeight = this._$container.get(0).offsetHeight\n\n    e = e.origEvent\n    const deltaX = eventClient('x', e) - this._startX\n    const deltaY = eventClient('y', e) - this._startY\n    if (Math.abs(deltaX) > 3 || Math.abs(deltaY) > 3) {\n      this._isClick = false\n    }\n    let newX = this._oldX + deltaX\n    let newY = this._oldY + deltaY\n    if (newX < 0) {\n      newX = 0\n    } else if (newX > maxWidth - btnSize) {\n      newX = maxWidth - btnSize\n    }\n    if (newY < 0) {\n      newY = 0\n    } else if (newY > maxHeight - btnSize) {\n      newY = maxHeight - btnSize\n    }\n    this._$el.css({\n      left: newX,\n      top: newY,\n    })\n  }\n  _onDragEnd = (e) => {\n    const $el = this._$el\n\n    if (this._isClick) {\n      this.emit('click')\n    }\n\n    this._onDragMove(e)\n    $document.off(pointerEvent('move'), this._onDragMove)\n    $document.off(pointerEvent('up'), this._onDragEnd)\n\n    const cfg = this.config\n\n    if (cfg.get('rememberPos')) {\n      cfg.set('pos', {\n        x: pxToNum($el.css('left')),\n        y: pxToNum($el.css('top')),\n      })\n    }\n\n    $el.rmClass('eruda-active')\n  }\n  _bindEvent() {\n    const $el = this._$el\n\n    $el.on(pointerEvent('down'), this._onDragStart)\n\n    orientation.on('change', () => this._resetPos(true))\n    window.addEventListener('resize', () => this._resetPos())\n  }\n  initCfg(settings) {\n    const cfg = (this.config = Settings.createCfg('entry-button', {\n      rememberPos: true,\n      pos: this._getDefPos(),\n    }))\n\n    settings.switch(cfg, 'rememberPos', 'Remember Entry Button Position')\n\n    this._resetPos()\n  }\n  _getDefPos() {\n    const minWidth = this._$el.get(0).offsetWidth + 10\n\n    return {\n      x: window.innerWidth - minWidth,\n      y: window.innerHeight - minWidth,\n    }\n  }\n}\n"
  },
  {
    "path": "src/EntryBtn/EntryBtn.scss",
    "content": ".container {\n  .entry-btn {\n    touch-action: none;\n    width: 40px;\n    height: 40px;\n    display: flex;\n    background: #000;\n    opacity: 0.3;\n    border-radius: 10px;\n    position: relative;\n    z-index: 1000;\n    transition: opacity 0.3s;\n    color: #fff;\n    font-size: 25px;\n    align-items: center;\n    justify-content: center;\n    &.active,\n    &:active {\n      opacity: 0.8;\n    }\n  }\n}\n"
  },
  {
    "path": "src/Info/Info.js",
    "content": "import Tool from '../DevTools/Tool'\nimport defInfo from './defInfo'\nimport each from 'licia/each'\nimport isFn from 'licia/isFn'\nimport isUndef from 'licia/isUndef'\nimport cloneDeep from 'licia/cloneDeep'\nimport evalCss from '../lib/evalCss'\nimport map from 'licia/map'\nimport escape from 'licia/escape'\nimport copy from 'licia/copy'\nimport $ from 'licia/$'\nimport { classPrefix as c } from '../lib/util'\n\nexport default class Info extends Tool {\n  constructor() {\n    super()\n\n    this._style = evalCss(require('./Info.scss'))\n\n    this.name = 'info'\n    this._infos = []\n  }\n  init($el, container) {\n    super.init($el)\n    this._container = container\n\n    this._addDefInfo()\n    this._bindEvent()\n  }\n  destroy() {\n    super.destroy()\n\n    evalCss.remove(this._style)\n  }\n  add(name, val) {\n    const infos = this._infos\n    let isUpdate = false\n\n    each(infos, (info) => {\n      if (name !== info.name) return\n\n      info.val = val\n      isUpdate = true\n    })\n\n    if (!isUpdate) infos.push({ name, val })\n\n    this._render()\n\n    return this\n  }\n  get(name) {\n    const infos = this._infos\n\n    if (isUndef(name)) {\n      return cloneDeep(infos)\n    }\n\n    let result\n\n    each(infos, (info) => {\n      if (name === info.name) result = info.val\n    })\n\n    return result\n  }\n  remove(name) {\n    const infos = this._infos\n\n    for (let i = infos.length - 1; i >= 0; i--) {\n      if (infos[i].name === name) infos.splice(i, 1)\n    }\n\n    this._render()\n\n    return this\n  }\n  clear() {\n    this._infos = []\n\n    this._render()\n\n    return this\n  }\n  _addDefInfo() {\n    each(defInfo, (info) => this.add(info.name, info.val))\n  }\n  _render() {\n    const infos = []\n\n    each(this._infos, ({ name, val }) => {\n      if (isFn(val)) val = val()\n\n      infos.push({ name, val })\n    })\n\n    const html = `<ul>${map(\n      infos,\n      (info) =>\n        `<li><h2 class=\"${c('title')}\">${escape(info.name)}<span class=\"${c(\n          'icon-copy copy'\n        )}\"></span></h2><div class=\"${c('content')}\">${info.val}</div></li>`\n    ).join('')}</ul>`\n\n    this._renderHtml(html)\n  }\n  _bindEvent() {\n    const container = this._container\n\n    this._$el.on('click', c('.copy'), function () {\n      const $li = $(this).parent().parent()\n      const name = $li.find(c('.title')).text()\n      const content = $li.find(c('.content')).text()\n      copy(`${name}: ${content}`)\n      container.notify('Copied', { icon: 'success' })\n    })\n  }\n  _renderHtml(html) {\n    if (html === this._lastHtml) return\n    this._lastHtml = html\n    this._$el.html(html)\n  }\n}\n"
  },
  {
    "path": "src/Info/Info.scss",
    "content": "@use '../style/variable' as *;\n@use '../style/mixin' as *;\n\n#info {\n  @include overflow-auto(y);\n  li {\n    margin: 10px;\n    border: 1px solid var(--border);\n    .title,\n    .content {\n      padding: $padding;\n    }\n    .title {\n      position: relative;\n      padding-bottom: 0;\n      color: var(--accent);\n      .icon-copy {\n        position: absolute;\n        right: 10px;\n        top: 14px;\n        color: var(--primary);\n        cursor: pointer;\n        transition: color $anim-duration;\n        &:active {\n          color: var(--accent);\n        }\n      }\n    }\n    .content {\n      margin: 0;\n      user-select: text;\n      color: var(--foreground);\n      font-size: $font-size-s;\n      word-break: break-all;\n      table {\n        width: 100%;\n        border-collapse: collapse;\n        th,\n        td {\n          border: 1px solid var(--border);\n          padding: 10px;\n        }\n      }\n      * {\n        user-select: text;\n      }\n      a {\n        color: var(--link-color);\n      }\n    }\n    .device-key,\n    .system-key {\n      width: 100px;\n    }\n  }\n}\n\n.safe-area #info {\n  @include safe-area(padding-bottom, 10px);\n}\n"
  },
  {
    "path": "src/Info/defInfo.js",
    "content": "import detectBrowser from 'licia/detectBrowser'\nimport detectOs from 'licia/detectOs'\nimport escape from 'licia/escape'\nimport map from 'licia/map'\n\nconst browser = detectBrowser()\n\nexport default [\n  {\n    name: 'Location',\n    val() {\n      return escape(location.href)\n    },\n  },\n  {\n    name: 'User Agent',\n    val: navigator.userAgent,\n  },\n  {\n    name: 'Device',\n    val: [\n      '<table><tbody>',\n      `<tr><td class=\"eruda-device-key\">screen</td><td>${screen.width} * ${screen.height}</td></tr>`,\n      `<tr><td>viewport</td><td>${window.innerWidth} * ${window.innerHeight}</td></tr>`,\n      `<tr><td>pixel ratio</td><td>${window.devicePixelRatio}</td></tr>`,\n      '</tbody></table>',\n    ].join(''),\n  },\n  {\n    name: 'System',\n    val: [\n      '<table><tbody>',\n      `<tr><td class=\"eruda-system-key\">os</td><td>${detectOs()}</td></tr>`,\n      `<tr><td>browser</td><td>${\n        browser.name + ' ' + browser.version\n      }</td></tr>`,\n      '</tbody></table>',\n    ].join(''),\n  },\n  {\n    name: 'Sponsor this Project',\n    val() {\n      return (\n        '<table><tbody>' +\n        map(\n          [\n            {\n              name: 'Open Collective',\n              link: 'https://opencollective.com/eruda',\n            },\n            {\n              name: 'Ko-fi',\n              link: 'https://ko-fi.com/surunzi',\n            },\n            {\n              name: 'Wechat Pay',\n              link: 'https://surunzi.com/wechatpay.html',\n            },\n          ],\n          (item) => {\n            return `<tr><td>${\n              item.name\n            }</td><td><a rel=\"noreferrer noopener\" href=\"${\n              item.link\n            }\" target=\"_blank\">${item.link.replace(\n              'https://',\n              ''\n            )}</a></td></tr>`\n          }\n        ).join(' ') +\n        '</tbody></table>'\n      )\n    },\n  },\n  {\n    name: 'About',\n    val:\n      '<a href=\"https://eruda.liriliri.io\" target=\"_blank\">Eruda v' +\n      VERSION +\n      '</a>',\n  },\n]\n"
  },
  {
    "path": "src/Network/Detail.js",
    "content": "import trim from 'licia/trim'\nimport isEmpty from 'licia/isEmpty'\nimport map from 'licia/map'\nimport each from 'licia/each'\nimport escape from 'licia/escape'\nimport copy from 'licia/copy'\nimport isJson from 'licia/isJson'\nimport Emitter from 'licia/Emitter'\nimport truncate from 'licia/truncate'\nimport { classPrefix as c } from '../lib/util'\n\nexport default class Detail extends Emitter {\n  constructor($container, devtools) {\n    super()\n    this._$container = $container\n    this._devtools = devtools\n\n    this._detailData = {}\n    this._bindEvent()\n  }\n  show(data) {\n    if (data.resTxt && trim(data.resTxt) === '') {\n      delete data.resTxt\n    }\n    if (isEmpty(data.resHeaders)) {\n      delete data.resHeaders\n    }\n    if (isEmpty(data.reqHeaders)) {\n      delete data.reqHeaders\n    }\n\n    let postData = ''\n    if (data.data) {\n      postData = `<pre class=\"${c('data')}\">${escape(data.data)}</pre>`\n    }\n\n    let reqHeaders = '<tr><td>Empty</td></tr>'\n    if (data.reqHeaders) {\n      reqHeaders = map(data.reqHeaders, (val, key) => {\n        return `<tr>\n          <td class=\"${c('key')}\">${escape(key)}</td>\n          <td>${escape(val)}</td>\n        </tr>`\n      }).join('')\n    }\n\n    let resHeaders = '<tr><td>Empty</td></tr>'\n    if (data.resHeaders) {\n      resHeaders = map(data.resHeaders, (val, key) => {\n        return `<tr>\n          <td class=\"${c('key')}\">${escape(key)}</td>\n          <td>${escape(val)}</td>\n        </tr>`\n      }).join('')\n    }\n\n    let resTxt = ''\n    if (data.resTxt) {\n      let text = data.resTxt\n      if (text.length > MAX_RES_LEN) {\n        text = truncate(text, MAX_RES_LEN)\n      }\n      resTxt = `<pre class=\"${c('response')}\">${escape(text)}</pre>`\n    }\n\n    const html = `<div class=\"${c('control')}\">\n      <span class=\"${c('icon-left back')}\"></span>\n      <span class=\"${c('icon-delete back')}\"></span>\n      <span class=\"${c('url')}\">${escape(data.url)}</span>\n      <span class=\"${c('icon-copy copy-res')}\"></span>\n    </div>\n    <div class=\"${c('http')}\">\n      ${postData}\n      <div class=\"${c('section')}\">\n        <h2>Response Headers</h2>\n        <table class=\"${c('headers')}\">\n          <tbody>\n            ${resHeaders}\n          </tbody>\n        </table>\n      </div>\n      <div class=\"${c('section')}\">\n        <h2>Request Headers</h2>\n        <table class=\"${c('headers')}\">\n          <tbody>\n            ${reqHeaders}\n          </tbody>\n        </table>\n      </div>\n      ${resTxt}\n    </div>`\n\n    this._$container.html(html).show()\n    this._detailData = data\n  }\n  hide() {\n    this._$container.hide()\n    this.emit('hide')\n  }\n  _copyRes = () => {\n    const detailData = this._detailData\n\n    let data = `${detailData.method} ${detailData.url} ${detailData.status}\\n`\n    if (!isEmpty(detailData.data)) {\n      data += '\\nRequest Data\\n\\n'\n      data += `${detailData.data}\\n`\n    }\n    if (!isEmpty(detailData.reqHeaders)) {\n      data += '\\nRequest Headers\\n\\n'\n      each(detailData.reqHeaders, (val, key) => (data += `${key}: ${val}\\n`))\n    }\n    if (!isEmpty(detailData.resHeaders)) {\n      data += '\\nResponse Headers\\n\\n'\n      each(detailData.resHeaders, (val, key) => (data += `${key}: ${val}\\n`))\n    }\n    if (detailData.resTxt) {\n      data += `\\n${detailData.resTxt}\\n`\n    }\n\n    copy(data)\n    this._devtools.notify('Copied', { icon: 'success' })\n  }\n  _bindEvent() {\n    const devtools = this._devtools\n\n    this._$container\n      .on('click', c('.back'), () => this.hide())\n      .on('click', c('.copy-res'), this._copyRes)\n      .on('click', c('.http .response'), () => {\n        const data = this._detailData\n        const resTxt = data.resTxt\n\n        if (isJson(resTxt)) {\n          return showSources('object', resTxt)\n        }\n\n        switch (data.subType) {\n          case 'css':\n            return showSources('css', resTxt)\n          case 'html':\n            return showSources('html', resTxt)\n          case 'javascript':\n            return showSources('js', resTxt)\n          case 'json':\n            return showSources('object', resTxt)\n        }\n        switch (data.type) {\n          case 'image':\n            return showSources('img', data.url)\n        }\n      })\n\n    const showSources = (type, data) => {\n      const sources = devtools.get('sources')\n      if (!sources) {\n        return\n      }\n\n      sources.set(type, data)\n\n      devtools.showTool('sources')\n    }\n  }\n}\n\nconst MAX_RES_LEN = 100000\n"
  },
  {
    "path": "src/Network/Network.js",
    "content": "import Tool from '../DevTools/Tool'\nimport $ from 'licia/$'\nimport ms from 'licia/ms'\nimport each from 'licia/each'\nimport map from 'licia/map'\nimport Detail from './Detail'\nimport throttle from 'licia/throttle'\nimport { getFileName, classPrefix as c } from '../lib/util'\nimport evalCss from '../lib/evalCss'\nimport chobitsu from '../lib/chobitsu'\nimport emitter from '../lib/emitter'\nimport LunaDataGrid from 'luna-data-grid'\nimport ResizeSensor from 'licia/ResizeSensor'\nimport MediaQuery from 'licia/MediaQuery'\nimport { getType } from './util'\nimport copy from 'licia/copy'\nimport extend from 'licia/extend'\nimport trim from 'licia/trim'\nimport isNull from 'licia/isNull'\nimport LunaModal from 'luna-modal'\nimport { curlStr } from './util'\n\nexport default class Network extends Tool {\n  constructor() {\n    super()\n\n    this._style = evalCss(require('./Network.scss'))\n\n    this.name = 'network'\n    this._requests = {}\n    this._selectedRequest = null\n    this._isRecording = true\n  }\n  init($el, container) {\n    super.init($el)\n\n    this._container = container\n    this._initTpl()\n    this._detail = new Detail(this._$detail, container)\n    this._splitMediaQuery = new MediaQuery('screen and (min-width: 680px)')\n    this._splitMode = this._splitMediaQuery.isMatch()\n    this._requestDataGrid = new LunaDataGrid(this._$requests.get(0), {\n      columns: [\n        {\n          id: 'name',\n          title: 'Name',\n          sortable: true,\n          weight: 30,\n        },\n        {\n          id: 'method',\n          title: 'Method',\n          sortable: true,\n          weight: 14,\n        },\n        {\n          id: 'status',\n          title: 'Status',\n          sortable: true,\n          weight: 14,\n        },\n        {\n          id: 'type',\n          title: 'Type',\n          sortable: true,\n          weight: 14,\n        },\n        {\n          id: 'size',\n          title: 'Size',\n          sortable: true,\n          weight: 14,\n        },\n        {\n          id: 'time',\n          title: 'Time',\n          sortable: true,\n          weight: 14,\n        },\n      ],\n    })\n    this._resizeSensor = new ResizeSensor($el.get(0))\n    this._bindEvent()\n  }\n  show() {\n    super.show()\n    this._updateDataGridHeight()\n  }\n  clear() {\n    this._requests = {}\n    this._requestDataGrid.clear()\n  }\n  requests() {\n    const ret = []\n    each(this._requests, (request) => {\n      ret.push(request)\n    })\n    return ret\n  }\n  _updateDataGridHeight() {\n    this._requestDataGrid.fit()\n  }\n  _reqWillBeSent = (params) => {\n    if (!this._isRecording) {\n      return\n    }\n\n    const request = {\n      name: getFileName(params.request.url),\n      url: params.request.url,\n      status: 'pending',\n      type: 'unknown',\n      subType: 'unknown',\n      size: 0,\n      data: params.request.postData,\n      method: params.request.method,\n      startTime: params.timestamp * 1000,\n      time: 0,\n      resTxt: '',\n      done: false,\n      reqHeaders: params.request.headers || {},\n      resHeaders: {},\n    }\n    let node\n    request.render = () => {\n      const data = {\n        name: request.name,\n        method: request.method,\n        status: request.status,\n        type: request.subType,\n        size: request.size,\n        time: request.displayTime,\n      }\n      if (node) {\n        node.data = data\n        node.render()\n      } else {\n        node = this._requestDataGrid.append(data, { selectable: true })\n        $(node.container).data('id', params.requestId)\n      }\n      if (request.hasErr) {\n        $(node.container).addClass(c('request-error'))\n      }\n    }\n    request.render()\n    this._requests[params.requestId] = request\n  }\n  _resReceivedExtraInfo = (params) => {\n    const request = this._requests[params.requestId]\n    if (!this._isRecording || !request) {\n      return\n    }\n\n    request.resHeaders = params.headers\n\n    this._updateType(request)\n    request.render()\n  }\n  _updateType(request) {\n    const contentType = request.resHeaders['content-type'] || ''\n    const { type, subType } = getType(contentType)\n    request.type = type\n    request.subType = subType\n  }\n  _resReceived = (params) => {\n    const request = this._requests[params.requestId]\n    if (!this._isRecording || !request) {\n      return\n    }\n\n    const { response } = params\n    const { status, headers } = response\n    request.status = status\n    if (status < 200 || status >= 300) {\n      request.hasErr = true\n    }\n    if (headers) {\n      request.resHeaders = headers\n      this._updateType(request)\n    }\n\n    request.render()\n  }\n  _loadingFinished = (params) => {\n    const request = this._requests[params.requestId]\n    if (!this._isRecording || !request) {\n      return\n    }\n\n    const time = params.timestamp * 1000\n    request.time = time - request.startTime\n    request.displayTime = ms(request.time)\n\n    request.size = params.encodedDataLength\n    request.done = true\n    request.resTxt = chobitsu.domain('Network').getResponseBody({\n      requestId: params.requestId,\n    }).body\n\n    request.render()\n  }\n  _loadingFailed = (params) => {\n    const request = this._requests[params.requestId]\n    if (!this._isRecording || !request) {\n      return\n    }\n\n    const time = params.timestamp * 1000\n    request.time = time - request.startTime\n    request.displayTime = ms(request.time)\n\n    request.hasErr = true\n    request.status = 0\n    request.done = true\n\n    request.render()\n  }\n  _copyCurl = () => {\n    const request = this._selectedRequest\n\n    copy(\n      curlStr({\n        requestMethod: request.method,\n        url() {\n          return request.url\n        },\n        requestFormData() {\n          return request.data\n        },\n        requestHeaders() {\n          const reqHeaders = request.reqHeaders || {}\n          extend(reqHeaders, {\n            'User-Agent': navigator.userAgent,\n            Referer: location.href,\n          })\n\n          return map(reqHeaders, (value, name) => {\n            return {\n              name,\n              value,\n            }\n          })\n        },\n      })\n    )\n\n    this._container.notify('Copied', { icon: 'success' })\n  }\n  _updateButtons() {\n    const $control = this._$control\n    const $showDetail = $control.find(c('.show-detail'))\n    const $copyCurl = $control.find(c('.copy-curl'))\n    const iconDisabled = c('icon-disabled')\n\n    $showDetail.addClass(iconDisabled)\n    $copyCurl.addClass(iconDisabled)\n\n    if (this._selectedRequest) {\n      $showDetail.rmClass(iconDisabled)\n      $copyCurl.rmClass(iconDisabled)\n    }\n  }\n  _toggleRecording = () => {\n    this._$control.find(c('.record')).toggleClass(c('recording'))\n    this._isRecording = !this._isRecording\n  }\n  _showDetail = () => {\n    if (this._selectedRequest) {\n      if (this._splitMode) {\n        this._$network.css('width', '50%')\n      }\n      this._detail.show(this._selectedRequest)\n    }\n  }\n  _bindEvent() {\n    const $control = this._$control\n    const $filterText = this._$filterText\n    const requestDataGrid = this._requestDataGrid\n\n    const self = this\n\n    $control\n      .on('click', c('.clear-request'), () => this.clear())\n      .on('click', c('.show-detail'), this._showDetail)\n      .on('click', c('.copy-curl'), this._copyCurl)\n      .on('click', c('.record'), this._toggleRecording)\n      .on('click', c('.filter'), () => {\n        LunaModal.prompt('Filter').then((filter) => {\n          if (isNull(filter)) return\n\n          $filterText.text(filter)\n          requestDataGrid.setOption('filter', trim(filter))\n        })\n      })\n\n    requestDataGrid.on('select', (node) => {\n      const id = $(node.container).data('id')\n      const request = self._requests[id]\n      this._selectedRequest = request\n      this._updateButtons()\n      if (this._splitMode) {\n        this._showDetail()\n      }\n    })\n\n    requestDataGrid.on('deselect', () => {\n      this._selectedRequest = null\n      this._updateButtons()\n      this._detail.hide()\n    })\n\n    this._resizeSensor.addListener(\n      throttle(() => this._updateDataGridHeight(), 15)\n    )\n\n    this._splitMediaQuery.on('match', () => {\n      this._detail.hide()\n      this._splitMode = true\n    })\n    this._splitMediaQuery.on('unmatch', () => {\n      this._detail.hide()\n      this._splitMode = false\n    })\n    this._detail.on('hide', () => {\n      if (this._splitMode) {\n        this._$network.css('width', '100%')\n      }\n    })\n\n    chobitsu.domain('Network').enable()\n\n    const network = chobitsu.domain('Network')\n    network.on('requestWillBeSent', this._reqWillBeSent)\n    network.on('responseReceivedExtraInfo', this._resReceivedExtraInfo)\n    network.on('responseReceived', this._resReceived)\n    network.on('loadingFinished', this._loadingFinished)\n    network.on('loadingFailed', this._loadingFailed)\n\n    emitter.on(emitter.SCALE, this._updateScale)\n  }\n  _updateScale = (scale) => {\n    this._splitMediaQuery.setQuery(`screen and (min-width: ${680 * scale}px)`)\n  }\n  destroy() {\n    super.destroy()\n\n    this._resizeSensor.destroy()\n    evalCss.remove(this._style)\n    this._splitMediaQuery.removeAllListeners()\n\n    const network = chobitsu.domain('Network')\n    network.off('requestWillBeSent', this._reqWillBeSent)\n    network.off('responseReceivedExtraInfo', this._resReceivedExtraInfo)\n    network.off('responseReceived', this._resReceived)\n    network.off('loadingFinished', this._loadingFinished)\n\n    emitter.off(emitter.SCALE, this._updateScale)\n  }\n  _initTpl() {\n    const $el = this._$el\n    $el.html(\n      c(`<div class=\"network\">\n        <div class=\"control\">\n          <span class=\"icon-record record recording\"></span>\n          <span class=\"icon-clear clear-request\"></span>\n          <span class=\"icon-eye icon-disabled show-detail\"></span>\n          <span class=\"icon-copy icon-disabled copy-curl\"></span>\n          <span class=\"filter-text\"></span>\n          <span class=\"icon-filter filter\"></span>\n        </div>\n        <div class=\"requests\"></div>\n      </div>\n      <div class=\"detail\"></div>`)\n    )\n    this._$network = $el.find(c('.network'))\n    this._$detail = $el.find(c('.detail'))\n    this._$requests = $el.find(c('.requests'))\n    this._$control = $el.find(c('.control'))\n    this._$filterText = $el.find(c('.filter-text'))\n  }\n}\n"
  },
  {
    "path": "src/Network/Network.scss",
    "content": "@use '../style/variable' as *;\n@use '../style/mixin' as *;\n\n#network {\n  .network {\n    @include absolute();\n    padding-top: 39px;\n  }\n  .control {\n    padding: 10px;\n    border-bottom: none;\n    @include control();\n    .title {\n      font-size: $font-size;\n    }\n    .icon-clear {\n      left: 23px;\n    }\n    .icon-eye {\n      right: 0;\n    }\n    .icon-copy {\n      right: 23px;\n    }\n    .icon-filter {\n      right: 46px;\n    }\n    .filter-text {\n      white-space: nowrap;\n      position: absolute;\n      line-height: 20px;\n      max-width: 80px;\n      overflow: hidden;\n      right: 88px;\n      font-size: $font-size;\n      text-overflow: ellipsis;\n    }\n    .icon-record {\n      left: 0;\n      &.recording {\n        color: var(--console-error-foreground);\n        text-shadow: 0 0 4px var(--console-error-foreground);\n      }\n    }\n  }\n  .request-error {\n    color: var(--console-error-foreground);\n  }\n  .luna-data-grid:focus {\n    .luna-data-grid-data-container {\n      .request-error.luna-data-grid-selected {\n        background: var(--console-error-background);\n      }\n    }\n  }\n  .luna-data-grid {\n    border-left: none;\n    border-right: none;\n  }\n  .detail {\n    @include absolute();\n    z-index: 10;\n    display: none;\n    padding-top: 40px;\n    background: var(--background);\n    .control {\n      padding: 10px 35px;\n      border-bottom: 1px solid var(--border);\n      .url {\n        font-size: $font-size-s;\n        overflow: hidden;\n        white-space: nowrap;\n        text-overflow: ellipsis;\n        width: 100%;\n        display: inline-block;\n      }\n      .icon-left {\n        left: 0;\n      }\n      .icon-delete {\n        left: 0;\n        display: none;\n      }\n      .icon-copy {\n        right: 0;\n      }\n    }\n    .http {\n      @include overflow-auto(y);\n      height: 100%;\n      .section {\n        border-top: 1px solid var(--border);\n        border-bottom: 1px solid var(--border);\n        margin-top: 10px;\n        margin-bottom: 10px;\n        h2 {\n          background: var(--darker-background);\n          color: var(--primary);\n          padding: $padding;\n          line-height: 18px;\n          font-size: $font-size;\n        }\n        table {\n          color: var(--foreground);\n          * {\n            user-select: text;\n          }\n          td {\n            font-size: $font-size-s;\n            padding: 5px 10px;\n            word-break: break-all;\n          }\n          .key {\n            white-space: nowrap;\n            font-weight: bold;\n            color: var(--accent);\n          }\n        }\n      }\n      .response,\n      .data {\n        user-select: text;\n        @include overflow-auto(x);\n        padding: $padding;\n        font-size: $font-size-s;\n        margin: 10px 0;\n        white-space: pre-wrap;\n        border-top: 1px solid var(--border);\n        color: var(--foreground);\n        border-bottom: 1px solid var(--border);\n      }\n    }\n  }\n}\n\n.safe-area #network {\n  .http {\n    @include safe-area(padding-bottom, 0px);\n  }\n}\n\n@media screen and (min-width: 680px) {\n  #network {\n    .network {\n      .control {\n        .icon-eye {\n          display: none;\n        }\n        .icon-copy {\n          right: 0;\n        }\n        .icon-filter {\n          right: 23px;\n        }\n        .filter-text {\n          right: 55px;\n        }\n      }\n    }\n    .detail {\n      width: 50%;\n      left: initial;\n      right: 0;\n      border-left: 1px solid var(--border);\n      .control {\n        .icon-left {\n          display: none;\n        }\n        .icon-delete {\n          display: block;\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "src/Network/util.js",
    "content": "import last from 'licia/last'\nimport detectOs from 'licia/detectOs'\nimport arrToMap from 'licia/arrToMap'\n\nexport function getType(contentType) {\n  if (!contentType) return 'unknown'\n\n  const type = contentType.split(';')[0].split('/')\n\n  return {\n    type: type[0],\n    subType: last(type),\n  }\n}\n\nexport function curlStr(request) {\n  let platform = detectOs()\n  if (platform === 'windows') {\n    platform = 'win'\n  }\n  let command = []\n  const ignoredHeaders = arrToMap([\n    'accept-encoding',\n    'host',\n    'method',\n    'path',\n    'scheme',\n    'version',\n  ])\n\n  function escapeStringWin(str) {\n    const encapsChars = /[\\r\\n]/.test(str) ? '^\"' : '\"'\n    return (\n      encapsChars +\n      str\n        .replace(/\\\\/g, '\\\\\\\\')\n        .replace(/\"/g, '\\\\\"')\n        .replace(/[^a-zA-Z0-9\\s_\\-:=+~'/.',?;()*`&]/g, '^$&')\n        .replace(/%(?=[a-zA-Z0-9_])/g, '%^')\n        .replace(/\\r?\\n/g, '^\\n\\n') +\n      encapsChars\n    )\n  }\n\n  function escapeStringPosix(str) {\n    function escapeCharacter(x) {\n      const code = x.charCodeAt(0)\n      let hexString = code.toString(16)\n      while (hexString.length < 4) {\n        hexString = '0' + hexString\n      }\n\n      return '\\\\u' + hexString\n    }\n\n    // eslint-disable-next-line no-control-regex\n    if (/[\\0-\\x1F\\x7F-\\x9F!]|'/.test(str)) {\n      return (\n        \"$'\" +\n        str\n          .replace(/\\\\/g, '\\\\\\\\')\n          .replace(/'/g, \"\\\\'\")\n          .replace(/\\n/g, '\\\\n')\n          .replace(/\\r/g, '\\\\r')\n          // eslint-disable-next-line no-control-regex\n          .replace(/[\\0-\\x1F\\x7F-\\x9F!]/g, escapeCharacter) +\n        \"'\"\n      )\n    }\n    return \"'\" + str + \"'\"\n  }\n\n  const escapeString = platform === 'win' ? escapeStringWin : escapeStringPosix\n\n  command.push(escapeString(request.url()).replace(/[[{}\\]]/g, '\\\\$&'))\n\n  let inferredMethod = 'GET'\n  const data = []\n  const formData = request.requestFormData()\n  if (formData) {\n    data.push('--data-raw ' + escapeString(formData))\n    ignoredHeaders['content-length'] = true\n    inferredMethod = 'POST'\n  }\n\n  if (request.requestMethod !== inferredMethod) {\n    command.push('-X ' + escapeString(request.requestMethod))\n  }\n\n  const requestHeaders = request.requestHeaders()\n  for (let i = 0; i < requestHeaders.length; i++) {\n    const header = requestHeaders[i]\n    const name = header.name.replace(/^:/, '')\n    if (ignoredHeaders[name.toLowerCase()]) {\n      continue\n    }\n    command.push('-H ' + escapeString(name + ': ' + header.value))\n  }\n  command = command.concat(data)\n  command.push('--compressed')\n\n  return (\n    'curl ' +\n    command.join(\n      command.length >= 3 ? (platform === 'win' ? ' ^\\n  ' : ' \\\\\\n  ') : ' '\n    )\n  )\n}\n"
  },
  {
    "path": "src/Resources/Cookie.js",
    "content": "import map from 'licia/map'\nimport trim from 'licia/trim'\nimport isNull from 'licia/isNull'\nimport each from 'licia/each'\nimport copy from 'licia/copy'\nimport LunaModal from 'luna-modal'\nimport LunaDataGrid from 'luna-data-grid'\nimport { setState, getState } from './util'\nimport chobitsu from '../lib/chobitsu'\nimport { classPrefix as c } from '../lib/util'\n\nexport default class Cookie {\n  constructor($container, devtools) {\n    this._$container = $container\n    this._devtools = devtools\n    this._selectedItem = null\n\n    this._initTpl()\n    this._dataGrid = new LunaDataGrid(this._$dataGrid.get(0), {\n      columns: [\n        {\n          id: 'key',\n          title: 'Key',\n          weight: 30,\n        },\n        {\n          id: 'value',\n          title: 'Value',\n          weight: 90,\n        },\n      ],\n      minHeight: 60,\n      maxHeight: 223,\n    })\n\n    this._bindEvent()\n  }\n  refresh() {\n    const $container = this._$container\n    const dataGrid = this._dataGrid\n\n    const { cookies } = chobitsu.domain('Network').getCookies()\n    const cookieData = map(cookies, ({ name, value }) => ({\n      key: name,\n      val: value,\n    }))\n\n    dataGrid.clear()\n    each(cookieData, ({ key, val }) => {\n      dataGrid.append(\n        {\n          key,\n          value: val,\n        },\n        {\n          selectable: true,\n        }\n      )\n    })\n\n    const cookieState = getState('cookie', cookieData.length)\n    setState($container, cookieState)\n  }\n  _initTpl() {\n    const $container = this._$container\n\n    $container.html(\n      c(`<h2 class=\"title\">\n      Cookie\n      <div class=\"btn refresh-cookie\">\n        <span class=\"icon-refresh\"></span>\n      </div>\n      <div class=\"btn show-detail btn-disabled\">\n        <span class=\"icon icon-eye\"></span>\n      </div>\n      <div class=\"btn copy-cookie btn-disabled\">\n        <span class=\"icon icon-copy\"></span>\n      </div>\n      <div class=\"btn delete-cookie btn-disabled\">\n        <span class=\"icon icon-delete\"></span>\n      </div>\n      <div class=\"btn clear-cookie\">\n        <span class=\"icon-clear\"></span>\n      </div>\n      <div class=\"btn filter\" data-type=\"cookie\">\n        <span class=\"icon-filter\"></span>\n      </div>\n      <div class=\"btn filter-text\"></div>\n    </h2>\n    <div class=\"data-grid\"></div>`)\n    )\n\n    this._$dataGrid = $container.find(c('.data-grid'))\n    this._$filterText = $container.find(c('.filter-text'))\n  }\n  _updateButtons() {\n    const $container = this._$container\n    const $showDetail = $container.find(c('.show-detail'))\n    const $deleteCookie = $container.find(c('.delete-cookie'))\n    const $copyCookie = $container.find(c('.copy-cookie'))\n    const btnDisabled = c('btn-disabled')\n\n    $showDetail.addClass(btnDisabled)\n    $deleteCookie.addClass(btnDisabled)\n    $copyCookie.addClass(btnDisabled)\n\n    if (this._selectedItem) {\n      $showDetail.rmClass(btnDisabled)\n      $deleteCookie.rmClass(btnDisabled)\n      $copyCookie.rmClass(btnDisabled)\n    }\n  }\n  _getVal(key) {\n    const { cookies } = chobitsu.domain('Network').getCookies()\n\n    for (let i = 0, len = cookies.length; i < len; i++) {\n      if (cookies[i].name === key) {\n        return cookies[i].value\n      }\n    }\n\n    return ''\n  }\n  _bindEvent() {\n    const devtools = this._devtools\n\n    this._$container\n      .on('click', c('.refresh-cookie'), () => {\n        devtools.notify('Refreshed', { icon: 'success' })\n        this.refresh()\n      })\n      .on('click', c('.clear-cookie'), () => {\n        chobitsu.domain('Storage').clearDataForOrigin({\n          storageTypes: 'cookies',\n        })\n        this.refresh()\n      })\n      .on('click', c('.delete-cookie'), () => {\n        const key = this._selectedItem\n\n        chobitsu.domain('Network').deleteCookies({ name: key })\n        this.refresh()\n      })\n      .on('click', c('.show-detail'), () => {\n        const key = this._selectedItem\n        const val = this._getVal(key)\n\n        try {\n          showSources('object', JSON.parse(val))\n        } catch {\n          showSources('raw', val)\n        }\n      })\n      .on('click', c('.copy-cookie'), () => {\n        const key = this._selectedItem\n        copy(this._getVal(key))\n        devtools.notify('Copied', { icon: 'success' })\n      })\n      .on('click', c('.filter'), () => {\n        LunaModal.prompt('Filter').then((filter) => {\n          if (isNull(filter)) return\n          filter = trim(filter)\n          this._filter = filter\n          this._$filterText.text(filter)\n          this._dataGrid.setOption('filter', filter)\n        })\n      })\n\n    function showSources(type, data) {\n      const sources = devtools.get('sources')\n      if (!sources) return\n\n      sources.set(type, data)\n\n      devtools.showTool('sources')\n\n      return true\n    }\n\n    this._dataGrid\n      .on('select', (node) => {\n        this._selectedItem = node.data.key\n        this._updateButtons()\n      })\n      .on('deselect', () => {\n        this._selectedItem = null\n        this._updateButtons()\n      })\n  }\n}\n"
  },
  {
    "path": "src/Resources/Resources.js",
    "content": "import Tool from '../DevTools/Tool'\nimport Settings from '../Settings/Settings'\nimport $ from 'licia/$'\nimport escape from 'licia/escape'\nimport isEmpty from 'licia/isEmpty'\nimport contain from 'licia/contain'\nimport unique from 'licia/unique'\nimport each from 'licia/each'\nimport sameOrigin from 'licia/sameOrigin'\nimport ajax from 'licia/ajax'\nimport MutationObserver from 'licia/MutationObserver'\nimport toArr from 'licia/toArr'\nimport concat from 'licia/concat'\nimport map from 'licia/map'\nimport { isErudaEl, classPrefix as c } from '../lib/util'\nimport evalCss from '../lib/evalCss'\nimport Storage from './Storage'\nimport Cookie from './Cookie'\nimport { setState, getState } from './util'\n\nexport default class Resources extends Tool {\n  constructor() {\n    super()\n\n    this._style = evalCss(require('./Resources.scss'))\n\n    this.name = 'resources'\n    this._hideErudaSetting = false\n    this._observeElement = true\n  }\n  init($el, container) {\n    super.init($el)\n\n    this._container = container\n\n    this._initTpl()\n    this._localStorage = new Storage(\n      this._$localStorage,\n      container,\n      this,\n      'local'\n    )\n    this._sessionStorage = new Storage(\n      this._$sessionStorage,\n      container,\n      this,\n      'session'\n    )\n    this._cookie = new Cookie(this._$cookie, container)\n\n    this._bindEvent()\n    this._initObserver()\n    this._initCfg()\n  }\n  refresh() {\n    return this.refreshLocalStorage()\n      .refreshSessionStorage()\n      .refreshCookie()\n      .refreshScript()\n      .refreshStylesheet()\n      .refreshIframe()\n      .refreshImage()\n  }\n  destroy() {\n    super.destroy()\n\n    this._localStorage.destroy()\n    this._sessionStorage.destroy()\n    this._disableObserver()\n    evalCss.remove(this._style)\n    this._rmCfg()\n  }\n  refreshScript() {\n    let scriptData = []\n\n    $('script').each(function () {\n      const src = this.src\n\n      if (src !== '') scriptData.push(src)\n    })\n\n    scriptData = unique(scriptData)\n\n    const scriptState = getState('script', scriptData.length)\n    let scriptDataHtml = '<li>Empty</li>'\n    if (!isEmpty(scriptData)) {\n      scriptDataHtml = map(scriptData, (script) => {\n        script = escape(script)\n        return `<li><a href=\"${script}\" target=\"_blank\" class=\"${c(\n          'js-link'\n        )}\">${script}</a></li>`\n      }).join('')\n    }\n\n    const scriptHtml = `<h2 class=\"${c('title')}\">\n      Script\n      <div class=\"${c('btn refresh-script')}\">\n        <span class=\"${c('icon-refresh')}\"></span>\n      </div>\n    </h2>\n    <ul class=\"${c('link-list')}\">\n      ${scriptDataHtml}\n    </ul>`\n\n    const $script = this._$script\n    setState($script, scriptState)\n    $script.html(scriptHtml)\n\n    return this\n  }\n  refreshStylesheet() {\n    let stylesheetData = []\n\n    $('link').each(function () {\n      if (this.rel !== 'stylesheet') return\n\n      stylesheetData.push(this.href)\n    })\n\n    stylesheetData = unique(stylesheetData)\n\n    const stylesheetState = getState('stylesheet', stylesheetData.length)\n    let stylesheetDataHtml = '<li>Empty</li>'\n    if (!isEmpty(stylesheetData)) {\n      stylesheetDataHtml = map(stylesheetData, (stylesheet) => {\n        stylesheet = escape(stylesheet)\n        return ` <li><a href=\"${stylesheet}\" target=\"_blank\" class=\"${c(\n          'css-link'\n        )}\">${stylesheet}</a></li>`\n      }).join('')\n    }\n\n    const stylesheetHtml = `<h2 class=\"${c('title')}\">\n      Stylesheet\n      <div class=\"${c('btn refresh-stylesheet')}\">\n        <span class=\"${c('icon-refresh')}\"></span>\n      </div>\n    </h2>\n    <ul class=\"${c('link-list')}\">\n      ${stylesheetDataHtml}\n    </ul>`\n\n    const $stylesheet = this._$stylesheet\n    setState($stylesheet, stylesheetState)\n    $stylesheet.html(stylesheetHtml)\n\n    return this\n  }\n  refreshIframe() {\n    let iframeData = []\n\n    $('iframe').each(function () {\n      const $this = $(this)\n      const src = $this.attr('src')\n\n      if (src) iframeData.push(src)\n    })\n\n    iframeData = unique(iframeData)\n\n    let iframeDataHtml = '<li>Empty</li>'\n    if (!isEmpty(iframeData)) {\n      iframeDataHtml = map(iframeData, (iframe) => {\n        iframe = escape(iframe)\n        return `<li><a href=\"${iframe}\" target=\"_blank\" class=\"${c(\n          'iframe-link'\n        )}\">${iframe}</a></li>`\n      }).join('')\n    }\n    const iframeHtml = `<h2 class=\"${c('title')}\">\n      Iframe\n      <div class=\"${c('btn refresh-iframe')}\">\n        <span class=\"${c('icon-refresh')}\"></span>\n      </div>\n    </h2>\n    <ul class=\"${c('link-list')}\">\n      ${iframeDataHtml}\n    </ul>`\n\n    this._$iframe.html(iframeHtml)\n\n    return this\n  }\n  refreshLocalStorage() {\n    this._localStorage.refresh()\n\n    return this\n  }\n  refreshSessionStorage() {\n    this._sessionStorage.refresh()\n\n    return this\n  }\n  refreshCookie() {\n    this._cookie.refresh()\n\n    return this\n  }\n  refreshImage() {\n    let imageData = []\n\n    const performance = (this._performance =\n      window.webkitPerformance || window.performance)\n    if (performance && performance.getEntries) {\n      const entries = this._performance.getEntries()\n      entries.forEach((entry) => {\n        if (entry.initiatorType === 'img' || isImg(entry.name)) {\n          if (contain(entry.name, 'exclude=true')) {\n            return\n          }\n          imageData.push(entry.name)\n        }\n      })\n    } else {\n      $('img').each(function () {\n        const $this = $(this)\n        const src = $this.attr('src')\n\n        if ($this.data('exclude') === 'true') {\n          return\n        }\n\n        imageData.push(src)\n      })\n    }\n\n    imageData = unique(imageData)\n    imageData.sort()\n\n    const imageState = getState('image', imageData.length)\n    let imageDataHtml = '<li>Empty</li>'\n    if (!isEmpty(imageData)) {\n      // prettier-ignore\n      imageDataHtml = map(imageData, (image) => {\n        return `<li class=\"${c('image')}\">\n          <img src=\"${escape(image)}\" data-exclude=\"true\" class=\"${c('img-link')}\"/>\n        </li>`\n      }).join('')\n    }\n\n    const imageHtml = `<h2 class=\"${c('title')}\">\n      Image\n      <div class=\"${c('btn refresh-image')}\">\n        <span class=\"${c('icon-refresh')}\"></span>\n      </div>\n    </h2>\n    <ul class=\"${c('image-list')}\">\n      ${imageDataHtml}\n    </ul>`\n\n    const $image = this._$image\n    setState($image, imageState)\n    $image.html(imageHtml)\n\n    return this\n  }\n  show() {\n    super.show()\n    if (this._observeElement) this._enableObserver()\n\n    return this.refresh()\n  }\n  hide() {\n    this._disableObserver()\n\n    return super.hide()\n  }\n  _initTpl() {\n    const $el = this._$el\n    $el.html(\n      c(`<div class=\"section local-storage\"></div>\n      <div class=\"section session-storage\"></div>\n      <div class=\"section cookie\"></div>\n      <div class=\"section script\"></div>\n      <div class=\"section stylesheet\"></div>\n      <div class=\"section iframe\"></div>\n      <div class=\"section image\"></div>`)\n    )\n    this._$localStorage = $el.find(c('.local-storage'))\n    this._$sessionStorage = $el.find(c('.session-storage'))\n    this._$cookie = $el.find(c('.cookie'))\n    this._$script = $el.find(c('.script'))\n    this._$stylesheet = $el.find(c('.stylesheet'))\n    this._$iframe = $el.find(c('.iframe'))\n    this._$image = $el.find(c('.image'))\n  }\n  _bindEvent() {\n    const $el = this._$el\n    const container = this._container\n\n    $el\n      .on('click', '.eruda-refresh-script', () => {\n        container.notify('Refreshed', { icon: 'success' })\n        this.refreshScript()\n      })\n      .on('click', '.eruda-refresh-stylesheet', () => {\n        container.notify('Refreshed', { icon: 'success' })\n        this.refreshStylesheet()\n      })\n      .on('click', '.eruda-refresh-iframe', () => {\n        container.notify('Refreshed', { icon: 'success' })\n        this.refreshIframe()\n      })\n      .on('click', '.eruda-refresh-image', () => {\n        container.notify('Refreshed', { icon: 'success' })\n        this.refreshImage()\n      })\n      .on('click', '.eruda-img-link', function () {\n        const src = $(this).attr('src')\n\n        showSources('img', src)\n      })\n      .on('click', '.eruda-css-link', linkFactory('css'))\n      .on('click', '.eruda-js-link', linkFactory('js'))\n      .on('click', '.eruda-iframe-link', linkFactory('iframe'))\n\n    function showSources(type, data) {\n      const sources = container.get('sources')\n      if (!sources) return\n\n      sources.set(type, data)\n\n      container.showTool('sources')\n\n      return true\n    }\n\n    function linkFactory(type) {\n      return function (e) {\n        if (!container.get('sources')) return\n        e.preventDefault()\n\n        const url = $(this).attr('href')\n\n        if (type === 'iframe' || !sameOrigin(location.href, url)) {\n          showSources('iframe', url)\n        } else {\n          ajax({\n            url,\n            success: (data) => {\n              showSources(type, data)\n            },\n            dataType: 'raw',\n          })\n        }\n      }\n    }\n  }\n  _rmCfg() {\n    const cfg = this.config\n\n    const settings = this._container.get('settings')\n\n    if (!settings) return\n\n    settings\n      .remove(cfg, 'hideErudaSetting')\n      .remove(cfg, 'observeElement')\n      .remove('Resources')\n  }\n  _initCfg() {\n    const cfg = (this.config = Settings.createCfg('resources', {\n      hideErudaSetting: true,\n      observeElement: true,\n    }))\n\n    if (cfg.get('hideErudaSetting')) this._hideErudaSetting = true\n    if (!cfg.get('observeElement')) this._observeElement = false\n\n    cfg.on('change', (key, val) => {\n      switch (key) {\n        case 'hideErudaSetting':\n          this._hideErudaSetting = val\n          return\n        case 'observeElement':\n          this._observeElement = val\n          return val ? this._enableObserver() : this._disableObserver()\n      }\n    })\n\n    const settings = this._container.get('settings')\n    settings\n      .text('Resources')\n      .switch(cfg, 'hideErudaSetting', 'Hide Eruda Setting')\n      .switch(cfg, 'observeElement', 'Auto Refresh Elements')\n      .separator()\n  }\n  _initObserver() {\n    this._observer = new MutationObserver((mutations) => {\n      each(mutations, (mutation) => {\n        this._handleMutation(mutation)\n      })\n    })\n  }\n  _handleMutation(mutation) {\n    if (isErudaEl(mutation.target)) return\n\n    const checkEl = (el) => {\n      const tagName = getLowerCaseTagName(el)\n      switch (tagName) {\n        case 'script':\n          this.refreshScript()\n          break\n        case 'img':\n          this.refreshImage()\n          break\n        case 'link':\n          this.refreshStylesheet()\n          break\n      }\n    }\n\n    if (mutation.type === 'attributes') {\n      checkEl(mutation.target)\n    } else if (mutation.type === 'childList') {\n      checkEl(mutation.target)\n      let nodes = toArr(mutation.addedNodes)\n      nodes = concat(nodes, toArr(mutation.removedNodes))\n\n      for (const node of nodes) {\n        checkEl(node)\n      }\n    }\n  }\n  _enableObserver() {\n    this._observer.observe(document.documentElement, {\n      attributes: true,\n      childList: true,\n      subtree: true,\n    })\n  }\n  _disableObserver() {\n    this._observer.disconnect()\n  }\n}\n\nfunction getLowerCaseTagName(el) {\n  if (!el.tagName) return ''\n  return el.tagName.toLowerCase()\n}\n\nconst regImg = /\\.(jpeg|jpg|gif|png)$/\n\nconst isImg = (url) => regImg.test(url)\n"
  },
  {
    "path": "src/Resources/Resources.scss",
    "content": "@use '../style/variable' as *;\n@use '../style/mixin' as *;\n\n#resources {\n  @include overflow-auto(y);\n  padding: 10px;\n  font-size: 14px;\n  .section {\n    margin-bottom: 10px;\n    overflow: hidden;\n    border: 1px solid var(--border);\n    &.warn {\n      border: 1px solid var(--console-warn-border);\n      .title {\n        background: var(--console-warn-background);\n        color: var(--console-warn-foreground);\n      }\n    }\n    &.danger {\n      border: 1px solid var(--console-error-border);\n      .title {\n        background: var(--console-error-background);\n        color: var(--console-error-foreground);\n      }\n    }\n    &.local-storage,\n    &.session-storage,\n    &.cookie {\n      border: none;\n      .title {\n        border: 1px solid var(--border);\n        border-bottom: none;\n      }\n    }\n  }\n  .title {\n    padding: $padding;\n    line-height: 18px;\n    color: var(--primary);\n    background: var(--darker-background);\n    @include right-btn();\n  }\n  .link-list {\n    font-size: $font-size-s;\n    color: var(--foreground);\n    li {\n      padding: 10px;\n      word-break: break-all;\n      a {\n        color: var(--link-color) !important;\n      }\n    }\n  }\n  .image-list {\n    color: var(--foreground);\n    font-size: $font-size-s;\n    display: flex;\n    flex-wrap: wrap;\n    padding-left: $padding;\n    padding-top: $padding;\n    &::after {\n      content: '';\n      flex-grow: 1000;\n    }\n    li {\n      flex-grow: 1;\n      cursor: pointer;\n      overflow-y: hidden;\n      margin-right: $padding;\n      margin-bottom: $padding;\n      border: 1px solid var(--border);\n      &.image {\n        height: 100px;\n        font-size: 0;\n      }\n      img {\n        height: 100px;\n        min-width: 100%;\n        object-fit: cover;\n      }\n    }\n  }\n}\n\n.safe-area #resources {\n  @include safe-area(padding-bottom, 10px);\n}\n"
  },
  {
    "path": "src/Resources/Storage.js",
    "content": "import each from 'licia/each'\nimport isStr from 'licia/isStr'\nimport startWith from 'licia/startWith'\nimport truncate from 'licia/truncate'\nimport LunaModal from 'luna-modal'\nimport LunaDataGrid from 'luna-data-grid'\nimport isNull from 'licia/isNull'\nimport trim from 'licia/trim'\nimport copy from 'licia/copy'\nimport emitter from '../lib/emitter'\nimport { safeStorage, classPrefix as c } from '../lib/util'\n\nexport default class Storage {\n  constructor($container, devtools, resources, type) {\n    this._type = type\n    this._$container = $container\n    this._devtools = devtools\n    this._resources = resources\n    this._selectedItem = null\n    this._storeData = []\n\n    this._initTpl()\n    this._dataGrid = new LunaDataGrid(this._$dataGrid.get(0), {\n      columns: [\n        {\n          id: 'key',\n          title: 'Key',\n          weight: 30,\n        },\n        {\n          id: 'value',\n          title: 'Value',\n          weight: 90,\n        },\n      ],\n      minHeight: 60,\n      maxHeight: 223,\n    })\n\n    this._bindEvent()\n  }\n  destroy() {\n    emitter.off(emitter.SCALE, this._updateGridHeight)\n  }\n  refresh() {\n    const dataGrid = this._dataGrid\n\n    this._refreshStorage()\n    dataGrid.clear()\n\n    each(this._storeData, ({ key, val }) => {\n      dataGrid.append(\n        {\n          key,\n          value: val,\n        },\n        {\n          selectable: true,\n        }\n      )\n    })\n  }\n  _refreshStorage() {\n    const resources = this._resources\n\n    let store = safeStorage(this._type, false)\n\n    if (!store) return\n\n    const storeData = []\n\n    // Mobile safari is not able to loop through localStorage directly.\n    store = JSON.parse(JSON.stringify(store))\n\n    each(store, (val, key) => {\n      // According to issue 20, not all values are guaranteed to be string.\n      if (!isStr(val)) return\n\n      if (resources.config.get('hideErudaSetting')) {\n        if (startWith(key, 'eruda') || key === 'active-eruda') return\n      }\n\n      storeData.push({\n        key: key,\n        val: truncate(val, 200),\n      })\n    })\n\n    this._storeData = storeData\n  }\n  _updateButtons() {\n    const $container = this._$container\n    const $showDetail = $container.find(c('.show-detail'))\n    const $deleteStorage = $container.find(c('.delete-storage'))\n    const $copyStorage = $container.find(c('.copy-storage'))\n    const btnDisabled = c('btn-disabled')\n\n    $showDetail.addClass(btnDisabled)\n    $deleteStorage.addClass(btnDisabled)\n    $copyStorage.addClass(btnDisabled)\n\n    if (this._selectedItem) {\n      $showDetail.rmClass(btnDisabled)\n      $deleteStorage.rmClass(btnDisabled)\n      $copyStorage.rmClass(btnDisabled)\n    }\n  }\n  _initTpl() {\n    const $container = this._$container\n    const type = this._type\n\n    $container.html(\n      c(`<h2 class=\"title\">\n      ${type === 'local' ? 'Local' : 'Session'} Storage\n      <div class=\"btn refresh-storage\">\n        <span class=\"icon icon-refresh\"></span>\n      </div>\n      <div class=\"btn show-detail btn-disabled\">\n        <span class=\"icon icon-eye\"></span>\n      </div>\n      <div class=\"btn copy-storage btn-disabled\">\n        <span class=\"icon icon-copy\"></span>\n      </div>\n      <div class=\"btn delete-storage btn-disabled\">\n        <span class=\"icon icon-delete\"></span>\n      </div>\n      <div class=\"btn clear-storage\">\n        <span class=\"icon icon-clear\"></span>\n      </div>\n      <div class=\"btn filter\">\n        <span class=\"icon icon-filter\"></span>\n      </div>\n      <div class=\"btn filter-text\"></div>\n    </h2>\n    <div class=\"data-grid\"></div>`)\n    )\n\n    this._$dataGrid = $container.find(c('.data-grid'))\n    this._$filterText = $container.find(c('.filter-text'))\n  }\n  _getVal(key) {\n    return this._type === 'local'\n      ? localStorage.getItem(key)\n      : sessionStorage.getItem(key)\n  }\n  _updateGridHeight = (scale) => {\n    this._dataGrid.setOption({\n      minHeight: 60 * scale,\n      maxHeight: 223 * scale,\n    })\n  }\n  _bindEvent() {\n    const type = this._type\n    const devtools = this._devtools\n\n    this._$container\n      .on('click', c('.refresh-storage'), () => {\n        devtools.notify('Refreshed', { icon: 'success' })\n        this.refresh()\n      })\n      .on('click', c('.clear-storage'), () => {\n        each(this._storeData, (val) => {\n          if (type === 'local') {\n            localStorage.removeItem(val.key)\n          } else {\n            sessionStorage.removeItem(val.key)\n          }\n        })\n        this.refresh()\n      })\n      .on('click', c('.show-detail'), () => {\n        const key = this._selectedItem\n        const val = this._getVal(key)\n\n        try {\n          showSources('object', JSON.parse(val))\n        } catch {\n          showSources('raw', val)\n        }\n      })\n      .on('click', c('.copy-storage'), () => {\n        const key = this._selectedItem\n        copy(this._getVal(key))\n        devtools.notify('Copied', { icon: 'success' })\n      })\n      .on('click', c('.filter'), () => {\n        LunaModal.prompt('Filter').then((filter) => {\n          if (isNull(filter)) return\n          filter = trim(filter)\n          this._$filterText.text(filter)\n          this._dataGrid.setOption('filter', filter)\n        })\n      })\n      .on('click', c('.delete-storage'), () => {\n        const key = this._selectedItem\n\n        if (type === 'local') {\n          localStorage.removeItem(key)\n        } else {\n          sessionStorage.removeItem(key)\n        }\n\n        this.refresh()\n      })\n\n    function showSources(type, data) {\n      const sources = devtools.get('sources')\n      if (!sources) return\n\n      sources.set(type, data)\n\n      devtools.showTool('sources')\n\n      return true\n    }\n\n    this._dataGrid\n      .on('select', (node) => {\n        this._selectedItem = node.data.key\n        this._updateButtons()\n      })\n      .on('deselect', () => {\n        this._selectedItem = null\n        this._updateButtons()\n      })\n\n    emitter.on(emitter.SCALE, this._updateGridHeight)\n  }\n}\n"
  },
  {
    "path": "src/Resources/util.js",
    "content": "import { classPrefix as c } from '../lib/util'\n\nexport function setState($el, state) {\n  $el\n    .rmClass(c('ok'))\n    .rmClass(c('danger'))\n    .rmClass(c('warn'))\n    .addClass(c(state))\n}\n\nexport function getState(type, len) {\n  if (len === 0) return ''\n\n  let warn = 0\n  let danger = 0\n\n  switch (type) {\n    case 'cookie':\n      warn = 30\n      danger = 60\n      break\n    case 'script':\n      warn = 5\n      danger = 10\n      break\n    case 'stylesheet':\n      warn = 4\n      danger = 8\n      break\n    case 'image':\n      warn = 50\n      danger = 100\n      break\n  }\n\n  if (len >= danger) return 'danger'\n  if (len >= warn) return 'warn'\n\n  return 'ok'\n}\n"
  },
  {
    "path": "src/Settings/Settings.js",
    "content": "import Tool from '../DevTools/Tool'\nimport $ from 'licia/$'\nimport LocalStore from 'licia/LocalStore'\nimport uniqId from 'licia/uniqId'\nimport each from 'licia/each'\nimport filter from 'licia/filter'\nimport isStr from 'licia/isStr'\nimport contain from 'licia/contain'\nimport clone from 'licia/clone'\nimport evalCss from '../lib/evalCss'\nimport LunaSetting from 'luna-setting'\n\nexport default class Settings extends Tool {\n  constructor() {\n    super()\n\n    this._style = evalCss(require('./Settings.scss'))\n\n    this.name = 'settings'\n    this._settings = []\n  }\n  init($el) {\n    super.init($el)\n\n    this._setting = new LunaSetting($el.get(0))\n\n    this._bindEvent()\n  }\n  remove(config, key) {\n    if (isStr(config)) {\n      const self = this\n      this._$el.find('.luna-setting-item-title').each(function () {\n        const $this = $(this)\n        if ($this.text() === config) {\n          self._setting.remove(this.settingItem)\n        }\n      })\n    } else {\n      this._settings = filter(this._settings, (setting) => {\n        if (setting.config === config && setting.key === key) {\n          this._setting.remove(setting.item)\n          return false\n        }\n\n        return true\n      })\n    }\n\n    this._cleanSeparator()\n\n    return this\n  }\n  destroy() {\n    this._setting.destroy()\n    super.destroy()\n\n    evalCss.remove(this._style)\n  }\n  clear() {\n    this._settings = []\n    this._setting.clear()\n  }\n  switch(config, key, desc) {\n    const id = this._genId()\n\n    const item = this._setting.appendCheckbox(id, !!config.get(key), desc)\n    this._settings.push({ config, key, id, item })\n\n    return this\n  }\n  select(config, key, desc, selections) {\n    const id = this._genId()\n\n    const selectOptions = {}\n    each(selections, (selection) => (selectOptions[selection] = selection))\n    const item = this._setting.appendSelect(\n      id,\n      config.get(key),\n      '',\n      desc,\n      selectOptions\n    )\n    this._settings.push({ config, key, id, item })\n\n    return this\n  }\n  range(config, key, desc, { min = 0, max = 1, step = 0.1 }) {\n    const id = this._genId()\n\n    const item = this._setting.appendNumber(id, config.get(key), desc, {\n      max,\n      min,\n      step,\n      range: true,\n    })\n    this._settings.push({ config, key, min, max, step, id, item })\n\n    return this\n  }\n  button(text, handler) {\n    this._setting.appendButton(text, handler)\n\n    return this\n  }\n  separator() {\n    this._setting.appendSeparator()\n\n    return this\n  }\n  text(text) {\n    this._setting.appendTitle(text)\n\n    return this\n  }\n  // Merge adjacent separators\n  _cleanSeparator() {\n    const children = clone(this._$el.get(0).children)\n\n    function isSeparator(node) {\n      return contain(node.getAttribute('class'), 'luna-setting-item-separator')\n    }\n\n    for (let i = 0, len = children.length; i < len - 1; i++) {\n      if (isSeparator(children[i]) && isSeparator(children[i + 1])) {\n        $(children[i]).remove()\n      }\n    }\n  }\n  _genId() {\n    return uniqId('eruda-settings')\n  }\n  _getSetting(id) {\n    let ret\n\n    each(this._settings, (setting) => {\n      if (setting.id === id) ret = setting\n    })\n\n    return ret\n  }\n  _bindEvent() {\n    this._setting.on('change', (id, val) => {\n      const setting = this._getSetting(id)\n      setting.config.set(setting.key, val)\n    })\n  }\n  static createCfg(name, data) {\n    return new LocalStore('eruda-' + name, data)\n  }\n}\n"
  },
  {
    "path": "src/Settings/Settings.scss",
    "content": "@use '../style/variable' as *;\n@use '../style/mixin' as *;\n\n#settings {\n  @include overflow-auto(y);\n}\n\n.safe-area #settings {\n  @include safe-area(padding-bottom, 0px);\n}\n"
  },
  {
    "path": "src/Snippets/Snippets.js",
    "content": "import Tool from '../DevTools/Tool'\nimport defSnippets from './defSnippets'\nimport $ from 'licia/$'\nimport each from 'licia/each'\nimport escape from 'licia/escape'\nimport map from 'licia/map'\nimport remove from 'licia/remove'\nimport evalCss from '../lib/evalCss'\nimport { classPrefix as c } from '../lib/util'\n\nexport default class Snippets extends Tool {\n  constructor() {\n    super()\n\n    this._style = evalCss(require('./Snippets.scss'))\n\n    this.name = 'snippets'\n\n    this._snippets = []\n  }\n  init($el) {\n    super.init($el)\n\n    this._bindEvent()\n    this._addDefSnippets()\n  }\n  destroy() {\n    super.destroy()\n\n    evalCss.remove(this._style)\n  }\n  add(name, fn, desc) {\n    this._snippets.push({ name, fn, desc })\n\n    this._render()\n\n    return this\n  }\n  remove(name) {\n    remove(this._snippets, (snippet) => snippet.name === name)\n\n    this._render()\n\n    return this\n  }\n  run(name) {\n    const snippets = this._snippets\n\n    for (let i = 0, len = snippets.length; i < len; i++) {\n      if (snippets[i].name === name) this._run(i)\n    }\n\n    return this\n  }\n  clear() {\n    this._snippets = []\n    this._render()\n\n    return this\n  }\n  _bindEvent() {\n    const self = this\n\n    this._$el.on('click', '.eruda-run', function () {\n      const idx = $(this).data('idx')\n\n      self._run(idx)\n    })\n  }\n  _run(idx) {\n    this._snippets[idx].fn.call(null)\n  }\n  _addDefSnippets() {\n    each(defSnippets, (snippet) => {\n      this.add(snippet.name, snippet.fn, snippet.desc)\n    })\n  }\n  _render() {\n    const html = map(this._snippets, (snippet, idx) => {\n      return `<div class=\"${c('section run')}\" data-idx=\"${idx}\">\n        <h2 class=\"${c('name')}\">${escape(snippet.name)}\n          <div class=\"${c('btn')}\">\n            <span class=\"${c('icon-play')}\"></span>\n          </div>\n        </h2>\n        <div class=\"${c('description')}\">\n          ${escape(snippet.desc)}\n        </div>\n      </div>`\n    }).join('')\n\n    this._renderHtml(html)\n  }\n  _renderHtml(html) {\n    if (html === this._lastHtml) return\n    this._lastHtml = html\n    this._$el.html(html)\n  }\n}\n"
  },
  {
    "path": "src/Snippets/Snippets.scss",
    "content": "@use '../style/variable' as *;\n@use '../style/mixin' as *;\n\n#snippets {\n  @include overflow-auto(y);\n  padding: $padding;\n  .section {\n    margin-bottom: 10px;\n    border: 1px solid var(--border);\n    overflow: hidden;\n    cursor: pointer;\n    &:active {\n      .name {\n        background: var(--highlight);\n        color: var(--select-foreground);\n      }\n    }\n    .name {\n      padding: $padding;\n      line-height: 18px;\n      color: var(--primary);\n      background: var(--darker-background);\n      transition: background-color $anim-duration;\n      .btn {\n        margin-left: 10px;\n        float: right;\n        text-align: center;\n        width: 18px;\n        height: 18px;\n        font-size: $font-size-s;\n      }\n    }\n    .description {\n      font-size: $font-size-s;\n      color: var(--foreground);\n      padding: $padding;\n      transition: background-color $anim-duration;\n    }\n  }\n}\n\n.safe-area #snippets {\n  @include safe-area(padding-bottom, 10px);\n}\n"
  },
  {
    "path": "src/Snippets/defSnippets.js",
    "content": "import logger from '../lib/logger'\nimport emitter from '../lib/emitter'\nimport Url from 'licia/Url'\nimport now from 'licia/now'\nimport startWith from 'licia/startWith'\nimport $ from 'licia/$'\nimport upperFirst from 'licia/upperFirst'\nimport loadJs from 'licia/loadJs'\nimport trim from 'licia/trim'\nimport LunaModal from 'luna-modal'\nimport { isErudaEl } from '../lib/util'\nimport evalCss from '../lib/evalCss'\n\nlet style = null\n\nexport default [\n  {\n    name: 'Border All',\n    fn() {\n      if (style) {\n        evalCss.remove(style)\n        style = null\n        return\n      }\n\n      style = evalCss(\n        '* { outline: 2px dashed #707d8b; outline-offset: -3px; }',\n        document.head\n      )\n    },\n    desc: 'Add color borders to all elements',\n  },\n  {\n    name: 'Refresh Page',\n    fn() {\n      const url = new Url()\n      url.setQuery('timestamp', now())\n\n      window.location.replace(url.toString())\n    },\n    desc: 'Add timestamp to url and refresh',\n  },\n  {\n    name: 'Search Text',\n    fn() {\n      LunaModal.prompt('Enter the text').then((keyword) => {\n        if (!keyword || trim(keyword) === '') {\n          return\n        }\n\n        search(keyword)\n      })\n    },\n    desc: 'Highlight given text on page',\n  },\n  {\n    name: 'Edit Page',\n    fn() {\n      const body = document.body\n\n      body.contentEditable = body.contentEditable !== 'true'\n    },\n    desc: 'Toggle body contentEditable',\n  },\n  {\n    name: 'Fit Screen',\n    // https://achrafkassioui.com/birdview/\n    fn() {\n      const body = document.body\n      const html = document.documentElement\n      const $body = $(body)\n      if ($body.data('scaled')) {\n        window.scrollTo(0, +$body.data('scaled'))\n        $body.rmAttr('data-scaled')\n        $body.css('transform', 'none')\n      } else {\n        const documentHeight = Math.max(\n          body.scrollHeight,\n          body.offsetHeight,\n          html.clientHeight,\n          html.scrollHeight,\n          html.offsetHeight\n        )\n        const viewportHeight = Math.max(\n          document.documentElement.clientHeight,\n          window.innerHeight || 0\n        )\n        const scaleVal = viewportHeight / documentHeight\n        $body.css('transform', `scale(${scaleVal})`)\n        $body.data('scaled', window.scrollY)\n        window.scrollTo(0, documentHeight / 2 - viewportHeight / 2)\n      }\n    },\n    desc: 'Scale down the whole page to fit screen',\n  },\n  {\n    name: 'Load Vue Plugin',\n    fn() {\n      loadPlugin('vue')\n    },\n    desc: 'Vue devtools',\n  },\n  {\n    name: 'Load Monitor Plugin',\n    fn() {\n      loadPlugin('monitor')\n    },\n    desc: 'Display page fps, memory and dom nodes',\n  },\n  {\n    name: 'Load Features Plugin',\n    fn() {\n      loadPlugin('features')\n    },\n    desc: 'Browser feature detections',\n  },\n  {\n    name: 'Load Timing Plugin',\n    fn() {\n      loadPlugin('timing')\n    },\n    desc: 'Show performance and resource timing',\n  },\n  {\n    name: 'Load Code Plugin',\n    fn() {\n      loadPlugin('code')\n    },\n    desc: 'Edit and run JavaScript',\n  },\n  {\n    name: 'Load Benchmark Plugin',\n    fn() {\n      loadPlugin('benchmark')\n    },\n    desc: 'Run JavaScript benchmarks',\n  },\n  {\n    name: 'Load Geolocation Plugin',\n    fn() {\n      loadPlugin('geolocation')\n    },\n    desc: 'Test geolocation',\n  },\n  {\n    name: 'Load Orientation Plugin',\n    fn() {\n      loadPlugin('orientation')\n    },\n    desc: 'Test orientation api',\n  },\n  {\n    name: 'Load Touches Plugin',\n    fn() {\n      loadPlugin('touches')\n    },\n    desc: 'Visualize screen touches',\n  },\n]\n\nevalCss(require('./searchText.scss'), document.head)\n\nfunction search(text) {\n  const root = document.body\n  const regText = new RegExp(text, 'ig')\n\n  traverse(root, (node) => {\n    const $node = $(node)\n\n    if (!$node.hasClass('eruda-search-highlight-block')) return\n\n    return document.createTextNode($node.text())\n  })\n\n  traverse(root, (node) => {\n    if (node.nodeType !== 3) return\n\n    let val = node.nodeValue\n    val = val.replace(\n      regText,\n      (match) => `<span class=\"eruda-keyword\">${match}</span>`\n    )\n    if (val === node.nodeValue) return\n\n    const $ret = $(document.createElement('div'))\n\n    $ret.html(val)\n    $ret.addClass('eruda-search-highlight-block')\n\n    return $ret.get(0)\n  })\n}\n\nfunction traverse(root, processor) {\n  const childNodes = root.childNodes\n\n  if (isErudaEl(root)) return\n\n  for (let i = 0, len = childNodes.length; i < len; i++) {\n    const newNode = traverse(childNodes[i], processor)\n    if (newNode) root.replaceChild(newNode, childNodes[i])\n  }\n\n  return processor(root)\n}\n\nfunction loadPlugin(name) {\n  const globalName = 'eruda' + upperFirst(name)\n  if (window[globalName]) return\n\n  let protocol = location.protocol\n  if (!startWith(protocol, 'http')) protocol = 'http:'\n\n  loadJs(\n    `${protocol}//cdn.jsdelivr.net/npm/eruda-${name}@${pluginVersion[name]}`,\n    (isLoaded) => {\n      if (!isLoaded || !window[globalName])\n        return logger.error('Fail to load plugin ' + name)\n\n      emitter.emit(emitter.ADD, window[globalName])\n      emitter.emit(emitter.SHOW, name)\n    }\n  )\n}\n\nconst pluginVersion = {\n  monitor: '1.1.1',\n  features: '2.1.0',\n  timing: '2.0.1',\n  code: '2.2.0',\n  benchmark: '2.0.1',\n  geolocation: '2.1.0',\n  orientation: '2.1.1',\n  touches: '2.1.0',\n  vue: '1.1.1',\n}\n"
  },
  {
    "path": "src/Snippets/searchText.scss",
    "content": "@use '../style/variable' as *;\n\n.search-highlight-block {\n  display: inline;\n  .keyword {\n    background: var(--console-warn-background);\n    color: var(--console-warn-foreground);\n  }\n}\n"
  },
  {
    "path": "src/Sources/Sources.js",
    "content": "import Tool from '../DevTools/Tool'\nimport LunaObjectViewer from 'luna-object-viewer'\nimport Settings from '../Settings/Settings'\nimport ajax from 'licia/ajax'\nimport each from 'licia/each'\nimport isStr from 'licia/isStr'\nimport escape from 'licia/escape'\nimport truncate from 'licia/truncate'\nimport replaceAll from 'licia/replaceAll'\nimport highlight from 'licia/highlight'\nimport LunaTextViewer from 'luna-text-viewer'\nimport evalCss from '../lib/evalCss'\nimport { classPrefix as c } from '../lib/util'\n\nexport default class Sources extends Tool {\n  constructor() {\n    super()\n\n    this._style = evalCss(require('./Sources.scss'))\n\n    this.name = 'sources'\n    this._showLineNum = true\n  }\n  init($el, container) {\n    super.init($el)\n\n    this._container = container\n    this._bindEvent()\n    this._initCfg()\n  }\n  destroy() {\n    super.destroy()\n\n    evalCss.remove(this._style)\n    this._rmCfg()\n  }\n  set(type, val) {\n    if (type === 'img') {\n      this._isFetchingData = true\n\n      const img = new Image()\n\n      const self = this\n\n      img.onload = function () {\n        self._isFetchingData = false\n        self._data = {\n          type: 'img',\n          val: {\n            width: this.width,\n            height: this.height,\n            src: val,\n          },\n        }\n\n        self._render()\n      }\n      img.onerror = function () {\n        self._isFetchingData = false\n      }\n\n      img.src = val\n\n      return\n    }\n\n    this._data = { type, val }\n\n    this._render()\n\n    return this\n  }\n  show() {\n    super.show()\n\n    if (!this._data && !this._isFetchingData) {\n      this._renderDef()\n    }\n\n    return this\n  }\n  _renderDef() {\n    if (this._html) {\n      this._data = {\n        type: 'html',\n        val: this._html,\n      }\n\n      return this._render()\n    }\n\n    if (this._isGettingHtml) return\n    this._isGettingHtml = true\n\n    ajax({\n      url: location.href,\n      success: (data) => (this._html = data),\n      error: () => (this._html = 'Sorry, unable to fetch source code:('),\n      complete: () => {\n        this._isGettingHtml = false\n        this._renderDef()\n      },\n      dataType: 'raw',\n    })\n  }\n  _bindEvent() {\n    this._container.on('showTool', (name, lastTool) => {\n      if (name !== this.name && lastTool.name === this.name) {\n        delete this._data\n      }\n    })\n  }\n  _rmCfg() {\n    const cfg = this.config\n\n    const settings = this._container.get('settings')\n\n    if (!settings) return\n\n    settings.remove(cfg, 'showLineNum').remove('Sources')\n  }\n  _initCfg() {\n    const cfg = (this.config = Settings.createCfg('sources', {\n      showLineNum: true,\n    }))\n\n    if (!cfg.get('showLineNum')) this._showLineNum = false\n\n    cfg.on('change', (key, val) => {\n      switch (key) {\n        case 'showLineNum':\n          this._showLineNum = val\n          return\n      }\n    })\n\n    const settings = this._container.get('settings')\n    settings\n      .text('Sources')\n      .switch(cfg, 'showLineNum', 'Show Line Numbers')\n      .separator()\n  }\n  _render() {\n    this._isInit = true\n\n    const data = this._data\n\n    switch (data.type) {\n      case 'html':\n      case 'js':\n      case 'css':\n        return this._renderCode()\n      case 'img':\n        return this._renderImg()\n      case 'object':\n        return this._renderObj()\n      case 'raw':\n        return this._renderRaw()\n      case 'iframe':\n        return this._renderIframe()\n    }\n  }\n  _renderImg() {\n    const { width, height, src } = this._data.val\n\n    this._renderHtml(`<div class=\"${c('image')}\">\n      <div class=\"${c('breadcrumb')}\">${escape(src)}</div>\n      <div class=\"${c('img-container')}\" data-exclude=\"true\">\n        <img src=\"${escape(src)}\">\n      </div>\n      <div class=\"${c('img-info')}\">${escape(width)} × ${escape(height)}</div>\n    </div>`)\n  }\n  _renderCode() {\n    const data = this._data\n\n    this._renderHtml(\n      `<div class=\"${c('code')}\" data-type=\"${data.type}\"></div>`,\n      false\n    )\n\n    let code = data.val\n    const len = data.val.length\n\n    if (len > MAX_RAW_LEN) {\n      code = truncate(code, MAX_RAW_LEN)\n    }\n\n    // If source code too big, don't process it.\n    if (len < MAX_BEAUTIFY_LEN) {\n      code = highlight(code, data.type, {\n        comment: '',\n        string: '',\n        number: '',\n        keyword: '',\n        operator: '',\n      })\n      each(['comment', 'string', 'number', 'keyword', 'operator'], (type) => {\n        code = replaceAll(code, `class=\"${type}\"`, `class=\"${c(type)}\"`)\n      })\n    } else {\n      code = escape(code)\n    }\n\n    const container = this._$el.find(c('.code')).get(0)\n    new LunaTextViewer(container, {\n      text: code,\n      escape: false,\n      wrapLongLines: true,\n      showLineNumbers: data.val.length < MAX_LINE_NUM_LEN && this._showLineNum,\n    })\n  }\n  _renderObj() {\n    // Using cache will keep binding events to the same elements.\n    this._renderHtml(`<ul class=\"${c('json')}\"></ul>`, false)\n\n    let val = this._data.val\n\n    try {\n      if (isStr(val)) {\n        val = JSON.parse(val)\n      }\n    } catch {\n      // No op\n    }\n\n    const objViewer = new LunaObjectViewer(\n      this._$el.find('.eruda-json').get(0),\n      {\n        unenumerable: true,\n        accessGetter: true,\n        prototype: false,\n      }\n    )\n    objViewer.set(val)\n  }\n  _renderRaw() {\n    const data = this._data\n\n    this._renderHtml(`<div class=\"${c('raw-wrapper')}\">\n      <div class=\"${c('raw')}\"></div>\n    </div>`)\n\n    let val = data.val\n    const container = this._$el.find(c('.raw')).get(0)\n    if (val.length > MAX_RAW_LEN) {\n      val = truncate(val, MAX_RAW_LEN)\n    }\n\n    new LunaTextViewer(container, {\n      text: val,\n      wrapLongLines: true,\n      showLineNumbers: val.length < MAX_LINE_NUM_LEN && this._showLineNum,\n    })\n  }\n  _renderIframe() {\n    this._renderHtml(`<iframe src=\"${escape(this._data.val)}\"></iframe>`)\n  }\n  _renderHtml(html, cache = true) {\n    if (cache && html === this._lastHtml) return\n    this._lastHtml = html\n    this._$el.html(html)\n    // Need setTimeout to make it work\n    setTimeout(() => (this._$el.get(0).scrollTop = 0), 0)\n  }\n}\n\nconst MAX_BEAUTIFY_LEN = 30000\nconst MAX_LINE_NUM_LEN = 80000\nconst MAX_RAW_LEN = 100000\n"
  },
  {
    "path": "src/Sources/Sources.scss",
    "content": "@use '../style/variable' as *;\n@use '../style/mixin' as *;\n\n#sources {\n  font-size: 0;\n  @include overflow-auto(y);\n  color: var(--foreground);\n  .code-wrapper,\n  .raw-wrapper {\n    @include overflow-auto(x);\n    width: 100%;\n    min-height: 100%;\n  }\n  .raw,\n  .code {\n    height: 100%;\n    .keyword {\n      color: var(--keyword-color);\n    }\n    .comment {\n      color: var(--comment-color);\n    }\n    .number {\n      color: var(--number-color);\n    }\n    .string {\n      color: var(--string-color);\n    }\n    .operator {\n      color: var(--operator-color);\n    }\n    &[data-type='html'] {\n      .keyword {\n        color: var(--tag-name-color);\n      }\n    }\n  }\n  .image {\n    font-size: $font-size-s;\n    .breadcrumb {\n      @include breadcrumb();\n    }\n    .img-container {\n      text-align: center;\n      img {\n        max-width: 100%;\n      }\n    }\n    .img-info {\n      text-align: center;\n      margin: 20px 0;\n      color: var(--foreground);\n    }\n  }\n  .json {\n    padding: 0 $padding;\n    * {\n      user-select: text;\n    }\n  }\n  iframe {\n    width: 100%;\n    height: 100%;\n  }\n}\n"
  },
  {
    "path": "src/eruda.js",
    "content": "import EntryBtn from './EntryBtn/EntryBtn'\nimport DevTools from './DevTools/DevTools'\nimport Tool from './DevTools/Tool'\nimport Console from './Console/Console'\nimport Network from './Network/Network'\nimport Elements from './Elements/Elements'\nimport Snippets from './Snippets/Snippets'\nimport Resources from './Resources/Resources'\nimport Info from './Info/Info'\nimport Sources from './Sources/Sources'\nimport Settings from './Settings/Settings'\nimport emitter from './lib/emitter'\nimport logger from './lib/logger'\nimport * as util from './lib/util'\nimport { isDarkTheme } from './lib/themes'\nimport themes from './lib/themes'\nimport isFn from 'licia/isFn'\nimport isNum from 'licia/isNum'\nimport isObj from 'licia/isObj'\nimport each from 'licia/each'\nimport isMobile from 'licia/isMobile'\nimport viewportScale from 'licia/viewportScale'\nimport detectBrowser from 'licia/detectBrowser'\nimport $ from 'licia/$'\nimport toArr from 'licia/toArr'\nimport upperFirst from 'licia/upperFirst'\nimport nextTick from 'licia/nextTick'\nimport isEqual from 'licia/isEqual'\nimport extend from 'licia/extend'\nimport evalCss from './lib/evalCss'\nimport chobitsu from './lib/chobitsu'\n\nexport default {\n  init({\n    container,\n    tool,\n    autoScale = true,\n    useShadowDom = true,\n    inline = false,\n    defaults = {},\n  } = {}) {\n    if (this._isInit) {\n      return\n    }\n\n    this._isInit = true\n    this._scale = 1\n\n    this._initContainer(container, useShadowDom)\n    this._initStyle()\n    this._initDevTools(defaults, inline)\n    this._initEntryBtn()\n    this._initSettings()\n    this._initTools(tool)\n    this._registerListener()\n\n    if (autoScale) {\n      this._autoScale()\n    }\n    if (inline) {\n      this._entryBtn.hide()\n      this._$el.addClass('eruda-inline')\n      this.show()\n    }\n  },\n  _isInit: false,\n  version: VERSION,\n  util: {\n    isErudaEl: util.isErudaEl,\n    evalCss,\n    isDarkTheme(theme) {\n      if (!theme) {\n        theme = this.getTheme()\n      }\n      return isDarkTheme(theme)\n    },\n    getTheme: () => {\n      const curTheme = evalCss.getCurTheme()\n\n      let result = 'Light'\n      each(themes, (theme, name) => {\n        if (isEqual(theme, curTheme)) {\n          result = name\n        }\n      })\n\n      return result\n    },\n  },\n  chobitsu,\n  Tool,\n  Console,\n  Elements,\n  Network,\n  Sources,\n  Resources,\n  Info,\n  Snippets,\n  Settings,\n  get(name) {\n    if (!this._checkInit()) return\n\n    if (name === 'entryBtn') return this._entryBtn\n\n    const devTools = this._devTools\n\n    return name ? devTools.get(name) : devTools\n  },\n  add(tool) {\n    if (!this._checkInit()) return\n\n    if (isFn(tool)) tool = tool(this)\n\n    this._devTools.add(tool)\n\n    return this\n  },\n  remove(name) {\n    this._devTools.remove(name)\n\n    return this\n  },\n  show(name) {\n    if (!this._checkInit()) return\n\n    const devTools = this._devTools\n\n    name ? devTools.showTool(name) : devTools.show()\n\n    return this\n  },\n  hide() {\n    if (!this._checkInit()) return\n\n    this._devTools.hide()\n\n    return this\n  },\n  destroy() {\n    this._devTools.destroy()\n    delete this._devTools\n    this._entryBtn.destroy()\n    delete this._entryBtn\n    this._unregisterListener()\n    $(this._container).remove()\n    evalCss.clear()\n    this._isInit = false\n    this._container = null\n    this._shadowRoot = null\n  },\n  scale(s) {\n    if (isNum(s)) {\n      this._scale = s\n      emitter.emit(emitter.SCALE, s)\n      return this\n    }\n\n    return this._scale\n  },\n  position(p) {\n    const entryBtn = this._entryBtn\n\n    if (isObj(p)) {\n      entryBtn.setPos(p)\n      return this\n    }\n\n    return entryBtn.getPos()\n  },\n  _autoScale() {\n    if (!isMobile()) return\n\n    this.scale(1 / viewportScale())\n  },\n  _registerListener() {\n    this._addListener = (...args) => this.add(...args)\n    this._showListener = (...args) => this.show(...args)\n\n    emitter.on(emitter.ADD, this._addListener)\n    emitter.on(emitter.SHOW, this._showListener)\n    emitter.on(emitter.SCALE, evalCss.setScale)\n  },\n  _unregisterListener() {\n    emitter.off(emitter.ADD, this._addListener)\n    emitter.off(emitter.SHOW, this._showListener)\n    emitter.off(emitter.SCALE, evalCss.setScale)\n  },\n  _checkInit() {\n    if (!this._isInit) logger.error('Please call \"eruda.init()\" first')\n    return this._isInit\n  },\n  _initContainer(container, useShadowDom) {\n    if (!container) {\n      container = document.createElement('div')\n      document.documentElement.appendChild(container)\n    }\n\n    container.id = 'eruda'\n    container.style.all = 'initial'\n    this._container = container\n\n    let shadowRoot\n    let el\n    if (useShadowDom) {\n      if (container.attachShadow) {\n        shadowRoot = container.attachShadow({ mode: 'open' })\n      } else if (container.createShadowRoot) {\n        shadowRoot = container.createShadowRoot()\n      }\n      if (shadowRoot) {\n        // font-face doesn't work inside shadow dom.\n        evalCss.container = document.head\n        evalCss(\n          require('./style/icon.css') +\n            require('luna-console/luna-console.css') +\n            require('luna-object-viewer/luna-object-viewer.css') +\n            require('luna-dom-viewer/luna-dom-viewer.css') +\n            require('luna-text-viewer/luna-text-viewer.css') +\n            require('luna-notification/luna-notification.css')\n        )\n\n        el = document.createElement('div')\n        shadowRoot.appendChild(el)\n        this._shadowRoot = shadowRoot\n      }\n    }\n\n    if (!this._shadowRoot) {\n      el = document.createElement('div')\n      container.appendChild(el)\n    }\n\n    extend(el, {\n      className: 'eruda-container __chobitsu-hide__',\n      contentEditable: false,\n    })\n\n    // http://stackoverflow.com/questions/3885018/active-pseudo-class-doesnt-work-in-mobile-safari\n    if (detectBrowser().name === 'ios') el.setAttribute('ontouchstart', '')\n\n    this._$el = $(el)\n  },\n  _initDevTools(defaults, inline) {\n    this._devTools = new DevTools(this._$el, {\n      defaults,\n      inline,\n    })\n  },\n  _initStyle() {\n    const className = 'eruda-style-container'\n    const $el = this._$el\n\n    if (this._shadowRoot) {\n      evalCss.container = this._shadowRoot\n      evalCss(':host { all: initial }')\n    } else {\n      $el.append(`<div class=\"${className}\"></div>`)\n      evalCss.container = $el.find(`.${className}`).get(0)\n    }\n\n    evalCss(\n      require('./style/reset.scss') +\n        require('luna-object-viewer/luna-object-viewer.css') +\n        require('luna-console/luna-console.css') +\n        require('luna-notification/luna-notification.css') +\n        require('luna-data-grid/luna-data-grid.css') +\n        require('luna-dom-viewer/luna-dom-viewer.css') +\n        require('luna-modal/luna-modal.css') +\n        require('luna-tab/luna-tab.css') +\n        require('luna-text-viewer/luna-text-viewer.css') +\n        require('luna-setting/luna-setting.css') +\n        require('luna-box-model/luna-box-model.css') +\n        require('./style/style.scss') +\n        require('./style/icon.css')\n    )\n  },\n  _initEntryBtn() {\n    this._entryBtn = new EntryBtn(this._$el)\n    this._entryBtn.on('click', () => this._devTools.toggle())\n  },\n  _initSettings() {\n    const devTools = this._devTools\n    const settings = new Settings()\n\n    devTools.add(settings)\n\n    this._entryBtn.initCfg(settings)\n    devTools.initCfg(settings)\n  },\n  _initTools(\n    tool = [\n      'console',\n      'elements',\n      'network',\n      'resources',\n      'sources',\n      'info',\n      'snippets',\n    ]\n  ) {\n    tool = toArr(tool)\n\n    const devTools = this._devTools\n\n    tool.forEach((name) => {\n      const Tool = this[upperFirst(name)]\n      try {\n        if (Tool) devTools.add(new Tool())\n      } catch (e) {\n        // Use nextTick to make sure it is possible to be caught by console panel.\n        nextTick(() => {\n          logger.error(\n            `Something wrong when initializing tool ${name}:`,\n            e.message\n          )\n        })\n      }\n    })\n\n    devTools.showTool(tool[0] || 'settings')\n  },\n}\n"
  },
  {
    "path": "src/index.js",
    "content": "const eruda = require('./eruda').default\nmodule.exports = eruda\nmodule.exports.default = eruda\n\n//# sourceMappingURL=index.js.map\n"
  },
  {
    "path": "src/lib/chobitsu.js",
    "content": "import Chobitsu from 'chobitsu/Chobitsu'\nimport * as Network from 'chobitsu/domains/Network'\nimport * as Overlay from 'chobitsu/domains/Overlay'\nimport * as DOM from 'chobitsu/domains/DOM'\nimport * as Storage from 'chobitsu/domains/Storage'\n\nconst chobitsu = new Chobitsu()\nchobitsu.register('Network', Network)\nchobitsu.register('Overlay', Overlay)\nchobitsu.register('DOM', {\n  ...DOM,\n  getNodeId: DOM.getDOMNodeId,\n  getNode: DOM.getDOMNode,\n})\nchobitsu.register('Storage', Storage)\n\nexport default chobitsu\n"
  },
  {
    "path": "src/lib/emitter.js",
    "content": "import Emitter from 'licia/Emitter'\n\nconst emitter = new Emitter()\nemitter.ADD = 'ADD'\nemitter.SHOW = 'SHOW'\nemitter.SCALE = 'SCALE'\n\nexport default emitter\n"
  },
  {
    "path": "src/lib/empty.js",
    "content": "export default {}\n"
  },
  {
    "path": "src/lib/evalCss.js",
    "content": "import toStr from 'licia/toStr'\nimport each from 'licia/each'\nimport filter from 'licia/filter'\nimport isStr from 'licia/isStr'\nimport keys from 'licia/keys'\nimport kebabCase from 'licia/kebabCase'\nimport defaults from 'licia/defaults'\nimport themes from './themes'\n\nlet styleList = []\nlet scale = 1\n\nlet curTheme = themes.Light\n\nconst exports = function (css, container) {\n  css = toStr(css)\n\n  for (let i = 0, len = styleList.length; i < len; i++) {\n    if (styleList[i].css === css) return\n  }\n\n  container = container || exports.container || document.head\n  const el = document.createElement('style')\n\n  el.type = 'text/css'\n  container.appendChild(el)\n\n  const style = { css, el, container }\n  resetStyle(style)\n  styleList.push(style)\n\n  return style\n}\n\nexports.setScale = function (s) {\n  scale = s\n  resetStyles()\n}\n\nexports.setTheme = function (theme) {\n  if (isStr(theme)) {\n    curTheme = themes[theme] || themes.Light\n  } else {\n    curTheme = defaults(theme, themes.Light)\n  }\n\n  resetStyles()\n}\n\nexports.getCurTheme = () => curTheme\n\nexports.getThemes = () => themes\n\nexports.clear = function () {\n  each(styleList, ({ container, el }) => container.removeChild(el))\n  styleList = []\n}\n\nexports.remove = function (style) {\n  styleList = filter(styleList, (s) => s !== style)\n\n  style.container.removeChild(style.el)\n}\n\nfunction resetStyles() {\n  each(styleList, (style) => resetStyle(style))\n}\n\nfunction resetStyle({ css, el }) {\n  css = css.replace(/(\\d+)px/g, ($0, $1) => +$1 * scale + 'px')\n  css = css.replace(/_/g, 'eruda-')\n  const _keys = keys(themes.Light)\n  each(_keys, (key) => {\n    css = css.replace(\n      new RegExp(`var\\\\(--${kebabCase(key)}\\\\)`, 'g'),\n      curTheme[key]\n    )\n  })\n  el.innerText = css\n}\n\nexport default exports\n"
  },
  {
    "path": "src/lib/logger.js",
    "content": "import Logger from 'licia/Logger'\n\nlet logger\n\nexport default logger = new Logger(\n  '[Eruda]',\n  ENV === 'production' ? 'warn' : 'debug'\n)\n\nlogger.formatter = function (type, argList) {\n  argList.unshift(this.name)\n\n  return argList\n}\n"
  },
  {
    "path": "src/lib/micromark.js",
    "content": "export function micromark(str) {\n  return str\n}\n"
  },
  {
    "path": "src/lib/themes.js",
    "content": "import extend from 'licia/extend'\nimport isArr from 'licia/isArr'\nimport contain from 'licia/contain'\n\nconst keyMap = [\n  'background',\n  'foreground',\n  'selectForeground',\n  'accent',\n  'highlight',\n  'border',\n  'primary',\n  'contrast',\n  'varColor',\n  'stringColor',\n  'keywordColor',\n  'numberColor',\n  'operatorColor',\n  'linkColor',\n  'textColor',\n  'tagNameColor',\n  'functionColor',\n  'attributeNameColor',\n  'commentColor',\n]\n\nconst keyMapLen = keyMap.length\n\nfunction arrToMap(arr) {\n  const ret = {}\n\n  for (let i = 0; i < keyMapLen; i++) {\n    ret[keyMap[i]] = arr[i]\n  }\n\n  return ret\n}\n\nfunction createDarkTheme(theme) {\n  if (isArr(theme)) theme = arrToMap(theme)\n  if (!theme.darkerBackground) theme.darkerBackground = theme.contrast\n  return extend(\n    {\n      consoleWarnBackground: '#332a00',\n      consoleWarnForeground: '#ffcb6b',\n      consoleWarnBorder: '#650',\n      consoleErrorBackground: '#290000',\n      consoleErrorForeground: '#ff8080',\n      consoleErrorBorder: '#5c0000',\n      light: '#ccc',\n      dark: '#aaa',\n    },\n    theme\n  )\n}\n\nfunction createLightTheme(theme) {\n  if (isArr(theme)) theme = arrToMap(theme)\n  if (!theme.darkerBackground) theme.darkerBackground = theme.contrast\n  return extend(\n    {\n      consoleWarnBackground: '#fffbe5',\n      consoleWarnForeground: '#5c5c00',\n      consoleWarnBorder: '#fff5c2',\n      consoleErrorBackground: '#fff0f0',\n      consoleErrorForeground: '#f00',\n      consoleErrorBorder: '#ffd6d6',\n      light: '#fff',\n      dark: '#eee',\n    },\n    theme\n  )\n}\n\nconst darkThemes = [\n  'Dark',\n  'Material Oceanic',\n  'Material Darker',\n  'Material Palenight',\n  'Material Deep Ocean',\n  'Monokai Pro',\n  'Dracula',\n  'Arc Dark',\n  'Atom One Dark',\n  'Solarized Dark',\n  'Night Owl',\n  'AMOLED',\n]\n\nexport function isDarkTheme(theme) {\n  return contain(darkThemes, theme)\n}\n\n// prettier-ignore\nexport default {\n  Light: createLightTheme({\n    darkerBackground: '#f3f3f3',\n    background: '#fff',\n    foreground: '#333',\n    selectForeground: '#333',\n    accent: '#1a73e8',\n    highlight: '#eaeaea',\n    border: '#ccc',\n    primary: '#333',\n    contrast: '#f2f7fd',\n    varColor: '#c80000',\n    stringColor: '#1a1aa6',\n    keywordColor: '#881280',\n    numberColor: '#1c00cf',\n    operatorColor: '#808080',\n    linkColor: '#1155cc',\n    textColor: '#8097bd',\n    tagNameColor: '#881280',\n    functionColor: '#222',\n    attributeNameColor: '#994500',\n    commentColor: '#236e25',\n    cssProperty: '#c80000',\n  }),\n  Dark: createDarkTheme({\n    darkerBackground: '#333',\n    background: '#242424',\n    foreground: '#a5a5a5',\n    selectForeground: '#eaeaea',\n    accent: '#7cacf8',\n    highlight: '#000',\n    border: '#3d3d3d',\n    primary: '#ccc',\n    contrast: '#0b2544',\n    varColor: '#e36eec',\n    stringColor: '#f29766',\n    keywordColor: '#9980ff',\n    numberColor: '#9980ff',\n    operatorColor: '#7f7f7f',\n    linkColor: '#ababab',\n    textColor: '#42597f',\n    tagNameColor: '#5db0d7',\n    functionColor: '#d5d5d5',\n    attributeNameColor: '#9bbbdc',\n    commentColor: '#747474',\n  }),\n  'Material Oceanic': createDarkTheme([\n    '#263238', '#B0BEC5', '#FFFFFF', '#009688', '#425B67',\n    '#2A373E', '#607D8B', '#1E272C', '#eeffff', '#c3e88d',\n    '#c792ea', '#f78c6c', '#89ddff', '#80cbc4', '#B0BEC5',\n    '#f07178', '#82aaff', '#ffcb6b', '#546e7a',\n  ]),\n  'Material Darker': createDarkTheme([\n    '#212121', '#B0BEC5', '#FFFFFF', '#FF9800', '#3F3F3F',\n    '#292929', '#727272', '#1A1A1A', '#eeffff', '#c3e88d',\n    '#c792ea', '#f78c6c', '#89ddff', '#80cbc4', '#B0BEC5',\n    '#f07178', '#82aaff', '#ffcb6b', '#616161',\n  ]),\n  'Material Lighter': createLightTheme([\n    '#FAFAFA', '#546E7A', '#546e7a', '#00BCD4', '#E7E7E8',\n    '#d3e1e8', '#94A7B0', '#F4F4F4', '#272727', '#91B859',\n    '#7C4DFF', '#F76D47', '#39ADB5', '#39ADB5', '#546E7A',\n    '#E53935', '#6182B8', '#F6A434', '#AABFC9',\n  ]),\n  'Material Palenight': createDarkTheme([\n    '#292D3E', '#A6ACCD', '#FFFFFF', '#ab47bc', '#444267',\n    '#2b2a3e', '#676E95', '#202331', '#eeffff', '#c3e88d',\n    '#c792ea', '#f78c6c', '#89ddff', '#80cbc4', '#A6ACCD',\n    '#f07178', '#82aaff', '#ffcb6b', '#676E95',\n  ]),\n  'Material Deep Ocean': createDarkTheme([\n    '#0F111A', '#8F93A2', '#FFFFFF', '#84ffff', '#1F2233',\n    '#41465b', '#4B526D', '#090B10', '#eeffff', '#c3e88d',\n    '#c792ea', '#f78c6c', '#89ddff', '#80cbc4', '#8F93A2',\n    '#f07178', '#82aaff', '#ffcb6b', '#717CB4',\n  ]),\n  'Monokai Pro': createDarkTheme([\n    '#2D2A2E', '#fcfcfa', '#FFFFFF', '#ffd866', '#5b595c',\n    '#423f43', '#939293', '#221F22', '#FCFCFA', '#FFD866',\n    '#FF6188', '#AB9DF2', '#FF6188', '#78DCE8', '#fcfcfa',\n    '#FF6188', '#A9DC76', '#78DCE8', '#727072',\n  ]),\n  Dracula: createDarkTheme([\n    '#282A36', '#F8F8F2', '#8BE9FD', '#FF79C5', '#6272A4',\n    '#21222C', '#6272A4', '#191A21', '#F8F8F2', '#F1FA8C',\n    '#FF79C6', '#BD93F9', '#FF79C6', '#F1FA8C', '#F8F8F2',\n    '#FF79C6', '#50FA78', '#50FA7B', '#6272A4',\n  ]),\n  'Arc Dark': createDarkTheme([\n    '#2f343f', '#D3DAE3', '#FFFFFF', '#42A5F5', '#3F3F46',\n    '#404552', '#8b9eb5', '#262b33', '#CF6A4C', '#8F9D6A',\n    '#9B859D', '#CDA869', '#A7A7A7', '#7587A6', '#D3DAE3',\n    '#CF6A4C', '#7587A6', '#F9EE98', '#747C84',\n  ]),\n  'Atom One Dark': createDarkTheme([\n    '#282C34', '#979FAD', '#FFFFFF', '#2979ff', '#383D48',\n    '#2e3239', '#979FAD', '#21252B', '#D19A66', '#98C379',\n    '#C679DD', '#D19A66', '#61AFEF', '#56B6C2', '#979FAD',\n    '#F07178', '#61AEEF', '#E5C17C', '#59626F',\n  ]),\n  'Atom One Light': createLightTheme([\n    '#FAFAFA', '#232324', '#232324', '#2979ff', '#EAEAEB',\n    '#DBDBDC', '#9D9D9F', '#FFFFFF', '#986801', '#50A14E',\n    '#A626A4', '#986801', '#4078F2', '#0184BC', '#232324',\n    '#E4564A', '#4078F2', '#C18401', '#A0A1A7',\n  ]),\n  'Solarized Dark': createDarkTheme([\n    '#002B36', '#839496', '#FFFFFF', '#d33682', '#11353F',\n    '#0D3640', '#586e75', '#00252E', '#268BD2', '#2AA198',\n    '#859900', '#D33682', '#93A1A1', '#268BD2', '#839496',\n    '#268BD2', '#B58900', '#B58900', '#657B83',\n  ]),\n  'Solarized Light': createLightTheme([\n    '#fdf6e3', '#586e75', '#002b36', '#d33682', '#F6F0DE',\n    '#f7f2e2', '#93a1a1', '#eee8d5', '#268BD2', '#2AA198',\n    '#859900', '#D33682', '#657B83', '#268BD2', '#586e75',\n    '#268BD2', '#B58900', '#657B83', '#93A1A1',\n  ]),\n  Github: createLightTheme([\n    '#F7F8FA', '#5B6168', '#FFFFFF', '#79CB60', '#CCE5FF',\n    '#DFE1E4', '#292D31', '#FFFFFF', '#24292E', '#032F62',\n    '#D73A49', '#005CC5', '#D73A49', '#005CC5', '#5B6168',\n    '#22863A', '#6F42C1', '#6F42C1', '#6A737D',\n  ]),\n  'Night Owl': createDarkTheme([\n    '#011627', '#b0bec5', '#ffffff', '#7e57c2', '#152C3B',\n    '#2a373e', '#607d8b', '#001424', '#addb67', '#ecc48d',\n    '#c792ea', '#f78c6c', '#c792ea', '#80CBC4', '#b0bec5',\n    '#7fdbca', '#82AAFF', '#FAD430', '#637777',\n  ]),\n  'Light Owl': createLightTheme([\n    '#FAFAFA', '#546e7a', '#403f53', '#269386', '#E0E7EA',\n    '#efefef', '#403F53', '#FAFAFA', '#0C969B', '#c96765',\n    '#994cc3', '#aa0982', '#7d818b', '#994cc3', '#546e7a',\n    '#994cc3', '#4876d6', '#4876d6', '#637777',\n  ]),\n  AMOLED: createDarkTheme([\n    '#000000', '#8F93A2', '#FFFFFF', '#68FFAE', '#000000',\n    '#41465b', '#4B526D', '#000000', '#DEFDF7', '#38ff9f',\n    '#ab2eff', '#A76DF7', '#38ff9f', '#86F3C7', '#8F93A2',\n    '#ab2eff', '#8293FF', '#38ff9f', '#6575c7',\n  ]),\n}\n"
  },
  {
    "path": "src/lib/util.js",
    "content": "import Url from 'licia/Url'\nimport contain from 'licia/contain'\nimport escapeJsStr from 'licia/escapeJsStr'\nimport isUndef from 'licia/isUndef'\nimport last from 'licia/last'\nimport map from 'licia/map'\nimport memStorage from 'licia/memStorage'\nimport toNum from 'licia/toNum'\nimport trim from 'licia/trim'\nimport html from 'licia/html'\n\n// https://stackoverflow.com/questions/46318395/detecting-mobile-device-notch\nexport function hasSafeArea() {\n  let proceed = false\n  const div = document.createElement('div')\n  if (CSS.supports('padding-bottom: env(safe-area-inset-bottom)')) {\n    div.style.paddingBottom = 'env(safe-area-inset-bottom)'\n    proceed = true\n  } else if (CSS.supports('padding-bottom: constant(safe-area-inset-bottom)')) {\n    div.style.paddingBottom = 'constant(safe-area-inset-bottom)'\n    proceed = true\n  }\n  if (proceed) {\n    document.body.appendChild(div)\n    const calculatedPadding = parseInt(\n      window.getComputedStyle(div).paddingBottom\n    )\n    document.body.removeChild(div)\n    if (calculatedPadding > 0) {\n      return true\n    }\n  }\n  return false\n}\n\nexport function escapeJsonStr(str) {\n  return escapeJsStr(str).replace(/\\\\'/g, \"'\").replace(/\\t/g, '\\\\t')\n}\n\nexport function safeStorage(type, memReplacement) {\n  if (isUndef(memReplacement)) memReplacement = true\n\n  let ret\n\n  switch (type) {\n    case 'local':\n      ret = window.localStorage\n      break\n    case 'session':\n      ret = window.sessionStorage\n      break\n  }\n\n  try {\n    // Safari private browsing\n    const x = 'test-localStorage-' + Date.now()\n    ret.setItem(x, x)\n    const y = ret.getItem(x)\n    ret.removeItem(x)\n    if (y !== x) throw new Error()\n  } catch {\n    if (memReplacement) return memStorage\n    return\n  }\n\n  return ret\n}\n\nexport function getFileName(url) {\n  let ret = last(url.split('/'))\n\n  if (ret === '') {\n    url = new Url(url)\n    ret = url.hostname\n  }\n\n  return ret\n}\n\nexport function pxToNum(str) {\n  return toNum(str.replace('px', ''))\n}\n\nexport function isErudaEl(el) {\n  while (el) {\n    if (el.id === 'eruda') return true\n    el = el.parentNode\n  }\n\n  return false\n}\n\nexport function isChobitsuEl(el) {\n  while (el) {\n    let className = ''\n    if (el.getAttribute) {\n      className = el.getAttribute('class') || ''\n    }\n    if (contain(className, '__chobitsu-hide__')) {\n      return true\n    }\n    el = el.parentNode\n  }\n\n  return false\n}\n\nexport function classPrefix(str) {\n  if (/<[^>]*>/g.test(str)) {\n    try {\n      const tree = html.parse(str)\n      traverseTree(tree, (node) => {\n        if (node.attrs && node.attrs.class) {\n          node.attrs.class = processClass(node.attrs.class)\n        }\n      })\n      return html.stringify(tree)\n    } catch {\n      return processClass(str)\n    }\n  }\n\n  return processClass(str)\n}\n\nfunction traverseTree(tree, handler) {\n  for (let i = 0, len = tree.length; i < len; i++) {\n    const node = tree[i]\n    handler(node)\n    if (node.content) {\n      traverseTree(node.content, handler)\n    }\n  }\n}\n\nfunction processClass(str) {\n  const prefix = 'eruda-'\n\n  return map(trim(str).split(/\\s+/), (singleClass) => {\n    if (contain(singleClass, prefix)) {\n      return singleClass\n    }\n\n    return singleClass.replace(/[\\w-]+/, (match) => `${prefix}${match}`)\n  }).join(' ')\n}\n\nexport function eventClient(type, e) {\n  const name = type === 'x' ? 'clientX' : 'clientY'\n\n  if (e[name]) {\n    return e[name]\n  }\n  if (e.changedTouches) {\n    return e.changedTouches[0][name]\n  }\n\n  return 0\n}\n\nexport function eventPage(type, e) {\n  const name = type === 'x' ? 'pageX' : 'pageY'\n\n  if (e[name]) {\n    return e[name]\n  }\n  if (e.changedTouches) {\n    return e.changedTouches[0][name]\n  }\n\n  return 0\n}\n"
  },
  {
    "path": "src/polyfill.js",
    "content": "import 'core-js/modules/es.map'\nimport 'core-js/stable/promise'\n"
  },
  {
    "path": "src/style/icon.css",
    "content": "@font-face {\n  font-family: 'eruda-icon';\n  src: url('data:application/x-font-woff;charset=utf-8;base64,d09GRgABAAAAAA7UAAsAAAAAGoAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAABHU1VCAAABCAAAARAAAAHGLTYxKE9TLzIAAAIYAAAAPwAAAFZWm1KoY21hcAAAAlgAAAFTAAADwhIFxPxnbHlmAAADrAAACEoAAA78hUhXQGhlYWQAAAv4AAAAMQAAADZ26MSyaGhlYQAADCwAAAAdAAAAJAgEBC9obXR4AAAMTAAAAB0AAACwXAv//GxvY2EAAAxsAAAAOwAAAFpp4mZEbWF4cAAADKgAAAAfAAAAIAE9AQ1uYW1lAAAMyAAAASkAAAIWm5e+CnBvc3QAAA30AAAA3wAAAUT1LH8yeJxNkLtOw0AQRe+GmJhXeFlhnRgwsTF2aChSUVAgRJUKUdBGioSQohQRX8YXcnZMAK/GOztzZubuykna1Z0e1Hl6nr2qv5x/rjRUV+0X8v99t/x4nyvenMh1bY/l3IJOQ9V60UJrfbnEVW7qHo0KlpIdaQt2oLH2ta1b6CtVKqiMlLFK7ABmAh1q+lDn8tSlZEJkgJUsz97Om+pYPeue8W90qSOqUuXWraBbRI8L4iOd4AcmxY/QUOpeM73h76Eks/iYfcOHGX/xAv4avT3uUOqG2e3EhnkRilJjY1NYc4egd8e0eOvQ3ukMC1xlp0PMc56oA5no1PiQz1GQ/Fbn1DvU+J93DPkCNbWab0arGKZ4nGNgZJJgnMDAysDA1Mt0hoGBoR9CM75mMGLkAIoysDIzYAUBaa4pDAcYdD+KsIC4MSxMDIxAGoQZALgnCOUAeJy100lOw0AQheHfZGAmAYLIPM+BQ0VhlJgUIkFuw4FYcRLqBPDaXQu2IFHRl8hlt7vjfgZyQEbOJAvJBwmh3tVN0n6GnbSf5U3HRX3C9efMueWJFWsrfX15Z8EdS14sSTs/K0lHFqnTZ5zO1qVDmx4VqjRp0aDMiGF6vs6EGgNmTDVyQ3PnyLPJFttazS77HFDQ3Q454pgSJ5xqijy/q+4vrw+lNfcq1WarAeXRsK+lTmowmE3/cK//qL3wlXz60ZiwL1H4t3PXkYVry4XryaWryJWryrVryo1rya3TE9HuR2W5dyN5cEN5dH15cmGdS1eXZzeRldNj5sUN5NXNZO20C5ZEIb22ERF+M1FIubmQfnPKF+aUNMwpc5hT+jAX3gpzSiTmlE1sL1JKsf1IecUOIiUXK0TKMFaMlGbsMFKusaNICceOI2UdK0WcfgOIClGwAHic7VddbBTXFb7n3vnZn9mZnd3Zmd21Peud9c54jb1e7y+sje2NYyg11OC/8mOww58KVCEkokACCn9CeYioitL2ASmVUNKH5IEkPFhFDRKoVZW2SquEqqr61odUlUqrChX1JzvuvePFpq3y0Lc+4LHOnHvPued+e86559xBgOgf/oBsQTxC2ZofHMAnN7oPIbTRXVoaghCEhtwlhP5Nzw+iHwz8gbu0EULuw40YwZYh96H7cOh/0yOLhEeYGk8DWWgegEnCud98vNdPyaQnUwF2wgky2dwPk3SJt+4t8jaKog6UpXJblLGJa1X22A59RJOIMAwGDzLkcRW3d6Ze/OF+7dIvThzb9dWBEq6Udu6+svXiNj7jbnfmB9y/wfvV868ermByA2Pfqx//7va+YGCoPr/w6327N9RP4okL3xmDr5smvOm+0R1Pz56+xOB5ON4j7yEDFRhKTbD6QWTELo+AY5erpXRRB00BQ9eETNqyK2q5WkxBjREdI70jOP2hoAgNnr8zFdRlQIoON1f4ZcqvyeG3y0jWg1N3eL5BJz6cDnboOpwQW5wCa9KWf14gR1ES9aA8RbYCJEYBrgOLwhoENR2jaO1KZk3k4YuqaRUakRD8JRSJhG4zct3dFklGbrM5KRqVXCUUGYZbZILJ2IAK4dZ1NnospqqfuNvgFmrFkYyRMRSkA9EQjZpRc2qOCNG5B3+cffDAo2RsrsVRivDyMkIccIiiH6OrBAUEJ0/9OQL9kIeqTn3HfKgbuoINXZSpz6nL+yGrGSZ1dbE2TKVlb4nlSeFROD0Y7whFMKedtKZU3JlTj2COnNE3CFzsaPCUEeB0OaEdwxxuut3O1fmFXy7MX3W6n2DhJrXR0vtGekrtSWH1KOb500bUTGrHfKeNDpnZP4YFbvxr/7naY1dzZoksobiXM1+UG1+YTH8XaHbEFC87zMBKdghwZ41fkXuZ9LaPpkfA9NJDidH0EJjuZ8trA6Wl8H+LayWXf0XmUASl0ABF5kV0NcwsxsTKQ3kYiiZoMhBLxjHNhJI+jMt5DP88srj3ajZ7de/ip4+Zo/UD9fqBk4wMJgpdWmYwk6QvratAZnO5a/sWrnX39HRfW9h3LZdrftbSpAR2UZVE16AF9F2iK9DjWvQjchkFqOfaaf2kuCpQSccgZrBSpHq8SoGV1Ax8z3kxB7hZB7wwkitlXZe85rq9B184e/bPitoER5Ld37gneEnpBD90f/u7dPTXjk645H6+epYa5DxSWF21M5ZAz61eKtagWiY9zfvBoGoYakDCvTrOw1bBUJvbVUOEich6tLr+MnkJxRCKaobOfEgrZsUrmZYoiMI5Vhg/2b2zVOGq5bldP985V6lgdIRNMMmRw3MzA8XiwMzck/auoC52tvOQ8Ww8YThDA7ISnFJRN8QsOXSocnnzqjlvowvjp14/NT5+alN+2joJNXLlYH9xVehtuomJqU6q/aWVtuDte4G8iyTUoDsXh6GcB0sGGvZsHjt2piIalSKrBSbENK8CiCsVhCKi4Oh0iQGk+TMMlTJGmUHLGtzECFxXZs5N2N3vvJMy68+lE8l4sDM2snNoiA5Ks1Znz8bC8xsce2LErGjpNMxm6t6yTfXMx18+NxMW586fnxWkxkB2tm9ofVtBG4lIktQobN2z4fn+4Z5Oy6esi5VT/dTG499xk3yEErTSIcg4DE0e0wzWTFwqVmtGzKhlrTwuD+OiiTUZ4/e7ZSdXmn/54pm9xeLe088MHu8LuRPNT/tmRjOZ0Zk9M6OWNToD10N9xwefOc10zlx8eb6Uc+Tu5tam1Zje01KcblhrvvwxuUR7aZJmb8aix50eaBbDDshU0mqJVW1HJHD+xuIiHl+XiIhco/nKPfzs3buj9y5cOPscHl+MR9qV4I17zVcAX7g3evcupsUbiQgtL3M8B+hb6F10Hz1C/0BNFkGojkCF5lyeBsbLvVbXFlicrJZAoa2bFhxa31nwmEyBGAue98RogfKm2KwXUa8fMG1KUlDy7gIlo7gy8oJNKxbbqlZlUzp9UxSt7aH1XpEarV1F2i5rtoeHraWpRT3hbeZZYBw2HG+JQ3EwruaBsr0O5DALVIttRI3TvkdXO6JBfiKbaiQeFPVIRz7R6ZfC2C9WxqQkp8Rj2/WkQtpEs56SAxy9xBA+3a/06m3+gB7mOFGN+kKcyAH4eEkMxORwUhIN1exLdPokmUhie70rETC0VDSnCTLvC0vxXLlN9QsgYGqpENCCBlGjopQI94ajhIicO3pwarK3j5Biccf0woGp7X15jAuFyakF6fd8INTev+VZOeLzx7JWsb8Bok+qxdV4T39cpXO2VdJ9Ulj6SOvm/CHJxymKXw1xAtEJL+aKRpTT8k6iP0v4zbE3e2e2fukQTuGvBNRA3Km2qQFRkhKWnsryBm6Lc0FfpyQFQ2kxQPyJkBBVAnow4hvoDIZ9sp4sd4cB8/6gEAjoZiTrE3ycjxckMWwPpVYspeNmVtA5ORm2owqPISCE2mNRg3mZ04NRfyElECwC7w+3V+14MhxtM6qKu4uUBmZmD+/bvq03T3BxYMf0/oUdk/nCD2wtIUVHNivReIcpk3p5vdCjJiU1mMgVEhE2hzeYgt8nDFKH+sIRRSUiHwpKQcEQpZBdiGs9Wr4rPuAQ4S3ttdwfJiaPQ+rpmXh6Jp6eif86E2S5Se/7hMNIpj0oQ7sQveULIgi0Tadpc6efLytfKPQuarDPlyK9mZI/ufNatapR6q+uI4V1VX/zZ2aPSf9LbdlsNZt9g/TabW325/edhm038O6Yacbc7zP6KFux7Qr9cET/AqBddaEAAHicY2BkYGAA4irvctZ4fpuvDNwsIIEozsf7GmD0/7///7OwsjABJTgYQCQDADqDDA8AAAB4nGNgZGBgYQABFtb/f///ZWFlYGRABToAW+YEPQAAAHicY2BgYGAhCv//T5w6IGaFYob/fxmoDADd6QRhAAAAeJxjYAACEQYNBhsGL4YIhjkMLxj1GD0YtzExMfkxzWO6wvSFWYY5gnkP8z8WHdZt7A7sdcRCAEgDFOMAeJxjYGRgYNBhZGRgZwABJiDmAkIGhv9gPgMADcIBTAB4nGWQPW7CQBSEx2BIAlKCFCkps1UKIpmfkgNAT0GXwpi1MbK91npBossJcoQcIaeIcoIcKGPzaGAtP38zb97uygAG+IWHenm4bWq9WrihOnGb9CDsk5+FO+jjRbhLfyjcwxumwn084p07eP4dnQFK4Rbu8SHcpv8p7JO/hDt4wrdwl/6PcA8r/An38eoN08gUsSncUif7LLRnef6utK1SU6hJMD5bC11oGzq9Ueujqg7J1LlYxdbkas6uzjKjSmt2OnLB1rlyNhrF4geRyZEigkGBuKkOS2gk2CNDCHvVvdQrpi0q+rVWmCDA+Cq1YKpokiGVxobJNY6sFQ48bUrXMa34Ws7kpLnMat4kIyv+77q3oxPRD7BtpkrMMOITX+SD5g75Pz0RXqgAAAB4nG2M2Y6CQBREOQqto7Pv+76PPPhJpLmISUuTSyfK3w8jr3MeTlVSSUWDqGcS/c+MAUNiEgwjxuwwYcoue+xzwCFHHHPCKWecc8ElV1xzwy133PPAI08888Irb7zzwSdffPPDjDSKnRQh0eWiDKOt0/nEZiohzf26mvZ1OyTWSaZj61e1StPE1tetycVJkERUvRrZ1FmVj/tI50NpxRRLF0Tj2mWtUbFe85FK0R2USScJpulObdmFExvi4L0zf0rn8TrTCodQEFCWLCixZKTkeNZUrKhpaNlE0S9fBESeAA==')\n    format('woff');\n}\n\n[class^='icon-'],\n[class*=' icon-'] {\n  display: inline-block;\n  font-family: 'eruda-icon' !important;\n  font-size: 16px;\n  font-style: normal;\n  -webkit-font-smoothing: antialiased;\n  -moz-osx-font-smoothing: grayscale;\n}\n\n.icon-left:before {\n  content: '\\f101';\n}\n.icon-right:before {\n  content: '\\f102';\n}\n.icon-caret-down:before {\n  content: '\\f103';\n}\n.icon-caret-right:before {\n  content: '\\f104';\n}\n.icon-clear:before {\n  content: '\\f105';\n}\n.icon-compress:before {\n  content: '\\f106';\n}\n.icon-copy:before {\n  content: '\\f107';\n}\n.icon-delete:before {\n  content: '\\f108';\n}\n.icon-error:before {\n  content: '\\f109';\n}\n.icon-expand:before {\n  content: '\\f10a';\n}\n.icon-eye:before {\n  content: '\\f10b';\n}\n.icon-filter:before {\n  content: '\\f10c';\n}\n.icon-play:before {\n  content: '\\f10d';\n}\n.icon-record:before {\n  content: '\\f10e';\n}\n.icon-refresh:before {\n  content: '\\f10f';\n}\n.icon-reset:before {\n  content: '\\f110';\n}\n.icon-search:before {\n  content: '\\f111';\n}\n.icon-select:before {\n  content: '\\f112';\n}\n.icon-tool:before {\n  content: '\\f113';\n}\n.icon-warn:before {\n  content: '\\f114';\n}\n"
  },
  {
    "path": "src/style/icon.json",
    "content": "[\n  \"left.svg\",\n  \"right.svg\",\n  \"caret-down.svg\",\n  \"caret-right.svg\",\n  \"clear.svg\",\n  \"compress.svg\",\n  \"copy.svg\",\n  \"delete.svg\",\n  \"error.svg\",\n  \"expand.svg\",\n  \"eye.svg\",\n  \"filter.svg\",\n  \"play.svg\",\n  \"record.svg\",\n  \"refresh.svg\",\n  \"reset.svg\",\n  \"search.svg\",\n  \"select.svg\",\n  \"tool.svg\",\n  \"warn.svg\"\n]\n"
  },
  {
    "path": "src/style/luna.scss",
    "content": "@use './variable' as *;\n\n.container {\n  .luna-console {\n    background: var(--background);\n  }\n\n  @mixin luna-console-highlight {\n    .luna-console-key {\n      color: var(--var-color);\n    }\n    .luna-console-number {\n      color: var(--number-color);\n    }\n    .luna-console-null {\n      color: var(--operator-color);\n    }\n    .luna-console-string {\n      color: var(--string-color);\n    }\n    .luna-console-boolean {\n      color: var(--keyword-color);\n    }\n    .luna-console-special {\n      color: var(--operator-color);\n    }\n    .luna-console-keyword {\n      color: var(--keyword-color);\n    }\n    .luna-console-operator {\n      color: var(--operator-color);\n    }\n    .luna-console-comment {\n      color: var(--comment-color);\n    }\n  }\n\n  .luna-console-header {\n    color: var(--link-color);\n    border-bottom-color: var(--border);\n  }\n\n  .luna-console-nesting-level {\n    border-right-color: var(--border);\n    &::before {\n      border-bottom-color: var(--border);\n    }\n  }\n\n  .luna-console-log-container {\n    &.luna-console-selected {\n      .luna-console-log-item {\n        background: var(--contrast);\n        &:not(.luna-console-error):not(.luna-console-warn) {\n          border-color: var(--border);\n        }\n      }\n    }\n  }\n\n  .luna-console-log-item {\n    border-bottom-color: var(--border);\n    color: var(--foreground);\n    a {\n      color: var(--link-color) !important;\n    }\n    .luna-console-icon-container {\n      .luna-console-icon {\n        color: var(--foreground);\n      }\n      .luna-console-icon-error {\n        color: #ef3842;\n      }\n      .luna-console-icon-warn {\n        color: #e8a400;\n      }\n    }\n    .luna-console-count {\n      color: var(--select-foreground);\n      background: var(--highlight);\n    }\n    &.luna-console-warn {\n      color: var(--console-warn-foreground);\n      background: var(--console-warn-background);\n      border-color: var(--console-warn-border);\n    }\n    &.luna-console-error {\n      background: var(--console-error-background);\n      color: var(--console-error-foreground);\n      border-color: var(--console-error-border);\n      .luna-console-count {\n        background: var(--console-error-foreground);\n      }\n    }\n    .luna-console-code {\n      @include luna-console-highlight();\n    }\n    .luna-console-log-content {\n      .luna-console-undefined,\n      .luna-console-null {\n        color: var(--operator-color);\n      }\n      .luna-console-number {\n        color: var(--number-color);\n      }\n      .luna-console-boolean {\n        color: var(--keyword-color);\n      }\n      .luna-console-symbol,\n      .luna-console-regexp {\n        color: var(--var-color);\n      }\n    }\n  }\n\n  .luna-console-preview {\n    @include luna-console-highlight();\n  }\n\n  .luna-object-viewer {\n    color: var(--primary);\n    font-size: 12px !important;\n  }\n  .luna-object-viewer-null {\n    color: var(--operator-color);\n  }\n  .luna-object-viewer-string,\n  .luna-object-viewer-regexp {\n    color: var(--string-color);\n  }\n  .luna-object-viewer-number {\n    color: var(--number-color);\n  }\n  .luna-object-viewer-boolean {\n    color: var(--keyword-color);\n  }\n  .luna-object-viewer-special {\n    color: var(--operator-color);\n  }\n  .luna-object-viewer-key,\n  .luna-object-viewer-key-lighter {\n    color: var(--var-color);\n  }\n  .luna-object-viewer-expanded:before {\n    border-color: transparent;\n    border-top-color: var(--foreground);\n  }\n  .luna-object-viewer-collapsed:before {\n    border-top-color: transparent;\n    border-left-color: var(--foreground);\n  }\n\n  .luna-notification {\n    pointer-events: none !important;\n    padding: $padding;\n    z-index: 1000;\n  }\n\n  .luna-notification-item {\n    z-index: 500;\n    color: var(--foreground);\n    background: var(--background);\n    box-shadow: none;\n    padding: 5px 10px;\n    border: 1px solid var(--border);\n  }\n\n  .luna-notification-upper {\n    margin-bottom: 10px;\n  }\n\n  .luna-notification-lower {\n    margin-top: 10px;\n  }\n\n  .luna-data-grid {\n    color: var(--foreground);\n    background: var(--background);\n    border-color: var(--border);\n    th,\n    td {\n      border-color: var(--border);\n    }\n    th {\n      background: var(--darker-background);\n      &.luna-data-grid-sortable {\n        &:hover,\n        &:active {\n          color: var(--select-foreground);\n          background: var(--highlight);\n        }\n      }\n    }\n    .luna-data-grid-data-container {\n      .luna-data-grid-node.luna-data-grid-selected,\n      .luna-data-grid-node.luna-data-grid-selectable:hover {\n        background: var(--highlight);\n      }\n      tr:nth-child(even) {\n        background: var(--contrast);\n      }\n    }\n    &:focus {\n      .luna-data-grid-data-container {\n        .luna-data-grid-node.luna-data-grid-selected {\n          background: var(--accent);\n        }\n      }\n    }\n  }\n\n  .luna-dom-viewer {\n    color: var(--foreground);\n    .luna-dom-viewer-html-tag,\n    .luna-dom-viewer-tag-name {\n      color: var(--tag-name-color);\n    }\n    .luna-dom-viewer-attribute-name {\n      color: var(--attribute-name-color);\n    }\n    .luna-dom-viewer-attribute-value {\n      color: var(--string-color);\n    }\n    .luna-dom-viewer-html-comment {\n      color: var(--comment-color);\n    }\n    .luna-dom-viewer-tree-item {\n      &:hover {\n        .luna-dom-viewer-selection {\n          background: var(--contrast);\n        }\n      }\n      &.luna-dom-viewer-selected {\n        .luna-dom-viewer-selection {\n          background: var(--highlight);\n        }\n      }\n      &.luna-dom-viewer-selected:focus {\n        .luna-dom-viewer-selection {\n          background: var(--accent);\n          opacity: 0.2;\n        }\n      }\n    }\n    .luna-dom-viewer-text-node {\n      .luna-dom-viewer-key {\n        color: var(--var-color);\n      }\n      .luna-dom-viewer-number {\n        color: var(--number-color);\n      }\n      .luna-dom-viewer-null {\n        color: var(--operator-color);\n      }\n      .luna-dom-viewer-string {\n        color: var(--string-color);\n      }\n      .luna-dom-viewer-boolean {\n        color: var(--keyword-color);\n      }\n      .luna-dom-viewer-special {\n        color: var(--operator-color);\n      }\n      .luna-dom-viewer-keyword {\n        color: var(--keyword-color);\n      }\n      .luna-dom-viewer-operator {\n        color: var(--operator-color);\n      }\n      .luna-dom-viewer-comment {\n        color: var(--comment-color);\n      }\n    }\n  }\n\n  .luna-dom-viewer-children {\n    margin: 0;\n    padding-left: 15px !important;\n  }\n\n  .inline {\n    .luna-modal,\n    .luna-notification {\n      position: absolute;\n    }\n  }\n\n  .luna-modal {\n    z-index: 9999999;\n  }\n\n  .luna-modal-body,\n  .luna-modal-input {\n    color: var(--foreground);\n    background: var(--background);\n  }\n\n  .luna-modal-body {\n    border-color: var(--border);\n  }\n\n  .luna-modal-input {\n    user-select: text !important;\n    border-color: var(--border);\n  }\n\n  .luna-modal-button-group {\n    .luna-modal-secondary {\n      border-color: var(--border);\n      color: var(--foreground);\n      background: var(--background);\n    }\n    .luna-modal-primary {\n      background: var(--accent);\n    }\n    .luna-modal-button {\n      &:active {\n        &::before {\n          background: var(--accent);\n        }\n      }\n    }\n  }\n\n  .luna-tab {\n    position: absolute;\n    left: 0;\n    top: 0;\n    color: var(--foreground);\n    background: var(--darker-background);\n  }\n\n  .luna-tab-tabs-container {\n    border-color: var(--border);\n  }\n\n  .luna-tab-item {\n    &.luna-tab-selected,\n    &:hover {\n      background: var(--highlight);\n      color: var(--select-foreground);\n    }\n  }\n\n  .luna-tab-slider {\n    background: var(--accent);\n  }\n\n  .luna-text-viewer {\n    color: var(--foreground);\n    border: none;\n    border-bottom: 1px solid var(--border);\n    background: var(--background);\n    font-size: $font-size-s;\n    .luna-text-viewer-line-text {\n      user-select: text;\n      * {\n        user-select: text;\n      }\n    }\n    .luna-text-viewer-copy,\n    .luna-text-viewer-line-number {\n      border-color: var(--border);\n    }\n    .luna-text-viewer-copy .luna-text-viewer-icon-check {\n      color: var(--accent);\n    }\n    .luna-text-viewer-copy {\n      background-color: var(--background);\n    }\n  }\n\n  .luna-setting {\n    color: var(--foreground);\n    background: var(--background);\n  }\n\n  .luna-setting-item {\n    &:hover,\n    &.luna-setting-selected {\n      background: var(--darker-background);\n    }\n    &.luna-setting-selected:focus {\n      outline: none;\n    }\n  }\n\n  .luna-setting-item-title {\n    font-size: $font-size;\n  }\n\n  .luna-setting-item-separator {\n    border-color: var(--border);\n  }\n\n  .luna-setting-item-checkbox {\n    input {\n      border-color: var(--border);\n      &:checked {\n        background-color: var(--accent);\n        border-color: var(--accent);\n      }\n    }\n  }\n\n  .luna-setting-item-select {\n    .luna-setting-select {\n      select {\n        color: var(--foreground);\n        border-color: var(--border);\n        background: var(--background);\n      }\n      &:after {\n        border-top-color: var(--foreground);\n      }\n    }\n  }\n\n  .luna-setting-item-button {\n    button {\n      color: var(--accent);\n      background: var(--background);\n      border-color: var(--border);\n      &:hover,\n      &:active {\n        background: var(--darker-background);\n      }\n      &:active {\n        border: 1px solid var(--accent);\n      }\n    }\n  }\n\n  .luna-setting-item-number {\n    .luna-setting-range-container {\n      .luna-setting-range-track {\n        .luna-setting-range-track-bar {\n          background: var(--border);\n          .luna-setting-range-track-progress {\n            background: var(--accent);\n          }\n        }\n      }\n      input::-webkit-slider-thumb {\n        border-color: var(--border);\n        background: radial-gradient(\n          circle at center,\n          var(--dark) 0,\n          var(--dark) 15%,\n          var(--light) 22%,\n          var(--light) 100%\n        );\n      }\n    }\n  }\n\n  .luna-box-model {\n    background: var(--background);\n  }\n\n  .luna-box-model-position,\n  .luna-box-model-margin,\n  .luna-box-model-border,\n  .luna-box-model-padding,\n  .luna-box-model-content {\n    color: var(--foreground);\n    background: var(--background);\n  }\n}\n"
  },
  {
    "path": "src/style/mixin.scss",
    "content": "@use './variable' as *;\n\n@mixin absolute($width: 100%, $height: 100%) {\n  position: absolute;\n  width: $width;\n  height: $height;\n  left: 0;\n  top: 0;\n}\n\n@mixin overflow-auto($direction: 'both') {\n  @if $direction == 'both' {\n    overflow: auto;\n  } @else {\n    overflow-#{$direction}: auto;\n  }\n  -webkit-overflow-scrolling: touch;\n}\n\n@mixin safe-area($prop, $value, $pos: 'bottom') {\n  #{$prop}: calc(#{$value} + env(safe-area-inset-#{$pos}));\n}\n\n@mixin breadcrumb {\n  background: var(--darker-background);\n  color: var(--primary);\n  user-select: text;\n  margin-bottom: 10px;\n  word-break: break-all;\n  padding: $padding;\n  font-size: $font-size-l;\n  min-height: 40px;\n  border-bottom: 1px solid var(--border);\n}\n\n@mixin control {\n  @include absolute(100%, 40px);\n  cursor: default;\n  font-size: 0;\n  background: var(--darker-background);\n  color: var(--primary);\n  line-height: 20px;\n  border-bottom: 1px solid var(--border);\n  [class^='eruda-icon-'],\n  [class*=' icon-'] {\n    display: inline-block;\n    padding: 10px;\n    font-size: $font-size-l;\n    position: absolute;\n    top: 0;\n    cursor: pointer;\n    transition: color $anim-duration;\n    &:active,\n    &.active {\n      color: var(--accent);\n    }\n  }\n}\n\n@mixin clear-float {\n  &:after {\n    content: '';\n    display: block;\n    clear: both;\n  }\n}\n\n@mixin right-btn {\n  .btn {\n    margin-left: 5px;\n    float: right;\n    color: var(--primary);\n    width: 18px;\n    height: 18px;\n    font-size: $font-size-l;\n    font-weight: normal;\n    cursor: pointer;\n    transition: color $anim-duration;\n    &.filter-text {\n      width: auto;\n      max-width: 80px;\n      font-size: $font-size;\n      overflow: hidden;\n      text-overflow: ellipsis;\n      display: inline-block;\n    }\n    &:active {\n      color: var(--accent);\n    }\n    &.btn-disabled {\n      color: inherit !important;\n      cursor: default !important;\n      pointer-events: none;\n      opacity: 0.5;\n      * {\n        pointer-events: none;\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "src/style/reset.scss",
    "content": ".container {\n  span,\n  applet,\n  object,\n  iframe,\n  h1,\n  h2,\n  h3,\n  h4,\n  h5,\n  h6,\n  p,\n  blockquote,\n  pre,\n  a,\n  abbr,\n  acronym,\n  address,\n  big,\n  cite,\n  code,\n  del,\n  dfn,\n  em,\n  img,\n  ins,\n  kbd,\n  q,\n  s,\n  samp,\n  small,\n  strike,\n  strong,\n  sub,\n  sup,\n  tt,\n  var,\n  b,\n  u,\n  i,\n  center,\n  dl,\n  dt,\n  dd,\n  ol,\n  ul,\n  li,\n  fieldset,\n  form,\n  label,\n  legend,\n  table,\n  caption,\n  tbody,\n  tfoot,\n  thead,\n  tr,\n  th,\n  td,\n  article,\n  aside,\n  canvas,\n  details,\n  embed,\n  figure,\n  figcaption,\n  footer,\n  header,\n  hgroup,\n  menu,\n  nav,\n  output,\n  ruby,\n  section,\n  summary,\n  time,\n  mark,\n  audio,\n  video {\n    margin: 0;\n    padding: 0;\n    border: 0;\n    font-size: 100%;\n  }\n  article,\n  aside,\n  details,\n  figcaption,\n  figure,\n  footer,\n  header,\n  hgroup,\n  menu,\n  nav,\n  section {\n    display: block;\n  }\n  body {\n    line-height: 1;\n  }\n  ol,\n  ul {\n    list-style: none;\n  }\n  blockquote,\n  q {\n    quotes: none;\n  }\n  blockquote:before,\n  blockquote:after,\n  q:before,\n  q:after {\n    content: '';\n    content: none;\n  }\n  table {\n    border-collapse: collapse;\n    border-spacing: 0;\n    color: inherit;\n    font-size: 1em;\n    font-style: inherit;\n    font-variant: inherit;\n    font-weight: inherit;\n    line-height: inherit;\n    text-decoration: inherit;\n    white-space: inherit;\n  }\n}\n"
  },
  {
    "path": "src/style/style.scss",
    "content": "@use 'variable' as *;\n@use 'mixin' as *;\n@use 'luna' as *;\n\n.container {\n  min-width: 320px;\n  pointer-events: none;\n  position: fixed;\n  left: 0;\n  top: 0;\n  width: 100%;\n  height: 100%;\n  z-index: 9999999;\n  color: var(--foreground);\n  font-family: $font-family;\n  font-size: $font-size;\n  direction: ltr;\n  &.dark {\n    color-scheme: dark;\n  }\n  * {\n    box-sizing: border-box;\n    pointer-events: all;\n    user-select: none;\n    -webkit-tap-highlight-color: transparent;\n    -webkit-text-size-adjust: none;\n  }\n  ul {\n    list-style: none;\n    padding: 0;\n    margin: 0;\n  }\n  h1,\n  h2,\n  h3,\n  h4 {\n    margin: 0;\n  }\n  h2 {\n    font-size: $font-size;\n    [class^='icon-'],\n    [class*=' icon-'] {\n      font-weight: normal;\n    }\n  }\n  &.inline {\n    position: static;\n  }\n}\n\n.hidden {\n  display: none;\n}\n\n.icon-disabled {\n  opacity: 0.5;\n  pointer-events: none;\n  cursor: default !important;\n  &:active {\n    color: inherit !important;\n  }\n}\n\n.tag-name-color {\n  color: var(--tag-name-color);\n}\n\n.function-color {\n  color: var(--function-color);\n}\n\n.attribute-name-color {\n  color: var(--attribute-name-color);\n}\n\n.operator-color {\n  color: var(--operator-color);\n}\n\n.string-color {\n  color: var(--string-color);\n}\n"
  },
  {
    "path": "src/style/variable.scss",
    "content": "$padding: 10px;\n\n$font-size: 14px;\n$font-size-s: 12px;\n$font-size-l: 16px;\n\n$font-family: -apple-system, system-ui, BlinkMacSystemFont,\n  '.SFNSDisplay-Regular', 'Helvetica Neue', 'Lucida Grande', 'Segoe UI', Tahoma,\n  sans-serif;\n\n$anim-duration: 0.3s;\n"
  },
  {
    "path": "test/boot.js",
    "content": "function boot(name, cb) {\n  // Need a little delay to make sure width and height of webpack dev server iframe are initialized.\n  setTimeout(function () {\n    let options = {\n      useShadowDom: false,\n      defaults: {\n        displaySize: 50,\n        transparency: 0.9,\n        theme: 'Monokai Pro',\n      },\n    }\n    if (name) {\n      options.tool = name === 'settings' ? [] : name\n    }\n\n    try {\n      eruda.init(options)\n    } catch (e) {\n      alert(e)\n    }\n    eruda.show()\n\n    cb && cb()\n\n    if (name == null) return\n\n    loadJs('lib/boot', function () {\n      loadJs('lib/jasmine-jquery', function () {\n        // This is needed to trigger jasmine initialization.\n        loadJs(name, function () {\n          window.onload()\n        })\n      })\n    })\n  }, 500)\n}\n\nfunction loadJs(src, cb) {\n  let script = document.createElement('script')\n  script.src = src + '.js'\n  script.onload = cb\n  document.body.appendChild(script)\n}\n"
  },
  {
    "path": "test/console.html",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <meta charset=\"UTF-8\" />\n    <meta\n      name=\"viewport\"\n      content=\"width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no\"\n    />\n    <title>Console</title>\n    <link rel=\"stylesheet\" href=\"lib/jasmine.css\" />\n    <script src=\"lib/jquery.js\"></script>\n    <script src=\"lib/jasmine.js\"></script>\n    <script src=\"lib/jasmine-html.js\"></script>\n    <script src=\"assets/eruda.js\"></script>\n    <script src=\"boot.js\"></script>\n  </head>\n  <body>\n    <script>\n      boot('console')\n    </script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/console.js",
    "content": "describe('console', function () {\n  let tool = eruda.get('console')\n  tool.config.set('asyncRender', false)\n  let $tool = $('.eruda-console')\n  let logger = tool._logger\n\n  function log(i) {\n    return logs()[i].container\n  }\n\n  function logs() {\n    return logger.displayLogs\n  }\n\n  beforeEach(function () {\n    eruda.show('console')\n    logger.clear(true)\n  })\n\n  describe('config', function () {\n    let config = tool.config\n\n    it('override console', function () {\n      config.set('overrideConsole', true)\n      console.log('test')\n      expect($(log(0))).toContainText('test')\n    })\n  })\n\n  describe('ui', function () {\n    it('clear', function () {\n      tool.log('test')\n      $('.eruda-clear-console').click()\n      expect($tool.find('.eruda-logs li')).toHaveLength(0)\n    })\n\n    it('level', function () {\n      tool.log('test')\n      tool.warn('test')\n      expect(logs()).toHaveLength(2)\n      $('.eruda-level[data-level=\"warning\"]').click()\n      expect(logs()).toHaveLength(1)\n      $('.eruda-level[data-level=\"all\"]').click()\n    })\n  })\n\n  describe('execute', function () {\n    it('js', function () {\n      $tool.find('textarea').val('1+2')\n      $('.eruda-execute').click()\n      expect($(log(1))).toContainText('3')\n    })\n  })\n\n  describe('events', function () {\n    it('log', function () {\n      let sum = 0\n      function add(num) {\n        sum += num\n      }\n      tool.on('log', add)\n      tool.log(5)\n      expect(sum).toBe(5)\n      tool.log(6)\n      expect(sum).toBe(11)\n      tool.off('log', add)\n      tool.log(1)\n      expect(sum).toBe(11)\n    })\n  })\n})\n"
  },
  {
    "path": "test/data.json",
    "content": "[\n    {\n        \"name\": \"Test\",\n        \"author\": {\n            \"name\": \"Redhoodsu\",\n            \"email\": \"surunzi@foxmail.com\",\n            \"contact\": [\n                {\n                    \"location\": \"<span>office</span>\",\n                    \"number\": 123456\n                },\n                {\n                    \"location\": \"A very very long long address!!!!!!!!!!!!\",\n                    \"number\": 654321\n                },\n                null\n            ]\n        }\n    }\n]"
  },
  {
    "path": "test/elements.html",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <meta charset=\"UTF-8\" />\n    <meta\n      name=\"viewport\"\n      content=\"width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no\"\n    />\n    <title>Elements</title>\n    <link rel=\"stylesheet\" href=\"lib/jasmine.css\" />\n    <script src=\"lib/jquery.js\"></script>\n    <script src=\"lib/jasmine.js\"></script>\n    <script src=\"lib/jasmine-html.js\"></script>\n    <script src=\"assets/eruda.js\"></script>\n    <script src=\"boot.js\"></script>\n  </head>\n  <body>\n    <script>\n      boot('elements')\n    </script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/elements.js",
    "content": "describe('elements', function () {\n  let tool = eruda.get('elements')\n\n  beforeEach(function () {\n    eruda.show('elements')\n  })\n\n  describe('api', function () {\n    it('select element', function () {\n      tool.select(document.body)\n    })\n  })\n})\n"
  },
  {
    "path": "test/eruda.html",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <meta charset=\"UTF-8\" />\n    <meta\n      name=\"viewport\"\n      content=\"width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no\"\n    />\n    <title>Features</title>\n    <link rel=\"stylesheet\" href=\"lib/jasmine.css\" />\n    <script src=\"lib/jquery.js\"></script>\n    <script src=\"lib/jasmine.js\"></script>\n    <script src=\"lib/jasmine-html.js\"></script>\n    <script src=\"assets/eruda.js\"></script>\n    <script src=\"boot.js\"></script>\n  </head>\n  <body>\n    <script>\n      boot('eruda')\n    </script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/eruda.js",
    "content": "describe('devTools', function () {\n  describe('init', function () {\n    it('destroy', function () {\n      eruda.destroy()\n\n      expect($('#eruda')).toHaveLength(0)\n    })\n\n    it('init', function () {\n      let container = document.createElement('div')\n      container.id = 'eruda'\n      document.body.appendChild(container)\n\n      eruda.init({\n        container: container,\n        tool: [],\n        useShadowDom: false,\n      })\n\n      let $eruda = $('#eruda')\n      expect($eruda.find('.eruda-dev-tools')).toHaveLength(1)\n    })\n  })\n\n  describe('tool', function () {\n    it('add', function () {\n      eruda.add({\n        name: 'test',\n        init: function ($el) {\n          this._$el = $el\n          $el.html('Test Plugin')\n        },\n      })\n\n      expect($('.eruda-test')).toContainText('Test Plugin')\n    })\n\n    it('show', function () {\n      let $tool = $('.eruda-test')\n      expect($tool).toBeHidden()\n      eruda.show('test')\n      expect($tool).toHaveCss({ display: 'block' })\n    })\n\n    it('remove', function () {\n      eruda.remove('test')\n      expect($('.eruda-test')).toHaveLength(0)\n    })\n  })\n\n  describe('display', function () {\n    it('show', function () {\n      eruda.show()\n      expect($('.eruda-dev-tools')).toHaveCss({ display: 'block' })\n    })\n\n    it('hide', function (done) {\n      eruda.hide()\n      setTimeout(function () {\n        expect($('.eruda-dev-tools')).toBeHidden()\n        done()\n      }, 500)\n    })\n  })\n\n  describe('scale', function () {\n    it('get', function () {\n      eruda.scale(1)\n      expect(eruda.scale()).toBe(1)\n    })\n  })\n})\n"
  },
  {
    "path": "test/index.html",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <meta charset=\"UTF-8\" />\n    <meta\n      name=\"viewport\"\n      content=\"width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no\"\n    />\n    <title>Eruda Test Page</title>\n    <link rel=\"stylesheet\" href=\"style.css\" />\n    <script src=\"assets/eruda.js\"></script>\n  </head>\n  <body>\n    <header>ERUDA TEST PAGE</header>\n    <nav id=\"test-list\">\n      <ul>\n        <li>\n          <a href=\"eruda.html\">eruda</a>\n        </li>\n        <li>\n          <a href=\"console.html\">console</a>\n        </li>\n        <li>\n          <a href=\"elements.html\">elements</a>\n        </li>\n        <li>\n          <a href=\"network.html\">network</a>\n        </li>\n        <li>\n          <a href=\"resources.html\">resources</a>\n        </li>\n        <li>\n          <a href=\"sources.html\">sources</a>\n        </li>\n        <li>\n          <a href=\"info.html\">info</a>\n        </li>\n        <li>\n          <a href=\"snippets.html\">snippets</a>\n        </li>\n        <li>\n          <a href=\"settings.html\">settings</a>\n        </li>\n        <li>\n          <a href=\"manual.html\">manual</a>\n        </li>\n        <li>\n          <a href=\"inline.html\">inline</a>\n        </li>\n      </ul>\n    </nav>\n    <script>\n      ;(function () {\n        let el = document.createElement('div')\n        document.body.appendChild(el)\n        setTimeout(function () {\n          try {\n            eruda.init({\n              container: el,\n              useShadowDom: false,\n              defaults: {\n                displaySize: 50,\n                transparency: 0.9,\n                theme: 'Monokai Pro',\n              },\n            })\n          } catch (e) {\n            alert(e)\n          }\n          eruda.show().get().config.set('displaySize', 50)\n        }, 1000)\n      })()\n    </script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/info.html",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <meta charset=\"UTF-8\" />\n    <meta\n      name=\"viewport\"\n      content=\"width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no\"\n    />\n    <title>Info</title>\n    <link rel=\"stylesheet\" href=\"lib/jasmine.css\" />\n    <script src=\"lib/jquery.js\"></script>\n    <script src=\"lib/jasmine.js\"></script>\n    <script src=\"lib/jasmine-html.js\"></script>\n    <script src=\"assets/eruda.js\"></script>\n    <script src=\"boot.js\"></script>\n  </head>\n  <body>\n    <script>\n      boot('info')\n    </script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/info.js",
    "content": "describe('info', function () {\n  let tool = eruda.get('info')\n  let $tool = $('.eruda-info')\n\n  describe('default', function () {\n    it('location', function () {\n      expect($tool.find('.eruda-content').eq(0)).toContainText(location.href)\n    })\n\n    it('user agent', function () {\n      expect($tool.find('.eruda-content').eq(1)).toContainText(\n        navigator.userAgent\n      )\n    })\n\n    it('device', function () {\n      expect($tool.find('.eruda-content').eq(2)).toContainText(\n        window.innerWidth\n      )\n    })\n\n    it('system', function () {\n      expect($tool.find('.eruda-content').eq(3)).toContainText('os')\n    })\n\n    it('sponsor', function () {\n      expect($tool.find('.eruda-content').eq(4)).toContainText(\n        'Open Collective'\n      )\n    })\n\n    it('about', function () {\n      expect($tool.find('.eruda-content').eq(5)).toHaveText(/Eruda v[\\d.]+/)\n    })\n  })\n\n  it('clear', function () {\n    tool.clear()\n    expect($tool.find('li')).toHaveLength(0)\n  })\n\n  it('add', function () {\n    tool.add('test', 'eruda')\n    expect($tool.find('.eruda-title')).toContainText('test')\n    expect($tool.find('.eruda-content')).toContainText('eruda')\n    tool.add('test', 'update')\n    tool.add('test', 'update')\n    expect($tool.find('.eruda-content')).toContainText('update')\n  })\n\n  it('get', function () {\n    expect(tool.get()).toEqual([{ name: 'test', val: 'update' }])\n    expect(tool.get('test')).toBe('update')\n    expect(tool.get('test2')).not.toBeDefined()\n  })\n\n  it('remove', function () {\n    tool.remove('test')\n    expect($tool.find('li')).toHaveLength(0)\n  })\n})\n"
  },
  {
    "path": "test/init.js",
    "content": "eruda.init({\n  useShadowDom: false,\n})\n"
  },
  {
    "path": "test/inline.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"UTF-8\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n    <title>Inline</title>\n    <link rel=\"stylesheet\" href=\"style.css\" />\n    <link\n      rel=\"stylesheet\"\n      href=\"https://cdn.jsdelivr.net/npm/luna-window/luna-window.css\"\n    />\n    <script src=\"assets/eruda.js\"></script>\n    <script src=\"https://cdn.jsdelivr.net/npm/luna-window/luna-window.js\"></script>\n  </head>\n  <body>\n    <script>\n      const content = document.createElement('div')\n      const win = new LunaWindow({\n        title: 'DevTools',\n        x: 50,\n        y: 50,\n        width: window.innerWidth - 100,\n        height: window.innerHeight - 100,\n        content,\n      })\n      win.show()\n      win.$container\n        .parent()\n        .addClass('__chobitsu-hide__')\n        .css('z-index', '200000')\n      eruda.init({\n        container: content,\n        inline: true,\n      })\n    </script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/manual.html",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <meta charset=\"UTF-8\" />\n    <meta\n      name=\"viewport\"\n      content=\"width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no\"\n    />\n    <title>Manual</title>\n    <link rel=\"stylesheet\" href=\"style.css\" />\n    <script src=\"assets/eruda.js\"></script>\n    <script src=\"util.js\"></script>\n  </head>\n  <body id=\"body\" class=\"test\">\n    <header>Manual Test</header>\n    <nav>\n      <ul>\n        <li>\n          <a href=\"#\" id=\"issue17\">#17</a>\n        </li>\n        <li>\n          <a href=\"#\" id=\"plugin\">Plugin</a>\n        </li>\n        <li>\n          <a href=\"#\" id=\"issue29\">#29</a>\n        </li>\n        <li>\n          <a href=\"#\" id=\"issue31\">#31</a>\n        </li>\n        <li>\n          <a href=\"#\" id=\"issue33\">#33</a>\n        </li>\n        <li>\n          <a href=\"#\" id=\"trigger-error\">Trigger Error</a>\n        </li>\n        <li>\n          <a href=\"#\" id=\"big-array\">Big Array</a>\n        </li>\n        <li>\n          <a href=\"#\" id=\"override-style\">Override Style</a>\n        </li>\n        <li>\n          <a href=\"#\" id=\"log\">Log</a>\n        </li>\n        <li>\n          <a href=\"#\" id=\"log-10000\">Log 10000</a>\n        </li>\n        <li>\n          <a href=\"#\" id=\"log-1000\">Log 1000</a>\n        </li>\n      </ul>\n    </nav>\n    <script>\n      function addClickEvent(id, listener) {\n        document.getElementById(id).addEventListener(\n          'click',\n          function (e) {\n            e.preventDefault()\n            listener()\n          },\n          false\n        )\n      }\n      addClickEvent('issue17', function () {\n        var B = function () {}\n        var A = function () {\n          this._data = 'eruda'\n        }\n        A.prototype = Object.create(B.prototype)\n        Object.defineProperty(A.prototype, 'data', {\n          get: function () {\n            return this._data\n          },\n        })\n        var a = new A()\n        console.log(a)\n      })\n      addClickEvent('plugin', function () {\n        eruda.add({ name: 'test' })\n        eruda.add(function (eruda) {\n          return {\n            name: 'test2',\n            init: function ($el) {\n              this._$el = $el\n              this._$el.html('This is the new plugin')\n            },\n          }\n        })\n        var Tool = eruda.Tool\n        eruda.add(\n          new (Tool.extend({\n            name: 'test plugin',\n            init: function ($el) {\n              this.callSuper(Tool, 'init', arguments)\n              this._$el.html('This is another new plugin')\n            },\n          }))()\n        )\n      })\n      addClickEvent('issue29', function () {\n        new Promise(function (resolve, reject) {\n          resolve()\n        }).then(function (res) {\n          var a = res.a\n        })\n      })\n      addClickEvent('issue31', function () {\n        addEventListener('resize', function () {})\n      })\n      addClickEvent('issue33', function () {\n        util.evalCss(':root {--test: 10px;}')\n      })\n      addClickEvent('trigger-error', function () {\n        triggerError()\n      })\n      addClickEvent('big-array', function () {\n        var arr = []\n        for (var i = 0; i < 10000; i++) {\n          arr.push(i)\n        }\n        console.log(arr)\n      })\n      addClickEvent('override-style', function () {\n        util.evalCss('.eruda-nav-bar {background: red !important;}')\n      })\n      addClickEvent('log', function () {\n        console.clear()\n        console.log('log')\n        console.log('number:', 5)\n        console.log('boolean:', true, false)\n        console.log('null:', null)\n        console.log('undefined:', undefined)\n        console.log('regexp:', /test/gi)\n        for (var i = 0; i < 10; i++) {\n          console.log('repeat log')\n        }\n        console.warn('warn')\n        console.error(Error('test'))\n        console.info('info')\n        console.debug('debug')\n        console.time('test')\n        console.timeEnd('test')\n        console.count('luna')\n        console.count('luna')\n        console.assert(true, 'assert msg')\n        var site1 = { name: 'Runoob', site: 'www.runoob.com' }\n        var site2 = { name: 'Google', site: 'www.google.com' }\n        var site3 = { name: 'Taobao', site: 'www.taobao.com' }\n        console.table([site1, site2, site3], ['site'])\n        var el = util.toEl(\n          '<div class=\"test\"><div class=\"test-inner\"></div></div>'\n        )\n        console.log('test dom', el)\n        console.dir(el)\n        console.log('%c Oh my heavens!', 'background: #222; color: #bada55')\n        console.log('This is the outer level')\n        console.group()\n        console.log('Level 2')\n        console.group()\n        console.log('Level 3')\n        console.warn('More of level 3')\n        console.groupEnd()\n        console.log('Back to level 2')\n        console.groupEnd()\n        console.log('Back to the outer level')\n        console.log(\n          'navigator: %o location: %o performance: %o',\n          navigator,\n          location,\n          performance\n        )\n        var arr = []\n        for (var i = 0; i < 10000; i++) arr.push(i)\n        console.log(arr)\n      })\n      addClickEvent('log-10000', function () {\n        for (var i = 0; i < 10000; i++) {\n          console.log(location, i)\n        }\n      })\n      addClickEvent('log-1000', function () {\n        for (var i = 0; i < 1000; i++) {\n          console.log(location, i)\n        }\n      })\n    </script>\n    <script>\n      eruda.init()\n      eruda.show()\n    </script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/network.html",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <meta charset=\"UTF-8\" />\n    <meta\n      name=\"viewport\"\n      content=\"width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no\"\n    />\n    <title>Network</title>\n    <link rel=\"stylesheet\" href=\"lib/jasmine.css\" />\n    <script src=\"lib/jquery.js\"></script>\n    <script src=\"lib/jasmine.js\"></script>\n    <script src=\"lib/jasmine-html.js\"></script>\n    <script src=\"assets/eruda.js\"></script>\n    <script src=\"boot.js\"></script>\n    <script src=\"util.js\"></script>\n  </head>\n  <body>\n    <script>\n      boot('network')\n    </script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/network.js",
    "content": "describe('network', function () {\n  beforeEach(function () {\n    eruda.show('network')\n  })\n\n  describe('request', function () {\n    it('xhr', function (done) {\n      $('.eruda-clear-xhr').click()\n      util.ajax.get(window.location.toString(), function () {\n        setTimeout(function () {\n          expect($('.eruda-requests .luna-data-grid-node')).toHaveLength(1)\n          done()\n        }, 500)\n      })\n    })\n  })\n})\n"
  },
  {
    "path": "test/resources.html",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <meta charset=\"UTF-8\" />\n    <meta\n      name=\"viewport\"\n      content=\"width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no\"\n    />\n    <title>Resources</title>\n    <link rel=\"stylesheet\" href=\"lib/jasmine.css\" />\n    <script src=\"lib/jquery.js\"></script>\n    <script src=\"lib/jasmine.js\"></script>\n    <script src=\"lib/jasmine-html.js\"></script>\n    <script src=\"assets/eruda.js\"></script>\n    <script src=\"boot.js\"></script>\n    <script src=\"util.js\"></script>\n  </head>\n  <body>\n    <script>\n      boot('resources')\n    </script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/resources.js",
    "content": "describe('resources', function () {\n  let $tool = $('.eruda-resources')\n\n  beforeEach(function () {\n    eruda.show('resources')\n  })\n\n  describe('localStorage', function () {\n    it('show', function () {\n      localStorage.clear()\n      localStorage.setItem('testKey', 'testVal')\n    })\n\n    it('clear', function () {\n      $tool.find('.eruda-local-storage .eruda-clear-storage').click()\n    })\n  })\n\n  describe('sessionStorage', function () {\n    it('show', function () {\n      sessionStorage.clear()\n      sessionStorage.setItem('testKey', 'testVal')\n    })\n\n    it('clear', function () {\n      $tool.find('.eruda-session-storage .eruda-clear-storage').click()\n    })\n  })\n\n  describe('cookie', function () {\n    it('show', function () {\n      util.cookie.set('testKey', 'testVal')\n      $tool.find('.eruda-refresh-cookie').click()\n    })\n\n    it('clear', function () {\n      $tool.find('.eruda-clear-cookie').click()\n    })\n  })\n})\n"
  },
  {
    "path": "test/settings.html",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <meta charset=\"UTF-8\" />\n    <meta\n      name=\"viewport\"\n      content=\"width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no\"\n    />\n    <title>Settings</title>\n    <link rel=\"stylesheet\" href=\"lib/jasmine.css\" />\n    <script src=\"lib/jquery.js\"></script>\n    <script src=\"lib/jasmine.js\"></script>\n    <script src=\"lib/jasmine-html.js\"></script>\n    <script src=\"assets/eruda.js\"></script>\n    <script src=\"boot.js\"></script>\n  </head>\n  <body>\n    <script>\n      boot('settings')\n    </script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/settings.js",
    "content": "describe('settings', function () {\n  let tool = eruda.get('settings')\n  let $tool = $('.eruda-settings')\n\n  let cfg = eruda.Settings.createCfg('test')\n  cfg.set({\n    testSwitch: false,\n    testSelect: '1',\n    testRange: 1,\n    testColor: '#fff',\n  })\n\n  beforeEach(function () {\n    tool.clear()\n  })\n\n  it('switch', function () {\n    let text = 'Test Switch'\n\n    tool.switch(cfg, 'testSwitch', text)\n  })\n\n  it('separator', function () {\n    tool.separator()\n  })\n\n  it('select', function () {\n    let text = 'Test Select'\n\n    tool.select(cfg, 'testSelect', text, ['1', '2', '3'])\n  })\n\n  it('range', function () {\n    let text = 'Test Range'\n\n    tool.range(cfg, 'testRange', text, { min: 0, max: 1, step: 0.1 })\n  })\n\n  it('remove', function () {\n    let text = 'Test Switch'\n    tool.switch(cfg, 'testSwitch', text)\n    tool.remove(cfg, 'testSwitch')\n  })\n})\n"
  },
  {
    "path": "test/snippets.html",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <meta charset=\"UTF-8\" />\n    <meta\n      name=\"viewport\"\n      content=\"width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no\"\n    />\n    <title>Snippets</title>\n    <link rel=\"stylesheet\" href=\"lib/jasmine.css\" />\n    <script src=\"lib/jquery.js\"></script>\n    <script src=\"lib/jasmine.js\"></script>\n    <script src=\"lib/jasmine-html.js\"></script>\n    <script src=\"assets/eruda.js\"></script>\n    <script src=\"boot.js\"></script>\n  </head>\n  <body>\n    <script>\n      boot('snippets')\n    </script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/snippets.js",
    "content": "describe('snippets', function () {\n  let tool = eruda.get('snippets')\n  let $tool = $('.eruda-snippets')\n\n  describe('default', function () {\n    it('border all', function () {\n      expect($tool.find('.eruda-name').eq(0)).toContainText('Border All')\n\n      let $body = $('body')\n      let $btn = $tool.find('.eruda-run').eq(0)\n\n      $btn.click()\n      expect($body).toHaveCss({ outlineWidth: '2px' })\n      $btn.click()\n      expect($body).toHaveCss({ outlineWidth: '0px' })\n    })\n\n    it('refresh page', function () {\n      expect($tool.find('.eruda-name').eq(1)).toContainText('Refresh Page')\n    })\n\n    it('search text', function () {\n      expect($tool.find('.eruda-name').eq(2)).toContainText('Search Text')\n    })\n\n    it('edit page', function () {\n      expect($tool.find('.eruda-name').eq(3)).toContainText('Edit Page')\n\n      let $body = $('body')\n      let $btn = $tool.find('.eruda-run').eq(3)\n\n      $btn.click()\n      expect($body).toHaveAttr('contenteditable', 'true')\n      $btn.click()\n      expect($body).toHaveAttr('contenteditable', 'false')\n    })\n  })\n\n  it('clear', function () {\n    tool.clear()\n    expect($tool.find('.eruda-name')).toHaveLength(0)\n  })\n\n  it('add', function () {\n    tool.add(\n      'Test',\n      function () {\n        console.log('eruda')\n      },\n      'This is the description'\n    )\n    expect($tool.find('.eruda-name')).toContainText('Test')\n    expect($tool.find('.eruda-description')).toContainText(\n      'This is the description'\n    )\n  })\n\n  it('remove', function () {\n    tool.remove('Test')\n    expect($tool.find('.eruda-name')).toHaveLength(0)\n  })\n})\n"
  },
  {
    "path": "test/sources.html",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <meta charset=\"UTF-8\" />\n    <meta\n      name=\"viewport\"\n      content=\"width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no\"\n    />\n    <title>Sources</title>\n    <link rel=\"stylesheet\" href=\"lib/jasmine.css\" />\n    <script src=\"lib/jquery.js\"></script>\n    <script src=\"lib/jasmine.js\"></script>\n    <script src=\"lib/jasmine-html.js\"></script>\n    <script src=\"assets/eruda.js\"></script>\n    <script src=\"boot.js\"></script>\n  </head>\n  <body>\n    <script>\n      boot('sources')\n    </script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/sources.js",
    "content": "describe('sources', function () {\n  let tool = eruda.get('sources')\n  let $tool = $('.eruda-sources')\n\n  beforeEach(function () {\n    eruda.show('sources')\n  })\n\n  it('raw', function () {\n    tool.set('raw', '/* test */')\n  })\n})\n"
  },
  {
    "path": "test/style.css",
    "content": "body, html {\n    padding: 0;\n    margin: 0;\n    font-family: 'Avenir Next', Avenir, 'Helvetica Neue', Helvetica, 'Franklin Gothic Medium', 'Franklin Gothic', 'ITC Franklin Gothic', Arial, sans-serif;\n}\n\nheader {\n    position: relative;\n    z-index: 15;\n    background: #eda29b;\n    text-align: center;\n    color: #fff;\n    padding: 10px;\n    font-size: 30px;\n    box-shadow: 0 2px 2px 0 rgba(0,0,0,.05),0 1px 4px 0 rgba(0,0,0,.2), 0 3px 1px -2px rgba(0,0,0,.1);\n}\n\nnav ul {\n    list-style: none;\n    padding: 0;\n    margin: 15px;\n}\n\nnav ul li {\n    background: #f2d367;\n    width: 50%;\n    float: left;\n}\n\nnav ul li:nth-child(4n), nav ul li:nth-child(4n-3) {\n    background: #e17555;\n}\n\nnav ul li a {\n    text-align: center;\n    width: 100%;\n    height: 100%;\n    box-sizing: border-box;\n    display: block;\n    padding: 10px;\n    color: #e07556;\n    font-size: 16px;\n    text-decoration: none;\n}\n\nnav ul li:nth-child(4n) a, nav ul li:nth-child(4n-3) a{\n    color: #9c3c53;\n}\n\n"
  },
  {
    "path": "test/util.js",
    "content": "// Built by eustia.\n(function(root, factory)\n{\n    if (typeof define === 'function' && define.amd)\n    {\n        define([], factory);\n    } else if (typeof module === 'object' && module.exports)\n    {\n        module.exports = factory();\n    } else { root.util = factory(); }\n}(this, function ()\n{\n    /* eslint-disable */\n\n    var _ = {};\n\n    if (typeof window === 'object' && window.util) _ = window.util;\n\n    /* ------------------------------ types ------------------------------ */\n\n    var types = _.types = (function (exports) {\n        /* Used for typescript definitions only.\n         */\n\n        /* typescript\n         * export declare namespace types {\n         *     interface Collection<T> {}\n         *     interface List<T> extends Collection<T> {\n         *         [index: number]: T;\n         *         length: number;\n         *     }\n         *     interface ListIterator<T, TResult> {\n         *         (value: T, index: number, list: List<T>): TResult;\n         *     }\n         *     interface Dictionary<T> extends Collection<T> {\n         *         [index: string]: T;\n         *     }\n         *     interface ObjectIterator<T, TResult> {\n         *         (element: T, key: string, list: Dictionary<T>): TResult;\n         *     }\n         *     interface MemoIterator<T, TResult> {\n         *         (prev: TResult, curr: T, index: number, list: List<T>): TResult;\n         *     }\n         *     interface MemoObjectIterator<T, TResult> {\n         *         (prev: TResult, curr: T, key: string, list: Dictionary<T>): TResult;\n         *     }\n         *     type Fn<T> = (...args: any[]) => T;\n         *     type AnyFn = Fn<any>;\n         *     type PlainObj<T> = { [name: string]: T };\n         * }\n         * export declare const types: {};\n         */\n        exports = {};\n\n        return exports;\n    })({});\n\n    /* ------------------------------ noop ------------------------------ */\n\n    var noop = _.noop = (function (exports) {\n        /* A no-operation function.\n         */\n\n        /* example\n         * noop(); // Does nothing\n         */\n\n        /* typescript\n         * export declare function noop(): void;\n         */\n        exports = function() {};\n\n        return exports;\n    })({});\n\n    /* ------------------------------ isObj ------------------------------ */\n\n    var isObj = _.isObj = (function (exports) {\n        /* Check if value is the language type of Object.\n         *\n         * |Name  |Desc                      |\n         * |------|--------------------------|\n         * |val   |Value to check            |\n         * |return|True if value is an object|\n         *\n         * [Language Spec](http://www.ecma-international.org/ecma-262/6.0/#sec-ecmascript-language-types)\n         */\n\n        /* example\n         * isObj({}); // -> true\n         * isObj([]); // -> true\n         */\n\n        /* typescript\n         * export declare function isObj(val: any): boolean;\n         */\n        exports = function(val) {\n            var type = typeof val;\n            return !!val && (type === 'function' || type === 'object');\n        };\n\n        return exports;\n    })({});\n\n    /* ------------------------------ has ------------------------------ */\n\n    var has = _.has = (function (exports) {\n        /* Checks if key is a direct property.\n         *\n         * |Name  |Desc                            |\n         * |------|--------------------------------|\n         * |obj   |Object to query                 |\n         * |key   |Path to check                   |\n         * |return|True if key is a direct property|\n         */\n\n        /* example\n         * has({ one: 1 }, 'one'); // -> true\n         */\n\n        /* typescript\n         * export declare function has(obj: {}, key: string): boolean;\n         */\n        var hasOwnProp = Object.prototype.hasOwnProperty;\n\n        exports = function(obj, key) {\n            return hasOwnProp.call(obj, key);\n        };\n\n        return exports;\n    })({});\n\n    /* ------------------------------ keys ------------------------------ */\n\n    var keys = _.keys = (function (exports) {\n        /* Create an array of the own enumerable property names of object.\n         *\n         * |Name  |Desc                   |\n         * |------|-----------------------|\n         * |obj   |Object to query        |\n         * |return|Array of property names|\n         */\n\n        /* example\n         * keys({ a: 1 }); // -> ['a']\n         */\n\n        /* typescript\n         * export declare function keys(obj: any): string[];\n         */\n\n        /* dependencies\n         * has \n         */\n\n        if (Object.keys && !false) {\n            exports = Object.keys;\n        } else {\n            exports = function(obj) {\n                var ret = [];\n\n                for (var key in obj) {\n                    if (has(obj, key)) ret.push(key);\n                }\n\n                return ret;\n            };\n        }\n\n        return exports;\n    })({});\n\n    /* ------------------------------ chunk ------------------------------ */\n\n    var chunk = _.chunk = (function (exports) {\n        /* Split array into groups the length of given size.\n         *\n         * |Name  |Desc                |\n         * |------|--------------------|\n         * |arr   |Array to process    |\n         * |size=1|Length of each chunk|\n         * |return|Chunks of given size|\n         */\n\n        /* example\n         * chunk([1, 2, 3, 4], 2); // -> [[1, 2], [3, 4]]\n         * chunk([1, 2, 3, 4], 3); // -> [[1, 2, 3], [4]]\n         * chunk([1, 2, 3, 4]); // -> [[1], [2], [3], [4]]\n         */\n\n        /* typescript\n         * export declare function chunk(arr: any[], size?: number): Array<any[]>;\n         */\n        exports = function(arr, size) {\n            var ret = [];\n            size = size || 1;\n\n            for (var i = 0, len = Math.ceil(arr.length / size); i < len; i++) {\n                var start = i * size;\n                var end = start + size;\n                ret.push(arr.slice(start, end));\n            }\n\n            return ret;\n        };\n\n        return exports;\n    })({});\n\n    /* ------------------------------ idxOf ------------------------------ */\n\n    var idxOf = _.idxOf = (function (exports) {\n        /* Get the index at which the first occurrence of value.\n         *\n         * |Name     |Desc                |\n         * |---------|--------------------|\n         * |arr      |Array to search     |\n         * |val      |Value to search for |\n         * |fromIdx=0|Index to search from|\n         * |return   |Value index         |\n         */\n\n        /* example\n         * idxOf([1, 2, 1, 2], 2, 2); // -> 3\n         */\n\n        /* typescript\n         * export declare function idxOf(arr: any[], val: any, fromIdx?: number): number;\n         */\n        exports = function(arr, val, fromIdx) {\n            return Array.prototype.indexOf.call(arr, val, fromIdx);\n        };\n\n        return exports;\n    })({});\n\n    /* ------------------------------ isUndef ------------------------------ */\n\n    var isUndef = _.isUndef = (function (exports) {\n        /* Check if value is undefined.\n         *\n         * |Name  |Desc                      |\n         * |------|--------------------------|\n         * |val   |Value to check            |\n         * |return|True if value is undefined|\n         */\n\n        /* example\n         * isUndef(void 0); // -> true\n         * isUndef(null); // -> false\n         */\n\n        /* typescript\n         * export declare function isUndef(val: any): boolean;\n         */\n        exports = function(val) {\n            return val === void 0;\n        };\n\n        return exports;\n    })({});\n\n    /* ------------------------------ create ------------------------------ */\n\n    var create = _.create = (function (exports) {\n        /* Create new object using given object as prototype.\n         *\n         * |Name  |Desc                   |\n         * |------|-----------------------|\n         * |proto |Prototype of new object|\n         * |return|Created object         |\n         */\n\n        /* example\n         * const obj = create({ a: 1 });\n         * console.log(obj.a); // -> 1\n         */\n\n        /* typescript\n         * export declare function create(proto?: object): any;\n         */\n\n        /* dependencies\n         * isObj \n         */\n\n        exports = function(proto) {\n            if (!isObj(proto)) return {};\n            if (objCreate && !false) return objCreate(proto);\n\n            function noop() {}\n\n            noop.prototype = proto;\n            return new noop();\n        };\n\n        var objCreate = Object.create;\n\n        return exports;\n    })({});\n\n    /* ------------------------------ inherits ------------------------------ */\n\n    var inherits = _.inherits = (function (exports) {\n        /* Inherit the prototype methods from one constructor into another.\n         *\n         * |Name      |Desc       |\n         * |----------|-----------|\n         * |Class     |Child Class|\n         * |SuperClass|Super Class|\n         */\n\n        /* example\n         * function People(name) {\n         *     this._name = name;\n         * }\n         * People.prototype = {\n         *     getName: function() {\n         *         return this._name;\n         *     }\n         * };\n         * function Student(name) {\n         *     this._name = name;\n         * }\n         * inherits(Student, People);\n         * const s = new Student('RedHood');\n         * s.getName(); // -> 'RedHood'\n         */\n\n        /* typescript\n         * export declare function inherits(\n         *     Class: types.AnyFn,\n         *     SuperClass: types.AnyFn\n         * ): void;\n         */\n\n        /* dependencies\n         * create types \n         */\n\n        exports = function(Class, SuperClass) {\n            Class.prototype = create(SuperClass.prototype);\n        };\n\n        return exports;\n    })({});\n\n    /* ------------------------------ restArgs ------------------------------ */\n\n    var restArgs = _.restArgs = (function (exports) {\n        /* This accumulates the arguments passed into an array, after a given index.\n         *\n         * |Name      |Desc                                   |\n         * |----------|---------------------------------------|\n         * |function  |Function that needs rest parameters    |\n         * |startIndex|The start index to accumulates         |\n         * |return    |Generated function with rest parameters|\n         */\n\n        /* example\n         * const paramArr = restArgs(function(rest) {\n         *     return rest;\n         * });\n         * paramArr(1, 2, 3, 4); // -> [1, 2, 3, 4]\n         */\n\n        /* typescript\n         * export declare function restArgs(\n         *     fn: types.AnyFn,\n         *     startIndex?: number\n         * ): types.AnyFn;\n         */\n\n        /* dependencies\n         * types \n         */\n\n        exports = function(fn, startIdx) {\n            startIdx = startIdx == null ? fn.length - 1 : +startIdx;\n            return function() {\n                var len = Math.max(arguments.length - startIdx, 0);\n                var rest = new Array(len);\n                var i;\n\n                for (i = 0; i < len; i++) {\n                    rest[i] = arguments[i + startIdx];\n                } // Call runs faster than apply.\n\n                switch (startIdx) {\n                    case 0:\n                        return fn.call(this, rest);\n\n                    case 1:\n                        return fn.call(this, arguments[0], rest);\n\n                    case 2:\n                        return fn.call(this, arguments[0], arguments[1], rest);\n                }\n\n                var args = new Array(startIdx + 1);\n\n                for (i = 0; i < startIdx; i++) {\n                    args[i] = arguments[i];\n                }\n\n                args[startIdx] = rest;\n                return fn.apply(this, args);\n            };\n        };\n\n        return exports;\n    })({});\n\n    /* ------------------------------ optimizeCb ------------------------------ */\n\n    var optimizeCb = _.optimizeCb = (function (exports) {\n        /* Used for function context binding.\n         */\n\n        /* typescript\n         * export declare function optimizeCb(\n         *     fn: types.AnyFn,\n         *     ctx: any,\n         *     argCount?: number\n         * ): types.AnyFn;\n         */\n\n        /* dependencies\n         * isUndef types \n         */\n\n        exports = function(fn, ctx, argCount) {\n            if (isUndef(ctx)) return fn;\n\n            switch (argCount == null ? 3 : argCount) {\n                case 1:\n                    return function(val) {\n                        return fn.call(ctx, val);\n                    };\n\n                case 3:\n                    return function(val, idx, collection) {\n                        return fn.call(ctx, val, idx, collection);\n                    };\n\n                case 4:\n                    return function(accumulator, val, idx, collection) {\n                        return fn.call(ctx, accumulator, val, idx, collection);\n                    };\n            }\n\n            return function() {\n                return fn.apply(ctx, arguments);\n            };\n        };\n\n        return exports;\n    })({});\n\n    /* ------------------------------ endWith ------------------------------ */\n\n    var endWith = _.endWith = (function (exports) {\n        /* Check if string ends with the given target string.\n         *\n         * |Name  |Desc                           |\n         * |------|-------------------------------|\n         * |str   |The string to search           |\n         * |suffix|String suffix                  |\n         * |return|True if string ends with target|\n         */\n\n        /* example\n         * endWith('ab', 'b'); // -> true\n         */\n\n        /* typescript\n         * export declare function endWith(str: string, suffix: string): boolean;\n         */\n        exports = function(str, suffix) {\n            var idx = str.length - suffix.length;\n            return idx >= 0 && str.indexOf(suffix, idx) === idx;\n        };\n\n        return exports;\n    })({});\n\n    /* ------------------------------ toStr ------------------------------ */\n\n    var toStr = _.toStr = (function (exports) {\n        /* Convert value to a string.\n         *\n         * |Name  |Desc            |\n         * |------|----------------|\n         * |val   |Value to convert|\n         * |return|Result string   |\n         */\n\n        /* example\n         * toStr(null); // -> ''\n         * toStr(1); // -> '1'\n         * toStr(false); // -> 'false'\n         * toStr([1, 2, 3]); // -> '1,2,3'\n         */\n\n        /* typescript\n         * export declare function toStr(val: any): string;\n         */\n        exports = function(val) {\n            return val == null ? '' : val.toString();\n        };\n\n        return exports;\n    })({});\n\n    /* ------------------------------ escapeJsStr ------------------------------ */\n\n    var escapeJsStr = _.escapeJsStr = (function (exports) {\n        /* Escape string to be a valid JavaScript string literal between quotes.\n         *\n         * http://www.ecma-international.org/ecma-262/5.1/#sec-7.8.4\n         *\n         * |Name  |Desc            |\n         * |------|----------------|\n         * |str   |String to escape|\n         * |return|Escaped string  |\n         */\n\n        /* example\n         * escapeJsStr('\"\\n'); // -> '\\\\\"\\\\\\\\n'\n         */\n\n        /* typescript\n         * export declare function escapeJsStr(str: string): string;\n         */\n\n        /* dependencies\n         * toStr \n         */\n\n        exports = function(str) {\n            return toStr(str).replace(regEscapeChars, function(char) {\n                switch (char) {\n                    case '\"':\n                    case \"'\":\n                    case '\\\\':\n                        return '\\\\' + char;\n\n                    case '\\n':\n                        return '\\\\n';\n\n                    case '\\r':\n                        return '\\\\r';\n                    // Line separator\n\n                    case '\\u2028':\n                        return '\\\\u2028';\n                    // Paragraph separator\n\n                    case '\\u2029':\n                        return '\\\\u2029';\n                }\n            });\n        };\n\n        var regEscapeChars = /[\"'\\\\\\n\\r\\u2028\\u2029]/g;\n\n        return exports;\n    })({});\n\n    /* ------------------------------ evalCss ------------------------------ */\n    _.evalCss = (function (exports) {\n        /* Load css into page.\n         *\n         * |Name  |Desc         |\n         * |------|-------------|\n         * |css   |Css code     |\n         * |return|Style element|\n         */\n\n        /* example\n         * evalCss('body{background:#08c}');\n         */\n\n        /* typescript\n         * export declare function evalCss(css: string): HTMLStyleElement;\n         */\n        exports = function(css) {\n            var style = document.createElement('style');\n            style.textContent = css;\n            style.type = 'text/css';\n            document.head.appendChild(style);\n            return style;\n        };\n\n        return exports;\n    })({});\n\n    /* ------------------------------ identity ------------------------------ */\n\n    var identity = _.identity = (function (exports) {\n        /* Return the first argument given.\n         *\n         * |Name  |Desc       |\n         * |------|-----------|\n         * |val   |Any value  |\n         * |return|Given value|\n         */\n\n        /* example\n         * identity('a'); // -> 'a'\n         */\n\n        /* typescript\n         * export declare function identity<T>(val: T): T;\n         */\n        exports = function(val) {\n            return val;\n        };\n\n        return exports;\n    })({});\n\n    /* ------------------------------ objToStr ------------------------------ */\n\n    var objToStr = _.objToStr = (function (exports) {\n        /* Alias of Object.prototype.toString.\n         *\n         * |Name  |Desc                                |\n         * |------|------------------------------------|\n         * |val   |Source value                        |\n         * |return|String representation of given value|\n         */\n\n        /* example\n         * objToStr(5); // -> '[object Number]'\n         */\n\n        /* typescript\n         * export declare function objToStr(val: any): string;\n         */\n        var ObjToStr = Object.prototype.toString;\n\n        exports = function(val) {\n            return ObjToStr.call(val);\n        };\n\n        return exports;\n    })({});\n\n    /* ------------------------------ isArgs ------------------------------ */\n\n    var isArgs = _.isArgs = (function (exports) {\n        /* Check if value is classified as an arguments object.\n         *\n         * |Name  |Desc                                |\n         * |------|------------------------------------|\n         * |val   |Value to check                      |\n         * |return|True if value is an arguments object|\n         */\n\n        /* example\n         * isArgs(\n         *     (function() {\n         *         return arguments;\n         *     })()\n         * ); // -> true\n         */\n\n        /* typescript\n         * export declare function isArgs(val: any): boolean;\n         */\n\n        /* dependencies\n         * objToStr \n         */\n\n        exports = function(val) {\n            return objToStr(val) === '[object Arguments]';\n        };\n\n        return exports;\n    })({});\n\n    /* ------------------------------ isArr ------------------------------ */\n\n    var isArr = _.isArr = (function (exports) {\n        /* Check if value is an `Array` object.\n         *\n         * |Name  |Desc                              |\n         * |------|----------------------------------|\n         * |val   |Value to check                    |\n         * |return|True if value is an `Array` object|\n         */\n\n        /* example\n         * isArr([]); // -> true\n         * isArr({}); // -> false\n         */\n\n        /* typescript\n         * export declare function isArr(val: any): boolean;\n         */\n\n        /* dependencies\n         * objToStr \n         */\n\n        if (Array.isArray && !false) {\n            exports = Array.isArray;\n        } else {\n            exports = function(val) {\n                return objToStr(val) === '[object Array]';\n            };\n        }\n\n        return exports;\n    })({});\n\n    /* ------------------------------ castPath ------------------------------ */\n\n    var castPath = _.castPath = (function (exports) {\n        /* Cast value into a property path array.\n         *\n         * |Name  |Desc               |\n         * |------|-------------------|\n         * |path  |Value to inspect   |\n         * |obj   |Object to query    |\n         * |return|Property path array|\n         */\n\n        /* example\n         * castPath('a.b.c'); // -> ['a', 'b', 'c']\n         * castPath(['a']); // -> ['a']\n         * castPath('a[0].b'); // -> ['a', '0', 'b']\n         * castPath('a.b.c', { 'a.b.c': true }); // -> ['a.b.c']\n         */\n\n        /* typescript\n         * export declare function castPath(path: string | string[], obj?: any): string[];\n         */\n\n        /* dependencies\n         * has isArr \n         */\n\n        exports = function(str, obj) {\n            if (isArr(str)) return str;\n            if (obj && has(obj, str)) return [str];\n            var ret = [];\n            str.replace(regPropName, function(match, number, quote, str) {\n                ret.push(quote ? str.replace(regEscapeChar, '$1') : number || match);\n            });\n            return ret;\n        }; // Lodash _stringToPath\n\n        var regPropName = /[^.[\\]]+|\\[(?:(-?\\d+(?:\\.\\d+)?)|([\"'])((?:(?!\\2)[^\\\\]|\\\\.)*?)\\2)\\]|(?=(?:\\.|\\[\\])(?:\\.|\\[\\]|$))/g;\n        var regEscapeChar = /\\\\(\\\\)?/g;\n\n        return exports;\n    })({});\n\n    /* ------------------------------ safeGet ------------------------------ */\n\n    var safeGet = _.safeGet = (function (exports) {\n        /* Get object property, don't throw undefined error.\n         *\n         * |Name  |Desc                     |\n         * |------|-------------------------|\n         * |obj   |Object to query          |\n         * |path  |Path of property to get  |\n         * |return|Target value or undefined|\n         */\n\n        /* example\n         * const obj = { a: { aa: { aaa: 1 } } };\n         * safeGet(obj, 'a.aa.aaa'); // -> 1\n         * safeGet(obj, ['a', 'aa']); // -> {aaa: 1}\n         * safeGet(obj, 'a.b'); // -> undefined\n         */\n\n        /* typescript\n         * export declare function safeGet(obj: any, path: string | string[]): any;\n         */\n\n        /* dependencies\n         * isUndef castPath \n         */\n\n        exports = function(obj, path) {\n            path = castPath(path, obj);\n            var prop;\n            prop = path.shift();\n\n            while (!isUndef(prop)) {\n                obj = obj[prop];\n                if (obj == null) return;\n                prop = path.shift();\n            }\n\n            return obj;\n        };\n\n        return exports;\n    })({});\n\n    /* ------------------------------ flatten ------------------------------ */\n\n    var flatten = _.flatten = (function (exports) {\n        /* Recursively flatten an array.\n         *\n         * |Name  |Desc               |\n         * |------|-------------------|\n         * |arr   |Array to flatten   |\n         * |return|New flattened array|\n         */\n\n        /* example\n         * flatten(['a', ['b', ['c']], 'd', ['e']]); // -> ['a', 'b', 'c', 'd', 'e']\n         */\n\n        /* typescript\n         * export declare function flatten(arr: any[]): any[];\n         */\n\n        /* dependencies\n         * isArr \n         */\n\n        exports = function(arr) {\n            return flat(arr, []);\n        };\n\n        function flat(arr, res) {\n            var len = arr.length,\n                i = -1,\n                cur;\n\n            while (len--) {\n                cur = arr[++i];\n                isArr(cur) ? flat(cur, res) : res.push(cur);\n            }\n\n            return res;\n        }\n\n        return exports;\n    })({});\n\n    /* ------------------------------ isFn ------------------------------ */\n\n    var isFn = _.isFn = (function (exports) {\n        /* Check if value is a function.\n         *\n         * |Name  |Desc                       |\n         * |------|---------------------------|\n         * |val   |Value to check             |\n         * |return|True if value is a function|\n         *\n         * Generator function is also classified as true.\n         */\n\n        /* example\n         * isFn(function() {}); // -> true\n         * isFn(function*() {}); // -> true\n         * isFn(async function() {}); // -> true\n         */\n\n        /* typescript\n         * export declare function isFn(val: any): boolean;\n         */\n\n        /* dependencies\n         * objToStr \n         */\n\n        exports = function(val) {\n            var objStr = objToStr(val);\n            return (\n                objStr === '[object Function]' ||\n                objStr === '[object GeneratorFunction]' ||\n                objStr === '[object AsyncFunction]'\n            );\n        };\n\n        return exports;\n    })({});\n\n    /* ------------------------------ getProto ------------------------------ */\n\n    var getProto = _.getProto = (function (exports) {\n        /* Get prototype of an object.\n         *\n         * |Name  |Desc                                         |\n         * |------|---------------------------------------------|\n         * |obj   |Target object                                |\n         * |return|Prototype of given object, null if not exists|\n         */\n\n        /* example\n         * const a = {};\n         * getProto(Object.create(a)); // -> a\n         */\n\n        /* typescript\n         * export declare function getProto(obj: any): any;\n         */\n\n        /* dependencies\n         * isObj isFn \n         */\n\n        var getPrototypeOf = Object.getPrototypeOf;\n        var ObjectCtr = {}.constructor;\n\n        exports = function(obj) {\n            if (!isObj(obj)) return;\n            if (getPrototypeOf && !false) return getPrototypeOf(obj);\n            var proto = obj.__proto__;\n            if (proto || proto === null) return proto;\n            if (isFn(obj.constructor)) return obj.constructor.prototype;\n            if (obj instanceof ObjectCtr) return ObjectCtr.prototype;\n        };\n\n        return exports;\n    })({});\n\n    /* ------------------------------ isMiniProgram ------------------------------ */\n\n    var isMiniProgram = _.isMiniProgram = (function (exports) {\n        /* Check if running in wechat mini program.\n         */\n\n        /* example\n         * console.log(isMiniProgram); // -> true if running in mini program.\n         */\n\n        /* typescript\n         * export declare const isMiniProgram: boolean;\n         */\n\n        /* dependencies\n         * isFn \n         */\n        /* eslint-disable no-undef */\n\n        exports = typeof wx !== 'undefined' && isFn(wx.openLocation);\n\n        return exports;\n    })({});\n\n    /* ------------------------------ isNum ------------------------------ */\n\n    var isNum = _.isNum = (function (exports) {\n        /* Check if value is classified as a Number primitive or object.\n         *\n         * |Name  |Desc                                 |\n         * |------|-------------------------------------|\n         * |val   |Value to check                       |\n         * |return|True if value is correctly classified|\n         */\n\n        /* example\n         * isNum(5); // -> true\n         * isNum(5.1); // -> true\n         * isNum({}); // -> false\n         */\n\n        /* typescript\n         * export declare function isNum(val: any): boolean;\n         */\n\n        /* dependencies\n         * objToStr \n         */\n\n        exports = function(val) {\n            return objToStr(val) === '[object Number]';\n        };\n\n        return exports;\n    })({});\n\n    /* ------------------------------ isArrLike ------------------------------ */\n\n    var isArrLike = _.isArrLike = (function (exports) {\n        /* Check if value is array-like.\n         *\n         * |Name  |Desc                       |\n         * |------|---------------------------|\n         * |val   |Value to check             |\n         * |return|True if value is array like|\n         *\n         * Function returns false.\n         */\n\n        /* example\n         * isArrLike('test'); // -> true\n         * isArrLike(document.body.children); // -> true;\n         * isArrLike([1, 2, 3]); // -> true\n         */\n\n        /* typescript\n         * export declare function isArrLike(val: any): boolean;\n         */\n\n        /* dependencies\n         * isNum isFn \n         */\n\n        var MAX_ARR_IDX = Math.pow(2, 53) - 1;\n\n        exports = function(val) {\n            if (!val) return false;\n            var len = val.length;\n            return isNum(len) && len >= 0 && len <= MAX_ARR_IDX && !isFn(val);\n        };\n\n        return exports;\n    })({});\n\n    /* ------------------------------ each ------------------------------ */\n\n    var each = _.each = (function (exports) {\n        /* Iterate over elements of collection and invokes iterator for each element.\n         *\n         * |Name    |Desc                          |\n         * |--------|------------------------------|\n         * |obj     |Collection to iterate over    |\n         * |iterator|Function invoked per iteration|\n         * |ctx     |Function context              |\n         */\n\n        /* example\n         * each({ a: 1, b: 2 }, function(val, key) {});\n         */\n\n        /* typescript\n         * export declare function each<T>(\n         *     list: types.List<T>,\n         *     iterator: types.ListIterator<T, void>,\n         *     ctx?: any\n         * ): types.List<T>;\n         * export declare function each<T>(\n         *     object: types.Dictionary<T>,\n         *     iterator: types.ObjectIterator<T, void>,\n         *     ctx?: any\n         * ): types.Collection<T>;\n         */\n\n        /* dependencies\n         * isArrLike keys optimizeCb types \n         */\n\n        exports = function(obj, iterator, ctx) {\n            iterator = optimizeCb(iterator, ctx);\n            var i, len;\n\n            if (isArrLike(obj)) {\n                for (i = 0, len = obj.length; i < len; i++) {\n                    iterator(obj[i], i, obj);\n                }\n            } else {\n                var _keys = keys(obj);\n\n                for (i = 0, len = _keys.length; i < len; i++) {\n                    iterator(obj[_keys[i]], _keys[i], obj);\n                }\n            }\n\n            return obj;\n        };\n\n        return exports;\n    })({});\n\n    /* ------------------------------ createAssigner ------------------------------ */\n\n    var createAssigner = _.createAssigner = (function (exports) {\n        /* Used to create extend, extendOwn and defaults.\n         *\n         * |Name    |Desc                          |\n         * |--------|------------------------------|\n         * |keysFn  |Function to get object keys   |\n         * |defaults|No override when set to true  |\n         * |return  |Result function, extend...    |\n         */\n\n        /* typescript\n         * export declare function createAssigner(\n         *     keysFn: types.AnyFn,\n         *     defaults: boolean\n         * ): types.AnyFn;\n         */\n\n        /* dependencies\n         * isUndef each types \n         */\n\n        exports = function(keysFn, defaults) {\n            return function(obj) {\n                each(arguments, function(src, idx) {\n                    if (idx === 0) return;\n                    var keys = keysFn(src);\n                    each(keys, function(key) {\n                        if (!defaults || isUndef(obj[key])) obj[key] = src[key];\n                    });\n                });\n                return obj;\n            };\n        };\n\n        return exports;\n    })({});\n\n    /* ------------------------------ extendOwn ------------------------------ */\n\n    var extendOwn = _.extendOwn = (function (exports) {\n        /* Like extend, but only copies own properties over to the destination object.\n         *\n         * |Name       |Desc              |\n         * |-----------|------------------|\n         * |destination|Destination object|\n         * |...sources |Sources objects   |\n         * |return     |Destination object|\n         */\n\n        /* example\n         * extendOwn({ name: 'RedHood' }, { age: 24 }); // -> {name: 'RedHood', age: 24}\n         */\n\n        /* typescript\n         * export declare function extendOwn(destination: any, ...sources: any[]): any;\n         */\n\n        /* dependencies\n         * keys createAssigner \n         */\n\n        exports = createAssigner(keys);\n\n        return exports;\n    })({});\n\n    /* ------------------------------ values ------------------------------ */\n\n    var values = _.values = (function (exports) {\n        /* Create an array of the own enumerable property values of object.\n         *\n         * |Name  |Desc                    |\n         * |------|------------------------|\n         * |obj   |Object to query         |\n         * |return|Array of property values|\n         */\n\n        /* example\n         * values({ one: 1, two: 2 }); // -> [1, 2]\n         */\n\n        /* typescript\n         * export declare function values(obj: any): any[];\n         */\n\n        /* dependencies\n         * each \n         */\n\n        exports = function(obj) {\n            var ret = [];\n            each(obj, function(val) {\n                ret.push(val);\n            });\n            return ret;\n        };\n\n        return exports;\n    })({});\n\n    /* ------------------------------ isStr ------------------------------ */\n\n    var isStr = _.isStr = (function (exports) {\n        /* Check if value is a string primitive.\n         *\n         * |Name  |Desc                               |\n         * |------|-----------------------------------|\n         * |val   |Value to check                     |\n         * |return|True if value is a string primitive|\n         */\n\n        /* example\n         * isStr('licia'); // -> true\n         */\n\n        /* typescript\n         * export declare function isStr(val: any): boolean;\n         */\n\n        /* dependencies\n         * objToStr \n         */\n\n        exports = function(val) {\n            return objToStr(val) === '[object String]';\n        };\n\n        return exports;\n    })({});\n\n    /* ------------------------------ contain ------------------------------ */\n\n    var contain = _.contain = (function (exports) {\n        /* Check if the value is present in the list.\n         *\n         * |Name  |Desc                                |\n         * |------|------------------------------------|\n         * |target|Target object                       |\n         * |val   |Value to check                      |\n         * |return|True if value is present in the list|\n         */\n\n        /* example\n         * contain([1, 2, 3], 1); // -> true\n         * contain({ a: 1, b: 2 }, 1); // -> true\n         * contain('abc', 'a'); // -> true\n         */\n\n        /* typescript\n         * export declare function contain(arr: any[] | {} | string, val: any): boolean;\n         */\n\n        /* dependencies\n         * idxOf isStr isArrLike values \n         */\n\n        exports = function(arr, val) {\n            if (isStr(arr)) return arr.indexOf(val) > -1;\n            if (!isArrLike(arr)) arr = values(arr);\n            return idxOf(arr, val) >= 0;\n        };\n\n        return exports;\n    })({});\n\n    /* ------------------------------ defineProp ------------------------------ */\n\n    var defineProp = _.defineProp = (function (exports) {\n        /* Shortcut for Object.defineProperty(defineProperties).\n         *\n         * |Name      |Desc               |\n         * |----------|-------------------|\n         * |obj       |Object to define   |\n         * |prop      |Property path      |\n         * |descriptor|Property descriptor|\n         * |return    |Object itself      |\n         *\n         * |Name  |Desc                |\n         * |------|--------------------|\n         * |obj   |Object to define    |\n         * |prop  |Property descriptors|\n         * |return|Object itself       |\n         */\n\n        /* example\n         * const obj = { b: { c: 3 }, d: 4, e: 5 };\n         * defineProp(obj, 'a', {\n         *     get: function() {\n         *         return this.e * 2;\n         *     }\n         * });\n         * // obj.a is equal to 10\n         * defineProp(obj, 'b.c', {\n         *     set: function(val) {\n         *         // this is pointed to obj.b\n         *         this.e = val;\n         *     }.bind(obj)\n         * });\n         * obj.b.c = 2;\n         * // obj.a is equal to 4\n         *\n         * const obj2 = { a: 1, b: 2, c: 3 };\n         * defineProp(obj2, {\n         *     a: {\n         *         get: function() {\n         *             return this.c;\n         *         }\n         *     },\n         *     b: {\n         *         set: function(val) {\n         *             this.c = val / 2;\n         *         }\n         *     }\n         * });\n         * // obj2.a is equal to 3\n         * obj2.b = 4;\n         * // obj2.a is equal to 2\n         */\n\n        /* typescript\n         * export declare function defineProp<T>(\n         *     obj: T,\n         *     prop: string,\n         *     descriptor: PropertyDescriptor\n         * ): T;\n         * export declare function defineProp<T>(\n         *     obj: T,\n         *     descriptor: PropertyDescriptorMap\n         * ): T;\n         */\n\n        /* dependencies\n         * castPath isStr isObj each \n         */\n\n        exports = function(obj, prop, descriptor) {\n            if (isStr(prop)) {\n                defineProp(obj, prop, descriptor);\n            } else if (isObj(prop)) {\n                each(prop, function(descriptor, prop) {\n                    defineProp(obj, prop, descriptor);\n                });\n            }\n\n            return obj;\n        };\n\n        function defineProp(obj, prop, descriptor) {\n            var path = castPath(prop, obj);\n            var lastProp = path.pop();\n            /* eslint-disable no-cond-assign */\n\n            while ((prop = path.shift())) {\n                if (!obj[prop]) obj[prop] = {};\n                obj = obj[prop];\n            }\n\n            Object.defineProperty(obj, lastProp, descriptor);\n        }\n\n        return exports;\n    })({});\n\n    /* ------------------------------ isBuffer ------------------------------ */\n\n    var isBuffer = _.isBuffer = (function (exports) {\n        /* Check if value is a buffer.\n         *\n         * |Name  |Desc                     |\n         * |------|-------------------------|\n         * |val   |The value to check       |\n         * |return|True if value is a buffer|\n         */\n\n        /* example\n         * isBuffer(new Buffer(4)); // -> true\n         */\n\n        /* typescript\n         * export declare function isBuffer(val: any): boolean;\n         */\n\n        /* dependencies\n         * isFn \n         */\n\n        exports = function(val) {\n            if (val == null) return false;\n            if (val._isBuffer) return true;\n            return (\n                val.constructor &&\n                isFn(val.constructor.isBuffer) &&\n                val.constructor.isBuffer(val)\n            );\n        };\n\n        return exports;\n    })({});\n\n    /* ------------------------------ isEmpty ------------------------------ */\n\n    var isEmpty = _.isEmpty = (function (exports) {\n        /* Check if value is an empty object or array.\n         *\n         * |Name  |Desc                  |\n         * |------|----------------------|\n         * |val   |Value to check        |\n         * |return|True if value is empty|\n         */\n\n        /* example\n         * isEmpty([]); // -> true\n         * isEmpty({}); // -> true\n         * isEmpty(''); // -> true\n         */\n\n        /* typescript\n         * export declare function isEmpty(val: any): boolean;\n         */\n\n        /* dependencies\n         * isArrLike isArr isStr isArgs keys \n         */\n\n        exports = function(val) {\n            if (val == null) return true;\n\n            if (isArrLike(val) && (isArr(val) || isStr(val) || isArgs(val))) {\n                return val.length === 0;\n            }\n\n            return keys(val).length === 0;\n        };\n\n        return exports;\n    })({});\n\n    /* ------------------------------ isMatch ------------------------------ */\n\n    var isMatch = _.isMatch = (function (exports) {\n        /* Check if keys and values in src are contained in obj.\n         *\n         * |Name  |Desc                              |\n         * |------|----------------------------------|\n         * |obj   |Object to inspect                 |\n         * |src   |Object of property values to match|\n         * |return|True if object is match           |\n         */\n\n        /* example\n         * isMatch({ a: 1, b: 2 }, { a: 1 }); // -> true\n         */\n\n        /* typescript\n         * export declare function isMatch(obj: any, src: any): boolean;\n         */\n\n        /* dependencies\n         * keys \n         */\n\n        exports = function(obj, src) {\n            var _keys = keys(src);\n\n            var len = _keys.length;\n            if (obj == null) return !len;\n            obj = Object(obj);\n\n            for (var i = 0; i < len; i++) {\n                var key = _keys[i];\n                if (src[key] !== obj[key] || !(key in obj)) return false;\n            }\n\n            return true;\n        };\n\n        return exports;\n    })({});\n\n    /* ------------------------------ isNaN ------------------------------ */\n\n    var isNaN = _.isNaN = (function (exports) {\n        /* Check if value is an NaN.\n         *\n         * |Name  |Desc                   |\n         * |------|-----------------------|\n         * |val   |Value to check         |\n         * |return|True if value is an NaN|\n         *\n         * Undefined is not an NaN, different from global isNaN function.\n         */\n\n        /* example\n         * isNaN(0); // -> false\n         * isNaN(NaN); // -> true\n         */\n\n        /* typescript\n         * export declare function isNaN(val: any): boolean;\n         */\n\n        /* dependencies\n         * isNum \n         */\n\n        exports = function(val) {\n            return isNum(val) && val !== +val;\n        };\n\n        return exports;\n    })({});\n\n    /* ------------------------------ isNil ------------------------------ */\n\n    var isNil = _.isNil = (function (exports) {\n        /* Check if value is null or undefined, the same as value == null.\n         *\n         * |Name  |Desc                              |\n         * |------|----------------------------------|\n         * |val   |Value to check                    |\n         * |return|True if value is null or undefined|\n         */\n\n        /* example\n         * isNil(null); // -> true\n         * isNil(void 0); // -> true\n         * isNil(undefined); // -> true\n         * isNil(false); // -> false\n         * isNil(0); // -> false\n         * isNil([]); // -> false\n         */\n\n        /* typescript\n         * export declare function isNil(val: any): boolean;\n         */\n        exports = function(val) {\n            return val == null;\n        };\n\n        return exports;\n    })({});\n\n    /* ------------------------------ isPromise ------------------------------ */\n\n    var isPromise = _.isPromise = (function (exports) {\n        /* Check if value looks like a promise.\n         *\n         * |Name  |Desc                              |\n         * |------|----------------------------------|\n         * |val   |Value to check                    |\n         * |return|True if value looks like a promise|\n         */\n\n        /* example\n         * isPromise(new Promise(function() {})); // -> true\n         * isPromise({}); // -> false\n         */\n\n        /* typescript\n         * export declare function isPromise(val: any): boolean;\n         */\n\n        /* dependencies\n         * isObj isFn \n         */\n\n        exports = function(val) {\n            return isObj(val) && isFn(val.then) && isFn(val.catch);\n        };\n\n        return exports;\n    })({});\n\n    /* ------------------------------ isSymbol ------------------------------ */\n\n    var isSymbol = _.isSymbol = (function (exports) {\n        /* Check if value is a symbol.\n         *\n         * |Name  |Desc                     |\n         * |------|-------------------------|\n         * |val   |Value to check           |\n         * |return|True if value is a symbol|\n         */\n\n        /* example\n         * isSymbol(Symbol('test')); // -> true\n         */\n\n        /* typescript\n         * export declare function isSymbol(val: any): boolean;\n         */\n        exports = function(val) {\n            return typeof val === 'symbol';\n        };\n\n        return exports;\n    })({});\n\n    /* ------------------------------ lowerCase ------------------------------ */\n\n    var lowerCase = _.lowerCase = (function (exports) {\n        /* Convert string to lower case.\n         *\n         * |Name  |Desc              |\n         * |------|------------------|\n         * |str   |String to convert |\n         * |return|Lower cased string|\n         */\n\n        /* example\n         * lowerCase('TEST'); // -> 'test'\n         */\n\n        /* typescript\n         * export declare function lowerCase(str: string): string;\n         */\n\n        /* dependencies\n         * toStr \n         */\n\n        exports = function(str) {\n            return toStr(str).toLocaleLowerCase();\n        };\n\n        return exports;\n    })({});\n\n    /* ------------------------------ ltrim ------------------------------ */\n\n    var ltrim = _.ltrim = (function (exports) {\n        /* Remove chars or white-spaces from beginning of string.\n         *\n         * |Name  |Desc              |\n         * |------|------------------|\n         * |str   |String to trim    |\n         * |chars |Characters to trim|\n         * |return|Trimmed string    |\n         */\n\n        /* example\n         * ltrim(' abc  '); // -> 'abc  '\n         * ltrim('_abc_', '_'); // -> 'abc_'\n         * ltrim('_abc_', ['a', '_']); // -> 'bc_'\n         */\n\n        /* typescript\n         * export declare function ltrim(str: string, chars?: string | string[]): string;\n         */\n        var regSpace = /^\\s+/;\n\n        exports = function(str, chars) {\n            if (chars == null) {\n                if (str.trimLeft) {\n                    return str.trimLeft();\n                }\n\n                return str.replace(regSpace, '');\n            }\n\n            var start = 0;\n            var len = str.length;\n            var charLen = chars.length;\n            var found = true;\n            var i;\n            var c;\n\n            while (found && start < len) {\n                found = false;\n                i = -1;\n                c = str.charAt(start);\n\n                while (++i < charLen) {\n                    if (c === chars[i]) {\n                        found = true;\n                        start++;\n                        break;\n                    }\n                }\n            }\n\n            return start >= len ? '' : str.substr(start, len);\n        };\n\n        return exports;\n    })({});\n\n    /* ------------------------------ matcher ------------------------------ */\n\n    var matcher = _.matcher = (function (exports) {\n        /* Return a predicate function that checks if attrs are contained in an object.\n         *\n         * |Name  |Desc                              |\n         * |------|----------------------------------|\n         * |attrs |Object of property values to match|\n         * |return|New predicate function            |\n         */\n\n        /* example\n         * const filter = require('licia/filter');\n         *\n         * const objects = [\n         *     { a: 1, b: 2, c: 3 },\n         *     { a: 4, b: 5, c: 6 }\n         * ];\n         * filter(objects, matcher({ a: 4, c: 6 })); // -> [{a: 4, b: 5, c: 6}]\n         */\n\n        /* typescript\n         * export declare function matcher(attrs: any): types.AnyFn;\n         */\n\n        /* dependencies\n         * extendOwn isMatch types \n         */\n\n        exports = function(attrs) {\n            attrs = extendOwn({}, attrs);\n            return function(obj) {\n                return isMatch(obj, attrs);\n            };\n        };\n\n        return exports;\n    })({});\n\n    /* ------------------------------ now ------------------------------ */\n\n    var now = _.now = (function (exports) {\n        /* Gets the number of milliseconds that have elapsed since the Unix epoch.\n         */\n\n        /* example\n         * now(); // -> 1468826678701\n         */\n\n        /* typescript\n         * export declare function now(): number;\n         */\n        if (Date.now && !false) {\n            exports = Date.now;\n        } else {\n            exports = function() {\n                return new Date().getTime();\n            };\n        }\n\n        return exports;\n    })({});\n\n    /* ------------------------------ pick ------------------------------ */\n\n    var pick = _.pick = (function (exports) {\n        /* Return a filtered copy of an object.\n         *\n         * |Name  |Desc           |\n         * |------|---------------|\n         * |object|Source object  |\n         * |filter|Object filter  |\n         * |return|Filtered object|\n         */\n\n        /* example\n         * pick({ a: 1, b: 2 }, 'a'); // -> {a: 1}\n         * pick({ a: 1, b: 2, c: 3 }, ['b', 'c']); // -> {b: 2, c: 3}\n         * pick({ a: 1, b: 2, c: 3, d: 4 }, function(val, key) {\n         *     return val % 2;\n         * }); // -> {a: 1, c: 3}\n         */\n\n        /* typescript\n         * export declare function pick(\n         *     object: any,\n         *     filter: string | string[] | Function\n         * ): any;\n         */\n\n        /* dependencies\n         * isStr isArr contain each \n         */\n\n        exports = function(obj, filter, omit) {\n            if (isStr(filter)) filter = [filter];\n\n            if (isArr(filter)) {\n                var keys = filter;\n\n                filter = function(val, key) {\n                    return contain(keys, key);\n                };\n            }\n\n            var ret = {};\n\n            var iteratee = function(val, key) {\n                if (filter(val, key)) ret[key] = val;\n            };\n\n            if (omit) {\n                iteratee = function(val, key) {\n                    if (!filter(val, key)) ret[key] = val;\n                };\n            }\n\n            each(obj, iteratee);\n            return ret;\n        };\n\n        return exports;\n    })({});\n\n    /* ------------------------------ property ------------------------------ */\n\n    var property = _.property = (function (exports) {\n        /* Return a function that will itself return the key property of any passed-in object.\n         *\n         * |Name  |Desc                       |\n         * |------|---------------------------|\n         * |path  |Path of the property to get|\n         * |return|New accessor function      |\n         */\n\n        /* example\n         * const obj = { a: { b: 1 } };\n         * property('a')(obj); // -> {b: 1}\n         * property(['a', 'b'])(obj); // -> 1\n         */\n\n        /* typescript\n         * export declare function property(path: string | string[]): types.AnyFn;\n         */\n\n        /* dependencies\n         * isArr safeGet types \n         */\n\n        exports = function(path) {\n            if (!isArr(path)) return shallowProperty(path);\n            return function(obj) {\n                return safeGet(obj, path);\n            };\n        };\n\n        function shallowProperty(key) {\n            return function(obj) {\n                return obj == null ? void 0 : obj[key];\n            };\n        }\n\n        return exports;\n    })({});\n\n    /* ------------------------------ safeCb ------------------------------ */\n\n    var safeCb = _.safeCb = (function (exports) {\n        /* Create callback based on input value.\n         */\n\n        /* typescript\n         * export declare function safeCb(\n         *     val?: any,\n         *     ctx?: any,\n         *     argCount?: number\n         * ): types.AnyFn;\n         */\n\n        /* dependencies\n         * isFn isObj isArr optimizeCb matcher identity types property \n         */\n\n        exports = function(val, ctx, argCount) {\n            if (val == null) return identity;\n            if (isFn(val)) return optimizeCb(val, ctx, argCount);\n            if (isObj(val) && !isArr(val)) return matcher(val);\n            return property(val);\n        };\n\n        return exports;\n    })({});\n\n    /* ------------------------------ filter ------------------------------ */\n\n    var filter = _.filter = (function (exports) {\n        /* Iterates over elements of collection, returning an array of all the values that pass a truth test.\n         *\n         * |Name     |Desc                                   |\n         * |---------|---------------------------------------|\n         * |obj      |Collection to iterate over             |\n         * |predicate|Function invoked per iteration         |\n         * |ctx      |Predicate context                      |\n         * |return   |Array of all values that pass predicate|\n         */\n\n        /* example\n         * filter([1, 2, 3, 4, 5], function(val) {\n         *     return val % 2 === 0;\n         * }); // -> [2, 4]\n         */\n\n        /* typescript\n         * export declare function filter<T>(\n         *     list: types.List<T>,\n         *     iterator: types.ListIterator<T, boolean>,\n         *     context?: any\n         * ): T[];\n         * export declare function filter<T>(\n         *     object: types.Dictionary<T>,\n         *     iterator: types.ObjectIterator<T, boolean>,\n         *     context?: any\n         * ): T[];\n         */\n\n        /* dependencies\n         * safeCb each types \n         */\n\n        exports = function(obj, predicate, ctx) {\n            var ret = [];\n            predicate = safeCb(predicate, ctx);\n            each(obj, function(val, idx, list) {\n                if (predicate(val, idx, list)) ret.push(val);\n            });\n            return ret;\n        };\n\n        return exports;\n    })({});\n\n    /* ------------------------------ difference ------------------------------ */\n\n    var difference = _.difference = (function (exports) {\n        /* Create an array of unique array values not included in the other given array.\n         *\n         * |Name   |Desc                        |\n         * |-------|----------------------------|\n         * |arr    |Array to inspect            |\n         * |...args|Values to exclude           |\n         * |return |New array of filtered values|\n         */\n\n        /* example\n         * difference([3, 2, 1], [4, 2]); // -> [3, 1]\n         */\n\n        /* typescript\n         * export declare function difference(arr: any[], ...args: any[]): any[];\n         */\n\n        /* dependencies\n         * restArgs flatten filter contain \n         */\n\n        exports = restArgs(function(arr, args) {\n            args = flatten(args);\n            return filter(arr, function(val) {\n                return !contain(args, val);\n            });\n        });\n\n        return exports;\n    })({});\n\n    /* ------------------------------ unique ------------------------------ */\n\n    var unique = _.unique = (function (exports) {\n        /* Create duplicate-free version of an array.\n         *\n         * |Name  |Desc                         |\n         * |------|-----------------------------|\n         * |arr   |Array to inspect             |\n         * |cmp   |Function for comparing values|\n         * |return|New duplicate free array     |\n         */\n\n        /* example\n         * unique([1, 2, 3, 1]); // -> [1, 2, 3]\n         */\n\n        /* typescript\n         * export declare function unique(\n         *     arr: any[],\n         *     cmp?: (a: any, b: any) => boolean | number\n         * ): any[];\n         */\n\n        /* dependencies\n         * filter \n         */\n\n        exports = function(arr, cmp) {\n            cmp = cmp || isEqual;\n            return filter(arr, function(item, idx, arr) {\n                var len = arr.length;\n\n                while (++idx < len) {\n                    if (cmp(item, arr[idx])) return false;\n                }\n\n                return true;\n            });\n        };\n\n        function isEqual(a, b) {\n            return a === b;\n        }\n\n        return exports;\n    })({});\n\n    /* ------------------------------ allKeys ------------------------------ */\n\n    var allKeys = _.allKeys = (function (exports) {\n        /* Retrieve all the names of object's own and inherited properties.\n         *\n         * |Name   |Desc                       |\n         * |-------|---------------------------|\n         * |obj    |Object to query            |\n         * |options|Options                    |\n         * |return |Array of all property names|\n         *\n         * Available options:\n         *\n         * |Name              |Desc                     |\n         * |------------------|-------------------------|\n         * |prototype=true    |Include prototype keys   |\n         * |unenumerable=false|Include unenumerable keys|\n         * |symbol=false      |Include symbol keys      |\n         *\n         * Members of Object's prototype won't be retrieved.\n         */\n\n        /* example\n         * const obj = Object.create({ zero: 0 });\n         * obj.one = 1;\n         * allKeys(obj); // -> ['zero', 'one']\n         */\n\n        /* typescript\n         * export declare namespace allKeys {\n         *     interface IOptions {\n         *         prototype?: boolean;\n         *         unenumerable?: boolean;\n         *     }\n         * }\n         * export declare function allKeys(\n         *     obj: any,\n         *     options: { symbol: true } & allKeys.IOptions\n         * ): Array<string | Symbol>;\n         * export declare function allKeys(\n         *     obj: any,\n         *     options?: ({ symbol: false } & allKeys.IOptions) | allKeys.IOptions\n         * ): string[];\n         */\n\n        /* dependencies\n         * keys getProto unique \n         */\n\n        var getOwnPropertyNames = Object.getOwnPropertyNames;\n        var getOwnPropertySymbols = Object.getOwnPropertySymbols;\n\n        exports = function(obj) {\n            var _ref =\n                    arguments.length > 1 && arguments[1] !== undefined\n                        ? arguments[1]\n                        : {},\n                _ref$prototype = _ref.prototype,\n                prototype = _ref$prototype === void 0 ? true : _ref$prototype,\n                _ref$unenumerable = _ref.unenumerable,\n                unenumerable = _ref$unenumerable === void 0 ? false : _ref$unenumerable,\n                _ref$symbol = _ref.symbol,\n                symbol = _ref$symbol === void 0 ? false : _ref$symbol;\n\n            var ret = [];\n\n            if ((unenumerable || symbol) && getOwnPropertyNames) {\n                var getKeys = keys;\n                if (unenumerable && getOwnPropertyNames) getKeys = getOwnPropertyNames;\n\n                do {\n                    ret = ret.concat(getKeys(obj));\n\n                    if (symbol && getOwnPropertySymbols) {\n                        ret = ret.concat(getOwnPropertySymbols(obj));\n                    }\n                } while (\n                    prototype &&\n                    (obj = getProto(obj)) &&\n                    obj !== Object.prototype\n                );\n\n                ret = unique(ret);\n            } else {\n                if (prototype) {\n                    for (var key in obj) {\n                        ret.push(key);\n                    }\n                } else {\n                    ret = keys(obj);\n                }\n            }\n\n            return ret;\n        };\n\n        return exports;\n    })({});\n\n    /* ------------------------------ defaults ------------------------------ */\n\n    var defaults = _.defaults = (function (exports) {\n        /* Fill in undefined properties in object with the first value present in the following list of defaults objects.\n         *\n         * |Name  |Desc              |\n         * |------|------------------|\n         * |obj   |Destination object|\n         * |...src|Sources objects   |\n         * |return|Destination object|\n         */\n\n        /* example\n         * defaults({ name: 'RedHood' }, { name: 'Unknown', age: 24 }); // -> {name: 'RedHood', age: 24}\n         */\n\n        /* typescript\n         * export declare function defaults(obj: any, ...src: any[]): any;\n         */\n\n        /* dependencies\n         * createAssigner allKeys \n         */\n\n        exports = createAssigner(allKeys, true);\n\n        return exports;\n    })({});\n\n    /* ------------------------------ extend ------------------------------ */\n\n    var extend = _.extend = (function (exports) {\n        /* Copy all of the properties in the source objects over to the destination object.\n         *\n         * |Name       |Desc              |\n         * |-----------|------------------|\n         * |destination|Destination object|\n         * |...sources |Sources objects   |\n         * |return     |Destination object|\n         */\n\n        /* example\n         * extend({ name: 'RedHood' }, { age: 24 }); // -> {name: 'RedHood', age: 24}\n         */\n\n        /* typescript\n         * export declare function extend(destination: any, ...sources: any[]): any;\n         */\n\n        /* dependencies\n         * createAssigner allKeys \n         */\n\n        exports = createAssigner(allKeys);\n\n        return exports;\n    })({});\n\n    /* ------------------------------ map ------------------------------ */\n\n    var map = _.map = (function (exports) {\n        /* Create an array of values by running each element in collection through iteratee.\n         *\n         * |Name    |Desc                          |\n         * |--------|------------------------------|\n         * |object  |Collection to iterate over    |\n         * |iterator|Function invoked per iteration|\n         * |context |Function context              |\n         * |return  |New mapped array              |\n         */\n\n        /* example\n         * map([4, 8], function(n) {\n         *     return n * n;\n         * }); // -> [16, 64]\n         */\n\n        /* typescript\n         * export declare function map<T, TResult>(\n         *     list: types.List<T>,\n         *     iterator: types.ListIterator<T, TResult>,\n         *     context?: any\n         * ): TResult[];\n         * export declare function map<T, TResult>(\n         *     object: types.Dictionary<T>,\n         *     iterator: types.ObjectIterator<T, TResult>,\n         *     context?: any\n         * ): TResult[];\n         */\n\n        /* dependencies\n         * safeCb keys isArrLike types \n         */\n\n        exports = function(obj, iterator, ctx) {\n            iterator = safeCb(iterator, ctx);\n\n            var _keys = !isArrLike(obj) && keys(obj);\n\n            var len = (_keys || obj).length;\n            var results = Array(len);\n\n            for (var i = 0; i < len; i++) {\n                var curKey = _keys ? _keys[i] : i;\n                results[i] = iterator(obj[curKey], curKey, obj);\n            }\n\n            return results;\n        };\n\n        return exports;\n    })({});\n\n    /* ------------------------------ toArr ------------------------------ */\n\n    var toArr = _.toArr = (function (exports) {\n        /* Convert value to an array.\n         *\n         * |Name  |Desc            |\n         * |------|----------------|\n         * |val   |Value to convert|\n         * |return|Converted array |\n         */\n\n        /* example\n         * toArr({ a: 1, b: 2 }); // -> [{a: 1, b: 2}]\n         * toArr('abc'); // -> ['abc']\n         * toArr(1); // -> [1]\n         * toArr(null); // -> []\n         */\n\n        /* typescript\n         * export declare function toArr(val: any): any[];\n         */\n\n        /* dependencies\n         * isArrLike map isArr isStr \n         */\n\n        exports = function(val) {\n            if (!val) return [];\n            if (isArr(val)) return val;\n            if (isArrLike(val) && !isStr(val)) return map(val);\n            return [val];\n        };\n\n        return exports;\n    })({});\n\n    /* ------------------------------ Class ------------------------------ */\n\n    var Class = _.Class = (function (exports) {\n        /* Create JavaScript class.\n         *\n         * |Name   |Desc                             |\n         * |-------|---------------------------------|\n         * |methods|Public methods                   |\n         * [statics|Static methods                   |\n         * |return |Function used to create instances|\n         */\n\n        /* example\n         * const People = Class({\n         *     initialize: function People(name, age) {\n         *         this.name = name;\n         *         this.age = age;\n         *     },\n         *     introduce: function() {\n         *         return 'I am ' + this.name + ', ' + this.age + ' years old.';\n         *     }\n         * });\n         *\n         * const Student = People.extend(\n         *     {\n         *         initialize: function Student(name, age, school) {\n         *             this.callSuper(People, 'initialize', arguments);\n         *\n         *             this.school = school;\n         *         },\n         *         introduce: function() {\n         *             return (\n         *                 this.callSuper(People, 'introduce') +\n         *                 '\\n I study at ' +\n         *                 this.school +\n         *                 '.'\n         *             );\n         *         }\n         *     },\n         *     {\n         *         is: function(obj) {\n         *             return obj instanceof Student;\n         *         }\n         *     }\n         * );\n         *\n         * const a = new Student('allen', 17, 'Hogwarts');\n         * a.introduce(); // -> 'I am allen, 17 years old. \\n I study at Hogwarts.'\n         * Student.is(a); // -> true\n         */\n\n        /* typescript\n         * export declare namespace Class {\n         *     class Base {\n         *         toString(): string;\n         *     }\n         *     class IConstructor extends Base {\n         *         constructor(...args: any[]);\n         *         static extend(methods: any, statics: any): IConstructor;\n         *         static inherits(Class: types.AnyFn): void;\n         *         static methods(methods: any): IConstructor;\n         *         static statics(statics: any): IConstructor;\n         *         [method: string]: any;\n         *     }\n         * }\n         * export declare function Class(methods: any, statics?: any): Class.IConstructor;\n         */\n\n        /* dependencies\n         * extend toArr inherits safeGet isMiniProgram types \n         */\n\n        exports = function(methods, statics) {\n            return Base.extend(methods, statics);\n        };\n\n        function makeClass(parent, methods, statics) {\n            statics = statics || {};\n            var className =\n                methods.className || safeGet(methods, 'initialize.name') || '';\n            delete methods.className;\n\n            var ctor = function() {\n                var args = toArr(arguments);\n                return this.initialize\n                    ? this.initialize.apply(this, args) || this\n                    : this;\n            };\n\n            if (!isMiniProgram) {\n                // unsafe-eval CSP violation\n                try {\n                    ctor = new Function(\n                        'toArr',\n                        'return function ' +\n                            className +\n                            '()' +\n                            '{' +\n                            'var args = toArr(arguments);' +\n                            'return this.initialize ? this.initialize.apply(this, args) || this : this;' +\n                            '};'\n                    )(toArr);\n                } catch (e) {\n                    /* eslint-disable no-empty */\n                }\n            }\n\n            inherits(ctor, parent);\n            ctor.prototype.constructor = ctor;\n\n            ctor.extend = function(methods, statics) {\n                return makeClass(ctor, methods, statics);\n            };\n\n            ctor.inherits = function(Class) {\n                inherits(ctor, Class);\n            };\n\n            ctor.methods = function(methods) {\n                extend(ctor.prototype, methods);\n                return ctor;\n            };\n\n            ctor.statics = function(statics) {\n                extend(ctor, statics);\n                return ctor;\n            };\n\n            ctor.methods(methods).statics(statics);\n            return ctor;\n        }\n\n        var Base = (exports.Base = makeClass(Object, {\n            className: 'Base',\n            callSuper: function(parent, name, args) {\n                var superMethod = parent.prototype[name];\n                return superMethod.apply(this, args);\n            },\n            toString: function() {\n                return this.constructor.name;\n            }\n        }));\n\n        return exports;\n    })({});\n\n    /* ------------------------------ ucs2 ------------------------------ */\n\n    var ucs2 = _.ucs2 = (function (exports) {\n        /* UCS-2 encoding and decoding.\n         *\n         * ### encode\n         *\n         * Create a string using an array of code point values.\n         *\n         * |Name  |Desc                |\n         * |------|--------------------|\n         * |arr   |Array of code points|\n         * |return|Encoded string      |\n         *\n         * ### decode\n         *\n         * Create an array of code point values using a string.\n         *\n         * |Name  |Desc                |\n         * |------|--------------------|\n         * |str   |Input string        |\n         * |return|Array of code points|\n         */\n\n        /* example\n         * ucs2.encode([0x61, 0x62, 0x63]); // -> 'abc'\n         * ucs2.decode('abc'); // -> [0x61, 0x62, 0x63]\n         * '𝌆'.length; // -> 2\n         * ucs2.decode('𝌆').length; // -> 1\n         */\n\n        /* typescript\n         * export declare const ucs2: {\n         *     encode(arr: number[]): string;\n         *     decode(str: string): number[];\n         * };\n         */\n\n        /* dependencies\n         * chunk map \n         */ // https://mathiasbynens.be/notes/javascript-encoding\n\n        exports = {\n            encode: function(arr) {\n                // https://stackoverflow.com/questions/22747068/is-there-a-max-number-of-arguments-javascript-functions-can-accept\n                if (arr.length < 32768) {\n                    return String.fromCodePoint.apply(String, arr);\n                }\n\n                return map(chunk(arr, 32767), function(nums) {\n                    return String.fromCodePoint.apply(String, nums);\n                }).join('');\n            },\n            decode: function(str) {\n                var ret = [];\n                var i = 0;\n                var len = str.length;\n\n                while (i < len) {\n                    var c = str.charCodeAt(i++); // A high surrogate\n\n                    if (c >= 0xd800 && c <= 0xdbff && i < len) {\n                        var tail = str.charCodeAt(i++); // nextC >= 0xDC00 && nextC <= 0xDFFF\n\n                        if ((tail & 0xfc00) === 0xdc00) {\n                            // C = (H - 0xD800) * 0x400 + L - 0xDC00 + 0x10000\n                            ret.push(((c & 0x3ff) << 10) + (tail & 0x3ff) + 0x10000);\n                        } else {\n                            ret.push(c);\n                            i--;\n                        }\n                    } else {\n                        ret.push(c);\n                    }\n                }\n\n                return ret;\n            }\n        };\n\n        return exports;\n    })({});\n\n    /* ------------------------------ utf8 ------------------------------ */\n\n    var utf8 = _.utf8 = (function (exports) {\n        /* UTF-8 encoding and decoding.\n         *\n         * ### encode\n         *\n         * Turn any UTF-8 decoded string into UTF-8 encoded string.\n         *\n         * |Name  |Desc            |\n         * |------|----------------|\n         * |str   |String to encode|\n         * |return|Encoded string  |\n         *\n         * ### decode\n         *\n         * Turn any UTF-8 encoded string into UTF-8 decoded string.\n         *\n         * |Name      |Desc                  |\n         * |----------|----------------------|\n         * |str       |String to decode      |\n         * |safe=false|Suppress error if true|\n         * |return    |Decoded string        |\n         */\n\n        /* example\n         * utf8.encode('\\uD800\\uDC00'); // ->  '\\xF0\\x90\\x80\\x80'\n         * utf8.decode('\\xF0\\x90\\x80\\x80'); // -> '\\uD800\\uDC00'\n         */\n\n        /* typescript\n         * export declare const utf8: {\n         *     encode(str: string): string;\n         *     decode(str: string, safe?: boolean): string;\n         * };\n         */\n\n        /* dependencies\n         * ucs2 \n         */ // https://encoding.spec.whatwg.org/#utf-8\n\n        exports = {\n            encode: function(str) {\n                var codePoints = ucs2.decode(str);\n                var byteArr = '';\n\n                for (var i = 0, len = codePoints.length; i < len; i++) {\n                    byteArr += encodeCodePoint(codePoints[i]);\n                }\n\n                return byteArr;\n            },\n            decode: function(str, safe) {\n                byteArr = ucs2.decode(str);\n                byteIdx = 0;\n                byteCount = byteArr.length;\n                codePoint = 0;\n                bytesSeen = 0;\n                bytesNeeded = 0;\n                lowerBoundary = 0x80;\n                upperBoundary = 0xbf;\n                var codePoints = [];\n                var tmp;\n\n                while ((tmp = decodeCodePoint(safe)) !== false) {\n                    codePoints.push(tmp);\n                }\n\n                return ucs2.encode(codePoints);\n            }\n        };\n        var fromCharCode = String.fromCharCode;\n\n        function encodeCodePoint(codePoint) {\n            // U+0000 to U+0080, ASCII code point\n            if ((codePoint & 0xffffff80) === 0) {\n                return fromCharCode(codePoint);\n            }\n\n            var ret = '',\n                count,\n                offset; // U+0080 to U+07FF, inclusive\n\n            if ((codePoint & 0xfffff800) === 0) {\n                count = 1;\n                offset = 0xc0;\n            } else if ((codePoint & 0xffff0000) === 0) {\n                // U+0800 to U+FFFF, inclusive\n                count = 2;\n                offset = 0xe0;\n            } else if ((codePoint & 0xffe00000) == 0) {\n                // U+10000 to U+10FFFF, inclusive\n                count = 3;\n                offset = 0xf0;\n            }\n\n            ret += fromCharCode((codePoint >> (6 * count)) + offset);\n\n            while (count > 0) {\n                var tmp = codePoint >> (6 * (count - 1));\n                ret += fromCharCode(0x80 | (tmp & 0x3f));\n                count--;\n            }\n\n            return ret;\n        }\n\n        var byteArr,\n            byteIdx,\n            byteCount,\n            codePoint,\n            bytesSeen,\n            bytesNeeded,\n            lowerBoundary,\n            upperBoundary;\n\n        function decodeCodePoint(safe) {\n            /* eslint-disable no-constant-condition */\n            while (true) {\n                if (byteIdx >= byteCount && bytesNeeded) {\n                    if (safe) return goBack();\n                    throw new Error('Invalid byte index');\n                }\n\n                if (byteIdx === byteCount) return false;\n                var byte = byteArr[byteIdx];\n                byteIdx++;\n\n                if (!bytesNeeded) {\n                    // 0x00 to 0x7F\n                    if ((byte & 0x80) === 0) {\n                        return byte;\n                    } // 0xC2 to 0xDF\n\n                    if ((byte & 0xe0) === 0xc0) {\n                        bytesNeeded = 1;\n                        codePoint = byte & 0x1f;\n                    } else if ((byte & 0xf0) === 0xe0) {\n                        // 0xE0 to 0xEF\n                        if (byte === 0xe0) lowerBoundary = 0xa0;\n                        if (byte === 0xed) upperBoundary = 0x9f;\n                        bytesNeeded = 2;\n                        codePoint = byte & 0xf;\n                    } else if ((byte & 0xf8) === 0xf0) {\n                        // 0xF0 to 0xF4\n                        if (byte === 0xf0) lowerBoundary = 0x90;\n                        if (byte === 0xf4) upperBoundary = 0x8f;\n                        bytesNeeded = 3;\n                        codePoint = byte & 0x7;\n                    } else {\n                        if (safe) return goBack();\n                        throw new Error('Invalid UTF-8 detected');\n                    }\n\n                    continue;\n                }\n\n                if (byte < lowerBoundary || byte > upperBoundary) {\n                    if (safe) {\n                        byteIdx--;\n                        return goBack();\n                    }\n\n                    throw new Error('Invalid continuation byte');\n                }\n\n                lowerBoundary = 0x80;\n                upperBoundary = 0xbf;\n                codePoint = (codePoint << 6) | (byte & 0x3f);\n                bytesSeen++;\n                if (bytesSeen !== bytesNeeded) continue;\n                var tmp = codePoint;\n                codePoint = 0;\n                bytesNeeded = 0;\n                bytesSeen = 0;\n                return tmp;\n            }\n        }\n\n        function goBack() {\n            var start = byteIdx - bytesSeen - 1;\n            byteIdx = start + 1;\n            codePoint = 0;\n            bytesNeeded = 0;\n            bytesSeen = 0;\n            lowerBoundary = 0x80;\n            upperBoundary = 0xbf;\n            return byteArr[start];\n        }\n\n        return exports;\n    })({});\n\n    /* ------------------------------ decodeUriComponent ------------------------------ */\n\n    var decodeUriComponent = _.decodeUriComponent = (function (exports) {\n        /* Better decodeURIComponent that does not throw if input is invalid.\n         *\n         * |Name  |Desc            |\n         * |------|----------------|\n         * |str   |String to decode|\n         * |return|Decoded string  |\n         */\n\n        /* example\n         * decodeUriComponent('%%25%'); // -> '%%%'\n         * decodeUriComponent('%E0%A4%A'); // -> '\\xE0\\xA4%A'\n         */\n\n        /* typescript\n         * export declare function decodeUriComponent(str: string): string;\n         */\n\n        /* dependencies\n         * each ucs2 map utf8 \n         */\n\n        exports = function(str) {\n            try {\n                return decodeURIComponent(str);\n            } catch (e) {\n                var matches = str.match(regMatcher);\n\n                if (!matches) {\n                    return str;\n                }\n\n                each(matches, function(match) {\n                    str = str.replace(match, decode(match));\n                });\n                return str;\n            }\n        };\n\n        function decode(str) {\n            str = str.split('%').slice(1);\n            var bytes = map(str, hexToInt);\n            str = ucs2.encode(bytes);\n            str = utf8.decode(str, true);\n            return str;\n        }\n\n        function hexToInt(numStr) {\n            return +('0x' + numStr);\n        }\n\n        var regMatcher = /(%[a-f0-9]{2})+/gi;\n\n        return exports;\n    })({});\n\n    /* ------------------------------ cookie ------------------------------ */\n    _.cookie = (function (exports) {\n        /* Simple api for handling browser cookies.\n         *\n         * ### get\n         *\n         * Get cookie value.\n         *\n         * |Name  |Desc                      |\n         * |------|--------------------------|\n         * |key   |Cookie key                |\n         * |return|Corresponding cookie value|\n         *\n         * ### set\n         *\n         * Set cookie value.\n         *\n         * |Name   |Desc          |\n         * |-------|--------------|\n         * |key    |Cookie key    |\n         * |val    |Cookie value  |\n         * |options|Cookie options|\n         * |return |Module cookie |\n         *\n         * ### remove\n         *\n         * Remove cookie value.\n         *\n         * |Name   |Desc          |\n         * |-------|--------------|\n         * |key    |Cookie key    |\n         * |options|Cookie options|\n         * |return |Module cookie |\n         */\n\n        /* example\n         * cookie.set('a', '1', { path: '/' });\n         * cookie.get('a'); // -> '1'\n         * cookie.remove('a');\n         */\n\n        /* typescript\n         * export declare namespace cookie {\n         *     interface IOptions {\n         *         path?: string;\n         *         expires?: number;\n         *         domain?: string;\n         *         secure?: boolean;\n         *     }\n         *     interface ICookie {\n         *         get(key: string, options?: cookie.IOptions): string;\n         *         set(key: string, val: string, options?: cookie.IOptions): ICookie;\n         *         remove(key: string, options?: cookie.IOptions): ICookie;\n         *     }\n         * }\n         * export declare const cookie: cookie.ICookie;\n         */\n\n        /* dependencies\n         * defaults isNum isUndef decodeUriComponent \n         */\n\n        var defOpts = {\n            path: '/'\n        };\n\n        function setCookie(key, val, options) {\n            if (!isUndef(val)) {\n                options = options || {};\n                options = defaults(options, defOpts);\n\n                if (isNum(options.expires)) {\n                    var expires = new Date();\n                    expires.setMilliseconds(\n                        expires.getMilliseconds() + options.expires * 864e5\n                    );\n                    options.expires = expires;\n                }\n\n                val = encodeURIComponent(val);\n                key = encodeURIComponent(key);\n                document.cookie = [\n                    key,\n                    '=',\n                    val,\n                    options.expires && '; expires=' + options.expires.toUTCString(),\n                    options.path && '; path=' + options.path,\n                    options.domain && '; domain=' + options.domain,\n                    options.secure ? '; secure' : ''\n                ].join('');\n                return exports;\n            }\n\n            var cookies = document.cookie ? document.cookie.split('; ') : [];\n            var result = key ? undefined : {};\n\n            for (var i = 0, len = cookies.length; i < len; i++) {\n                var c = cookies[i];\n                var parts = c.split('=');\n                var name = decodeUriComponent(parts.shift());\n                c = parts.join('=');\n                c = decodeUriComponent(c);\n\n                if (key === name) {\n                    result = c;\n                    break;\n                }\n\n                if (!key) result[name] = c;\n            }\n\n            return result;\n        }\n\n        exports = {\n            get: setCookie,\n            set: setCookie,\n            remove: function(key, options) {\n                options = options || {};\n                options.expires = -1;\n                return setCookie(key, '', options);\n            }\n        };\n\n        return exports;\n    })({});\n\n    /* ------------------------------ rtrim ------------------------------ */\n\n    var rtrim = _.rtrim = (function (exports) {\n        /* Remove chars or white-spaces from end of string.\n         *\n         * |Name  |Desc              |\n         * |------|------------------|\n         * |str   |String to trim    |\n         * |chars |Characters to trim|\n         * |return|Trimmed string    |\n         */\n\n        /* example\n         * rtrim(' abc  '); // -> ' abc'\n         * rtrim('_abc_', '_'); // -> '_abc'\n         * rtrim('_abc_', ['c', '_']); // -> '_ab'\n         */\n\n        /* typescript\n         * export declare function rtrim(str: string, chars?: string | string[]): string;\n         */\n        exports = function(str, chars) {\n            if (chars == null) {\n                if (str.trimRight) {\n                    return str.trimRight();\n                }\n\n                chars = ' \\r\\n\\t\\f\\v';\n            }\n\n            var end = str.length - 1;\n            var charLen = chars.length;\n            var found = true;\n            var i;\n            var c;\n\n            while (found && end >= 0) {\n                found = false;\n                i = -1;\n                c = str.charAt(end);\n\n                while (++i < charLen) {\n                    if (c === chars[i]) {\n                        found = true;\n                        end--;\n                        break;\n                    }\n                }\n            }\n\n            return end >= 0 ? str.substring(0, end + 1) : '';\n        };\n\n        return exports;\n    })({});\n\n    /* ------------------------------ trim ------------------------------ */\n\n    var trim = _.trim = (function (exports) {\n        /* Remove chars or white-spaces from beginning end of string.\n         *\n         * |Name  |Desc              |\n         * |------|------------------|\n         * |str   |String to trim    |\n         * |chars |Characters to trim|\n         * |return|Trimmed string    |\n         */\n\n        /* example\n         * trim(' abc  '); // -> 'abc'\n         * trim('_abc_', '_'); // -> 'abc'\n         * trim('_abc_', ['a', 'c', '_']); // -> 'b'\n         */\n\n        /* typescript\n         * export declare function trim(str: string, chars?: string | string[]): string;\n         */\n\n        /* dependencies\n         * ltrim rtrim \n         */\n\n        exports = function(str, chars) {\n            if (chars == null && str.trim) {\n                return str.trim();\n            }\n\n            return ltrim(rtrim(str, chars), chars);\n        };\n\n        return exports;\n    })({});\n\n    /* ------------------------------ query ------------------------------ */\n\n    var query = _.query = (function (exports) {\n        /* Parse and stringify url query strings.\n         *\n         * ### parse\n         *\n         * Parse a query string into an object.\n         *\n         * |Name  |Desc        |\n         * |------|------------|\n         * |str   |Query string|\n         * |return|Query object|\n         *\n         * ### stringify\n         *\n         * Stringify an object into a query string.\n         *\n         * |Name  |Desc        |\n         * |------|------------|\n         * |obj   |Query object|\n         * |return|Query string|\n         */\n\n        /* example\n         * query.parse('foo=bar&eruda=true'); // -> {foo: 'bar', eruda: 'true'}\n         * query.stringify({ foo: 'bar', eruda: 'true' }); // -> 'foo=bar&eruda=true'\n         * query.parse('name=eruda&name=eustia'); // -> {name: ['eruda', 'eustia']}\n         */\n\n        /* typescript\n         * export declare const query: {\n         *     parse(str: string): any;\n         *     stringify(object: any): string;\n         * };\n         */\n\n        /* dependencies\n         * trim each isUndef isArr map isEmpty filter isObj \n         */\n\n        exports = {\n            parse: function(str) {\n                var ret = {};\n                str = trim(str).replace(regIllegalChars, '');\n                each(str.split('&'), function(param) {\n                    var parts = param.split('=');\n                    var key = parts.shift(),\n                        val = parts.length > 0 ? parts.join('=') : null;\n                    key = decodeURIComponent(key);\n                    val = decodeURIComponent(val);\n\n                    if (isUndef(ret[key])) {\n                        ret[key] = val;\n                    } else if (isArr(ret[key])) {\n                        ret[key].push(val);\n                    } else {\n                        ret[key] = [ret[key], val];\n                    }\n                });\n                return ret;\n            },\n            stringify: function(obj, arrKey) {\n                return filter(\n                    map(obj, function(val, key) {\n                        if (isObj(val) && isEmpty(val)) return '';\n                        if (isArr(val)) return exports.stringify(val, key);\n                        return (\n                            (arrKey\n                                ? encodeURIComponent(arrKey)\n                                : encodeURIComponent(key)) +\n                            '=' +\n                            encodeURIComponent(val)\n                        );\n                    }),\n                    function(str) {\n                        return str.length > 0;\n                    }\n                ).join('&');\n            }\n        };\n        var regIllegalChars = /^(\\?|#|&)/g;\n\n        return exports;\n    })({});\n\n    /* ------------------------------ ajax ------------------------------ */\n    _.ajax = (function (exports) {\n        /* Perform an asynchronous HTTP request.\n         *\n         * |Name   |Desc        |\n         * |-------|------------|\n         * |options|Ajax options|\n         *\n         * Available options:\n         *\n         * |Name                                         |Desc                       |\n         * |---------------------------------------------|---------------------------|\n         * |type=get                                     |Request type               |\n         * |url                                          |Request url                |\n         * |data                                         |Request data               |\n         * |dataType=json                                |Response type(json, xml)   |\n         * |contentType=application/x-www-form-urlencoded|Request header Content-Type|\n         * |success                                      |Success callback           |\n         * |error                                        |Error callback             |\n         * |complete                                     |Callback after request     |\n         * |timeout                                      |Request timeout            |\n         *\n         * ### get\n         *\n         * Shortcut for type = GET;\n         *\n         * ### post\n         *\n         * Shortcut for type = POST;\n         *\n         * |Name    |Desc            |\n         * |--------|----------------|\n         * |url     |Request url     |\n         * |data    |Request data    |\n         * |success |Success callback|\n         * |dataType|Response type   |\n         */\n\n        /* example\n         * ajax({\n         *     url: 'http://example.com',\n         *     data: { test: 'true' },\n         *     error() {},\n         *     success(data) {\n         *         // ...\n         *     },\n         *     dataType: 'json'\n         * });\n         *\n         * ajax.get('http://example.com', {}, function(data) {\n         *     // ...\n         * });\n         */\n\n        /* typescript\n         * export declare namespace ajax {\n         *     function get(\n         *         url: string,\n         *         data: string | {},\n         *         success: types.AnyFn,\n         *         dataType?: string\n         *     ): XMLHttpRequest;\n         *     function get(\n         *         url: string,\n         *         success: types.AnyFn,\n         *         dataType?: string\n         *     ): XMLHttpRequest;\n         *     function post(\n         *         url: string,\n         *         data: string | {},\n         *         success: types.AnyFn,\n         *         dataType?: string\n         *     ): XMLHttpRequest;\n         *     function post(\n         *         url: string,\n         *         success: types.AnyFn,\n         *         dataType?: string\n         *     ): XMLHttpRequest;\n         * }\n         * export declare function ajax(options: {\n         *     type?: string;\n         *     url: string;\n         *     data?: string | {};\n         *     dataType?: string;\n         *     contentType?: string;\n         *     success?: types.AnyFn;\n         *     error?: types.AnyFn;\n         *     complete?: types.AnyFn;\n         *     timeout?: number;\n         * }): XMLHttpRequest;\n         */\n\n        /* dependencies\n         * isFn noop defaults isObj query types \n         */\n\n        exports = function(options) {\n            defaults(options, exports.setting);\n            var type = options.type;\n            var url = options.url;\n            var data = options.data;\n            var dataType = options.dataType;\n            var success = options.success;\n            var error = options.error;\n            var timeout = options.timeout;\n            var complete = options.complete;\n            var xhr = options.xhr();\n            var abortTimeout;\n\n            xhr.onreadystatechange = function() {\n                if (xhr.readyState !== 4) return;\n                clearTimeout(abortTimeout);\n                var result;\n                var status = xhr.status;\n\n                if ((status >= 200 && status < 300) || status === 304) {\n                    result = xhr.responseText;\n                    if (dataType === 'xml') result = xhr.responseXML;\n\n                    try {\n                        if (dataType === 'json') result = JSON.parse(result);\n                        /* eslint-disable no-empty */\n                    } catch (e) {}\n\n                    success(result, xhr);\n                } else {\n                    error(xhr);\n                }\n\n                complete(xhr);\n            };\n\n            if (type === 'GET') {\n                data = query.stringify(data);\n                if (data) url += url.indexOf('?') > -1 ? '&' + data : '?' + data;\n            } else if (options.contentType === 'application/x-www-form-urlencoded') {\n                if (isObj(data)) data = query.stringify(data);\n            } else if (options.contentType === 'application/json') {\n                if (isObj(data)) data = JSON.stringify(data);\n            }\n\n            xhr.open(type, url, true);\n            xhr.setRequestHeader('Content-Type', options.contentType);\n\n            if (timeout > 0) {\n                abortTimeout = setTimeout(function() {\n                    xhr.onreadystatechange = noop;\n                    xhr.abort();\n                    error(xhr, 'timeout');\n                    complete(xhr);\n                }, timeout);\n            }\n\n            xhr.send(type === 'GET' ? null : data);\n            return xhr;\n        };\n\n        exports.setting = {\n            type: 'GET',\n            success: noop,\n            error: noop,\n            complete: noop,\n            dataType: 'json',\n            contentType: 'application/x-www-form-urlencoded',\n            data: {},\n            xhr: function() {\n                return new XMLHttpRequest();\n            },\n            timeout: 0\n        };\n\n        exports.get = function() {\n            return exports(parseArgs.apply(null, arguments));\n        };\n\n        exports.post = function() {\n            var options = parseArgs.apply(null, arguments);\n            options.type = 'POST';\n            return exports(options);\n        };\n\n        function parseArgs(url, data, success, dataType) {\n            if (isFn(data)) {\n                dataType = success;\n                success = data;\n                data = {};\n            }\n\n            return {\n                url: url,\n                data: data,\n                success: success,\n                dataType: dataType\n            };\n        }\n\n        return exports;\n    })({});\n\n    /* ------------------------------ safeSet ------------------------------ */\n\n    var safeSet = _.safeSet = (function (exports) {\n        /* Set value at path of object.\n         *\n         * If a portion of path doesn't exist, it's created.\n         *\n         * |Name|Desc                   |\n         * |----|-----------------------|\n         * |obj |Object to modify       |\n         * |path|Path of property to set|\n         * |val |Value to set           |\n         */\n\n        /* example\n         * const obj = {};\n         * safeSet(obj, 'a.aa.aaa', 1); // obj = {a: {aa: {aaa: 1}}}\n         * safeSet(obj, ['a', 'aa'], 2); // obj = {a: {aa: 2}}\n         * safeSet(obj, 'a.b', 3); // obj = {a: {aa: 2, b: 3}}\n         */\n\n        /* typescript\n         * export declare function safeSet(\n         *     obj: any,\n         *     path: string | string[],\n         *     val: any\n         * ): void;\n         */\n\n        /* dependencies\n         * castPath isUndef toStr isSymbol isStr \n         */\n\n        exports = function(obj, path, val) {\n            path = castPath(path, obj);\n            var lastProp = path.pop();\n            var prop;\n            prop = path.shift();\n\n            while (!isUndef(prop)) {\n                // #25\n                if (!isStr(prop) && !isSymbol(prop)) {\n                    prop = toStr(prop);\n                }\n\n                if (\n                    prop === '__proto__' ||\n                    prop === 'constructor' ||\n                    prop === 'prototype'\n                ) {\n                    return;\n                }\n\n                if (!obj[prop]) obj[prop] = {};\n                obj = obj[prop];\n                prop = path.shift();\n            }\n\n            obj[lastProp] = val;\n        };\n\n        return exports;\n    })({});\n\n    /* ------------------------------ startWith ------------------------------ */\n\n    var startWith = _.startWith = (function (exports) {\n        /* Check if string starts with the given target string.\n         *\n         * |Name  |Desc                             |\n         * |------|---------------------------------|\n         * |str   |String to search                 |\n         * |prefix|String prefix                    |\n         * |return|True if string starts with prefix|\n         */\n\n        /* example\n         * startWith('ab', 'a'); // -> true\n         */\n\n        /* typescript\n         * export declare function startWith(str: string, prefix: string): boolean;\n         */\n        exports = function(str, prefix) {\n            return str.indexOf(prefix) === 0;\n        };\n\n        return exports;\n    })({});\n\n    /* ------------------------------ type ------------------------------ */\n\n    var type = _.type = (function (exports) {\n        /* Determine the internal JavaScript [[Class]] of an object.\n         *\n         * |Name          |Desc             |\n         * |--------------|-----------------|\n         * |val           |Value to get type|\n         * |lowerCase=true|LowerCase result |\n         * |return        |Type of object   |\n         */\n\n        /* example\n         * type(5); // -> 'number'\n         * type({}); // -> 'object'\n         * type(function() {}); // -> 'function'\n         * type([]); // -> 'array'\n         * type([], false); // -> 'Array'\n         * type(async function() {}, false); // -> 'AsyncFunction'\n         */\n\n        /* typescript\n         * export declare function type(val: any, lowerCase?: boolean): string;\n         */\n\n        /* dependencies\n         * objToStr isNaN lowerCase isBuffer \n         */\n\n        exports = function(val) {\n            var lower =\n                arguments.length > 1 && arguments[1] !== undefined\n                    ? arguments[1]\n                    : true;\n            var ret;\n            if (val === null) ret = 'Null';\n            if (val === undefined) ret = 'Undefined';\n            if (isNaN(val)) ret = 'NaN';\n            if (isBuffer(val)) ret = 'Buffer';\n\n            if (!ret) {\n                ret = objToStr(val).match(regObj);\n                if (ret) ret = ret[1];\n            }\n\n            if (!ret) return '';\n            return lower ? lowerCase(ret) : ret;\n        };\n\n        var regObj = /^\\[object\\s+(.*?)]$/;\n\n        return exports;\n    })({});\n\n    /* ------------------------------ toSrc ------------------------------ */\n\n    var toSrc = _.toSrc = (function (exports) {\n        /* Convert function to its source code.\n         *\n         * |Name  |Desc               |\n         * |------|-------------------|\n         * |fn    |Function to convert|\n         * |return|Source code        |\n         */\n\n        /* example\n         * toSrc(Math.min); // -> 'function min() { [native code] }'\n         * toSrc(function() {}); // -> 'function () { }'\n         */\n\n        /* typescript\n         * export declare function toSrc(fn: types.AnyFn): string;\n         */\n\n        /* dependencies\n         * isNil types \n         */\n\n        exports = function(fn) {\n            if (isNil(fn)) return '';\n\n            try {\n                return fnToStr.call(fn);\n                /* eslint-disable no-empty */\n            } catch (e) {}\n\n            try {\n                return fn + '';\n                /* eslint-disable no-empty */\n            } catch (e) {}\n\n            return '';\n        };\n\n        var fnToStr = Function.prototype.toString;\n\n        return exports;\n    })({});\n\n    /* ------------------------------ stringifyAll ------------------------------ */\n    _.stringifyAll = (function (exports) {\n        /* Stringify object into json with types.\n         *\n         * |Name   |Desc               |\n         * |-------|-------------------|\n         * |obj    |Object to stringify|\n         * |options|Stringify options  |\n         * |return |Stringified object |\n         *\n         * Available options:\n         *\n         * |Name              |Desc                     |\n         * |------------------|-------------------------|\n         * |unenumerable=false|Include unenumerable keys|\n         * |symbol=false      |Include symbol keys      |\n         * |accessGetter=false|Access getter value      |\n         * |timeout=0         |Timeout of stringify     |\n         * |depth=0           |Max depth of recursion   |\n         * |ignore            |Values to ignore         |\n         *\n         * When time is out, all remaining values will all be \"Timeout\".\n         *\n         * ### parse\n         *\n         * Parse result string back to object.\n         *\n         * |Name  |Type           |\n         * |------|---------------|\n         * |obj   |String to parse|\n         * |return|Result object  |\n         */\n\n        /* example\n         * stringifyAll(function test() {}); // -> '{\"value\":\"function test() {}\",\"type\":\"Function\",...}'\n         */\n\n        /* typescript\n         * export declare namespace stringifyAll {\n         *     function parse(str: string): any;\n         * }\n         * export declare function stringifyAll(\n         *     obj: any,\n         *     options?: {\n         *         unenumerable?: boolean;\n         *         symbol?: boolean;\n         *         accessGetter?: boolean;\n         *         timeout?: number;\n         *         depth?: number;\n         *         ignore?: any[];\n         *     }\n         * ): string;\n         */\n\n        /* dependencies\n         * escapeJsStr type toStr endWith toSrc keys each Class getProto difference extend isPromise filter now allKeys contain isObj isMiniProgram create startWith safeSet defineProp pick isArrLike \n         */\n\n        exports = function(obj) {\n            var _ref =\n                    arguments.length > 1 && arguments[1] !== undefined\n                        ? arguments[1]\n                        : {},\n                self = _ref.self,\n                _ref$startTime = _ref.startTime,\n                startTime = _ref$startTime === void 0 ? now() : _ref$startTime,\n                _ref$timeout = _ref.timeout,\n                timeout = _ref$timeout === void 0 ? 0 : _ref$timeout,\n                _ref$depth = _ref.depth,\n                depth = _ref$depth === void 0 ? 0 : _ref$depth,\n                _ref$curDepth = _ref.curDepth,\n                curDepth = _ref$curDepth === void 0 ? 1 : _ref$curDepth,\n                _ref$visitor = _ref.visitor,\n                visitor = _ref$visitor === void 0 ? new Visitor() : _ref$visitor,\n                _ref$unenumerable = _ref.unenumerable,\n                unenumerable = _ref$unenumerable === void 0 ? false : _ref$unenumerable,\n                _ref$symbol = _ref.symbol,\n                symbol = _ref$symbol === void 0 ? false : _ref$symbol,\n                _ref$accessGetter = _ref.accessGetter,\n                accessGetter = _ref$accessGetter === void 0 ? false : _ref$accessGetter,\n                _ref$ignore = _ref.ignore,\n                ignore = _ref$ignore === void 0 ? [] : _ref$ignore;\n\n            var json = '';\n            var options = {\n                visitor: visitor,\n                unenumerable: unenumerable,\n                symbol: symbol,\n                accessGetter: accessGetter,\n                depth: depth,\n                curDepth: curDepth + 1,\n                timeout: timeout,\n                startTime: startTime,\n                ignore: ignore\n            };\n            var t = type(obj, false);\n\n            if (t === 'String') {\n                json = wrapStr(obj);\n            } else if (t === 'Number') {\n                json = toStr(obj);\n\n                if (endWith(json, 'Infinity')) {\n                    json = '{\"value\":\"'.concat(json, '\",\"type\":\"Number\"}');\n                }\n            } else if (t === 'NaN') {\n                json = '{\"value\":\"NaN\",\"type\":\"Number\"}';\n            } else if (t === 'Boolean') {\n                json = obj ? 'true' : 'false';\n            } else if (t === 'Null') {\n                json = 'null';\n            } else if (t === 'Undefined') {\n                json = '{\"type\":\"Undefined\"}';\n            } else if (t === 'Symbol') {\n                var val = 'Symbol';\n\n                try {\n                    val = toStr(obj);\n                    /* eslint-disable no-empty */\n                } catch (e) {}\n\n                json = '{\"value\":'.concat(wrapStr(val), ',\"type\":\"Symbol\"}');\n            } else {\n                if (timeout && now() - startTime > timeout) {\n                    return wrapStr('Timeout');\n                }\n\n                if (depth && curDepth > depth) {\n                    return wrapStr('{...}');\n                }\n\n                json = '{';\n                var parts = [];\n                var visitedObj = visitor.get(obj);\n                var id;\n\n                if (visitedObj) {\n                    id = visitedObj.id;\n                    parts.push('\"reference\":'.concat(id));\n                } else {\n                    id = visitor.set(obj);\n                    parts.push('\"id\":'.concat(id));\n                }\n\n                parts.push('\"type\":\"'.concat(t, '\"'));\n\n                if (endWith(t, 'Function')) {\n                    parts.push('\"value\":'.concat(wrapStr(toSrc(obj))));\n                } else if (t === 'RegExp') {\n                    parts.push('\"value\":'.concat(wrapStr(obj)));\n                }\n\n                if (!visitedObj) {\n                    var enumerableKeys = keys(obj);\n\n                    if (enumerableKeys.length) {\n                        parts.push(\n                            iterateObj(\n                                'enumerable',\n                                enumerableKeys,\n                                self || obj,\n                                options\n                            )\n                        );\n                    }\n\n                    if (unenumerable) {\n                        var unenumerableKeys = difference(\n                            allKeys(obj, {\n                                prototype: false,\n                                unenumerable: true\n                            }),\n                            enumerableKeys\n                        );\n\n                        if (unenumerableKeys.length) {\n                            parts.push(\n                                iterateObj(\n                                    'unenumerable',\n                                    unenumerableKeys,\n                                    self || obj,\n                                    options\n                                )\n                            );\n                        }\n                    }\n\n                    if (symbol) {\n                        var symbolKeys = filter(\n                            allKeys(obj, {\n                                prototype: false,\n                                symbol: true\n                            }),\n                            function(key) {\n                                return typeof key === 'symbol';\n                            }\n                        );\n\n                        if (symbolKeys.length) {\n                            parts.push(\n                                iterateObj('symbol', symbolKeys, self || obj, options)\n                            );\n                        }\n                    }\n\n                    var prototype = getProto(obj);\n\n                    if (prototype && !contain(ignore, prototype)) {\n                        var proto = '\"proto\":'.concat(\n                            exports(\n                                prototype,\n                                extend(options, {\n                                    self: self || obj\n                                })\n                            )\n                        );\n                        parts.push(proto);\n                    }\n                }\n\n                json += parts.join(',') + '}';\n            }\n\n            return json;\n        };\n\n        function iterateObj(name, keys, obj, options) {\n            var parts = [];\n            each(keys, function(key) {\n                var val;\n                var descriptor = Object.getOwnPropertyDescriptor(obj, key);\n                var hasGetter = descriptor && descriptor.get;\n                var hasSetter = descriptor && descriptor.set;\n\n                if (!options.accessGetter && hasGetter) {\n                    val = '(...)';\n                } else {\n                    try {\n                        val = obj[key];\n\n                        if (contain(options.ignore, val)) {\n                            return;\n                        }\n\n                        if (isPromise(val)) {\n                            val.catch(function() {});\n                        }\n                    } catch (e) {\n                        val = e.message;\n                    }\n                }\n\n                parts.push(''.concat(wrapKey(key), ':').concat(exports(val, options)));\n\n                if (hasGetter) {\n                    parts.push(\n                        ''\n                            .concat(wrapKey('get ' + toStr(key)), ':')\n                            .concat(exports(descriptor.get, options))\n                    );\n                }\n\n                if (hasSetter) {\n                    parts.push(\n                        ''\n                            .concat(wrapKey('set ' + toStr(key)), ':')\n                            .concat(exports(descriptor.set, options))\n                    );\n                }\n            });\n            return '\"'.concat(name, '\":{') + parts.join(',') + '}';\n        }\n\n        function wrapKey(key) {\n            return '\"'.concat(escapeJsonStr(key), '\"');\n        }\n\n        function wrapStr(str) {\n            return '\"'.concat(escapeJsonStr(toStr(str)), '\"');\n        }\n\n        function escapeJsonStr(str) {\n            return escapeJsStr(str)\n                .replace(/\\\\'/g, \"'\")\n                .replace(/\\t/g, '\\\\t');\n        }\n\n        var Visitor = Class({\n            initialize: function() {\n                this.id = 1;\n                this.visited = [];\n            },\n            set: function(val) {\n                var visited = this.visited,\n                    id = this.id;\n                var obj = {\n                    id: id,\n                    val: val\n                };\n                visited.push(obj);\n                this.id++;\n                return id;\n            },\n            get: function(val) {\n                var visited = this.visited;\n\n                for (var i = 0, len = visited.length; i < len; i++) {\n                    var obj = visited[i];\n                    if (val === obj.val) return obj;\n                }\n\n                return false;\n            }\n        });\n\n        exports.parse = function(str) {\n            var map = {};\n            var obj = parse(JSON.parse(str), {\n                map: map\n            });\n            correctReference(map);\n            return obj;\n        };\n\n        function correctReference(map) {\n            each(map, function(obj) {\n                var enumerableKeys = keys(obj);\n\n                for (var i = 0, len = enumerableKeys.length; i < len; i++) {\n                    var key = enumerableKeys[i];\n\n                    if (isObj(obj[key])) {\n                        var reference = obj[key].reference;\n\n                        if (reference && map[reference]) {\n                            obj[key] = map[reference];\n                        }\n                    }\n                }\n\n                var proto = getProto(obj);\n\n                if (proto && proto.reference) {\n                    if (map[proto.reference]) {\n                        Object.setPrototypeOf(obj, map[proto.reference]);\n                    }\n                }\n            });\n        }\n\n        function parse(obj, options) {\n            var map = options.map;\n\n            if (!isObj(obj)) {\n                return obj;\n            }\n\n            var id = obj.id,\n                type = obj.type,\n                value = obj.value,\n                proto = obj.proto,\n                reference = obj.reference;\n            var enumerable = obj.enumerable,\n                unenumerable = obj.unenumerable;\n\n            if (reference) {\n                return obj;\n            }\n\n            if (type === 'Number') {\n                if (value === 'Infinity') {\n                    return Number.POSITIVE_INFINITY;\n                } else if (value === '-Infinity') {\n                    return Number.NEGATIVE_INFINITY;\n                }\n\n                return NaN;\n            } else if (type === 'Undefined') {\n                return undefined;\n            }\n\n            var newObj;\n\n            if (type === 'Function') {\n                newObj = function() {};\n\n                newObj.toString = function() {\n                    return value;\n                };\n\n                if (proto) {\n                    Object.setPrototypeOf(newObj, parse(proto, options));\n                }\n            } else if (type === 'RegExp') {\n                newObj = strToRegExp(value);\n            } else {\n                if (type !== 'Object') {\n                    var Fn;\n\n                    if (!isMiniProgram) {\n                        Fn = new Function(type, '');\n                    } else {\n                        Fn = function() {};\n                    }\n\n                    if (proto) {\n                        Fn.prototype = parse(proto, options);\n                    }\n\n                    newObj = new Fn();\n                } else {\n                    if (proto) {\n                        newObj = create(parse(proto, options));\n                    } else {\n                        newObj = create(null);\n                    }\n                }\n            }\n\n            var defineProps = {};\n\n            if (enumerable) {\n                var len;\n\n                if (isArrLike(enumerable)) {\n                    len = enumerable.length;\n                    delete enumerable.length;\n                }\n\n                enumerable = pick(enumerable, function(value, key) {\n                    return !handleGetterSetter(enumerable, value, key);\n                });\n                each(enumerable, function(value, key) {\n                    var defineProp = defineProps[key] || {};\n\n                    if (!defineProp.get) {\n                        newObj[key] = parse(value, options);\n                    }\n                });\n\n                if (len) {\n                    newObj.length = len;\n                }\n            }\n\n            if (unenumerable) {\n                unenumerable = pick(unenumerable, function(value, key) {\n                    return !handleGetterSetter(unenumerable, value, key);\n                });\n                each(unenumerable, function(value, key) {\n                    var defineProp = defineProps[key] || {};\n\n                    if (!defineProp.get) {\n                        value = parse(value, options);\n\n                        if (isObj(value) && value.reference) {\n                            var _reference = value.reference;\n\n                            value = function() {\n                                return map[_reference];\n                            };\n\n                            defineProp.get = value;\n                        } else {\n                            defineProp.value = value;\n                        }\n                    }\n\n                    defineProp.enumerable = false;\n                    defineProps[key] = defineProp;\n                });\n            }\n\n            defineProp(newObj, defineProps);\n\n            function handleGetterSetter(obj, val, key) {\n                key = toStr(key);\n                var isGetterAndSetter = false;\n                each(['get', 'set'], function(type) {\n                    if (startWith(key, type + ' ')) {\n                        var realKey = key.replace(type + ' ', '');\n\n                        if (obj[realKey]) {\n                            val = parse(val, options);\n\n                            if (val === 'Timeout') {\n                                val = retTimeout;\n                            }\n\n                            safeSet(defineProps, [realKey, type], val);\n                            isGetterAndSetter = true;\n                        }\n                    }\n                });\n                return isGetterAndSetter;\n            }\n\n            map[id] = newObj;\n            return newObj;\n        }\n\n        function retTimeout() {\n            return 'Timeout';\n        }\n\n        function strToRegExp(str) {\n            var lastSlash = str.lastIndexOf('/');\n            return new RegExp(str.slice(1, lastSlash), str.slice(lastSlash + 1));\n        }\n\n        return exports;\n    })({});\n\n    /* ------------------------------ toEl ------------------------------ */\n    _.toEl = (function (exports) {\n        /* Convert html string to dom elements.\n         *\n         * There should be only one root element.\n         *\n         * |Name  |Desc        |\n         * |------|------------|\n         * |str   |Html string |\n         * |return|Html element|\n         */\n\n        /* example\n         * toEl('<div>test</div>');\n         */\n\n        /* typescript\n         * export declare function toEl(str: string): Element;\n         */\n        var doc = document;\n\n        exports = function(str) {\n            var fragment = doc.createElement('body');\n            fragment.innerHTML = str;\n            return fragment.childNodes[0];\n        };\n\n        if (doc.createRange && doc.body) {\n            var range = doc.createRange();\n            range.selectNode(doc.body);\n\n            if (range.createContextualFragment) {\n                exports = function(str) {\n                    return range.createContextualFragment(str).childNodes[0];\n                };\n            }\n        }\n\n        return exports;\n    })({});\n\n    return _;\n}));"
  }
]